# Dropdown

## Examples


### Dropdown

A Dropdown can contain any content. The content must constrain its own width.

```typescript
import React from 'react';

import Button from '@splunk/react-ui/Button';
import Dropdown from '@splunk/react-ui/Dropdown';


function Basic() {
    const toggle = <Button label="Open dropdown" />;

    return (
        <Dropdown toggle={toggle} retainFocus>
            <div style={{ padding: '20px', maxWidth: '300px' }}>
                This is the content of the dropdown. You can customize it as needed.
            </div>
        </Dropdown>
    );
}

export default Basic;
```



### Dropdown menu

```typescript
import React from 'react';

import Button from '@splunk/react-ui/Button';
import Dropdown from '@splunk/react-ui/Dropdown';
import Menu from '@splunk/react-ui/Menu';


function BasicMenu() {
    const toggle = <Button label="Direction" isMenu />;

    return (
        <Dropdown toggle={toggle}>
            <Menu style={{ width: 120 }}>
                <Menu.Item>Up</Menu.Item>
                <Menu.Item>Down</Menu.Item>
                <Menu.Item>Left</Menu.Item>
                <Menu.Item>Right</Menu.Item>
                <Menu.Divider />
                <Menu.Item to="n">North</Menu.Item>
                <Menu.Item to="s">South</Menu.Item>
                <Menu.Item to="w">West</Menu.Item>
                <Menu.Item to="e">East</Menu.Item>
            </Menu>
        </Dropdown>
    );
}

export default BasicMenu;
```



### Dropdown submenu

```typescript
import React from 'react';

import ChevronLeft from '@splunk/react-icons/ChevronLeft';
import EnterpriseChevronLeft from '@splunk/react-icons/enterprise/ChevronLeft';
import Button from '@splunk/react-ui/Button';
import Dropdown, { DropdownPossibleCloseReason } from '@splunk/react-ui/Dropdown';
import Menu from '@splunk/react-ui/Menu';
import SlidingPanels from '@splunk/react-ui/SlidingPanels';
import { useSplunkTheme } from '@splunk/themes';


function Submenu() {
    const [transition, setTransition] = React.useState<'forward' | 'backward' | undefined>(
        'forward'
    );
    const [activePanelId, setActivePanelId] = React.useState('one');

    const goBackward = () => {
        setActivePanelId(activePanelId === 'one' ? 'two' : 'one');
        setTransition('backward');
    };

    const goForward = () => {
        setActivePanelId(activePanelId === 'one' ? 'two' : 'one');
        setTransition('forward');
    };

    const toggle = <Button isMenu />;
    const closeReasons: DropdownPossibleCloseReason[] = [
        'clickAway',
        'escapeKey',
        'offScreen',
        'toggleClick',
    ];

    const { isEnterprise } = useSplunkTheme();

    return (
        <Dropdown closeReasons={closeReasons} toggle={toggle}>
            <SlidingPanels activePanelId={activePanelId} transition={transition}>
                <SlidingPanels.Panel key="one" panelId="one">
                    <Menu style={{ width: 160 }}>
                        <Menu.Item hasSubmenu onClick={goForward}>
                            Category 1
                        </Menu.Item>
                        <Menu.Item hasSubmenu onClick={goForward}>
                            Category 2
                        </Menu.Item>
                        <Menu.Item hasSubmenu onClick={goForward}>
                            Category 3
                        </Menu.Item>
                        <Menu.Item hasSubmenu onClick={goForward}>
                            Category 4
                        </Menu.Item>
                        <Menu.Item hasSubmenu onClick={goForward}>
                            Category 5
                        </Menu.Item>
                        <Menu.Item hasSubmenu onClick={goForward}>
                            Category 6
                        </Menu.Item>
                        <Menu.Item hasSubmenu onClick={goForward}>
                            Category 7
                        </Menu.Item>
                        <Menu.Item hasSubmenu onClick={goForward}>
                            Category 8
                        </Menu.Item>
                    </Menu>
                </SlidingPanels.Panel>
                <SlidingPanels.Panel key="two" panelId="two">
                    <Menu style={{ width: 160 }}>
                        <Menu.Item
                            startAdornment={
                                isEnterprise ? (
                                    <EnterpriseChevronLeft
                                        screenReaderText={null}
                                        hideDefaultTooltip
                                    />
                                ) : (
                                    <ChevronLeft />
                                )
                            }
                            onClick={goBackward}
                        >
                            Back
                        </Menu.Item>
                        <Menu.Divider />
                        <Menu.Item>Option 1</Menu.Item>
                        <Menu.Item>Option 2</Menu.Item>
                        <Menu.Item>Option 3</Menu.Item>
                        <Menu.Item>Option 4</Menu.Item>
                        <Menu.Item>Option 5</Menu.Item>
                        <Menu.Item>Option 6</Menu.Item>
                        <Menu.Item>Option 7</Menu.Item>
                    </Menu>
                </SlidingPanels.Panel>
            </SlidingPanels>
        </Dropdown>
    );
}

export default Submenu;
```



### Dropdown scrolling menu

If a Menu contains many items, you can let the Menu scroll by setting the overflowY and maxHeight style prop.

```typescript
import React from 'react';

import Button from '@splunk/react-ui/Button';
import Dropdown from '@splunk/react-ui/Dropdown';
import Menu from '@splunk/react-ui/Menu';


function ScrollingMenu() {
    const toggle = <Button label="Numbers" isMenu />;

    // Creates 100 menu items from 1 - 100
    const items: React.ReactElement[] = [];
    for (let index = 0; index < 100; index += 1) {
        items.push(<Menu.Item key={`menu-item-${index}`}>{index + 1}</Menu.Item>);
    }

    
    return (
        <Dropdown toggle={toggle}>
            {({ maxHeight }) => (
                <Menu
                    stopScrollPropagation
                    style={{
                        maxHeight: maxHeight ?? undefined,
                        overflowY: 'auto',
                    }}
                >
                    {items}
                </Menu>
            )}
        </Dropdown>
    );
}

export default ScrollingMenu;
```



### Dropdown dialog

By default, a Dropdown dialog box does not close on click. Set the closeReasons prop to ignore specific actions.

```typescript
import React from 'react';

import { without } from 'lodash';

import Button from '@splunk/react-ui/Button';
import ControlGroup from '@splunk/react-ui/ControlGroup';
import Dropdown from '@splunk/react-ui/Dropdown';
import Multiselect from '@splunk/react-ui/Multiselect';
import Select from '@splunk/react-ui/Select';
import Text from '@splunk/react-ui/Text';


function Dialog() {
    const toggle = <Button label="Open dropdown dialog" isMenu />;

    const closeReasons = without(Dropdown.possibleCloseReasons, 'contentClick');

    return (
        <Dropdown toggle={toggle} retainFocus closeReasons={closeReasons}>
            <div style={{ padding: 20, width: 300 }}>
                <ControlGroup label="First Name">
                    <Text />
                </ControlGroup>
                <ControlGroup
                    label="Last Name"
                    controlsLayout="fillJoin"
                    tooltip="More information..."
                >
                    <Text />
                </ControlGroup>
                <ControlGroup label="Office">
                    <Select defaultValue="sf">
                        <Select.Option label="San Francisco" value="sf" />
                        <Select.Option label="Cupertino" value="cu" />
                    </Select>
                </ControlGroup>
                <ControlGroup label="Office Multiselect">
                    <Multiselect defaultValue={['sf']}>
                        <Multiselect.Option label="San Francisco" value="sf" />
                        <Multiselect.Option label="Cupertino" value="cu" />
                    </Multiselect>
                </ControlGroup>
            </div>
        </Dropdown>
    );
}

export default Dialog;
```



### Toggle with tooltip

```typescript
import React from 'react';

import ArrowCounterClockwise from '@splunk/react-icons/ArrowCounterClockwise';
import Button, { ButtonPropsBase } from '@splunk/react-ui/Button';
import Dropdown from '@splunk/react-ui/Dropdown';
import Menu from '@splunk/react-ui/Menu';
import Tooltip from '@splunk/react-ui/Tooltip';

const TooltipButton = React.forwardRef<HTMLButtonElement, ButtonPropsBase>(
    ({ label, ...otherProps }, ref) => {
        return (
            <Tooltip content={label}>
                <Button {...otherProps} elementRef={ref} />
            </Tooltip>
        );
    }
);


function TooltipButtonToggle() {
    const tooltipButtonToggle = (
        <TooltipButton label="direction" icon={<ArrowCounterClockwise />} isMenu />
    );

    return (
        <Dropdown toggle={tooltipButtonToggle}>
            <Menu style={{ width: 120 }}>
                <Menu.Item>Up</Menu.Item>
                <Menu.Item>Down</Menu.Item>
                <Menu.Item>Left</Menu.Item>
                <Menu.Item>Right</Menu.Item>
            </Menu>
        </Dropdown>
    );
}

export default TooltipButtonToggle;
```



### Customized scroll container

By default, a Dropdown moves with the window scroll. Use Scroll Container Provider when you want a Dropdown to move within a different scroll container.

```typescript
import React, { useCallback, useState } from 'react';

import Button from '@splunk/react-ui/Button';
import Dropdown from '@splunk/react-ui/Dropdown';
import P from '@splunk/react-ui/Paragraph';
import { ScrollContainerProvider } from '@splunk/react-ui/ScrollContainerContext';


function ScrollContainer() {
    const [scrollContainer, setScrollContainer] = useState<HTMLDivElement>();
    const scrollContainerRef = useCallback((el: HTMLDivElement) => {
        setScrollContainer(el);
    }, []);

    const toggle = <Button label="Open dropdown in scroll container" />;
    return (
        <div
            ref={scrollContainerRef}
            style={{
                border: '1px dashed #ccc',
                height: '100px',
                width: '500px',
                margin: '20px 20px 0px 0px',
                padding: '30px 20px 10px 20px',
                overflow: 'scroll',
            }}
        >
            <ScrollContainerProvider value={scrollContainer}>
                <Dropdown retainFocus toggle={toggle}>
                    <P style={{ padding: 20, maxWidth: 300 }}>
                        This dropdown moves with the custom scroll container.
                    </P>
                </Dropdown>
                <P style={{ marginTop: 20 }}>More content to demonstrate scrolling.</P>
                <P>Additional content for demonstration.</P>
            </ScrollContainerProvider>
        </div>
    );
}

export default ScrollContainer;
```



### Controlled

By default, Dropdown handles its own state. If you want more granular control of the events that open or close a Dropdown, use it as a [controlled component](https://reactjs.org/docs/forms.html#controlled-components). When you use Dropdown as a controlled component, the parent component manages the state of the Dropdown. Note that either the Dropdown or its parent handles its state, never both. See the onRequestClose and onRequestOpen callbacks for hooks you can use to manage state. This examples uses toggleClick and offScreen. It receives but ignores all other close actions.

```typescript
import React, { useState } from 'react';

import Button from '@splunk/react-ui/Button';
import Dropdown, {
    DropdownPossibleCloseReason,
    DropdownRequestCloseHandler,
} from '@splunk/react-ui/Dropdown';
import Menu from '@splunk/react-ui/Menu';
import P from '@splunk/react-ui/Paragraph';


function ControlledDropdown() {
    const [open, setOpen] = useState(false);
    const [closeReason, setCloseReason] = useState<DropdownPossibleCloseReason>();

    const handleRequestClose: DropdownRequestCloseHandler = ({ reason }) => {
        setCloseReason(reason);
        setOpen(!(reason === 'toggleClick' || reason === 'offScreen'));
    };

    const handleRequestOpen = () => {
        setOpen(true);
    };

    const toggle = <Button label="Direction" isMenu />;

    return (
        <>
            <P>
                The last call to onRequestClose provided the reason:{' '}
                {closeReason ?? 'No close requests yet'}
            </P>
            <Dropdown
                open={open}
                onRequestClose={handleRequestClose}
                onRequestOpen={handleRequestOpen}
                toggle={toggle}
            >
                <Menu style={{ width: 120 }}>
                    <Menu.Item>Up</Menu.Item>
                    <Menu.Item>Down</Menu.Item>
                    <Menu.Item>Left</Menu.Item>
                    <Menu.Item>Right</Menu.Item>
                </Menu>
            </Dropdown>
        </>
    );
}

export default ControlledDropdown;
```




## API


### Dropdown API

#### Props

| Name | Type | Required | Default | Description |
|------|------|------|------|------|
| canCoverAnchor | boolean | no | true | Enables the `Dropdown` to be rendered over the toggle if there isn't enough room to render it in a direction. |
| children | React.ReactNode \| DropdownChildrenRenderer | no |  | The content of the `Dropdown`. If a function is provided, it is invoked with an object containing `anchorHeight`, `anchorWidth`, `maxHeight`, `maxWidth`, and `placement`, and is expected to return renderable content. |
| closeReasons | DropdownPossibleCloseReason[] | no | [     'clickAway',     'contentClick',     'escapeKey',     'offScreen',     'tabKey',     'toggleClick', ] | An array of reasons for which this `Dropdown` closes. |
| defaultPlacement | DropdownPlacement | no | 'below' | The default placement of the `Dropdown`. It might be rendered in a different direction depending on the space available and the `repositionMode`. |
| elementRef | React.Ref<HTMLDivElement> | no |  | A React ref which is set to the DOM element when the component mounts and null when it unmounts. |
| focusToggleReasons | DropdownPossibleCloseReason[] | no | [     'contentClick',     'escapeKey',     'tabKey',     'toggleClick', ] | An array of reasons for which to set focus on the toggle. Only the subset of `closeReasons` is honored. When `Menu.Items` open a Modal or other dialog, it might be necessary to remove the 'contentClick' reason to allow focus to be passed to the dialog. |
| inputId | string | no |  | An id for the input, which may be necessary for accessibility, such as for aria attributes. |
| onRequestClose | DropdownRequestCloseHandler | no |  | A callback function invoked with a data object containing the event, if applicable, and a reason for the close request. |
| onRequestOpen | DropdownRequestOpenHandler | no |  | A callback function invoked with a data object containing the event. |
| open | boolean | no |  | If an open prop is provided, this component behaves as a [controlled component](https://reactjs.org/docs/forms.html#controlled-components). The consumer is responsible for handling the open/close state. If no open prop is provided, the component handles the open/close state internally. |
| repositionMode | 'none' \| 'flip' \| 'any' | no | 'flip' | See `repositionMode` on `Popover` for details. |
| retainFocus | boolean | no | false | Keeps focus within the Popover while open. Only use this for inputs used in a form control. Do not use this when the Dropdown contains a Menu because Menu handles its own focus. |
| takeFocus | boolean | no | true | When true, the Popover automatically takes focus when 'open' changes to `true`. Disable this for a Popover that has shows on hover, such as a tooltip. |
| toggle | DropdownToggleElement | yes |  | A toggle, such as a button or equivalent component that accepts `ref`, must be passed. `aria-haspopup`, `aria-expanded`, and `aria-controls` attributes are applied to the toggle to support accessibility. The result of the `ref` placed on the toggle must be an instance of `HTMLElement`. Results that are instances of class components are not supported. Also see ["Forwarding Refs"](https://reactjs.org/docs/forwarding-refs.html). |

#### Types

| Name | Type | Description |
|------|------|------|
| DropdownChildrenRenderer | (data: {     anchorHeight: number \| null;     anchorWidth: number \| null;     maxHeight: number \| null;     maxWidth: number \| null;     placement: DropdownPlacementStatus \| null;     toggleId?: string; }) => React.ReactNode |  |
| DropdownPlacement | 'above' \| 'below' \| 'left' \| 'right' \| 'vertical' \| 'horizontal' |  |
| DropdownPlacementStatus | 'above' \| 'below' \| 'left' \| 'right' \| 'misaligned' |  |
| DropdownPossibleCloseReason | \| 'clickAway'     \| 'contentClick'     \| 'escapeKey'     \| 'offScreen'     \| 'tabKey'     \| 'toggleClick' |  |
| DropdownPossibleOpenReason | 'toggleClick' \| 'toggleKeydown' |  |
| DropdownRequestCloseHandler | (data: {     event?: React.MouseEvent<HTMLElement> \| MouseEvent \| KeyboardEvent \| TouchEvent;     reason: DropdownPossibleCloseReason; }) => void |  |
| DropdownRequestOpenHandler | (     event: React.MouseEvent<HTMLElement> \| React.KeyboardEvent<HTMLElement>,     data: { reason: DropdownPossibleOpenReason } ) => void |  |
| DropdownToggleElement | React.ReactElement<     React.HTMLAttributes<HTMLElement> & {         'data-test-popover-id'?: string;         'data-test'?: string;         ref?: React.Ref<HTMLElement \| null>;     } > |  |





