-
Notifications
You must be signed in to change notification settings - Fork 52
feat(Dropdown): add clearable
prop
#885
Changes from 1 commit
1cc2977
afa62f3
2f7bae2
8acec0f
346493d
b8c5aa6
30967d0
3a604a8
1c54f09
230544e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { Dropdown } from '@stardust-ui/react' | ||
|
||
const selectors = { | ||
clearIndicator: `.${Dropdown.slotClassNames.clearIndicator}`, | ||
triggerButton: `.${Dropdown.slotClassNames.triggerButton}`, | ||
item: (itemIndex: number) => `.${Dropdown.slotClassNames.itemsList} li:nth-child(${itemIndex})`, | ||
} | ||
|
||
const steps = [ | ||
steps => steps.click(selectors.triggerButton).snapshot('Shows list'), | ||
steps => steps.click(selectors.item(3)).snapshot('Selects an item'), | ||
steps => steps.click(selectors.clearIndicator).snapshot('Clears the value'), | ||
steps => steps.click(selectors.triggerButton).snapshot('Closes the list'), | ||
] | ||
|
||
export default steps |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { Dropdown } from '@stardust-ui/react' | ||
import * as React from 'react' | ||
|
||
const inputItems = [ | ||
'Bruce Wayne', | ||
'Natasha Romanoff', | ||
'Steven Strange', | ||
'Alfred Pennyworth', | ||
`Scarlett O'Hara`, | ||
'Imperator Furiosa', | ||
'Bruce Banner', | ||
'Peter Parker', | ||
'Selina Kyle', | ||
] | ||
|
||
const DropdownClearableExample = () => ( | ||
<Dropdown clearable items={inputItems} placeholder="Select your hero" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we should add additional example for showing customization of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we will have request for it, we can introduce it. For now, I am not sure that we need to add example for an every slot, it can make docs unusable ⛸ |
||
) | ||
|
||
export default DropdownClearableExample |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,9 +38,11 @@ import DropdownSearchInput, { DropdownSearchInputProps } from './DropdownSearchI | |
import Button from '../Button/Button' | ||
import { screenReaderContainerStyles } from '../../lib/accessibility/Styles/accessibilityStyles' | ||
import ListItem from '../List/ListItem' | ||
import Icon from '../Icon/Icon' | ||
|
||
export interface DropdownSlotClassNames { | ||
container: string | ||
clearIndicator: string | ||
triggerButton: string | ||
itemsList: string | ||
selectedItems: string | ||
|
@@ -50,6 +52,12 @@ export interface DropdownProps extends UIComponentProps<DropdownProps, DropdownS | |
/** The index of the currently active selected item, if dropdown has a multiple selection. */ | ||
activeSelectedIndex?: number | ||
|
||
/** A dropdown can be clearable and let users remove their selection. */ | ||
clearable?: boolean | ||
|
||
/** A slot for a clearing indicator. */ | ||
clearIndicator?: ShorthandValue | ||
|
||
/** The initial value for the index of the currently active selected item, in a multiple selection. */ | ||
defaultActiveSelectedIndex?: number | ||
|
||
|
@@ -192,6 +200,8 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo | |
content: false, | ||
}), | ||
activeSelectedIndex: PropTypes.number, | ||
clearable: PropTypes.bool, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like that we have two new props in the API, but I don't have any better proposal for now... |
||
clearIndicator: customPropTypes.itemShorthand, | ||
defaultActiveSelectedIndex: PropTypes.number, | ||
defaultSearchQuery: PropTypes.string, | ||
defaultValue: PropTypes.oneOfType([ | ||
|
@@ -226,6 +236,7 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo | |
|
||
static defaultProps: DropdownProps = { | ||
as: 'div', | ||
clearIndicator: 'close', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please, don't assume that some icon exists, as in some theme they may not. That's the reason we introduced the Indicator component, instead of using the chevron icons inside the components. Can we use here some unicode char by default if no icon i provided? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am aware that the Input does the same thing, agreed to tackle this in separate PR. This brings me back to the fact that maybe we should have some icons in the base theme, at least for the things we need in the components (close, arrows etc..) Let's create separate issue for this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Opened #896. |
||
itemToString: item => { | ||
if (!item || React.isValidElement(item)) { | ||
return '' | ||
|
@@ -263,8 +274,16 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo | |
unhandledProps, | ||
rtl, | ||
}: RenderResultConfig<DropdownProps>) { | ||
const { search, multiple, getA11yStatusMessage, itemToString, toggleIndicator } = this.props | ||
const { defaultHighlightedIndex, searchQuery } = this.state | ||
const { | ||
clearable, | ||
clearIndicator, | ||
search, | ||
multiple, | ||
getA11yStatusMessage, | ||
itemToString, | ||
toggleIndicator, | ||
} = this.props | ||
const { defaultHighlightedIndex, searchQuery, value } = this.state | ||
|
||
return ( | ||
<ElementType className={classes.root} {...unhandledProps}> | ||
|
@@ -293,6 +312,8 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo | |
{ refKey: 'innerRef' }, | ||
{ suppressRefError: true }, | ||
) | ||
const showClearIndicator = clearable && !this.isValueEmpty(value) | ||
|
||
return ( | ||
<Ref innerRef={innerRef}> | ||
<div | ||
|
@@ -315,13 +336,22 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo | |
) | ||
: this.renderTriggerButton(styles, rtl, getToggleButtonProps)} | ||
</div> | ||
{Indicator.create(toggleIndicator, { | ||
defaultProps: { | ||
direction: isOpen ? 'top' : 'bottom', | ||
onClick: getToggleButtonProps().onClick, | ||
styles: styles.toggleIndicator, | ||
}, | ||
})} | ||
{showClearIndicator | ||
? Icon.create(clearIndicator, { | ||
defaultProps: { | ||
className: Dropdown.slotClassNames.clearIndicator, | ||
onClick: this.handleClear, | ||
layershifter marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
styles: styles.clearIndicator, | ||
xSpacing: 'none', | ||
}, | ||
}) | ||
: Indicator.create(toggleIndicator, { | ||
defaultProps: { | ||
direction: isOpen ? 'top' : 'bottom', | ||
onClick: getToggleButtonProps().onClick, | ||
styles: styles.toggleIndicator, | ||
}, | ||
})} | ||
{this.renderItemsList( | ||
styles, | ||
variables, | ||
|
@@ -748,6 +778,12 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo | |
} | ||
} | ||
|
||
private handleClear = () => { | ||
const { multiple } = this.props | ||
|
||
this.setState({ value: multiple ? [] : '' }) | ||
layershifter marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
} | ||
|
||
private handleContainerClick = () => { | ||
this.tryFocusSearchInput() | ||
} | ||
|
@@ -932,9 +968,8 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo | |
*/ | ||
private getSelectedItemAsString = (value: ShorthandValue): string => { | ||
const { itemToString, multiple, placeholder } = this.props | ||
const isValueEmpty = _.isArray(value) ? value.length < 1 : !value | ||
|
||
if (isValueEmpty) { | ||
if (this.isValueEmpty(value)) { | ||
return placeholder | ||
} | ||
|
||
|
@@ -944,10 +979,15 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo | |
|
||
return itemToString(value) | ||
} | ||
|
||
private isValueEmpty = (value: ShorthandValue | ShorthandValue[]) => { | ||
bmdalex marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
layershifter marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
return _.isArray(value) ? value.length < 1 : !value | ||
} | ||
} | ||
|
||
Dropdown.slotClassNames = { | ||
container: `${Dropdown.className}__container`, | ||
clearIndicator: `${Dropdown.className}__clear-indicator`, | ||
triggerButton: `${Dropdown.className}__trigger-button`, | ||
itemsList: `${Dropdown.className}__items-list`, | ||
selectedItems: `${Dropdown.className}__selected-items`, | ||
|
Uh oh!
There was an error while loading. Please reload this page.