Default

Loading interactive example. Full source is available below.

View example source
Download source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { useId } from "@coreai-ds/react-components";
import { Combobox } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";
import * as React from 'react';

export const Default = () => {
    const comboId = useId('combo-default');
    const options = ['Cat', 'Dog', 'Ferret', 'Fish', 'Hamster', 'Snake'];
    const styles = /* css */ `
      .combobox-default-example {
        display: grid;
        grid-template-rows: repeat(1fr);
        justify-items: start;
        gap: var(--spacingVerticalXXS);
        max-width: 400px;
      }
    `;
    return (<>
        <style>{styles}</style>
        <div className="combobox-default-example">
          <label id={comboId}>Best pet</label>
          <Combobox aria-labelledby={comboId} placeholder="Select an animal">
            {options.map((option) => (<Option key={option} disabled={option === 'Ferret'}>
                {option}
              </Option>))}
          </Combobox>
        </div>
      </>);
};

export default Default;
API referenceGenerated CoreAI prop and type summaries

API

Generated CoreAI prop summaries, not replacement type declarations. Most native DOM/event and ARIA props are omitted. ? means optional; JSDoc shows defaults, deprecations, and conditional requirements. Defaults assume the standard CoreAI provider.

Combobox API

A Combobox component that extends Fluent UI's Combobox with an inline label.

interface ComboboxProps {
  /**
   * Controls the colors and borders of the combobox trigger.
   * @default 'outline'
   */
  appearance?: "outline" | "filled-darker" | "filled-lighter" | "underline";

  as?: "input";

  /**
   * The primary slot, `<input>`, does not support children so we need to explicitly include it here.
   */
  children?: ReactNode;

  className?: string;

  /**
   * If set, the combobox will show an icon to clear the current value.
   */
  clearable?: boolean;

  /**
   * The dropdown clear icon
   */
  clearIcon?: WithSlotShorthandValue<{ as?: "span"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ref?: ((instance: HTMLSpanElement | null) => void) | RefObject<HTMLSpanElement> | null; }, "children"> & { children?: any; }> | null | undefined;

  /**
   * The default open state when open is uncontrolled
   */
  defaultOpen?: boolean;

  /**
   * For an uncontrolled component, sets the initial selection.
   * If this is set, the `defaultValue` prop MUST also be set.
   */
  defaultSelectedOptions?: string[];

  /**
   * The default value displayed in the trigger input or button when the combobox's value is uncontrolled
   */
  defaultValue?: string | (readonly string[] & string);

  /**
   * Disable auto-focusing on the first item when mounting.
   * @default false
   */
  disableAutoFocus?: boolean;

  /**
   * Collapse the control to its content width when nothing is selected and
   * grow it with the typed/selected content. Only available when `label` is set.
   */
  dynamicWidth?: boolean;

  /**
   * The dropdown arrow icon
   */
  expandIcon?: WithSlotShorthandValue<{ as?: "span"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ref?: ((instance: HTMLSpanElement | null) => void) | RefObject<HTMLSpanElement> | null; }, "children"> & { children?: any; }> | null | undefined;

  freeform?: boolean;

  /**
   * Render the combobox's popup inline in the DOM.
   * This has accessibility benefits, particularly for touch screen readers.
   */
  inlinePopup?: boolean;

  /**
   * The primary slot, an input with role="combobox"
   */
  input?: ({ as?: "input"; } & Omit<Omit<DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref"> & { ref?: ((instance: HTMLInputElement | null) => void) | RefObject<HTMLInputElement> | null; }, "children"> & { children?: undefined; }) | undefined;

  /**
   * Optional inline label rendered inside the control, before the value.
   */
  label?: string;

  /**
   * The dropdown listbox slot
   */
  listbox?: WithSlotShorthandValue<Omit<ListboxSlots, "root"> & Omit<{ as?: "div"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & { ref?: ((instance: HTMLDivElement | null) => void) | RefObject<HTMLDivElement> | null; }, "children"> & { children?: any; }, "ref"> & SelectionProps & { disableAutoFocus?: boolean | undefined; } & RefAttributes<HTMLDivElement>> | null | undefined;

  /**
   * Where the portal children are mounted on DOM
   * @default a new element on document.body without any styling
   */
  mountNode?: HTMLElement | { element?: HTMLElement | null; className?: string; } | null | undefined;

  /**
   * Sets the selection type to multiselect.
   * Set this to true for multiselect, even if fully controlling selection state.
   * This enables styles and accessibility properties to be set.
   * @default false
   */
  multiselect?: boolean;

  onActiveOptionChange?: EventHandler<ActiveOptionChangeData>;

  /**
   * Callback when the open/closed state of the dropdown changes
   */
  onOpenChange?: ((e: ComboboxBaseOpenEvents, data: ComboboxBaseOpenChangeData) => void);

  /**
   * Callback when an option is selected
   */
  onOptionSelect?: ((event: SelectionEvents, data: OptionOnSelectData) => void);

  /**
   * Sets the open/closed state of the dropdown.
   * Use together with onOpenChange to fully control the dropdown's visibility
   */
  open?: boolean;

  /**
   * If set, the placeholder will show when no value is selected
   */
  placeholder?: string;

  /**
   * Configure the positioning of the combobox dropdown.
   * Please refer to the [positioning documentation](https://react.fluentui.dev/?path=/docs/concepts-developer-positioning-components--default#anchor-to-target)
   * for more details.
   */
  positioning?: PositioningShorthand;

  ref?: Ref<HTMLInputElement>;

  /**
   * The root combobox slot
   */
  root?: NonNullable<WithSlotShorthandValue<{ as?: "div"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & { ref?: ((instance: HTMLDivElement | null) => void) | RefObject<HTMLDivElement> | null; }, "children"> & { children?: any; }> | null> | undefined;

  /**
   * An array of selected option keys.
   * Use this with `onOptionSelect` to directly control the selected option(s)
   * If this is set, the `value` prop MUST also be controlled.
   */
  selectedOptions?: string[];

  /**
   * Controls the size of the combobox faceplate
   * @default 'medium'
   */
  size?: "small" | "medium" | "large";

  style?: CSSProperties;

  /**
   * Controls text overflow behavior for the value. When true, long text is truncated with an ellipsis.
   */
  truncateTextOverflow?: boolean;

  /**
   * The value displayed by the Combobox.
   * Use this with `onOptionSelect` to directly control the displayed value string
   */
  value?: string | (readonly string[] & string);
}

Listbox API

Listbox component: a standalone selection control, or the popup in a Combobox

interface ListboxProps {
  as?: "div";

  children?: any;

  className?: string;

  /**
   * For an uncontrolled component, sets the initial selection.
   * If this is set, the `defaultValue` prop MUST also be set.
   */
  defaultSelectedOptions?: string[];

  /**
   * Disable auto-focusing on the first item when mounting.
   * @default false
   */
  disableAutoFocus?: boolean;

  /**
   * Sets the selection type to multiselect.
   * Set this to true for multiselect, even if fully controlling selection state.
   * This enables styles and accessibility properties to be set.
   * @default false
   */
  multiselect?: boolean;

  /**
   * Callback when an option is selected
   */
  onOptionSelect?: ((event: SelectionEvents, data: OptionOnSelectData) => void);

  ref?: Ref<HTMLDivElement>;

  /**
   * An array of selected option keys.
   * Use this with `onOptionSelect` to directly control the selected option(s)
   * If this is set, the `value` prop MUST also be controlled.
   */
  selectedOptions?: string[];

  style?: CSSProperties;
}

Option API

Option component: a styled child option of a Combobox

interface OptionProps {
  as?: "div";

  /**
   * The check icon that is visible for selected options
   */
  checkIcon?: WithSlotShorthandValue<{ as?: "span"; } & Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ref?: ((instance: HTMLSpanElement | null) => void) | RefObject<HTMLSpanElement> | null; }, "children"> & { children?: any; }> | null | undefined;

  children?: any;

  className?: string;

  /**
   * Sets an option to the `disabled` state.
   * Disabled options cannot be selected, but are still keyboard navigable
   */
  disabled?: boolean;

  ref?: Ref<HTMLDivElement>;

  style?: CSSProperties;

  /**
   * An optional override the string value of the Option's display text,
   * defaulting to the Option's child content.
   * This is used as the Dropdown button's or Combobox input's value when the option is selected,
   * and as the comparison for type-to-find keyboard functionality.
   * The string value of the Option's display text when the Option's children are not a string.
   * This is used as the Dropdown button's or Combobox input's value when the option is selected,
   * and as the comparison for type-to-find keyboard functionality.
   */
  text?: string;

  /**
   * Defines a unique identifier for the option.
   * Use this to control selectedOptions, or to get the option value in the onOptionSelect callback.
   * Defaults to `text` if not provided.
   */
  value?: string;
}

Implementation

CoreAI 0.12.0; Fluent 9.74.9.

Setup and dependencies.

Import components from @coreai-ds/react-components. CoreAI guidance above takes precedence over API demonstration examples.

Active Option Change

Loading interactive example. Full source is available below.

Download source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { ComboboxProps } from "@coreai-ds/react-components";
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import * as React from 'react';
import { Combobox } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";

const useStyles = makeStyles({
    root: {
        // Stack the label above the field with a gap
        display: 'grid',
        gridTemplateRows: 'repeat(1fr)',
        justifyItems: 'start',
        gap: '2px',
        maxWidth: '400px',
    },
});

export const ActiveOptionChange = (props: Partial<Omit<ComboboxProps, "label" | "dynamicWidth">>): JSXElement => {
    const comboId = useId('combo-active-option-change');
    const options = ['Cat', 'Dog', 'Ferret', 'Fish', 'Hamster', 'Snake'];
    const styles = useStyles();
    const [activeOptionText, setActiveOptionText] = React.useState('');
    const onActiveOptionChange = React.useCallback<NonNullable<ComboboxProps['onActiveOptionChange']>>((_, data) => {
        if (data?.nextOption?.text) {
            setActiveOptionText(data?.nextOption?.text);
        }
    }, [setActiveOptionText]);
    const onMouseEnter = React.useCallback((e: React.MouseEvent<HTMLDivElement>) => {
        setActiveOptionText(`${e.currentTarget.textContent} (Mouse enter)`);
    }, [setActiveOptionText]);
    return (<div className={styles.root}>
      {activeOptionText}
      <label id={comboId}>Best pet</label>
      <Combobox aria-labelledby={comboId} placeholder="Select an animal" onActiveOptionChange={onActiveOptionChange} {...props}>
        {options.map(option => (<Option key={option} disabled={option === 'Ferret'} onMouseEnter={onMouseEnter}>
            {option}
          </Option>))}
      </Combobox>
    </div>);
};

export default ActiveOptionChange;

Appearance

Loading interactive example. Full source is available below.

Download source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { ComboboxProps } from "@coreai-ds/react-components";
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { tokens } from "@coreai-ds/react-components";
import { Combobox } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";
import * as React from 'react';

const useStyles = makeStyles({
    root: {
        // Stack the label above the field with a gap
        display: 'grid',
        gridTemplateRows: 'repeat(1fr)',
        justifyItems: 'start',
        gap: '20px',
        maxWidth: '400px',
        '> div': {
            display: 'grid',
            gridTemplateRows: 'repeat(1fr)',
            justifyItems: 'start',
            gap: '2px',
            // need padding to see the background color for filled variants
            padding: '5px 20px 10px',
        },
    },
    // filledLighter and filledDarker appearances depend on particular background colors
    filledLighter: {
        backgroundColor: tokens.colorNeutralBackgroundInverted,
        '> label': {
            color: tokens.colorNeutralForegroundInverted2,
        },
    },
    filledDarker: {
        backgroundColor: tokens.colorNeutralBackgroundInverted,
        '> label': {
            color: tokens.colorNeutralForegroundInverted2,
        },
    },
});

export const Appearance = (props: Partial<Omit<ComboboxProps, "label" | "dynamicWidth">>): JSXElement => {
    const comboId = useId('combobox');
    const styles = useStyles();
    return (<div className={styles.root}>
      <div>
        <label id={`${comboId}-outline`}>Outline</label>
        <Combobox aria-labelledby={`${comboId}-outline`} placeholder="Select a color" appearance="outline" {...props}>
          <Option>Red</Option>
          <Option>Green</Option>
          <Option>Blue</Option>
        </Combobox>
      </div>

      <div>
        <label id={`${comboId}-underline`}>Underline</label>
        <Combobox aria-labelledby={`${comboId}-underline`} placeholder="Select a color" appearance="underline" {...props}>
          <Option>Red</Option>
          <Option>Green</Option>
          <Option>Blue</Option>
        </Combobox>
      </div>

      <div className={styles.filledDarker}>
        <label id={`${comboId}-filledDarker`}>Filled Darker</label>
        <Combobox aria-labelledby={`${comboId}-filledDarker`} placeholder="Select a color" appearance="filled-darker" {...props}>
          <Option>Red</Option>
          <Option>Green</Option>
          <Option>Blue</Option>
        </Combobox>
      </div>

      <div className={styles.filledLighter}>
        <label id={`${comboId}-filledLighter`}>Filled Lighter</label>
        <Combobox aria-labelledby={`${comboId}-filledLighter`} placeholder="Select a color" appearance="filled-lighter" {...props}>
          <Option>Red</Option>
          <Option>Green</Option>
          <Option>Blue</Option>
        </Combobox>
      </div>
    </div>);
};

export default Appearance;

Clearable

Loading interactive example. Full source is available below.

Download source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { Label } from "@coreai-ds/react-components";
import { Combobox } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";
import * as React from 'react';

const useStyles = makeStyles({
    root: {
        display: 'grid',
        gridTemplateRows: 'auto auto',
        justifyItems: 'start',
        gap: '2px',
    },
});

export const Clearable = (): JSXElement => {
    const comboboxId = useId('combobox');
    const styles = useStyles();
    return (<div className={styles.root}>
      <Label id={comboboxId}>Pick a color</Label>
      <Combobox clearable aria-labelledby={comboboxId} placeholder="Select a color">
        <Option>Red</Option>
        <Option>Green</Option>
        <Option>Blue</Option>
      </Combobox>
    </div>);
};

export default Clearable;

Complex Options

Loading interactive example. Full source is available below.

Download source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { ComboboxProps } from "@coreai-ds/react-components";
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { Combobox } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";
import { Persona } from "@coreai-ds/react-components";
import * as React from 'react';

const useStyles = makeStyles({
    root: {
        // Stack the label above the field with a gap
        display: 'grid',
        gridTemplateRows: 'repeat(1fr)',
        justifyItems: 'start',
        gap: '2px',
        maxWidth: '400px',
    },
});

export const ComplexOptions = (props: Partial<Omit<ComboboxProps, "label" | "dynamicWidth">>): JSXElement => {
    const comboId = useId('combo-default');
    const styles = useStyles();
    return (<div className={styles.root}>
      <label id={comboId}>Schedule a meeting</label>
      <Combobox aria-labelledby={comboId} {...props}>
        <Option text="Katri Athokas">
          <Persona avatar={{ color: 'colorful', 'aria-hidden': true }} name="Katri Athokas" presence={{
            status: 'available',
        }} secondaryText="Available"/>
        </Option>
        <Option text="Elvia Atkins">
          <Persona avatar={{ color: 'colorful', 'aria-hidden': true }} name="Elvia Atkins" presence={{
            status: 'busy',
        }} secondaryText="Busy"/>
        </Option>
        <Option text="Cameron Evans">
          <Persona avatar={{ color: 'colorful', 'aria-hidden': true }} name="Cameron Evans" presence={{
            status: 'away',
        }} secondaryText="Away"/>
        </Option>
        <Option text="Wanda Howard">
          <Persona avatar={{ color: 'colorful', 'aria-hidden': true }} name="Wanda Howard" presence={{
            status: 'out-of-office',
        }} secondaryText="Out of office"/>
        </Option>
      </Combobox>
    </div>);
};

export default ComplexOptions;

Controlled

Loading interactive example. Full source is available below.

Download source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { ComboboxProps } from "@coreai-ds/react-components";
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import * as React from 'react';
import { Combobox } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";
import { Persona } from "@coreai-ds/react-components";
import type { PresenceBadgeStatus } from "@coreai-ds/react-components";

const useStyles = makeStyles({
    root: {
        // Stack the label above the field with a gap
        display: 'grid',
        justifyItems: 'start',
        gap: '20px',
        maxWidth: '400px',
    },
    field: {
        display: 'grid',
        justifyItems: 'start',
        gap: '2px',
    },
});

const optionData = [
    { text: 'Katri Athokas', value: 'kathok', presence: 'available', secondaryText: 'Available' },
    { text: 'Elvia Atkins', value: 'eatkins', presence: 'busy', secondaryText: 'Busy' },
    { text: 'Cameron Evans', value: 'cevans', presence: 'away', secondaryText: 'Away' },
    { text: 'Wanda Howard', value: 'whoward', presence: 'out-of-office', secondaryText: 'Out of office' },
];

export const Controlled = (props: Partial<Omit<ComboboxProps, "label" | "dynamicWidth">>): JSXElement => {
    const comboId = useId('combo-controlled');
    const styles = useStyles();
    const [selectedOptions, setSelectedOptions] = React.useState<string[]>(['eatkins']);
    const [value, setValue] = React.useState('Elvia Atkins');
    const [open, setOpen] = React.useState(false);
    const onOptionSelect: (typeof props)['onOptionSelect'] = (ev, data) => {
        setSelectedOptions(data.selectedOptions);
        setValue(data.optionText ?? '');
    };
    const onInput = (ev: React.ChangeEvent<HTMLInputElement>) => {
        setValue(ev.target.value);
    };
    // Reset the typed value when the Combobox closes to reflect the selected options
    React.useEffect(() => {
        if (!open) {
            setValue(selectedOptions.join(', '));
        }
    }, [open, selectedOptions]);
    return (<div className={styles.root}>
      <div className={styles.field}>
        <label htmlFor={`${comboId}-default`}>Schedule a meeting (default selection)</label>
        <Combobox id={`${comboId}-default`} {...props} defaultValue="Elvia Atkins" defaultSelectedOptions={['eatkins']}>
          {optionData.map(opt => (<Option key={opt.value} text={opt.text} value={opt.value}>
              <Persona avatar={{ color: 'colorful', 'aria-hidden': true }} name={opt.text} presence={{
                status: opt.presence as PresenceBadgeStatus,
            }} secondaryText={opt.secondaryText}/>
            </Option>))}
        </Combobox>
      </div>

      <div className={styles.field}>
        <label htmlFor={`${comboId}-controlled`}>Schedule a meeting (controlled selection)</label>
        <Combobox id={`${comboId}-controlled`} {...props} value={value} selectedOptions={selectedOptions} onInput={onInput} onOptionSelect={onOptionSelect} onOpenChange={(_, data) => setOpen(data.open)}>
          {optionData.map(opt => (<Option key={opt.value} text={opt.text} value={opt.value}>
              <Persona avatar={{ color: 'colorful', 'aria-hidden': true }} name={opt.text} presence={{
                status: opt.presence as PresenceBadgeStatus,
            }} secondaryText={opt.secondaryText}/>
            </Option>))}
        </Combobox>
      </div>
    </div>);
};

export default Controlled;

Controlling Open And Close

Loading interactive example. Full source is available below.

Download source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { ComboboxProps } from "@coreai-ds/react-components";
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import * as React from 'react';
import type { CheckboxProps } from "@coreai-ds/react-components";
import { Checkbox } from "@coreai-ds/react-components";
import { Combobox } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";

const useStyles = makeStyles({
    root: {
        // Stack the label above the field with a gap
        display: 'grid',
        gridTemplateRows: 'repeat(1fr)',
        justifyItems: 'start',
        gap: '2px',
        maxWidth: '400px',
    },
});

export const ControllingOpenAndClose = (props: Partial<Omit<ComboboxProps, "label" | "dynamicWidth">>): JSXElement => {
    const comboId = useId('combo-default');
    const options = ['Cat', 'Dog', 'Ferret', 'Fish', 'Hamster', 'Snake'];
    const styles = useStyles();
    const [open, setOpen] = React.useState(false);
    const handleOpenChange: ComboboxProps['onOpenChange'] = (e, data) => setOpen(data.open);
    const onChange: CheckboxProps['onChange'] = (e, data) => setOpen(!!data.checked);
    return (<div className={styles.root}>
      <Checkbox value="open" name="state" label="open" capture="user" checked={open} onChange={onChange}/>
      <label id={comboId}>Best pet</label>
      <Combobox aria-labelledby={comboId} placeholder="Select an animal" open={open} onOpenChange={handleOpenChange} {...props}>
        {options.map(option => (<Option key={option} disabled={option === 'Ferret'}>
            {option}
          </Option>))}
      </Combobox>
    </div>);
};

export default ControllingOpenAndClose;

Custom Options

Loading interactive example. Full source is available below.

Download source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { ComboboxProps } from "@coreai-ds/react-components";
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { Combobox } from "@coreai-ds/react-components";
import type { OptionGroupProps } from "@coreai-ds/react-components";
import { OptionGroup } from "@coreai-ds/react-components";
import type { OptionProps } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";
import { CheckmarkCircle20Filled } from "@fluentui/react-icons";
import * as React from 'react';

const useStyles = makeStyles({
    root: {
        // Stack the label above the field with a gap
        display: 'grid',
        gridTemplateRows: 'repeat(1fr)',
        justifyItems: 'start',
        gap: '2px',
        maxWidth: '400px',
    },
});

const CustomOption = (props: OptionProps) => {
    return <Option {...props} checkIcon={<CheckmarkCircle20Filled />}/>;
};

const CustomOptionGroup = (props: Partial<OptionGroupProps> & {
    options: string[];
}) => {
    const { label, options, ...optionGroupProps } = props;
    const labelSlot = typeof label === 'object' ? label : { children: label };
    return (<OptionGroup label={{ style: { fontStyle: 'italic' }, ...labelSlot }} {...optionGroupProps}>
      {options.map(option => (<CustomOption key={option} disabled={option === 'Ferret'}>
          {option}
        </CustomOption>))}
    </OptionGroup>);
};

export const CustomOptions = (props: Partial<Omit<ComboboxProps, "label" | "dynamicWidth">>): JSXElement => {
    const comboId = useId('combo-default');
    const land = ['Cat', 'Dog', 'Ferret', 'Hamster'];
    const water = ['Fish', 'Jellyfish', 'Octopus', 'Seal'];
    const styles = useStyles();
    return (<div className={styles.root}>
      <label id={comboId}>Best pet</label>
      <Combobox aria-labelledby={comboId} placeholder="Select an animal" {...props}>
        <CustomOptionGroup label="Land" options={land}/>
        <CustomOptionGroup label="Sea" options={water}/>
      </Combobox>
    </div>);
};

export default CustomOptions;

Disabled

Loading interactive example. Full source is available below.

Download source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { ComboboxProps } from "@coreai-ds/react-components";
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { Combobox } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";
import * as React from 'react';

const useStyles = makeStyles({
    root: {
        // Stack the label above the field with a gap
        display: 'grid',
        gridTemplateRows: 'repeat(1fr)',
        justifyItems: 'start',
        gap: '2px',
        maxWidth: '400px',
    },
});

export const Disabled = (props: Partial<Omit<ComboboxProps, "label" | "dynamicWidth">>): JSXElement => {
    const comboId = useId('combo-disabled');
    const options = ['Cat', 'Dog', 'Ferret', 'Fish', 'Hamster', 'Snake'];
    const styles = useStyles();
    return (<div className={styles.root}>
      <label id={comboId}>Best pet</label>
      <Combobox aria-labelledby={comboId} disabled placeholder="Select an animal" {...props}>
        {options.map(option => (<Option key={option}>{option}</Option>))}
      </Combobox>
    </div>);
};

export default Disabled;

Filtering

Loading interactive example. Full source is available below.

Download source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import * as React from 'react';
import { useComboboxFilter } from "@coreai-ds/react-components";
import type { ComboboxProps } from "@coreai-ds/react-components";
import { Combobox } from "@coreai-ds/react-components";

const useStyles = makeStyles({
    root: {
        // Stack the label above the field with a gap
        display: 'grid',
        gridTemplateRows: 'repeat(1fr)',
        justifyItems: 'start',
        gap: '2px',
        maxWidth: '400px',
    },
});

const options = [
    { children: 'Alligator', value: 'Alligator' },
    { children: 'Bee', value: 'Bee' },
    { children: 'Bird', value: 'Bird' },
    { children: 'Cheetah', disabled: true, value: 'Cheetah' },
    { children: 'Dog', value: 'Dog' },
    { children: 'Dolphin', value: 'Dolphin' },
    { children: 'Ferret', value: 'Ferret' },
    { children: 'Firefly', value: 'Firefly' },
    { children: 'Fish', value: 'Fish' },
    { children: 'Goat', value: 'Goat' },
    { children: 'Horse', value: 'Horse' },
    { children: 'Lion', value: 'Lion' },
];

export const Filtering = (): JSXElement => {
    const comboId = useId();
    const styles = useStyles();
    const [query, setQuery] = React.useState<string>('');
    const children = useComboboxFilter(query, options, {
        noOptionsMessage: 'No animals match your search.',
    });
    const onOptionSelect: ComboboxProps['onOptionSelect'] = (e, data) => {
        setQuery(data.optionText ?? '');
    };
    return (<div className={styles.root}>
      <label id={comboId}>Best pet</label>
      <Combobox onOptionSelect={onOptionSelect} aria-labelledby={comboId} placeholder="Select an animal" onChange={ev => setQuery(ev.target.value)} value={query}>
        {children}
      </Combobox>
    </div>);
};

export default Filtering;

Freeform

Loading interactive example. Full source is available below.

Download source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { ComboboxProps } from "@coreai-ds/react-components";
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import * as React from 'react';
import { makeStyles } from "@coreai-ds/react-components";
import { Combobox } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";

const useStyles = makeStyles({
    root: {
        // Stack the label above the field with a gap
        display: 'grid',
        gridTemplateRows: 'repeat(1fr)',
        justifyItems: 'start',
        gap: '2px',
        maxWidth: '400px',
    },
});

export const Freeform = (props: Partial<Omit<ComboboxProps, "label" | "dynamicWidth">>): JSXElement => {
    const comboId = useId('combo-default');
    const options = [
        'Cat',
        'Caterpillar',
        'Catfish',
        'Cheetah',
        'Chicken',
        'Cockatiel',
        'Cow',
        'Dog',
        'Dolphin',
        'Ferret',
        'Firefly',
        'Fish',
        'Fox',
        'Fox Terrier',
        'Frog',
        'Hamster',
        'Snake',
    ];
    const [matchingOptions, setMatchingOptions] = React.useState([...options]);
    const [customSearch, setCustomSearch] = React.useState<string | undefined>();
    const styles = useStyles();
    const onChange: ComboboxProps['onChange'] = event => {
        const value = event.target.value.trim();
        const matches = options.filter(option => option.toLowerCase().indexOf(value.toLowerCase()) === 0);
        setMatchingOptions(matches);
        if (value.length && matches.length < 1) {
            setCustomSearch(value);
        }
        else {
            setCustomSearch(undefined);
        }
    };
    const onOptionSelect: ComboboxProps['onOptionSelect'] = (event, data) => {
        const matchingOption = data.optionText && options.includes(data.optionText);
        if (matchingOption) {
            setCustomSearch(undefined);
        }
        else {
            setCustomSearch(data.optionText);
        }
    };
    return (<div className={styles.root}>
      <label id={comboId}>Find pets</label>
      <Combobox aria-labelledby={comboId} freeform placeholder="Select an animal" onChange={onChange} onOptionSelect={onOptionSelect} {...props}>
        {customSearch ? (<Option key="freeform" text={customSearch}>
            Search for "{customSearch}"
          </Option>) : null}
        {matchingOptions.map(option => (<Option key={option}>{option}</Option>))}
      </Combobox>
    </div>);
};

export default Freeform;

Grouped

Loading interactive example. Full source is available below.

Download source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { ComboboxProps } from "@coreai-ds/react-components";
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { Combobox } from "@coreai-ds/react-components";
import { OptionGroup } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";
import * as React from 'react';

const useStyles = makeStyles({
    root: {
        // Stack the label above the field with a gap
        display: 'grid',
        gridTemplateRows: 'repeat(1fr)',
        justifyItems: 'start',
        gap: '2px',
        maxWidth: '400px',
    },
});

export const Grouped = (props: Partial<Omit<ComboboxProps, "label" | "dynamicWidth">>): JSXElement => {
    const comboId = useId('combo-grouped');
    const land = ['Cat', 'Dog', 'Ferret', 'Hamster'];
    const water = ['Fish', 'Jellyfish', 'Octopus', 'Seal'];
    const styles = useStyles();
    return (<div className={styles.root}>
      <label id={comboId}>Best pet</label>
      <Combobox aria-labelledby={comboId} placeholder="Select an animal" {...props}>
        <OptionGroup label="Land">
          {land.map(option => (<Option key={option} disabled={option === 'Ferret'}>
              {option}
            </Option>))}
        </OptionGroup>
        <OptionGroup label="Sea">
          {water.map(option => (<Option key={option}>{option}</Option>))}
        </OptionGroup>
      </Combobox>
    </div>);
};

export default Grouped;

Inline Label

Loading interactive example. Full source is available below.

Download source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import * as React from 'react';
import { Combobox } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";

export const InlineLabel = () => {
    const statusOptions = ['Not started', 'In progress', 'Blocked', 'Completed'];
    const [status, setStatus] = React.useState<string>('');
    const styles = /* css */ `
      .combobox-inline-label-example {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        row-gap: var(--spacingVerticalL);
        max-width: 320px;
        padding: var(--spacingVerticalL);
      }

      .combobox-inline-label-example__field {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        row-gap: var(--spacingVerticalXS);
      }

      .combobox-inline-label-example__prop-label {
        font-family: var(--fontFamilyMonospace);
        font-size: var(--fontSizeBase200);
        color: var(--colorNeutralForeground3);
      }
    `;
    return (<>
        <style>{styles}</style>
        <div className="combobox-inline-label-example">
          <div className="combobox-inline-label-example__field">
            <code className="combobox-inline-label-example__prop-label">label</code>
            <Combobox label="Status" placeholder="Select a status" value={status} selectedOptions={status ? [status] : []} clearable onOptionSelect={(_event, data) => setStatus(data.optionText ?? '')}>
              {statusOptions.map((option) => (<Option key={option}>{option}</Option>))}
            </Combobox>
          </div>

          <div className="combobox-inline-label-example__field">
            <code className="combobox-inline-label-example__prop-label">label, dynamicWidth</code>
            <Combobox label="Status" dynamicWidth clearable onOptionSelect={() => undefined}>
              {statusOptions.map((option) => (<Option key={option}>{option}</Option>))}
            </Combobox>
          </div>
        </div>
      </>);
};

export default InlineLabel;

Multiselect

Loading interactive example. Full source is available below.

Download source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { ComboboxProps } from "@coreai-ds/react-components";
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import * as React from 'react';
import { makeStyles } from "@coreai-ds/react-components";
import { typographyStyles } from "@coreai-ds/react-components";
import { Combobox } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";

const useStyles = makeStyles({
    root: {
        // Stack the label above the field with a gap
        display: 'grid',
        gridTemplateRows: 'repeat(1fr)',
        justifyItems: 'start',
        gap: '2px',
        maxWidth: '400px',
    },
    description: {
        ...typographyStyles.caption1,
    },
});

export const Multiselect = (props: Partial<Omit<ComboboxProps, "label" | "dynamicWidth">>): JSXElement => {
    const comboId = useId('combo-multi');
    const selectedListId = `${comboId}-selection`;
    const [selectedOptions, setSelectedOptions] = React.useState<string[]>([]);
    const options = ['Cat', 'Dog', 'Ferret', 'Fish', 'Hamster', 'Snake'];
    const styles = useStyles();
    const onSelect: ComboboxProps['onOptionSelect'] = (event, data) => {
        setSelectedOptions(data.selectedOptions);
    };
    const labelledBy = selectedOptions.length > 0 ? `${comboId} ${selectedListId}` : comboId;
    return (<div className={styles.root}>
      <label id={comboId}>Best pets</label>
      <Combobox aria-labelledby={labelledBy} multiselect={true} placeholder="Select one or more animals" onOptionSelect={onSelect} {...props}>
        {options.map(option => (<Option key={option} disabled={option === 'Ferret'}>
            {option}
          </Option>))}
      </Combobox>
      {selectedOptions.length ? (<span id={selectedListId} className={styles.description}>
          Chosen pets: {selectedOptions.join(', ')}
        </span>) : null}
    </div>);
};

export default Multiselect;

Multiselect With Tags

Loading interactive example. Full source is available below.

Download source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { ComboboxProps } from "@coreai-ds/react-components";
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import * as React from 'react';
import { makeStyles } from "@coreai-ds/react-components";
import { tokens } from "@coreai-ds/react-components";
import { Button } from "@coreai-ds/react-components";
import { Dismiss12Regular } from "@fluentui/react-icons";
import { Combobox } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";

const useStyles = makeStyles({
    root: {
        // Stack the label above the field with a gap
        display: 'grid',
        gridTemplateRows: 'repeat(1fr)',
        justifyItems: 'start',
        gap: '2px',
        maxWidth: '400px',
    },
    tagsList: {
        listStyleType: 'none',
        marginBottom: tokens.spacingVerticalXXS,
        marginTop: 0,
        paddingLeft: 0,
        display: 'flex',
        gridGap: tokens.spacingHorizontalXXS,
    },
});

export const MultiselectWithTags = (props: Partial<Omit<ComboboxProps, "label" | "dynamicWidth">>): JSXElement => {
    // generate ids for handling labelling
    const comboId = useId('combo-multi');
    const selectedListId = `${comboId}-selection`;
    // refs for managing focus when removing tags
    const selectedListRef = React.useRef<HTMLUListElement>(null);
    const comboboxInputRef = React.useRef<HTMLInputElement>(null);
    const options = ['Cat', 'Dog', 'Ferret', 'Fish', 'Hamster', 'Snake'];
    const styles = useStyles();
    // Handle selectedOptions both when an option is selected or deselected in the Combobox,
    // and when an option is removed by clicking on a tag
    const [selectedOptions, setSelectedOptions] = React.useState<string[]>([]);
    const onSelect: ComboboxProps['onOptionSelect'] = (event, data) => {
        setSelectedOptions(data.selectedOptions);
    };
    const onTagClick = (option: string, index: number) => {
        // remove selected option
        setSelectedOptions(selectedOptions.filter(o => o !== option));
        // focus previous or next option, defaulting to focusing back to the combo input
        const indexToFocus = index === 0 ? 1 : index - 1;
        const optionToFocus = selectedListRef.current?.querySelector(`#${comboId}-remove-${indexToFocus}`);
        if (optionToFocus) {
            (optionToFocus as HTMLButtonElement).focus();
        }
        else {
            comboboxInputRef.current?.focus();
        }
    };
    const labelledBy = selectedOptions.length > 0 ? `${comboId} ${selectedListId}` : comboId;
    return (<div className={styles.root}>
      <label id={comboId}>Best pets</label>
      {selectedOptions.length ? (<ul id={selectedListId} className={styles.tagsList} ref={selectedListRef}>
          {/* The "Remove" span is used for naming the buttons without affecting the Combobox name */}
          <span id={`${comboId}-remove`} hidden>
            Remove
          </span>
          {selectedOptions.map((option, i) => (<li key={option}>
              <Button size="small" shape="circular" appearance="primary" icon={<Dismiss12Regular />} iconPosition="after" onClick={() => onTagClick(option, i)} id={`${comboId}-remove-${i}`} aria-labelledby={`${comboId}-remove ${comboId}-remove-${i}`}>
                {option}
              </Button>
            </li>))}
        </ul>) : null}
      <Combobox aria-labelledby={labelledBy} multiselect={true} placeholder="Select one or more animals" selectedOptions={selectedOptions} onOptionSelect={onSelect} ref={comboboxInputRef} {...props}>
        {options.map(option => (<Option key={option}>{option}</Option>))}
      </Combobox>
    </div>);
};

export default MultiselectWithTags;

Multiselect With Value String

Loading interactive example. Full source is available below.

Download source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { ComboboxProps } from "@coreai-ds/react-components";
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import * as React from 'react';
import { makeStyles } from "@coreai-ds/react-components";
import { Combobox } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";

const useStyles = makeStyles({
    root: {
        // Stack the label above the field with a gap
        display: 'grid',
        gridTemplateRows: 'repeat(1fr)',
        justifyItems: 'start',
        gap: '2px',
        maxWidth: '400px',
    },
});

export const MultiselectWithValueString = (props: Partial<Omit<ComboboxProps, "label" | "dynamicWidth">>): JSXElement => {
    const comboId = useId('combo-multi');
    const [selectedOptions, setSelectedOptions] = React.useState<string[]>([]);
    const [value, setValue] = React.useState('');
    const options = ['Cat', 'Dog', 'Ferret', 'Fish', 'Hamster', 'Snake'];
    const styles = useStyles();
    const onSelect: ComboboxProps['onOptionSelect'] = (event, data) => {
        // update selectedOptions
        setSelectedOptions(data.selectedOptions);
        // reset value to an empty string after selection
        setValue('');
    };
    // clear value on focus
    const onFocus = () => {
        setValue('');
    };
    // update value to selected options on blur
    const onBlur = () => {
        setValue(selectedOptions.join(', '));
    };
    // update value on input change
    const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
        setValue(event.target.value);
    };
    return (<div className={styles.root}>
      <label id={comboId}>Best pets</label>
      <Combobox aria-labelledby={comboId} multiselect={true} placeholder="Select one or more animals" value={value} onBlur={onBlur} onChange={onChange} onFocus={onFocus} onOptionSelect={onSelect} {...props}>
        {options.map(option => (<Option key={option}>{option}</Option>))}
      </Combobox>
    </div>);
};

export default MultiselectWithValueString;

Size

Loading interactive example. Full source is available below.

Download source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { ComboboxProps } from "@coreai-ds/react-components";
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { Combobox } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";
import * as React from 'react';

const useStyles = makeStyles({
    root: {
        // Stack the label above the field with a gap
        display: 'grid',
        gridTemplateRows: 'repeat(1fr)',
        justifyItems: 'start',
        gap: '20px',
        maxWidth: '400px',
        '> div': {
            display: 'grid',
            gridTemplateRows: 'repeat(1fr)',
            justifyItems: 'start',
            gap: '2px',
        },
    },
});

export const Size = (props: Partial<Omit<ComboboxProps, "label" | "dynamicWidth">>): JSXElement => {
    const comboId = useId('combobox');
    const styles = useStyles();
    return (<div className={styles.root}>
      <div>
        <label id={`${comboId}-small`}>Small</label>
        <Combobox aria-labelledby={`${comboId}-small`} placeholder="Select a color" size="small" {...props}>
          <Option>Red</Option>
          <Option>Green</Option>
          <Option>Blue</Option>
        </Combobox>
      </div>

      <div>
        <label htmlFor={`${comboId}-med`}>Medium</label>
        <Combobox aria-labelledby={`${comboId}-med`} placeholder="Select a color" size="medium" {...props}>
          <Option>Red</Option>
          <Option>Green</Option>
          <Option>Blue</Option>
        </Combobox>
      </div>

      <div>
        <label htmlFor={`${comboId}-large`}>Large</label>
        <Combobox aria-labelledby={`${comboId}-large`} placeholder="Select a color" size="large" {...props}>
          <Option>Red</Option>
          <Option>Green</Option>
          <Option>Blue</Option>
        </Combobox>
      </div>
    </div>);
};

export default Size;

Truncate

Loading interactive example. Full source is available below.

Download source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import * as React from 'react';
import { Combobox } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";

export const Truncate = () => {
    const longOptions = [
        'Awaiting stakeholder review and final sign-off',
        'Blocked by an upstream dependency in another team',
        'In progress — implementation roughly halfway complete',
        'Completed and deployed to the production environment',
    ];
    const [status, setStatus] = React.useState<string>(longOptions[0]);
    const styles = /* css */ `
      .combobox-truncate-example {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        row-gap: var(--spacingVerticalL);
        max-width: 320px;
        padding: var(--spacingVerticalL);
      }

      .combobox-truncate-example__field {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        row-gap: var(--spacingVerticalXS);
      }

      .combobox-truncate-example__prop-label {
        font-family: var(--fontFamilyMonospace);
        font-size: var(--fontSizeBase200);
        color: var(--colorNeutralForeground3);
      }

      .combobox-truncate-example__combobox {
        width: 220px;
      }
    `;
    return (<>
        <style>{styles}</style>
        <div className="combobox-truncate-example">
          <div className="combobox-truncate-example__field">
            <code className="combobox-truncate-example__prop-label">
              label, truncateTextOverflow
            </code>
            <Combobox className="combobox-truncate-example__combobox" label="Status" placeholder="Select a status" truncateTextOverflow value={status} selectedOptions={status ? [status] : []} clearable onOptionSelect={(_event, data) => setStatus(data.optionText ?? '')}>
              {longOptions.map((option) => (<Option key={option}>{option}</Option>))}
            </Combobox>
          </div>

          <div className="combobox-truncate-example__field">
            <code className="combobox-truncate-example__prop-label">truncateTextOverflow</code>
            <Combobox className="combobox-truncate-example__combobox" placeholder="Select a status" truncateTextOverflow value={status} selectedOptions={status ? [status] : []} clearable onOptionSelect={(_event, data) => setStatus(data.optionText ?? '')}>
              {longOptions.map((option) => (<Option key={option}>{option}</Option>))}
            </Combobox>
          </div>
        </div>
      </>);
};

export default Truncate;

Virtualizer

Loading interactive example. Full source is available below.

Download source
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { ComboboxProps } from "@coreai-ds/react-components";
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { useStaticVirtualizerMeasure } from "@fluentui/react-components/unstable";
import * as React from 'react';
import { makeStyles } from "@coreai-ds/react-components";
import { useMergedRefs } from "@coreai-ds/react-components";
import { useTimeout } from "@coreai-ds/react-components";
import { Combobox } from "@coreai-ds/react-components";
import { Virtualizer } from "@fluentui/react-components/unstable";
import { Option } from "@coreai-ds/react-components";

const useStyles = makeStyles({
    listbox: {
        // maxHeight will be applied only positioning autoSize set.
        maxHeight: '250px',
    },
    option: {
        height: '32px',
    },
});

export const ComboboxVirtualizer = (props: Partial<Omit<ComboboxProps, "label" | "dynamicWidth">>): JSXElement => {
    const comboId = useId('combobox');
    //This should include the item height (32px) and account for rowGap (2px)
    const itemHeight = 32;
    const rowGap = 2;
    const numberOfItems = 10000;
    const { virtualizerLength, bufferItems, bufferSize, scrollRef, containerSizeRef } = useStaticVirtualizerMeasure({
        defaultItemSize: itemHeight,
        direction: 'vertical',
        // We want at least 10 additional items on each side of visible items for page up/down (+ 1 buffer)
        bufferItems: 11,
        // We need to recalculate index when at least 10 items (+1px) from the bottom or top for page up/down
        bufferSize: itemHeight * 10 + 1,
    });
    const selectedIndex = React.useRef(0);
    const styles = useStyles();
    const mergedRefs = useMergedRefs(scrollRef);
    // Scroll timer required to post scrollTo on stack post-open state change
    const [setScrollTimer, clearScrollTimer] = useTimeout();
    return (<div>
      <div>
        <label htmlFor={`${comboId}`}>Medium</label>
        <Combobox id={`${comboId}`} placeholder="Select a number" positioning={{ autoSize: true }} listbox={{ ref: mergedRefs, className: styles.listbox }} onOpenChange={(e, data) => {
            clearScrollTimer();
            if (data.open) {
                setScrollTimer(() => {
                    mergedRefs.current?.scrollTo({ top: (itemHeight + rowGap) * selectedIndex.current });
                }, 0);
            }
        }} onOptionSelect={(e, data) => {
            if (data.optionValue) {
                selectedIndex.current = parseInt(data.optionValue, 10);
            }
        }}>
          <Virtualizer numItems={numberOfItems} virtualizerLength={virtualizerLength} bufferItems={bufferItems} bufferSize={bufferSize} itemSize={itemHeight} containerSizeRef={containerSizeRef} gap={rowGap}>
            {index => {
            return (<Option className={styles.option} aria-posinset={index} aria-setsize={numberOfItems} key={`item-${index}`} value={index.toString()}>
                  {`Item ${index + 1}`}
                </Option>);
        }}
          </Virtualizer>
        </Combobox>
      </div>
    </div>);
};

export default ComboboxVirtualizer;