-
Notifications
You must be signed in to change notification settings - Fork 936
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
dev: add docs for multiple selections (#1396)
- Loading branch information
1 parent
e4d37fe
commit efba7f2
Showing
5 changed files
with
402 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,23 @@ | ||
import * as React from 'react' | ||
|
||
export default function Docs() { | ||
return <div>Downshift Docs for E2E Testing</div> | ||
return ( | ||
<div style={{width: '50%', margin: '200px auto'}}> | ||
Downshift Docs for E2E Testing | ||
<ul> | ||
<li> | ||
<a href="./useCombobox">useCombobox</a> | ||
</li> | ||
<li> | ||
<a href="./useSelect">useSelect</a> | ||
</li> | ||
<li> | ||
<a href="./useMultipleCombobox">useMultipleCombobox</a> | ||
</li> | ||
<li> | ||
<a href="./useMultipleSelect">useMultipleSelect</a> | ||
</li> | ||
</ul> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
import React from 'react' | ||
|
||
import {useCombobox, useMultipleSelection} from '../../src' | ||
|
||
const colors = ['Black', 'Red', 'Green', 'Blue', 'Orange', 'Purple'] | ||
const initialSelectedItems = [colors[0], colors[1]] | ||
|
||
function getFilteredItems(selectedItems, inputValue) { | ||
const lowerCasedInputValue = inputValue.toLowerCase() | ||
|
||
return colors.filter( | ||
colour => | ||
!selectedItems.includes(colour) && | ||
colour.toLowerCase().startsWith(lowerCasedInputValue), | ||
) | ||
} | ||
|
||
export default function DropdownMultipleCombobox() { | ||
const [inputValue, setInputValue] = React.useState('') | ||
const [selectedItems, setSelectedItems] = React.useState(initialSelectedItems) | ||
const items = React.useMemo( | ||
() => getFilteredItems(selectedItems, inputValue), | ||
[selectedItems, inputValue], | ||
) | ||
|
||
const {getSelectedItemProps, getDropdownProps, removeSelectedItem} = | ||
useMultipleSelection({ | ||
selectedItems, | ||
onStateChange({selectedItems: newSelectedItems, type}) { | ||
switch (type) { | ||
case useMultipleSelection.stateChangeTypes | ||
.SelectedItemKeyDownBackspace: | ||
case useMultipleSelection.stateChangeTypes.SelectedItemKeyDownDelete: | ||
case useMultipleSelection.stateChangeTypes.DropdownKeyDownBackspace: | ||
case useMultipleSelection.stateChangeTypes.FunctionRemoveSelectedItem: | ||
setSelectedItems(newSelectedItems) | ||
break | ||
default: | ||
break | ||
} | ||
}, | ||
}) | ||
const { | ||
isOpen, | ||
getToggleButtonProps, | ||
getLabelProps, | ||
getMenuProps, | ||
getInputProps, | ||
getComboboxProps, | ||
highlightedIndex, | ||
getItemProps, | ||
selectedItem, | ||
clearSelection, | ||
} = useCombobox({ | ||
items, | ||
inputValue, | ||
selectedItem: null, | ||
stateReducer(state, actionAndChanges) { | ||
const {changes, type} = actionAndChanges | ||
|
||
switch (type) { | ||
case useCombobox.stateChangeTypes.InputKeyDownEnter: | ||
case useCombobox.stateChangeTypes.ItemClick: | ||
case useCombobox.stateChangeTypes.InputBlur: | ||
return { | ||
...changes, | ||
...(changes.selectedItem && {isOpen: true, highlightedIndex: 0}), | ||
} | ||
default: | ||
return changes | ||
} | ||
}, | ||
onStateChange({ | ||
inputValue: newInputValue, | ||
type, | ||
selectedItem: newSelectedItem, | ||
}) { | ||
switch (type) { | ||
case useCombobox.stateChangeTypes.InputKeyDownEnter: | ||
case useCombobox.stateChangeTypes.ItemClick: | ||
setSelectedItems([...selectedItems, newSelectedItem]) | ||
|
||
break | ||
case useCombobox.stateChangeTypes.InputChange: | ||
setInputValue(newInputValue) | ||
break | ||
default: | ||
break | ||
} | ||
}, | ||
}) | ||
return ( | ||
<div | ||
style={{ | ||
display: 'flex', | ||
flexDirection: 'column', | ||
width: 'fit-content', | ||
justifyContent: 'center', | ||
marginTop: 100, | ||
alignSelf: 'center', | ||
}} | ||
> | ||
<label | ||
style={{ | ||
fontWeight: 'bolder', | ||
color: selectedItem ? selectedItem : 'black', | ||
}} | ||
{...getLabelProps()} | ||
> | ||
Choose an element: | ||
</label> | ||
<div | ||
style={{ | ||
display: 'inline-flex', | ||
gap: '8px', | ||
alignItems: 'center', | ||
flexWrap: 'wrap', | ||
padding: '6px', | ||
}} | ||
> | ||
{selectedItems.map(function renderSelectedItem( | ||
selectedItemForRender, | ||
index, | ||
) { | ||
return ( | ||
<span | ||
style={{ | ||
backgroundColor: 'lightgray', | ||
paddingLeft: '4px', | ||
paddingRight: '4px', | ||
borderRadius: '6px', | ||
}} | ||
key={`selected-item-${index}`} | ||
{...getSelectedItemProps({ | ||
selectedItem: selectedItemForRender, | ||
index, | ||
})} | ||
> | ||
{selectedItemForRender} | ||
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */} | ||
<span | ||
style={{padding: '4px', cursor: 'pointer'}} | ||
onClick={e => { | ||
e.stopPropagation() | ||
removeSelectedItem(null) | ||
}} | ||
> | ||
✕ | ||
</span> | ||
</span> | ||
) | ||
})} | ||
<div {...getComboboxProps()}> | ||
<input | ||
style={{padding: '4px'}} | ||
{...getInputProps(getDropdownProps({preventKeyAction: isOpen}))} | ||
data-testid="combobox-input" | ||
/> | ||
<button | ||
style={{padding: '4px 8px'}} | ||
aria-label="toggle menu" | ||
data-testid="combobox-toggle-button" | ||
{...getToggleButtonProps()} | ||
> | ||
{isOpen ? <>↑</> : <>↓</>} | ||
</button> | ||
<button | ||
style={{padding: '4px 8px'}} | ||
aria-label="toggle menu" | ||
data-testid="clear-button" | ||
onClick={clearSelection} | ||
> | ||
✗ | ||
</button> | ||
</div> | ||
</div> | ||
<ul | ||
{...getMenuProps()} | ||
style={{ | ||
listStyle: 'none', | ||
width: '100%', | ||
padding: '0', | ||
margin: '4px 0 0 0', | ||
}} | ||
> | ||
{isOpen && | ||
items.map((item, index) => ( | ||
<li | ||
style={{ | ||
padding: '4px', | ||
backgroundColor: highlightedIndex === index ? '#bde4ff' : null, | ||
}} | ||
key={`${item}${index}`} | ||
{...getItemProps({ | ||
item, | ||
index, | ||
'data-testid': `downshift-item-${index}`, | ||
})} | ||
> | ||
{item} | ||
</li> | ||
))} | ||
</ul> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
import React from 'react' | ||
|
||
import {useSelect, useMultipleSelection} from '../../src' | ||
|
||
const colors = ['Black', 'Red', 'Green', 'Blue', 'Orange', 'Purple'] | ||
const initialSelectedItems = [colors[0], colors[1]] | ||
|
||
function getFilteredItems(selectedItems) { | ||
return colors.filter(colour => !selectedItems.includes(colour)) | ||
} | ||
|
||
export default function DropdownMultipleSelect() { | ||
const { | ||
getSelectedItemProps, | ||
getDropdownProps, | ||
addSelectedItem, | ||
removeSelectedItem, | ||
selectedItems, | ||
} = useMultipleSelection({initialSelectedItems}) | ||
const items = getFilteredItems(selectedItems) | ||
const { | ||
isOpen, | ||
selectedItem, | ||
getToggleButtonProps, | ||
getLabelProps, | ||
getMenuProps, | ||
highlightedIndex, | ||
getItemProps, | ||
} = useSelect({ | ||
selectedItem: null, | ||
defaultHighlightedIndex: 0, // after selection, highlight the first item. | ||
items, | ||
stateReducer: (state, actionAndChanges) => { | ||
const {changes, type} = actionAndChanges | ||
switch (type) { | ||
case useSelect.stateChangeTypes.MenuKeyDownEnter: | ||
case useSelect.stateChangeTypes.MenuKeyDownSpaceButton: | ||
case useSelect.stateChangeTypes.ItemClick: | ||
return { | ||
...changes, | ||
isOpen: true, // keep the menu open after selection. | ||
} | ||
default: | ||
return changes | ||
} | ||
}, | ||
onStateChange: ({type, selectedItem: newSelectedItem}) => { | ||
switch (type) { | ||
case useSelect.stateChangeTypes.MenuKeyDownEnter: | ||
case useSelect.stateChangeTypes.MenuKeyDownSpaceButton: | ||
case useSelect.stateChangeTypes.ItemClick: | ||
if (newSelectedItem) { | ||
addSelectedItem(newSelectedItem) | ||
} | ||
break | ||
default: | ||
break | ||
} | ||
}, | ||
}) | ||
|
||
return ( | ||
<div | ||
style={{ | ||
display: 'flex', | ||
flexDirection: 'column', | ||
width: 'fit-content', | ||
justifyContent: 'center', | ||
marginTop: 100, | ||
alignSelf: 'center', | ||
}} | ||
> | ||
<div> | ||
<label | ||
style={{ | ||
fontWeight: 'bolder', | ||
color: selectedItem ? selectedItem : 'black', | ||
}} | ||
{...getLabelProps()} | ||
> | ||
Choose an element: | ||
</label> | ||
<div | ||
style={{ | ||
display: 'inline-flex', | ||
gap: '8px', | ||
alignItems: 'center', | ||
flexWrap: 'wrap', | ||
padding: '6px', | ||
}} | ||
> | ||
{selectedItems.map(function renderSelectedItem( | ||
selectedItemForRender, | ||
index, | ||
) { | ||
return ( | ||
<span | ||
style={{ | ||
backgroundColor: 'lightgray', | ||
paddingLeft: '4px', | ||
paddingRight: '4px', | ||
borderRadius: '6px', | ||
}} | ||
className="bg-gray-100 rounded-md px-1 focus:bg-red-400" | ||
key={`selected-item-${index}`} | ||
{...getSelectedItemProps({ | ||
selectedItem: selectedItemForRender, | ||
index, | ||
})} | ||
> | ||
{selectedItemForRender} | ||
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */} | ||
<span | ||
style={{padding: '4px', cursor: 'pointer'}} | ||
onClick={e => { | ||
e.stopPropagation() | ||
removeSelectedItem(selectedItemForRender) | ||
}} | ||
> | ||
✕ | ||
</span> | ||
</span> | ||
) | ||
})} | ||
<button | ||
style={{padding: '4px'}} | ||
type="button" | ||
{...getToggleButtonProps( | ||
getDropdownProps({preventKeyAction: isOpen}), | ||
)} | ||
> | ||
Pick some colors {isOpen ? <>↑</> : <>↓</>} | ||
</button> | ||
</div> | ||
</div> | ||
<ul | ||
{...getMenuProps()} | ||
style={{ | ||
listStyle: 'none', | ||
width: '100%', | ||
padding: '0', | ||
margin: '4px 0 0 0', | ||
}} | ||
> | ||
{isOpen && | ||
items.map((item, index) => ( | ||
<li | ||
style={{ | ||
padding: '4px', | ||
backgroundColor: highlightedIndex === index ? '#bde4ff' : null, | ||
}} | ||
key={`${item}${index}`} | ||
{...getItemProps({ | ||
item, | ||
index, | ||
'data-testid': `downshift-item-${index}`, | ||
})} | ||
> | ||
{item} | ||
</li> | ||
))} | ||
</ul> | ||
</div> | ||
) | ||
} |
Oops, something went wrong.