diff --git a/docs/downshift.mdx b/docs/downshift.mdx
index 2f48f46..9d69319 100644
--- a/docs/downshift.mdx
+++ b/docs/downshift.mdx
@@ -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'},
@@ -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'},
diff --git a/docs/hooks/index.mdx b/docs/hooks/index.mdx
index 107ba1c..83d60d0 100644
--- a/docs/hooks/index.mdx
+++ b/docs/hooks/index.mdx
@@ -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).
diff --git a/docs/hooks/useCombobox.mdx b/docs/hooks/useCombobox.mdx
index 7c82151..624a357 100644
--- a/docs/hooks/useCombobox.mdx
+++ b/docs/hooks/useCombobox.mdx
@@ -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
@@ -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'},
@@ -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
@@ -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'},
@@ -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'},
@@ -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'},
@@ -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'},
@@ -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'},
@@ -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 (
+
+ )
+ }
+
+ return
+}
+```
+
## Using action props
Action props are functions returned by _useSelect_ along with the state props
@@ -761,7 +918,7 @@ function SelectExample() {
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'},
@@ -1001,6 +1158,8 @@ repository][examples-code-sandbox].
https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fhooks%2FuseCombobox%2Faction-props.js&moduleview=1
[code-sandbox-react-virtual]:
https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fhooks%2FuseCombobox%2Freact-virtual.js&moduleview=1
+[code-sandbox-select-tag-group]:
+ https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fhooks%2FuseSelect%2Ftag-group-multiple-selection.js
[examples-code-sandbox]:
https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Findex.js&moduleview=1
[react-virtual-github]: https://github.com/tannerlinsley/react-virtual
@@ -1010,3 +1169,5 @@ repository][examples-code-sandbox].
https://github.com/downshift-js/downshift/tree/master/src/hooks/MIGRATION_V7.md#useselect
[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
diff --git a/docs/hooks/useTagGroup.mdx b/docs/hooks/useTagGroup.mdx
new file mode 100644
index 0000000..b0e1773
--- /dev/null
+++ b/docs/hooks/useTagGroup.mdx
@@ -0,0 +1,144 @@
+---
+title: useTagGroup
+description: useTagGroup for building tag lists
+slug: /use-tag-group
+---
+
+import {ThemedSnack} from '@site/src/components/ThemedSnack'
+
+# useTagGroup
+
+## The problem
+
+You want to build a tag group component in your app that's accessible and offers
+a great user experience. There is no dedicated ARIA design pattern for this
+component, but since it's widely used, we compiled the list of specifications
+and implemented them through a React hook that's compliant with Downshift's
+principles.
+
+## This solution
+
+**useTagGroup** is a React hook that manages all the stateful logic needed to
+make the tag group functional and accessible. It returns a set of props that are
+meant to be called, and their results destructured on the tag group's elements:
+its container, tag item and tag remove button. These are similar to the props
+provided by vanilla _Downshift_ to the children render prop.
+
+These props are called getter props, and their return values are destructured as
+a set of ARIA attributes and event listeners. Together with the action props and
+state props, they create all the stateful logic needed for the tag group to
+implement the list of requirements. Every functionality needed should be
+provided out-of-the-box: item removal and selection, and left/right arrow
+movement between items, screen reader support etc.
+
+## Props used in examples
+
+In the examples below, we use the _useTagGroup_ hook and destructure the getter
+props and state variables it returns. These are used in the following way:
+
+| Returned prop | Element | Comments |
+| ------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------ |
+| `getTagGroupProps` | `
` | Call and destructure its returned object on the container element. |
+| `getTagProps` | `` | Call and destructure its returned object on the tag element. |
+| `getTagRemoveProps` | `