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 { Dropdown } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";
import * as React from 'react';

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

export default Default;

Resources

Best practices

Do

  • Use placeholder text as an extra hint about the available options.
  • Use the text prop to define a string value for complex options, especially options containing JSX.
  • Set inlinePopup when possible for better VoiceOver support.

Don't

  • Use placeholder text as the dropdown label.
  • Repeat the same word or phrase at the beginning of every option.

Behavior and states

  • If placeholder text is not set, dropdowns show as empty until a selection is made.
  • Selecting a closed single-select field opens the list of options. Choosing an option closes the field.
  • For multi-select interactions, each list item is accompanied by a checkbox.
  • A multi-select list stays open until someone dismisses it by clicking off the list or pressing Esc.
  • Put Dropdown in a Field if you need a label, info button, and validation.
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.

Dropdown API

A Dropdown component that extends Fluent UI's Dropdown.

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

  as?: "button";

  /**
   * The primary slot, the element with role="combobox"
   */
  button?: NonNullable<WithSlotShorthandValue<{ as?: "button"; } & Omit<Omit<DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & { ref?: ((instance: HTMLButtonElement | null) => void) | RefObject<HTMLButtonElement> | null; }, "children"> & { children?: any; }> | null> | undefined;

  children?: any;

  className?: string;

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

  /**
   * The dropdown clear icon
   */
  clearButton?: WithSlotShorthandValue<{ as?: "button"; } & Omit<Omit<DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "ref"> & { ref?: ((instance: HTMLButtonElement | null) => void) | RefObject<HTMLButtonElement> | 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;

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

  /**
   * 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<HTMLButtonElement>;

  /**
   * The root dropdown 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 { DropdownProps } 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 { Dropdown } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";
import { Persona } 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<DropdownProps, "label" | "dynamicWidth">>): JSXElement => {
    const dropdownId = useId('dropdown');
    const styles = useStyles();
    const [activeOptionText, setActiveOptionText] = React.useState('');
    const onActiveOptionChange = React.useCallback<NonNullable<DropdownProps['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 htmlFor={dropdownId}>Schedule a meeting</label>
      <Dropdown id={dropdownId} onActiveOptionChange={onActiveOptionChange} {...props}>
        <Option text="Katri Athokas" onMouseEnter={onMouseEnter}>
          <Persona avatar={{ color: 'colorful', 'aria-hidden': true }} name="Katri Athokas" presence={{
            status: 'available',
        }} secondaryText="Available"/>
        </Option>
        <Option text="Elvia Atkins" onMouseEnter={onMouseEnter}>
          <Persona avatar={{ color: 'colorful', 'aria-hidden': true }} name="Elvia Atkins" presence={{
            status: 'busy',
        }} secondaryText="Busy"/>
        </Option>
        <Option text="Cameron Evans" onMouseEnter={onMouseEnter}>
          <Persona avatar={{ color: 'colorful', 'aria-hidden': true }} name="Cameron Evans" presence={{
            status: 'away',
        }} secondaryText="Away"/>
        </Option>
        <Option text="Wanda Howard" onMouseEnter={onMouseEnter}>
          <Persona avatar={{ color: 'colorful', 'aria-hidden': true }} name="Wanda Howard" presence={{
            status: 'out-of-office',
        }} secondaryText="Out of office"/>
        </Option>
      </Dropdown>
    </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 { DropdownProps } 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 { Dropdown } 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,
        },
        '> h3': {
            color: tokens.colorNeutralForegroundInverted2,
        },
    },
    filledDarker: {
        backgroundColor: tokens.colorNeutralBackgroundInverted,
        '> label': {
            color: tokens.colorNeutralForegroundInverted2,
        },
        '> h3': {
            color: tokens.colorNeutralForegroundInverted2,
        },
    },
});

export const Appearance = (props: Partial<Omit<DropdownProps, "label" | "dynamicWidth">>): JSXElement => {
    const dropdownId = useId('dropdown');
    const styles = useStyles();
    return (<div className={styles.root}>
      <div>
        <h3>Outline</h3>
        <label htmlFor={`${dropdownId}-outline`}>Select an animal</label>
        <Dropdown id={`${dropdownId}-outline`} placeholder="-" appearance="outline" {...props}>
          <Option>Cat</Option>
          <Option>Dog</Option>
          <Option>Bird</Option>
        </Dropdown>
      </div>

      <div>
        <h3>Underline</h3>
        <label htmlFor={`${dropdownId}-underline`}>Select an animal</label>
        <Dropdown id={`${dropdownId}-underline`} placeholder="-" appearance="underline" {...props}>
          <Option>Cat</Option>
          <Option>Dog</Option>
          <Option>Bird</Option>
        </Dropdown>
      </div>

      <div className={styles.filledDarker}>
        <h3>Filled Darker</h3>
        <label htmlFor={`${dropdownId}-filledDarker`}>Select an animal</label>
        <Dropdown id={`${dropdownId}-filledDarker`} placeholder="-" appearance="filled-darker" {...props}>
          <Option>Cat</Option>
          <Option>Dog</Option>
          <Option>Bird</Option>
        </Dropdown>
      </div>

      <div className={styles.filledLighter}>
        <h3>Filled Lighter</h3>
        <label htmlFor={`${dropdownId}-filledLighter`}>Select an animal</label>
        <Dropdown id={`${dropdownId}-filledLighter`} placeholder="-" appearance="filled-lighter" {...props}>
          <Option>Cat</Option>
          <Option>Dog</Option>
          <Option>Bird</Option>
        </Dropdown>
      </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 { Dropdown } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";
import * as React from 'react';

const useStyles = makeStyles({
    root: {
        display: 'grid',
        gridTemplateRows: 'repeat(1fr)',
        justifyItems: 'start',
        gap: '2px',
        maxWidth: '400px',
    },
});

export const Clearable = (): JSXElement => {
    const dropdownId = useId('');
    const styles = useStyles();
    return (<div className={styles.root}>
      <Label htmlFor={dropdownId}>Pick a color</Label>
      <Dropdown clearable id={dropdownId} placeholder="Select a color">
        <Option>Red</Option>
        <Option>Green</Option>
        <Option>Blue</Option>
      </Dropdown>
    </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 { DropdownProps } 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 { Dropdown } 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<DropdownProps, "label" | "dynamicWidth">>): JSXElement => {
    const dropdownId = useId('dropdown');
    const styles = useStyles();
    return (<div className={styles.root}>
      <label htmlFor={dropdownId}>Schedule a meeting</label>
      <Dropdown id={dropdownId} {...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>
      </Dropdown>
    </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 { DropdownProps } 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 { Dropdown } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";
import { Persona } 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',
    },
});

export const Controlled = (props: Partial<Omit<DropdownProps, "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 onOptionSelect: (typeof props)['onOptionSelect'] = (ev, data) => {
        setSelectedOptions(data.selectedOptions);
        setValue(data.optionText ?? '');
    };
    return (<div className={styles.root}>
      <div className={styles.field}>
        <label htmlFor={`${comboId}-default`}>Schedule a meeting (default selection)</label>
        <Dropdown id={`${comboId}-default`} {...props} defaultValue="Elvia Atkins" defaultSelectedOptions={['eatkins']}>
          <Option text="Katri Athokas" value="kathok">
            <Persona avatar={{ color: 'colorful', 'aria-hidden': true }} name="Katri Athokas" presence={{
            status: 'available',
        }} secondaryText="Available"/>
          </Option>
          <Option text="Elvia Atkins" value="eatkins">
            <Persona avatar={{ color: 'colorful', 'aria-hidden': true }} name="Elvia Atkins" presence={{
            status: 'busy',
        }} secondaryText="Busy"/>
          </Option>
          <Option text="Cameron Evans" value="cevans">
            <Persona avatar={{ color: 'colorful', 'aria-hidden': true }} name="Cameron Evans" presence={{
            status: 'away',
        }} secondaryText="Away"/>
          </Option>
          <Option text="Wanda Howard" value="whoward">
            <Persona avatar={{ color: 'colorful', 'aria-hidden': true }} name="Wanda Howard" presence={{
            status: 'out-of-office',
        }} secondaryText="Out of office"/>
          </Option>
        </Dropdown>
      </div>

      <div className={styles.field}>
        <label htmlFor={`${comboId}-controlled`}>Schedule a meeting (controlled selection)</label>
        <Dropdown id={`${comboId}-controlled`} {...props} value={value} selectedOptions={selectedOptions} onOptionSelect={onOptionSelect}>
          <Option text="Katri Athokas" value="kathok">
            <Persona avatar={{ color: 'colorful', 'aria-hidden': true }} name="Katri Athokas" presence={{
            status: 'available',
        }} secondaryText="Available"/>
          </Option>
          <Option text="Elvia Atkins" value="eatkins">
            <Persona avatar={{ color: 'colorful', 'aria-hidden': true }} name="Elvia Atkins" presence={{
            status: 'busy',
        }} secondaryText="Busy"/>
          </Option>
          <Option text="Cameron Evans" value="cevans">
            <Persona avatar={{ color: 'colorful', 'aria-hidden': true }} name="Cameron Evans" presence={{
            status: 'away',
        }} secondaryText="Away"/>
          </Option>
          <Option text="Wanda Howard" value="whoward">
            <Persona avatar={{ color: 'colorful', 'aria-hidden': true }} name="Wanda Howard" presence={{
            status: 'out-of-office',
        }} secondaryText="Out of office"/>
          </Option>
        </Dropdown>
      </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 { JSXElement } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import * as React from 'react';
import type { DropdownProps } from "@coreai-ds/react-components";
import type { CheckboxProps } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { Checkbox } from "@coreai-ds/react-components";
import { Dropdown } 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 = (): JSXElement => {
    const styles = useStyles();
    const [open, setOpen] = React.useState(false);
    const handleOpenChange: DropdownProps['onOpenChange'] = (e, data) => setOpen(data.open);
    const onChange: CheckboxProps['onChange'] = (e, data) => setOpen(!!data.checked);
    const dropdownId = useId('dropdown');
    const options = ['Cat', 'Caterpillar', 'Corgi', 'Chupacabra', 'Dog', 'Ferret', 'Fish', 'Fox', 'Hamster', 'Snake'];
    return (<div className={styles.root}>
      <Checkbox value="open" name="state" label="open" checked={open} onChange={onChange}/>
      <label htmlFor={dropdownId}>Best pet</label>
      <Dropdown id={dropdownId} placeholder="Select an animal" open={open} onOpenChange={handleOpenChange}>
        {options.map(option => (<Option key={option} disabled={option === 'Ferret'}>
            {option}
          </Option>))}
      </Dropdown>
    </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 { DropdownProps } from "@coreai-ds/react-components";
import type { JSXElement } from "@coreai-ds/react-components";
import { useId } from "@coreai-ds/react-components";
import { AnimalCat24Filled } from "@fluentui/react-icons";
import { AnimalDog24Filled } from "@fluentui/react-icons";
import { AnimalRabbit24Filled } from "@fluentui/react-icons";
import { AnimalTurtle24Filled } from "@fluentui/react-icons";
import { FoodFish24Filled } from "@fluentui/react-icons";
import { makeStyles } from "@coreai-ds/react-components";
import { Dropdown } 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 { CheckboxChecked24Regular } from "@fluentui/react-icons";
import * as React from 'react';

const animalIcons = {
    Cat: AnimalCat24Filled,
    Dog: AnimalDog24Filled,
    Rabbit: AnimalRabbit24Filled,
    Turtle: AnimalTurtle24Filled,
    Fish: FoodFish24Filled,
};

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

type CustomOptionProps = Partial<OptionProps> & {
    animal: keyof typeof animalIcons;
};

const useCustomOptionStyles = makeStyles({
    option: {
        display: 'flex',
        alignItems: 'center',
        gap: '5px',
    },
    icon: { flex: '0 0 auto' },
    text: { flex: '1 1 auto' },
});

const CustomOption = (props: CustomOptionProps) => {
    const { animal, ...optionProps } = props;
    const Icon = animalIcons[animal];
    const styles = useCustomOptionStyles();
    return (<Option text={animal} className={styles.option} checkIcon={<CheckboxChecked24Regular />} {...optionProps}>
      <Icon className={styles.icon}/>
      <span className={styles.text}>{animal}</span>
    </Option>);
};

const CustomOptionGroup = (props: Partial<OptionGroupProps> & {
    options: (keyof typeof animalIcons)[];
}) => {
    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} animal={option}/>))}
    </OptionGroup>);
};

export const CustomOptions = (props: Partial<Omit<DropdownProps, "label" | "dynamicWidth">>): JSXElement => {
    const dropdownId = useId('dropdown');
    const land = ['Cat', 'Dog', 'Rabbit'] as (keyof typeof animalIcons)[];
    const water = ['Fish', 'Turtle'] as (keyof typeof animalIcons)[];
    const styles = useStyles();
    return (<div className={styles.root}>
      <label htmlFor={dropdownId}>Best pet</label>
      <Dropdown id={dropdownId} listbox={{ className: styles.listbox }} placeholder="Select an animal" {...props}>
        <CustomOptionGroup label="Land" options={land}/>
        <CustomOptionGroup label="Sea" options={water}/>
      </Dropdown>
    </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 { DropdownProps } 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 { Dropdown } 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<DropdownProps, "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 htmlFor={comboId}>Best pet</label>
      <Dropdown id={comboId} disabled placeholder="Select an animal" {...props}>
        {options.map(option => (<Option key={option}>{option}</Option>))}
      </Dropdown>
    </div>);
};

export default Disabled;

Grouped

Loading interactive example. Full source is available below.

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

import type { DropdownProps } 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 { Dropdown } 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<DropdownProps, "label" | "dynamicWidth">>): JSXElement => {
    const dropdownId = useId('dropdown-grouped');
    const land = ['Cat', 'Dog', 'Ferret', 'Hamster'];
    const water = ['Fish', 'Jellyfish', 'Octopus', 'Seal'];
    const styles = useStyles();
    return (<div className={styles.root}>
      <label htmlFor={dropdownId}>Best pet</label>
      <Dropdown id={dropdownId} 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>
      </Dropdown>
    </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 { Dropdown } 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 [dynamicStatus, setDynamicStatus] = React.useState<string>('');
    const styles = /* css */ `
      .dropdown-inline-label-example {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        row-gap: var(--spacingVerticalL);
        max-width: 320px;
        padding: var(--spacingVerticalL);
      }

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

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

          <div className="dropdown-inline-label-example__field">
            <code className="dropdown-inline-label-example__prop-label">label, dynamicWidth</code>
            <Dropdown label="Status" dynamicWidth value={dynamicStatus} selectedOptions={dynamicStatus ? [dynamicStatus] : []} clearable onOptionSelect={(_event, data) => setDynamicStatus(data.optionValue ?? '')}>
              {statusOptions.map((option) => (<Option key={option}>{option}</Option>))}
            </Dropdown>
          </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 { DropdownProps } 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 { Dropdown } 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 Multiselect = (props: Partial<Omit<DropdownProps, "label" | "dynamicWidth">>): JSXElement => {
    const comboId = useId('combo-multi');
    const options = ['Cat', 'Dog', 'Ferret', 'Fish', 'Hamster', 'Snake'];
    const styles = useStyles();
    return (<div className={styles.root}>
      <label htmlFor={comboId}>Best pet</label>
      <Dropdown id={comboId} multiselect={true} placeholder="Select an animal" {...props}>
        {options.map(option => (<Option key={option} disabled={option === 'Ferret'}>
            {option}
          </Option>))}
      </Dropdown>
    </div>);
};

export default Multiselect;

Size

Loading interactive example. Full source is available below.

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

import type { DropdownProps } 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 { Dropdown } 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<DropdownProps, "label" | "dynamicWidth">>): JSXElement => {
    const comboId = useId('combobox');
    const styles = useStyles();
    return (<div className={styles.root}>
      <div>
        <h3>Small</h3>
        <label htmlFor={`${comboId}-small`}>Best pet</label>
        <Dropdown id={`${comboId}-small`} placeholder="Select an animal" size="small" {...props}>
          <Option>Cat</Option>
          <Option>Dog</Option>
          <Option>Bird</Option>
        </Dropdown>
      </div>

      <div>
        <h3>Medium</h3>
        <label htmlFor={`${comboId}-med`}>Best pet</label>
        <Dropdown id={`${comboId}-med`} placeholder="Select an animal" size="medium" {...props}>
          <Option>Cat</Option>
          <Option>Dog</Option>
          <Option>Bird</Option>
        </Dropdown>
      </div>

      <div>
        <h3>Large</h3>
        <label htmlFor={`${comboId}-large`}>Best pet</label>
        <Dropdown id={`${comboId}-large`} placeholder="Select an animal" size="large" {...props}>
          <Option>Cat</Option>
          <Option>Dog</Option>
          <Option>Bird</Option>
        </Dropdown>
      </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 { Dropdown } 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 */ `
      .dropdown-truncate-example {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        row-gap: var(--spacingVerticalL);
        max-width: 320px;
        padding: var(--spacingVerticalL);
      }

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

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

      .dropdown-truncate-example__dropdown--narrow {
        width: 220px;
      }

      .dropdown-truncate-example__dropdown--narrower {
        width: 160px;
      }
    `;
    return (<>
        <style>{styles}</style>
        <div className="dropdown-truncate-example">
          <div className="dropdown-truncate-example__field">
            <code className="dropdown-truncate-example__prop-label">truncateTextOverflow</code>
            <Dropdown className="dropdown-truncate-example__dropdown--narrow" aria-label="Status" placeholder="Select a status" truncateTextOverflow value={status} selectedOptions={status ? [status] : []} clearable onOptionSelect={(_event, data) => setStatus(data.optionValue ?? '')}>
              {longOptions.map((option) => (<Option key={option}>{option}</Option>))}
            </Dropdown>
          </div>

          <div className="dropdown-truncate-example__field">
            <code className="dropdown-truncate-example__prop-label">truncateTextOverflow</code>
            <Dropdown className="dropdown-truncate-example__dropdown--narrower" aria-label="Status" placeholder="Select a status" truncateTextOverflow value={status} selectedOptions={status ? [status] : []} clearable onOptionSelect={(_event, data) => setStatus(data.optionValue ?? '')}>
              {longOptions.map((option) => (<Option key={option}>{option}</Option>))}
            </Dropdown>
          </div>
        </div>
      </>);
};

export default Truncate;

Truncated Value

Loading interactive example. Full source is available below.

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

import type { DropdownProps } 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 { Dropdown } 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: '200px',
    },
    listbox: {
        maxHeight: '200px',
    },
    // these styles wrap the value text within the dropdown button and cause it to truncate
    truncatedText: {
        overflowX: 'hidden',
        textOverflow: 'ellipsis',
        whiteSpace: 'nowrap',
    },
    // these styles allow option text to break and wrap on long words, e.g. emails
    optionText: {
        overflow: 'hidden',
        overflowWrap: 'break-word',
    },
});

export const TruncatedValue = (props: Partial<Omit<DropdownProps, "label" | "dynamicWidth">>): JSXElement => {
    const dropdownId = useId('dropdown');
    const options = [
        'Cat',
        'Caterpillar',
        'Corgi',
        'Chupacabra',
        'Dog',
        'Ferret',
        'Fish',
        'Fox',
        'Hamster',
        'Snake',
        'SuperLongName_123456789_SomeMoreStuffToMakeItLonger@fluentui.dev',
        'Screaming hairy armadillo (Chaetophractus vellerosus)',
    ];
    const styles = useStyles();
    const placeholder = 'Select an animal';
    // show truncated option by default
    const defaultValue = options[11];
    const [value, setValue] = React.useState(defaultValue);
    return (<div className={styles.root}>
      <label htmlFor={dropdownId}>Best pet</label>
      <Dropdown id={dropdownId} listbox={{ className: styles.listbox }} button={<span className={styles.truncatedText}>{value}</span>} onOptionSelect={(e, data) => setValue(data.optionText ?? placeholder)} defaultSelectedOptions={[defaultValue]} defaultValue={defaultValue} {...props}>
        {options.map(option => (<Option key={option} text={option} disabled={option === 'Ferret'}>
            <span className={styles.optionText}>{option}</span>
          </Option>))}
      </Dropdown>
    </div>);
};

export default TruncatedValue;

With Field

Loading interactive example. Full source is available below.

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

import type { DropdownProps } from "@coreai-ds/react-components";
import type { JSXElement } from "@coreai-ds/react-components";
import { makeStyles } from "@coreai-ds/react-components";
import { Field } from "@coreai-ds/react-components";
import { Dropdown } from "@coreai-ds/react-components";
import { Option } from "@coreai-ds/react-components";
import * as React from 'react';

const useStyles = makeStyles({
    root: {
        maxWidth: '400px',
    },
});

export const WithField = (props: Partial<Omit<DropdownProps, "label" | "dynamicWidth">>): JSXElement => {
    const styles = useStyles();
    const options = ['Cat', 'Caterpillar', 'Corgi', 'Chupacabra', 'Dog', 'Ferret', 'Fish', 'Fox', 'Hamster', 'Snake'];
    return (<Field label="Best pet" required hint="Try picking 'Cat'" className={styles.root}>
      <Dropdown placeholder="Select an animal" {...props}>
        {options.map(option => (<Option key={option} disabled={option === 'Ferret'}>
            {option}
          </Option>))}
      </Dropdown>
    </Field>);
};

export default WithField;