Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(select): allow to search on group options #2425

Merged
merged 1 commit into from
Mar 11, 2024
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
37 changes: 37 additions & 0 deletions docs/pages/components/select.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,43 @@ function () {
}
```

## With option groups, multiple selection and searchable

```jsx
function () {
const [value, setValue] = React.useState(['github', 'instagram'])

const handleChange = (newValue) => {
setValue(newValue)
}

return (
<Field label="Social networks">
<Select
isMultiple
isSearchable
options={constants.OPT_GROUP_ITEMS}
name="welcome13"
groupsEnabled
onChange={handleChange}
value={value}
renderGroupHeader={({ label, options }) => (
<Box p="xxs">
<Box display="flex" justifyContent="space-between" alignItems="center">
<Text variant="sm" m="0">
{label}
</Text>
<Tag>{options.length}</Tag>
</Box>
{options.length === 0 && <Text variant="xs">No results found</Text>}
</Box>
)}
/>
</Field>
)
}
```

## Properties

<props propTypes={props.propTypes.Select} />
Expand Down
12 changes: 11 additions & 1 deletion packages/Select/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,17 @@ export const Select = forwardRef<'input', SelectProps>(
const handleInputChange = (value: string) => {
// Update
if (isSearchable && value !== inputValue) {
const options = matchSorter(defaultOptions, value, { keys: ['label'] })
let options: (Option | OptionGroup)[] = []

if (groupsEnabled) {
options = matchSorter(defaultOptions as OptionGroup[], value, {
// should match on group.label OR group.options.label
keys: [item => item.label, item => item.options.map(option => option.label)],
})
} else {
options = matchSorter(defaultOptions, value, { keys: ['label'] })
}

setInputValue(value)
setOptions(options)
}
Expand Down
48 changes: 48 additions & 0 deletions packages/Select/tests/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,54 @@ test("<Select isSearchable> doesn't show list if no results", async () => {
expect(options).toBeNull()
})

test('<Select isSearchable groupsEnabled> should filters options.label', async () => {
const { getByRole, getByTestId } = render(
<Select
dataTestId="select"
groupsEnabled
isSearchable
name="select"
options={SOCIAL_OPT_GROUP}
renderGroupHeader={({ label, options }) => (
<div data-testid="group-header">
<h4>{label}</h4>
<span>{options.length}</span>
</div>
)}
/>
)

const select = getByTestId('select')
await userEvent.type(select, 'Instagram')

const options = getByRole('listbox').querySelectorAll('li')
expect(options.length).toBe(2) // Facebook, Instagram
})

test('<Select isSearchable groupsEnabled> should filters group.label', async () => {
const { getByRole, getByTestId } = render(
<Select
dataTestId="select"
groupsEnabled
isSearchable
name="select"
options={SOCIAL_OPT_GROUP}
renderGroupHeader={({ label, options }) => (
<div data-testid="group-header">
<h4>{label}</h4>
<span>{options.length}</span>
</div>
)}
/>
)

const select = getByTestId('select')
await userEvent.type(select, 'Personal')

const options = getByRole('listbox').querySelectorAll('li')
expect(options.length).toBe(2) // Facebook, Instagram
})

test('<Select isCreatable> can create new items', async () => {
const handleCreate = jest.fn()

Expand Down
Loading