Skip to content

Commit

Permalink
fix: Added hook to combobox for when options change (#2715)
Browse files Browse the repository at this point in the history
Co-authored-by: Shauna Keating <[email protected]>
  • Loading branch information
werdnanoslen and shkeating authored Mar 7, 2024
1 parent 34ccc9f commit 031b9fb
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 13 deletions.
44 changes: 37 additions & 7 deletions src/components/forms/ComboBox/ComboBox.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, { useRef } from 'react'
import React, { useRef, useState } from 'react'

import { ComboBox, ComboBoxRef } from './ComboBox'
import { Form } from '../Form/Form'
import { Label } from '../Label/Label'
import { TextInput } from '../TextInput/TextInput'
import { Button } from '../../Button/Button'
import { fruits } from './fruits'
import { fruits, veggies } from './foods'
import { Radio } from '../Radio/Radio'

export default {
title: 'Components/Combo box',
Expand Down Expand Up @@ -112,12 +112,42 @@ export const WithOtherFields = (): React.ReactElement => {
label: key,
}))

const veggieList = Object.entries(veggies).map(([value, key]) => ({
value: value,
label: key,
}))

const [options, setOptions] = useState(fruitList)

const ref = useRef<ComboBoxRef>(null)
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
ref.current?.clearSelection()
const selection = e.target.id
setOptions(selection === 'fruit' ? fruitList : veggieList)
}

return (
<Form onSubmit={noop}>
<Label htmlFor="fruit">Select a fruit</Label>
<ComboBox id="fruit" name="fruit" options={fruitList} onChange={noop} />
<Label htmlFor="fruitDescription">Description</Label>
<TextInput id="fruitDescription" name="fruitDescription" type="text" />
<Label htmlFor="food">Select a group</Label>
<Radio
name="food"
id="fruit"
label="Fruits"
onChange={handleChange}
defaultChecked></Radio>
<Radio
name="food"
id="veggie"
label="Vegetables"
onChange={handleChange}></Radio>
<Label htmlFor="food">Select a food</Label>
<ComboBox
id="fruit"
name="fruit"
options={options}
onChange={noop}
ref={ref}
/>
</Form>
)
}
Expand Down
39 changes: 33 additions & 6 deletions src/components/forms/ComboBox/ComboBox.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,29 @@ import React from 'react'
import { screen, render, waitFor } from '@testing-library/react'
import { userEvent } from '@testing-library/user-event'

import { ComboBox, ComboBoxRef } from './ComboBox'
import { ComboBox, ComboBoxOption, ComboBoxRef } from './ComboBox'
import { TextInput } from '../TextInput/TextInput'
import { fruits } from './fruits'
import { fruits, veggies } from './foods'

/*
Source of truth for combo box behavior is USWDS storybook examples and tests. For more:
- https://designsystem.digital.gov/form-controls/03-combo-box/
- https://github.com/uswds/uswds/tree/7a89611fe649650922e4d431b78c39fed6a867e1/spec/unit/combo-box
*/

const fruitOptions = Object.entries(fruits).map(([value, key]) => ({
value: value,
label: key,
}))
const fruitOptions: ComboBoxOption[] = Object.entries(fruits).map(
([value, key]) => ({
value: value,
label: key,
})
)

const veggieOptions: ComboBoxOption[] = Object.entries(veggies).map(
([value, key]) => ({
value: value,
label: key,
})
)

describe('ComboBox component', () => {
it('renders the expected markup without errors', () => {
Expand Down Expand Up @@ -87,6 +96,24 @@ describe('ComboBox component', () => {
expect(comboBoxInput).toHaveValue('Avocado')
})

it('updates options when prop changes', async () => {
const Wrapper = (props: { options: ComboBoxOption[] }) => {
return (
<ComboBox
id="favorite-fruit"
name="favorite-fruit"
options={props.options}
onChange={vi.fn()}
/>
)
}
const { rerender } = render(<Wrapper options={fruitOptions} />)
const comboBoxSelect = screen.getByTestId('combo-box-select')
expect(comboBoxSelect).toHaveValue(fruitOptions[0].value)
rerender(<Wrapper options={veggieOptions} />)
expect(comboBoxSelect).toHaveValue(veggieOptions[0].value)
})

describe('toggling the list', () => {
it('renders all options when the list is open', async () => {
const fruitAbridged = fruitOptions.slice(0, 3)
Expand Down
4 changes: 4 additions & 0 deletions src/components/forms/ComboBox/ComboBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ const ComboBoxForwardRef: React.ForwardRefRenderFunction<
const listRef = useRef<HTMLUListElement>(null)
const focusedItemRef = useRef<HTMLLIElement>(null)

useEffect(() => {
state.filteredOptions = options
}, [options])

useEffect(() => {
onChange && onChange(state.selectedOption?.value || undefined)
}, [state.selectedOption])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,9 @@ export const fruits = {
'white currant': 'White currant',
yuzu: 'Yuzu',
}

export const veggies = {
celery: 'Celery',
onion: 'Onion',
pepper: 'Pepper',
}

0 comments on commit 031b9fb

Please sign in to comment.