Skip to content

Commit 53dd723

Browse files
authored
feat(useSelect): focus toggle on label click (#1561)
* feat(useSelect): focus toggle on label click * add types * add documentation
1 parent 4edfc30 commit 53dd723

File tree

4 files changed

+72
-8
lines changed

4 files changed

+72
-8
lines changed

src/hooks/useSelect/README.md

+4
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,10 @@ described below.
867867
- `Click`: It will select the item and close the menu.
868868
- `MouseOver`: It will highlight the item.
869869

870+
#### Label
871+
872+
- `Click`: It will move focus to the toggle element.
873+
870874
### Customizing Handlers
871875

872876
You can provide your own event handlers to `useSelect` which will be called

src/hooks/useSelect/__tests__/getLabelProps.test.js

+53-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import {renderUseSelect} from '../testUtils'
2-
import {defaultIds} from '../../testUtils'
1+
import {act, screen} from '@testing-library/react'
2+
import {renderSelect, renderUseSelect} from '../testUtils'
3+
import {defaultIds, getToggleButton, user} from '../../testUtils'
34

45
describe('getLabelProps', () => {
56
test('should have a default id assigned', () => {
@@ -36,4 +37,54 @@ describe('getLabelProps', () => {
3637

3738
expect(labelProps).toEqual(expect.objectContaining({foo: 'bar'}))
3839
})
40+
41+
test('on click moves focus to the toggle button', async () => {
42+
renderSelect()
43+
44+
await user.click(
45+
screen.getByText('Choose an element:', {selector: 'label'}),
46+
)
47+
48+
expect(getToggleButton()).toHaveFocus()
49+
})
50+
51+
test('event handler onClick is called along with downshift handler', () => {
52+
const userOnClick = jest.fn()
53+
const mockToggleButton = {focus: jest.fn()}
54+
const {result} = renderUseSelect()
55+
56+
act(() => {
57+
const {onClick} = result.current.getLabelProps({
58+
onClick: userOnClick,
59+
})
60+
const {ref} = result.current.getToggleButtonProps()
61+
ref(mockToggleButton)
62+
63+
onClick({})
64+
})
65+
66+
expect(userOnClick).toHaveBeenCalledTimes(1)
67+
expect(mockToggleButton.focus).toHaveBeenCalledTimes(1)
68+
})
69+
70+
test("the downshift handler is not called if 'preventDownshiftDefault' is passed in user event", () => {
71+
const userOnClick = jest.fn(event => {
72+
event.preventDownshiftDefault = true
73+
})
74+
const mockToggleButton = {focus: jest.fn()}
75+
const {result} = renderUseSelect()
76+
77+
act(() => {
78+
const {onClick} = result.current.getLabelProps({
79+
onClick: userOnClick,
80+
})
81+
const {ref} = result.current.getToggleButtonProps()
82+
ref(mockToggleButton)
83+
84+
onClick({})
85+
})
86+
87+
expect(userOnClick).toHaveBeenCalledTimes(1)
88+
expect(mockToggleButton.focus).not.toHaveBeenCalled()
89+
})
3990
})

src/hooks/useSelect/index.js

+12-5
Original file line numberDiff line numberDiff line change
@@ -322,11 +322,18 @@ function useSelect(userProps = {}) {
322322
)
323323
// Getter functions.
324324
const getLabelProps = useCallback(
325-
labelProps => ({
326-
id: elementIds.labelId,
327-
htmlFor: elementIds.toggleButtonId,
328-
...labelProps,
329-
}),
325+
({onClick, ...labelProps} = {}) => {
326+
const labelHandleClick = () => {
327+
toggleButtonRef.current?.focus()
328+
}
329+
330+
return {
331+
id: elementIds.labelId,
332+
htmlFor: elementIds.toggleButtonId,
333+
onClick: callAllEventHandlers(onClick, labelHandleClick),
334+
...labelProps,
335+
}
336+
},
330337
[elementIds],
331338
)
332339
const getMenuProps = useCallback(

typings/index.d.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,9 @@ export interface UseSelectGetToggleButtonReturnValue
419419
tabIndex: 0
420420
}
421421

422-
export interface UseSelectGetLabelPropsOptions extends GetLabelPropsOptions {}
422+
export interface UseSelectGetLabelPropsOptions extends GetLabelPropsOptions {
423+
onClick: React.MouseEventHandler
424+
}
423425
export interface UseSelectGetLabelPropsReturnValue
424426
extends GetLabelPropsReturnValue {}
425427

0 commit comments

Comments
 (0)