diff --git a/CHANGELOG.md b/CHANGELOG.md index 378c078651..771211d278 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Features - Add `on` and `mouseLeaveDelay` props to `Popup` component @mnajdova ([#622](https://github.com/stardust-ui/react/pull/622)) +- Add Dropdown Single Selection variant @silviuavram ([#584](https://github.com/stardust-ui/react/pull/584)) ### Fixes - Fix unicode arrow characters to be RTL aware ([#690](https://github.com/stardust-ui/react/pull/690)) diff --git a/docs/src/examples/components/Dropdown/Types/DropdownExampleMultipleSearch.shorthand.tsx b/docs/src/examples/components/Dropdown/Types/DropdownExampleMultipleSearch.shorthand.tsx index 4dc983cd0c..6aba57a1d9 100644 --- a/docs/src/examples/components/Dropdown/Types/DropdownExampleMultipleSearch.shorthand.tsx +++ b/docs/src/examples/components/Dropdown/Types/DropdownExampleMultipleSearch.shorthand.tsx @@ -13,21 +13,17 @@ const inputItems = [ 'Selina Kyle', ] -class DropdownExample extends React.Component { - render() { - return ( - - ) - } -} +const DropdownExample = () => ( + +) const getA11ySelectionMessage = { onAdd: item => `${item} has been selected.`, diff --git a/docs/src/examples/components/Dropdown/Types/DropdownExampleSingleSelection.shorthand.tsx b/docs/src/examples/components/Dropdown/Types/DropdownExampleSingleSelection.shorthand.tsx new file mode 100644 index 0000000000..c4f046d982 --- /dev/null +++ b/docs/src/examples/components/Dropdown/Types/DropdownExampleSingleSelection.shorthand.tsx @@ -0,0 +1,48 @@ +import * as React from 'react' +import { Dropdown } from '@stardust-ui/react' + +const inputItems = [ + 'Bruce Wayne', + 'Natasha Romanoff', + 'Steven Strange', + 'Alfred Pennyworth', + `Scarlett O'Hara`, + 'Imperator Furiosa', + 'Bruce Banner', + 'Peter Parker', + 'Selina Kyle', +] + +const DropdownExample = () => ( + `${item} has been selected.`, + }} + placeholder="Select your hero" + items={inputItems} + /> +) + +const getA11yStatusMessage = ({ + isOpen, + itemToString, + previousResultCount, + resultCount, + selectedItem, +}) => { + if (!isOpen) { + return selectedItem ? itemToString(selectedItem) : '' + } + if (!resultCount) { + return 'No results are available.' + } + if (resultCount !== previousResultCount) { + return `${resultCount} result${ + resultCount === 1 ? ' is' : 's are' + } available, use up and down arrow keys to navigate. Press Enter key to select.` + } + return '' +} + +export default DropdownExample diff --git a/docs/src/examples/components/Dropdown/Types/index.tsx b/docs/src/examples/components/Dropdown/Types/index.tsx index 6a93a98f50..9b11169a48 100644 --- a/docs/src/examples/components/Dropdown/Types/index.tsx +++ b/docs/src/examples/components/Dropdown/Types/index.tsx @@ -4,10 +4,15 @@ import ExampleSection from 'docs/src/components/ComponentDoc/ExampleSection' const Types = () => ( + ) diff --git a/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchFluid.shorthand.tsx b/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchFluid.shorthand.tsx index 7dd60f245a..5d1de96419 100644 --- a/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchFluid.shorthand.tsx +++ b/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchFluid.shorthand.tsx @@ -12,23 +12,18 @@ const inputItems = [ 'Peter Parker', 'Selina Kyle', ] - -class DropdownExample extends React.Component { - render() { - return ( - - ) - } -} +const DropdownExample = () => ( + +) const getA11ySelectionMessage = { onAdd: item => `${item} has been selected.`, diff --git a/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchImageAndContent.shorthand.tsx b/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchImageAndContent.shorthand.tsx index ed52d80f56..248055b67a 100644 --- a/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchImageAndContent.shorthand.tsx +++ b/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchImageAndContent.shorthand.tsx @@ -49,21 +49,17 @@ const inputItems = [ }, ] -class DropdownExample extends React.Component { - render() { - return ( - - ) - } -} +const DropdownExample = () => ( + +) const getA11ySelectionMessage = { onAdd: item => `${item.header} has been selected.`, diff --git a/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchToggleButton.shorthand.tsx b/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchToggleButton.shorthand.tsx index 1f85c9cc1b..3fb6470421 100644 --- a/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchToggleButton.shorthand.tsx +++ b/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchToggleButton.shorthand.tsx @@ -13,22 +13,18 @@ const inputItems = [ 'Selina Kyle', ] -class DropdownExample extends React.Component { - render() { - return ( - - ) - } -} +const DropdownExample = () => ( + +) const getA11ySelectionMessage = { onAdd: item => `${item} has been selected.`, diff --git a/docs/src/examples/components/Dropdown/Variations/index.tsx b/docs/src/examples/components/Dropdown/Variations/index.tsx index cd3ff955f8..ca3ecfd32f 100644 --- a/docs/src/examples/components/Dropdown/Variations/index.tsx +++ b/docs/src/examples/components/Dropdown/Variations/index.tsx @@ -7,17 +7,17 @@ const Variations = () => ( ) diff --git a/src/components/Dropdown/Dropdown.tsx b/src/components/Dropdown/Dropdown.tsx index bb7aded10a..98b3183ffc 100644 --- a/src/components/Dropdown/Dropdown.tsx +++ b/src/components/Dropdown/Dropdown.tsx @@ -3,7 +3,11 @@ import * as PropTypes from 'prop-types' import * as _ from 'lodash' import { Extendable, ShorthandValue, ComponentEventHandler } from '../../../types/utils' -import { ComponentSlotStylesInput, ComponentVariablesInput } from '../../themes/types' +import { + ComponentSlotStylesInput, + ComponentVariablesInput, + ComponentSlotClasses, +} from '../../themes/types' import Downshift, { DownshiftState, StateChangeOptions, @@ -19,17 +23,17 @@ import { RenderResultConfig, customPropTypes, commonPropTypes, + handleRef, } from '../../lib' import keyboardKey from 'keyboard-key' import List from '../List/List' import Text from '../Text/Text' -import Icon from '../Icon/Icon' import Ref from '../Ref/Ref' import { UIComponentProps } from '../../lib/commonPropInterfaces' -import DropdownItem, { DropdownItemProps } from './DropdownItem' +import DropdownItem from './DropdownItem' import DropdownLabel, { DropdownLabelProps } from './DropdownLabel' -import DropdownSearchInput from './DropdownSearchInput' -import { DropdownSearchInputProps } from 'semantic-ui-react' +import DropdownSearchInput, { DropdownSearchInputProps } from './DropdownSearchInput' +import Button from '../Button/Button' // TODO: To be replaced when Downshift will add highlightedItem in their interface. export interface A11yStatusMessageOptions extends DownshiftA11yStatusMessageOptions { @@ -130,7 +134,9 @@ export default class Dropdown extends AutoControlledComponent< Extendable, DropdownState > { - private inputNode: HTMLElement + private buttonRef = React.createRef() + private inputRef = React.createRef() + private listRef = React.createRef() static displayName = 'Dropdown' @@ -210,12 +216,18 @@ export default class Dropdown extends AutoControlledComponent< { + if (changes.isOpen && !search) { + this.listRef.current.focus() + } + }} > {({ getInputProps, @@ -224,32 +236,44 @@ export default class Dropdown extends AutoControlledComponent< getRootProps, getToggleButtonProps, isOpen, + toggleMenu, highlightedIndex, selectItemAtIndex, }) => { + const { innerRef, ...accessibilityRootPropsRest } = getRootProps( + { refKey: 'innerRef' }, + { suppressRefError: true }, + ) return ( -
- {multiple && this.renderSelectedItems(styles)} - {search && - this.renderSearchInput( - getRootProps, - getInputProps, + +
+ {multiple && this.renderSelectedItems(styles)} + {search + ? this.renderSearchInput( + accessibilityRootPropsRest, + getInputProps, + highlightedIndex, + selectItemAtIndex, + variables, + ) + : this.renderTriggerButton(styles, getToggleButtonProps)} + {toggleButton && this.renderToggleButton(getToggleButtonProps, classes, isOpen)} + {this.renderItemsList( + styles, + variables, + isOpen, highlightedIndex, + toggleMenu, selectItemAtIndex, + getMenuProps, + getItemProps, + getInputProps, )} - {toggleButton && this.renderToggleButton(getToggleButtonProps, styles, isOpen)} - {this.renderItemsList( - styles, - variables, - getMenuProps, - getItemProps, - isOpen, - highlightedIndex, - )} -
+
+ ) }}
@@ -257,8 +281,36 @@ export default class Dropdown extends AutoControlledComponent< ) } + private renderTriggerButton( + styles: ComponentSlotStylesInput, + getToggleButtonProps: (options?: GetToggleButtonPropsOptions) => any, + ): JSX.Element { + const { placeholder, itemToString, multiple } = this.props + const { value } = this.state + const content = value && !multiple ? itemToString(value) : placeholder + + return ( + +