Skip to content
This repository was archived by the owner on Mar 4, 2020. It is now read-only.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Edit `buttonBehavior` Adding aria-disabled when button has loading state @kolaps33 ([#1789](https://github.com/stardust-ui/react/pull/1789))
- Added `audio-off` and `clipboard-copied-to` icon to Teams theme @bcalvery ([#1792](https://github.com/stardust-ui/react/pull/1792))
- Export `FocusTrapZoneProps` and `AutoFocusZoneProps` from the main package @sophieH29 ([#1795](https://github.com/stardust-ui/react/pull/1795))
- Add `checkable` and `checkableIndicator` to the `Dropdown` and `DropdownItem` components @mnajdova ([#1738](https://github.com/stardust-ui/react/pull/1738))

### Documentation
- Restore docs for `Ref` component @layershifter ([#1777](https://github.com/stardust-ui/react/pull/1777))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const DropdownExample = () => (
<Dropdown
items={inputItems}
placeholder="Select your hero"
checkable
getA11ySelectionMessage={{ onAdd: item => `${item} has been selected.` }}
/>
)
Expand Down
52 changes: 39 additions & 13 deletions packages/react/src/components/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ export interface DropdownProps
/** The index of the currently active selected item, if dropdown has a multiple selection. */
activeSelectedIndex?: number

/** Item can show check indicator if selected. */
checkable?: boolean

/** A slot for a selected indicator in the dropdown list. */
checkableIndicator?: ShorthandValue<IconProps>

/** A dropdown can be clearable and let users remove their selection. */
clearable?: boolean

Expand Down Expand Up @@ -250,6 +256,8 @@ class Dropdown extends AutoControlledComponent<WithAsProp<DropdownProps>, Dropdo
}),
activeSelectedIndex: PropTypes.number,
align: PropTypes.oneOf(ALIGNMENTS),
checkable: PropTypes.bool,
checkableIndicator: customPropTypes.itemShorthandWithoutJSX,
clearable: PropTypes.bool,
clearIndicator: customPropTypes.itemShorthand,
defaultActiveSelectedIndex: PropTypes.number,
Expand Down Expand Up @@ -285,7 +293,7 @@ class Dropdown extends AutoControlledComponent<WithAsProp<DropdownProps>, Dropdo
search: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
searchQuery: PropTypes.string,
searchInput: customPropTypes.itemShorthand,
toggleIndicator: customPropTypes.itemShorthand,
toggleIndicator: customPropTypes.itemShorthandWithoutJSX,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed prop type

triggerButton: customPropTypes.itemShorthand,
unstable_pinned: PropTypes.bool,
value: PropTypes.oneOfType([
Expand All @@ -297,6 +305,7 @@ class Dropdown extends AutoControlledComponent<WithAsProp<DropdownProps>, Dropdo
static defaultProps = {
align: 'start',
as: 'div',
checkableIndicator: 'stardust-checkmark',
clearIndicator: 'stardust-close',
itemToString: item => {
if (!item || React.isValidElement(item)) {
Expand Down Expand Up @@ -694,24 +703,34 @@ class Dropdown extends AutoControlledComponent<WithAsProp<DropdownProps>, Dropdo
highlightedIndex: number,
value: ShorthandValue<DropdownItemProps> | ShorthandCollection<DropdownItemProps>,
) {
const { loading, loadingMessage, noResultsMessage, renderItem } = this.props
const {
loading,
loadingMessage,
noResultsMessage,
renderItem,
checkable,
checkableIndicator,
} = this.props
const { filteredItems } = this.state

const items = _.map(filteredItems, (item, index) => render =>
render(item, () => {
const selected = !this.props.multiple && value === item
return DropdownItem.create(item, {
defaultProps: {
className: Dropdown.slotClassNames.item,
active: highlightedIndex === index,
selected: !this.props.multiple && value === item,
selected,
checkable,
checkableIndicator,
isFromKeyboard: this.state.itemIsFromKeyboard,
variables,
...(typeof item === 'object' &&
!item.hasOwnProperty('key') && {
key: (item as any).header,
}),
},
overrideProps: this.handleItemOverrides(item, index, getItemProps),
overrideProps: this.handleItemOverrides(item, index, getItemProps, selected),
render: renderItem,
})
}),
Expand Down Expand Up @@ -819,16 +838,23 @@ class Dropdown extends AutoControlledComponent<WithAsProp<DropdownProps>, Dropdo
item: ShorthandValue<DropdownItemProps>,
index: number,
getItemProps: (options: GetItemPropsOptions<ShorthandValue<DropdownItemProps>>) => any,
selected: boolean,
) => (predefinedProps: DropdownItemProps) => ({
accessibilityItemProps: getItemProps({
item,
index,
onClick: e => {
e.stopPropagation()
e.nativeEvent.stopImmediatePropagation()
_.invoke(predefinedProps, 'onClick', e, predefinedProps)
},
}),
accessibilityItemProps: {
...getItemProps({
item,
index,
onClick: e => {
e.stopPropagation()
e.nativeEvent.stopImmediatePropagation()
_.invoke(predefinedProps, 'onClick', e, predefinedProps)
},
}),
// for single selection the selected item should have aria-selected, instead of the highlighted
...(!this.props.multiple && {
'aria-selected': selected,
}),
},
})

handleSelectedItemOverrides = (item: ShorthandValue<DropdownItemProps>, rtl: boolean) => (
Expand Down
33 changes: 32 additions & 1 deletion packages/react/src/components/Dropdown/DropdownItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import { UIComponent, RenderResultConfig, createShorthandFactory, commonPropType
import { ShorthandValue, ComponentEventHandler, WithAsProp, withSafeTypeForAs } from '../../types'
import { UIComponentProps } from '../../lib/commonPropInterfaces'
import ListItem from '../List/ListItem'
import Icon, { IconProps } from '../Icon/Icon'
import Image, { ImageProps } from '../Image/Image'
import Box, { BoxProps } from '../Box/Box'

export interface DropdownItemSlotClassNames {
content: string
header: string
image: string
checkableIndicator: string
}

export interface DropdownItemProps extends UIComponentProps<DropdownItemProps> {
Expand All @@ -26,6 +28,12 @@ export interface DropdownItemProps extends UIComponentProps<DropdownItemProps> {
/** Item's content. */
content?: ShorthandValue<BoxProps>

/** Item can show check indicator if selected. */
checkable?: boolean

/** A slot for a checkable indicator. */
checkableIndicator?: ShorthandValue<IconProps>

/** Item's header. */
header?: ShorthandValue<BoxProps>

Expand Down Expand Up @@ -65,6 +73,8 @@ class DropdownItem extends UIComponent<WithAsProp<DropdownItemProps>> {
accessibilityItemProps: PropTypes.object,
active: PropTypes.bool,
content: customPropTypes.itemShorthand,
checkable: PropTypes.bool,
checkableIndicator: customPropTypes.itemShorthandWithoutJSX,
header: customPropTypes.itemShorthand,
image: customPropTypes.itemShorthandWithoutJSX,
onClick: PropTypes.func,
Expand All @@ -77,7 +87,15 @@ class DropdownItem extends UIComponent<WithAsProp<DropdownItemProps>> {
}

renderComponent({ classes, styles, unhandledProps }: RenderResultConfig<DropdownItemProps>) {
const { content, header, image, accessibilityItemProps } = this.props
const {
content,
header,
image,
accessibilityItemProps,
selected,
checkable,
checkableIndicator,
} = this.props
return (
<ListItem
className={DropdownItem.className}
Expand All @@ -102,6 +120,18 @@ class DropdownItem extends UIComponent<WithAsProp<DropdownItemProps>> {
styles: styles.content,
},
})}
endMedia={
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is different then how the rest of the elements are created and where the styles are applied, but there are issues with the height if we don't provide these styles.. I am open for other ideas...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

selected &&
checkable && {
content: Icon.create(checkableIndicator, {
defaultProps: {
className: DropdownItem.slotClassNames.checkableIndicator,
styles: styles.checkableIndicator,
},
}),
styles: styles.endMedia,
}
}
truncateContent
truncateHeader
{...accessibilityItemProps}
Expand All @@ -115,6 +145,7 @@ DropdownItem.slotClassNames = {
content: `${DropdownItem.className}__content`,
header: `${DropdownItem.className}__header`,
image: `${DropdownItem.className}__image`,
checkableIndicator: `${DropdownItem.className}__checkable-indicator`,
}

DropdownItem.create = createShorthandFactory({ Component: DropdownItem, mappedProp: 'header' })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ const dropdownItemStyles: ComponentSlotStylesInput<DropdownItemProps, DropdownVa
fontSize: v.listItemContentFontSize,
color: v.listItemContentColor,
}),
checkableIndicator: ({ variables: v }) => ({
position: 'relative',
left: pxToRem(3),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The padding on the DropdownItem is 11px, but the icon should be 8px from the right border, that's why we need styles

}),
endMedia: () => ({
lineHeight: pxToRem(16),
}),
}

export default dropdownItemStyles