Skip to content
This repository has been archived by the owner on Aug 21, 2023. It is now read-only.

Commit

Permalink
Allow to disable item in a DropList (#915)
Browse files Browse the repository at this point in the history
* Allow to disable an item in a Droplist

* Provide isDisabled flag to custom renderer callback

* Prevent selecting internally item if disabled

* JP: Moves onClick event out of listItem

* JP: UX skip disabled items on arrow down and up

* adds tests for disabled item navigation

Co-authored-by: Juan Pablo Lomeli Diaz <juanpablo@thetinkertrain.codes>
  • Loading branch information
jakubjanczyk and tinkertrain committed Apr 19, 2021
1 parent 412e539 commit 07a683e
Show file tree
Hide file tree
Showing 10 changed files with 337 additions and 3 deletions.
13 changes: 12 additions & 1 deletion src/components/DropList/DropList.Combobox.jsx
Expand Up @@ -84,6 +84,7 @@ function Combobox({
return stateReducerCommon({
changes,
closeOnSelection,
items,
selectedItems,
state,
type: `${VARIANTS.COMBOBOX}.${type}`,
Expand Down Expand Up @@ -113,7 +114,17 @@ function Combobox({
key: generateListItemKey(item, index),
withMultipleSelection,
renderCustomListItem,
...getItemProps({ item, index }),
isDisabled: item.isDisabled,
...getItemProps({
item,
index,
onClick: event => {
if (item.isDisabled) {
event.nativeEvent.preventDownshiftDefault = true
return
}
},
}),
}

return <ListItem {...itemProps} />
Expand Down
3 changes: 3 additions & 0 deletions src/components/DropList/DropList.ListItem.jsx
Expand Up @@ -22,6 +22,7 @@ const ListItem = forwardRef(
withMultipleSelection,
isSelected,
renderCustomListItem,
isDisabled,
...itemProps
},
ref
Expand Down Expand Up @@ -49,6 +50,7 @@ const ListItem = forwardRef(
return classNames(
'DropListItem',
isSelected && 'is-selected',
isDisabled && 'is-disabled',
highlightedIndex === index && 'is-highlighted',
withMultipleSelection && 'with-multiple-selection',
isString(extraClassNames) && extraClassNames
Expand All @@ -67,6 +69,7 @@ const ListItem = forwardRef(
isSelected,
isHighlighted: highlightedIndex === index,
withMultipleSelection,
isDisabled,
})}
</li>
)
Expand Down
13 changes: 12 additions & 1 deletion src/components/DropList/DropList.Select.jsx
Expand Up @@ -79,6 +79,7 @@ function Select({
return stateReducerCommon({
changes,
closeOnSelection,
items,
selectedItems,
state,
type: `${VARIANTS.SELECT}.${type}`,
Expand All @@ -100,7 +101,17 @@ function Select({
key: generateListItemKey(item, index),
withMultipleSelection,
renderCustomListItem,
...getItemProps({ item, index }),
isDisabled: item.isDisabled,
...getItemProps({
item,
index,
onClick: event => {
if (item.isDisabled) {
event.nativeEvent.preventDownshiftDefault = true
return
}
},
}),
}

return <ListItem {...itemProps} />
Expand Down
7 changes: 7 additions & 0 deletions src/components/DropList/DropList.css.js
Expand Up @@ -113,6 +113,13 @@ export const ListItemUI = styled('li')`
background-color: ${getColor('blue.100')};
}
}
&.is-disabled,
&.with-multiple-selection.is-disabled {
color: ${getColor('charcoal.200')};
background-color: transparent;
cursor: default;
}
`

export const EmptyListUI = styled('div')`
Expand Down
35 changes: 34 additions & 1 deletion src/components/DropList/DropList.downshift.common.js
@@ -1,13 +1,18 @@
import { useSelect, useCombobox } from 'downshift'
import { isObject } from '../../utilities/is'
import { findItemInArray, getItemContentKeyName } from './DropList.utils'
import {
findItemInArray,
getEnabledItemIndex,
getItemContentKeyName,
} from './DropList.utils'
import { OPEN_ACTION_ORIGIN, VARIANTS } from './DropList.constants'

const { SELECT, COMBOBOX } = VARIANTS

export function stateReducerCommon({
changes,
closeOnSelection,
items,
selectedItems,
state,
type,
Expand Down Expand Up @@ -67,6 +72,34 @@ export function stateReducerCommon({
return { ...changes, inputValue: '' }
}

case `${COMBOBOX}.${useCombobox.stateChangeTypes.InputKeyDownArrowUp}`:
case `${SELECT}.${useSelect.stateChangeTypes.MenuKeyDownArrowUp}`: {
const { highlightedIndex } = changes

return {
...changes,
highlightedIndex: getEnabledItemIndex({
highlightedIndex,
items,
arrowKey: 'UP',
}),
}
}

case `${COMBOBOX}.${useCombobox.stateChangeTypes.InputKeyDownArrowDown}`:
case `${SELECT}.${useSelect.stateChangeTypes.MenuKeyDownArrowDown}`: {
const { highlightedIndex } = changes

return {
...changes,
highlightedIndex: getEnabledItemIndex({
highlightedIndex,
items,
arrowKey: 'DOWN',
}),
}
}

default:
return changes
}
Expand Down
4 changes: 4 additions & 0 deletions src/components/DropList/DropList.jsx
Expand Up @@ -153,6 +153,9 @@ function DropListManager({
}

function handleSelectedItemChange({ selectedItem }) {
if (selectedItem.isDisabled) {
return
}
if (withMultipleSelection) {
if (selectedItem) {
const { remove } = selectedItem
Expand Down Expand Up @@ -251,6 +254,7 @@ const itemShape = PropTypes.shape({
label: requiredItemPropsCheck,
value: requiredItemPropsCheck,
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
isDisabled: PropTypes.bool,
})
const dividerShape = PropTypes.shape({
type: PropTypes.oneOf(['divider', 'Divider']).isRequired,
Expand Down
74 changes: 74 additions & 0 deletions src/components/DropList/DropList.stories.mdx
Expand Up @@ -15,6 +15,7 @@ import {
plainItems,
regularItems,
simpleGroupedItems,
disabledItems,
} from '../../utilities/specs/dropdown.specs'

<Meta
Expand Down Expand Up @@ -373,6 +374,7 @@ function renderCustomListItem({
item,
isSelected,
isHighlighted,
isDisabled,
withMultipleSelection,
})

Expand All @@ -384,6 +386,7 @@ function renderCustomListItem({
item,
isSelected,
isHighlighted,
isDisabled,
withMultipleSelection,
}) => (
<div className={classnames(isSelected && 'is-selected')}>
Expand Down Expand Up @@ -426,6 +429,77 @@ function renderCustomListItem({
</Story>
</Canvas>

- Disabled List Items: Sometimes you might need to render the list items that would be disabled, for that you can pass `isDisbaled` flag with an item.

<Canvas>
<Story name="Items: disabled list items">
<div
style={{
width: '400px',
margin: '50px 100px 200px 150px',
border: '1px dashed silver',
borderRadius: '5px',
padding: '20px',
}}
>
<DropList
items={disabledItems}
toggler={<SimpleButton text="Click" />}
onSelect={selection => {
console.log('selection', selection)
}}
/>
</div>
</Story>
</Canvas>

<Canvas>
<Story name="Items: disabled list items (combobox)">
<div
style={{
width: '400px',
margin: '50px 100px 200px 150px',
border: '1px dashed silver',
borderRadius: '5px',
padding: '20px',
}}
>
<DropList
items={disabledItems}
variant="combobox"
toggler={<SimpleButton text="Click" />}
onSelect={selection => {
console.log('selection', selection)
}}
/>
</div>
</Story>
</Canvas>

<Canvas>
<Story name="Items: disabled list items (Multiple Selection)">
<div
style={{
width: '400px',
margin: '50px 100px 200px 150px',
border: '1px dashed silver',
borderRadius: '5px',
padding: '20px',
}}
>
<DropList
items={disabledItems}
closeOnSelection={false}
onSelect={selection => {
console.log('selection', selection)
}}
toggler={<SimpleButton text="Submit" />}
withMultipleSelection
/>
</div>
</Story>
</Canvas>

### Multiple selection

Depending on your use case, you might want to set `closeOnSelection = false` when multiple selection is enabled.
Expand Down

0 comments on commit 07a683e

Please sign in to comment.