Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/downshift.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ function ComboBox() {
const items = [
{author: 'Harper Lee', title: 'To Kill a Mockingbird'},
{author: 'Lev Tolstoy', title: 'War and Peace'},
{author: 'Fyodor Dostoyevsy', title: 'The Idiot'},
{author: 'Fyodor Dostoevsky', title: 'The Idiot'},
{author: 'Oscar Wilde', title: 'A Picture of Dorian Gray'},
{author: 'George Orwell', title: '1984'},
{author: 'Jane Austen', title: 'Pride and Prejudice'},
Expand Down Expand Up @@ -215,7 +215,7 @@ function ComboBox() {
const items = [
{author: 'Harper Lee', title: 'To Kill a Mockingbird'},
{author: 'Lev Tolstoy', title: 'War and Peace'},
{author: 'Fyodor Dostoyevsy', title: 'The Idiot'},
{author: 'Fyodor Dostoevsky', title: 'The Idiot'},
{author: 'Oscar Wilde', title: 'A Picture of Dorian Gray'},
{author: 'George Orwell', title: '1984'},
{author: 'Jane Austen', title: 'Pride and Prejudice'},
Expand Down
16 changes: 10 additions & 6 deletions docs/hooks/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,20 @@ If you need to add multiple selection for any of them and have the selected
items act as a Tag List next to the _select_ or _combobox_, then
[useMultipleSelection](/use-multiple-selection) is the right tool for that.

## Breaking Changes in v8
## Migration Guides

Since version 8, there have been breaking changes to functionality and TS types.
Check out the
[migration page](https://github.com/downshift-js/downshift/blob/master/src/hooks/MIGRATION_V8.md).

## Breaking Changes in v7
### v7

Since version 7, the _useSelect_ and _useCombobox_ hooks to support the ARIA 1.2
pattern for the combobox, which contains some changes from the ARIA 1.1 pattern.
This brings changes in the API and the behaviour of _useCombobox_, detailed in
the
[migration page](https://github.com/downshift-js/downshift/blob/master/src/hooks/MIGRATION_V7.md).

### v8 & v9

Since versions 8 and 9, there have been breaking changes to functionality and TS
types. Check out the migration pages for
[v8](https://github.com/downshift-js/downshift/blob/master/src/hooks/MIGRATION_V8.md)
and
[v9](https://github.com/downshift-js/downshift/blob/master/src/hooks/MIGRATION_V9.md).
195 changes: 184 additions & 11 deletions docs/hooks/useCombobox.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ implement the corresponding ARIA pattern. Every functionality needed should be
provided out-of-the-box: menu toggle, item selection and up/down movement
between them, screen reader support, focus management etc.

## Breaking Changes in v8
## Breaking Changes in v8 and v9

_useCombobox_ has been affected by breaking changes in v8, so check out the
[migration page][migration-guide-v8].
_useCombobox_ has been affected by breaking changes in v8 and v9, so check out
the migration pages for [v8][migration-guide-v8] and [v9][migration-guide-v9].

## Breaking Changes in v7

Expand Down Expand Up @@ -83,7 +83,7 @@ function ComboBoxExample() {
const books = [
{id: 'book-1', author: 'Harper Lee', title: 'To Kill a Mockingbird'},
{id: 'book-2', author: 'Lev Tolstoy', title: 'War and Peace'},
{id: 'book-3', author: 'Fyodor Dostoyevsy', title: 'The Idiot'},
{id: 'book-3', author: 'Fyodor Dostoevsky', title: 'The Idiot'},
{id: 'book-4', author: 'Oscar Wilde', title: 'A Picture of Dorian Gray'},
{id: 'book-5', author: 'George Orwell', title: '1984'},
{id: 'book-6', author: 'Jane Austen', title: 'Pride and Prejudice'},
Expand Down Expand Up @@ -207,7 +207,7 @@ with the resulting DOM element, we don't need to do anything specific rather
than just spreading the getter props, apart from the case of the _Input_, which
renders a wrapper element over the actual HTML _input_. In this case, since
_Input_ provides a prop for accessing the _input_ element called _inputRef_, we
will use the getter function like this: _getInputProps({refKey: 'inputRef'})_.
will use the getter function like this: `getInputProps({refKey: 'inputRef'})`.

Another point worth mentioning is that in this case items are objects and not
strings. As a result, the _itemToString_ prop is passed to _useCombobox_. It
Expand All @@ -226,7 +226,7 @@ function ComboBoxExample() {
const books = [
{id: 'book-1', author: 'Harper Lee', title: 'To Kill a Mockingbird'},
{id: 'book-2', author: 'Lev Tolstoy', title: 'War and Peace'},
{id: 'book-3', author: 'Fyodor Dostoyevsy', title: 'The Idiot'},
{id: 'book-3', author: 'Fyodor Dostoevsky', title: 'The Idiot'},
{id: 'book-4', author: 'Oscar Wilde', title: 'A Picture of Dorian Gray'},
{id: 'book-5', author: 'George Orwell', title: '1984'},
{id: 'book-6', author: 'Jane Austen', title: 'Pride and Prejudice'},
Expand Down Expand Up @@ -346,7 +346,7 @@ function ComboBoxExample() {
const books = [
{id: 'book-1', author: 'Harper Lee', title: 'To Kill a Mockingbird'},
{id: 'book-2', author: 'Lev Tolstoy', title: 'War and Peace'},
{id: 'book-3', author: 'Fyodor Dostoyevsy', title: 'The Idiot'},
{id: 'book-3', author: 'Fyodor Dostoevsky', title: 'The Idiot'},
{id: 'book-4', author: 'Oscar Wilde', title: 'A Picture of Dorian Gray'},
{id: 'book-5', author: 'George Orwell', title: '1984'},
{id: 'book-6', author: 'Jane Austen', title: 'Pride and Prejudice'},
Expand Down Expand Up @@ -477,7 +477,7 @@ function ComboBoxExample() {
const books = [
{id: 'book-1', author: 'Harper Lee', title: 'To Kill a Mockingbird'},
{id: 'book-2', author: 'Lev Tolstoy', title: 'War and Peace'},
{id: 'book-3', author: 'Fyodor Dostoyevsy', title: 'The Idiot'},
{id: 'book-3', author: 'Fyodor Dostoevsky', title: 'The Idiot'},
{id: 'book-4', author: 'Oscar Wilde', title: 'A Picture of Dorian Gray'},
{id: 'book-5', author: 'George Orwell', title: '1984'},
{id: 'book-6', author: 'Jane Austen', title: 'Pride and Prejudice'},
Expand Down Expand Up @@ -619,7 +619,7 @@ function ComboBoxExample() {
const books = [
{id: 'book-1', author: 'Harper Lee', title: 'To Kill a Mockingbird'},
{id: 'book-2', author: 'Lev Tolstoy', title: 'War and Peace'},
{id: 'book-3', author: 'Fyodor Dostoyevsy', title: 'The Idiot'},
{id: 'book-3', author: 'Fyodor Dostoevsky', title: 'The Idiot'},
{id: 'book-4', author: 'Oscar Wilde', title: 'A Picture of Dorian Gray'},
{id: 'book-5', author: 'George Orwell', title: '1984'},
{id: 'book-6', author: 'Jane Austen', title: 'Pride and Prejudice'},
Expand Down Expand Up @@ -751,7 +751,7 @@ function ComboBoxExample() {
const books = [
{id: 'book-1', author: 'Harper Lee', title: 'To Kill a Mockingbird'},
{id: 'book-2', author: 'Lev Tolstoy', title: 'War and Peace'},
{id: 'book-3', author: 'Fyodor Dostoyevsy', title: 'The Idiot'},
{id: 'book-3', author: 'Fyodor Dostoevsky', title: 'The Idiot'},
{id: 'book-4', author: 'Oscar Wilde', title: 'A Picture of Dorian Gray'},
{id: 'book-5', author: 'George Orwell', title: '1984'},
{id: 'book-6', author: 'Jane Austen', title: 'Pride and Prejudice'},
Expand Down Expand Up @@ -906,6 +906,175 @@ function ComboBoxExample() {
}
```

## Multiple selection with Tag Group

If your multiple selection scenario uses a tag group to display selected items,
you can use the hook in combination with [useTagGroup](/use-tag-group).

In the example below, we use:

- _null_ to control **selectedItem** as the hook should not care about
selection.
- **onSelectedItemChange** to be able to call **addItem** from _useTagGroup_.
- control **inputValue** to be able to filter combobox items by query and
selected items.
- **stateReducer** to keep the menu open on selection.

[CodeSandbox for combobox with tag group
example][code-sandbox-combobox-tag-group].

```jsx live
function ComboBoxExample() {
const books = [
{id: 'book-1', author: 'Harper Lee', title: 'To Kill a Mockingbird'},
{id: 'book-2', author: 'Lev Tolstoy', title: 'War and Peace'},
{id: 'book-3', author: 'Fyodor Dostoevsky', title: 'The Idiot'},
{id: 'book-4', author: 'Oscar Wilde', title: 'A Picture of Dorian Gray'},
{id: 'book-5', author: 'George Orwell', title: '1984'},
{id: 'book-6', author: 'Jane Austen', title: 'Pride and Prejudice'},
{id: 'book-7', author: 'Marcus Aurelius', title: 'Meditations'},
{
id: 'book-8',
author: 'Fyodor Dostoevsky',
title: 'The Brothers Karamazov',
},
{id: 'book-9', author: 'Lev Tolstoy', title: 'Anna Karenina'},
{id: 'book-10', author: 'Fyodor Dostoevsky', title: 'Crime and Punishment'},
]

function ComboBox() {
const initialItems = books.slice(0, 2)
const [inputValue, setInputValue] = React.useState('')

const {
addItem,
getTagProps,
getTagRemoveProps,
getTagGroupProps,
items,
activeIndex,
} = useTagGroup({
initialItems,
})
const itemsToAdd = books.filter(
book =>
!items.includes(book) &&
(!inputValue ||
book.title.toLowerCase().includes(inputValue.toLowerCase()) ||
book.author.toLowerCase().includes(inputValue.toLowerCase())),
)
const {
isOpen,
getToggleButtonProps,
getLabelProps,
getMenuProps,
getInputProps,
highlightedIndex,
getItemProps,
} = useCombobox({
items: itemsToAdd,
inputValue,
onInputValueChange: ({inputValue}) => {
setInputValue(inputValue)
},
onSelectedItemChange({selectedItem}) {
if (selectedItem) {
addItem(selectedItem)
}
},
selectedItem: null,
itemToString(item) {
return item ? item.title : ''
},
stateReducer(_state, actionAndChanges) {
const {changes, type} = actionAndChanges

if (
changes.selectedItem &&
type !== useCombobox.stateChangeTypes.InputBlur
) {
return {...changes, inputValue: '', highlightedIndex: 0, isOpen: true}
}

console.log(changes)

return changes
},
})

return (
<div>
<div
{...getTagGroupProps({'aria-label': 'books usage example'})}
className="inline-flex gap-2 items-center flex-wrap p-1.5"
>
{items.map((item, index) => (
<span
className={`${
index === activeIndex ? 'italic' : ''
} border border-green-800 bg-green-600 px-1.5 mx-0.5 rounded-[10px] cursor-default hover:opacity-50 focus:bg-red-600 focus:border-red-800`}
key={item.id}
{...getTagProps({index, 'aria-label': item.title})}
>
{item.title}
<span
className="p-1 cursor-pointer border-0 bg-transparent"
{...getTagRemoveProps({index, 'aria-label': 'remove'})}
>
&#10005;
</span>
</span>
))}
</div>
<div className="w-72 flex flex-col gap-1">
<label className="w-fit" {...getLabelProps()}>
Choose your favorite book:
</label>
<div className="flex shadow-sm bg-white gap-0.5">
<input
placeholder="Best book ever"
className="w-full p-1.5"
{...getInputProps()}
/>
<button
aria-label="toggle menu"
className="px-2"
type="button"
{...getToggleButtonProps()}
>
{isOpen ? <>&#8593;</> : <>&#8595;</>}
</button>
</div>
</div>
<ul
className={`absolute w-72 bg-white mt-1 shadow-md max-h-80 overflow-scroll p-0 z-10 ${
!(isOpen && itemsToAdd.length) && 'hidden'
}`}
{...getMenuProps()}
>
{isOpen &&
itemsToAdd.map((item, index) => (
<li
className={cx(
highlightedIndex === index && 'bg-blue-300',
'py-2 px-3 shadow-sm flex flex-col',
)}
key={item.id}
{...getItemProps({item, index})}
>
<span>{item.title}</span>
<span className="text-sm text-gray-700">{item.author}</span>
</li>
))}
</ul>
</div>
)
}

return <ComboBox />
}
```

## Using action props

Action props are functions returned by _useCombobox_ along with the state props
Expand All @@ -922,7 +1091,7 @@ function ComboBoxExample() {
const books = [
{id: 'book-1', author: 'Harper Lee', title: 'To Kill a Mockingbird'},
{id: 'book-2', author: 'Lev Tolstoy', title: 'War and Peace'},
{id: 'book-3', author: 'Fyodor Dostoyevsy', title: 'The Idiot'},
{id: 'book-3', author: 'Fyodor Dostoevsky', title: 'The Idiot'},
{id: 'book-4', author: 'Oscar Wilde', title: 'A Picture of Dorian Gray'},
{id: 'book-5', author: 'George Orwell', title: '1984'},
{id: 'book-6', author: 'Jane Austen', title: 'Pride and Prejudice'},
Expand Down Expand Up @@ -1200,10 +1369,14 @@ repository][examples-code-sandbox].
https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fhooks%2FuseSelect%2Freact-virtual.js&moduleview=1
[examples-code-sandbox]:
https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Findex.js&moduleview=1
[code-sandbox-combobox-tag-group]:
https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fhooks%2FuseCombobox%2Ftag-group-multiple-selection.js&moduleview=1
[react-virtual-github]: https://github.com/tannerlinsley/react-virtual
[react-virtualized-github]: https://github.com/bvaughn/react-virtualized
[react-window-github]: https://github.com/bvaughn/react-window
[migration-guide-v7]:
https://github.com/downshift-js/downshift/tree/master/src/hooks/MIGRATION_V7.md#usecombobox
[migration-guide-v8]:
https://github.com/downshift-js/downshift/tree/master/src/hooks/MIGRATION_V8.md
[migration-guide-v9]:
https://github.com/downshift-js/downshift/tree/master/src/hooks/MIGRATION_V9.md
21 changes: 16 additions & 5 deletions docs/hooks/useMultipleSelection.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ slug: /use-multiple-selection

# useMultipleSelection

## DEPRECATED

Please use the [useTagGroup](/use-tag-group) hook to create multiple selection
dropdowns with tag groups. Check out the examples for each:

- [useCombobox](/use-combobox#multiple-selection-with-tag-group)
- [useSelect](/use-select#multiple-selection-with-tag-group)

## The problem

You have a custom **select** or a **combobox** in your applications which
Expand Down Expand Up @@ -33,10 +41,11 @@ arrow navigation between dropdown and items, navigation between the items
themselves, removing and adding items, and also a helpful _aria-live_ message
when an item has been removed from the selection.

## Breaking Changes in v8
## Breaking Changes in v8 and v9

_useMultipleSelection_ has been affected by breaking changes in v8, so check out
the [migration page][migration-guide-v8].
_useMultipleSelection_ has been affected by breaking changes in v8 and v9, so
check out the migration pages for [v8][migration-guide-v8] and
[v9][migration-guide-v9].

## Props used in examples

Expand Down Expand Up @@ -83,7 +92,7 @@ function MultipleComboBoxExample() {
const books = [
{id: 'book-1', author: 'Harper Lee', title: 'To Kill a Mockingbird'},
{id: 'book-2', author: 'Lev Tolstoy', title: 'War and Peace'},
{id: 'book-3', author: 'Fyodor Dostoyevsy', title: 'The Idiot'},
{id: 'book-3', author: 'Fyodor Dostoevsky', title: 'The Idiot'},
{id: 'book-4', author: 'Oscar Wilde', title: 'A Picture of Dorian Gray'},
{id: 'book-5', author: 'George Orwell', title: '1984'},
{id: 'book-6', author: 'Jane Austen', title: 'Pride and Prejudice'},
Expand Down Expand Up @@ -287,7 +296,7 @@ function MultipleSelectExample() {
const books = [
{id: 'book-1', author: 'Harper Lee', title: 'To Kill a Mockingbird'},
{id: 'book-2', author: 'Lev Tolstoy', title: 'War and Peace'},
{id: 'book-3', author: 'Fyodor Dostoyevsy', title: 'The Idiot'},
{id: 'book-3', author: 'Fyodor Dostoevsky', title: 'The Idiot'},
{id: 'book-4', author: 'Oscar Wilde', title: 'A Picture of Dorian Gray'},
{id: 'book-5', author: 'George Orwell', title: '1984'},
{id: 'book-6', author: 'Jane Austen', title: 'Pride and Prejudice'},
Expand Down Expand Up @@ -452,3 +461,5 @@ To see more cool stuff you can build with _useMultipleSelection_, explore the
https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Findex.js%3A25%2C12&moduleview=1
[migration-guide-v8]:
https://github.com/downshift-js/downshift/tree/master/src/hooks/MIGRATION_V8.md
[migration-guide-v9]:
https://github.com/downshift-js/downshift/tree/master/src/hooks/MIGRATION_V9.md
Loading