Skip to content

Commit da94b80

Browse files
authored
Attempt form submission when pressing Enter on Checkbox component (#2962)
* attempt to submit form when pressing `Enter` on the `Checkbox` component * add test to verify form submissions when pressing enter works * update changelog
1 parent edbcb81 commit da94b80

File tree

3 files changed

+58
-9
lines changed

3 files changed

+58
-9
lines changed

packages/@headlessui-react/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1414
- Use `isFocused` instead of `isFocusVisible` for `Input` and `Textarea` components ([#2940](https://github.com/tailwindlabs/headlessui/pull/2940))
1515
- Ensure `children` prop of `Field` component can be a render prop ([#2941](https://github.com/tailwindlabs/headlessui/pull/2941))
1616
- Add `hidden` attribute to internal `<Hidden />` component when the `Features.Hidden` feature is used ([#2955](https://github.com/tailwindlabs/headlessui/pull/2955))
17+
- Attempt form submission when pressing `Enter` on `Checkbox` component ([#2962](https://github.com/tailwindlabs/headlessui/pull/2962))
1718

1819
## [2.0.0-alpha.4] - 2024-01-03
1920

packages/@headlessui-react/src/components/checkbox/checkbox.test.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,47 @@ describe.each([
119119
})
120120
)
121121
})
122+
123+
describe('Form submissions', () => {
124+
it('should be possible to use in an uncontrolled way', async () => {
125+
let handleSubmission = jest.fn()
126+
127+
render(
128+
<form
129+
onSubmit={(e) => {
130+
e.preventDefault()
131+
handleSubmission(Object.fromEntries(new FormData(e.target as HTMLFormElement)))
132+
}}
133+
>
134+
<Checkbox name="notifications" />
135+
</form>
136+
)
137+
138+
// Focus the checkbox
139+
await focus(getCheckbox())
140+
141+
// Submit
142+
await press(Keys.Enter)
143+
144+
// No values
145+
expect(handleSubmission).toHaveBeenLastCalledWith({})
146+
147+
// Toggle
148+
await click(getCheckbox())
149+
150+
// Submit
151+
await press(Keys.Enter)
152+
153+
// Notifications should be on
154+
expect(handleSubmission).toHaveBeenLastCalledWith({ notifications: 'on' })
155+
156+
// Toggle
157+
await click(getCheckbox())
158+
159+
// Submit
160+
await press(Keys.Enter)
161+
162+
// Notifications should be off (or in this case, non-existant)
163+
expect(handleSubmission).toHaveBeenLastCalledWith({})
164+
})
165+
})

packages/@headlessui-react/src/components/checkbox/checkbox.tsx

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { FormFields } from '../../internal/form-fields'
2121
import { useProvidedId } from '../../internal/id'
2222
import type { Props } from '../../types'
2323
import { isDisabledReactIssue7711 } from '../../utils/bugs'
24+
import { attemptSubmit } from '../../utils/form'
2425
import {
2526
forwardRefWithAs,
2627
mergeProps,
@@ -110,20 +111,22 @@ function CheckboxFn<TTag extends ElementType = typeof DEFAULT_CHECKBOX_TAG, TTyp
110111

111112
let handleClick = useEvent((event: ReactMouseEvent) => {
112113
if (isDisabledReactIssue7711(event.currentTarget)) return event.preventDefault()
114+
event.preventDefault()
113115
toggle()
114116
})
115117

116-
let handleKeyDown = useEvent((event: ReactKeyboardEvent) => {
117-
if (isDisabledReactIssue7711(event.currentTarget)) return event.preventDefault()
118-
119-
switch (event.key) {
120-
case Keys.Space:
121-
event.preventDefault()
122-
toggle()
123-
break
118+
let handleKeyUp = useEvent((event: ReactKeyboardEvent<HTMLButtonElement>) => {
119+
if (event.key === Keys.Space) {
120+
event.preventDefault()
121+
toggle()
122+
} else if (event.key === Keys.Enter) {
123+
attemptSubmit(event.currentTarget)
124124
}
125125
})
126126

127+
// This is needed so that we can "cancel" the click event when we use the `Enter` key on a button.
128+
let handleKeyPress = useEvent((event: ReactKeyboardEvent<HTMLElement>) => event.preventDefault())
129+
127130
let { isFocusVisible: focus, focusProps } = useFocusRing({ autoFocus: props.autoFocus ?? false })
128131
let { isHovered: hover, hoverProps } = useHover({ isDisabled: disabled ?? false })
129132
let { pressed: active, pressProps } = useActivePress({ disabled: disabled ?? false })
@@ -139,7 +142,8 @@ function CheckboxFn<TTag extends ElementType = typeof DEFAULT_CHECKBOX_TAG, TTyp
139142
'aria-disabled': disabled ? true : undefined,
140143
indeterminate: indeterminate ? 'true' : undefined,
141144
tabIndex: 0,
142-
onKeyDown: disabled ? undefined : handleKeyDown,
145+
onKeyUp: disabled ? undefined : handleKeyUp,
146+
onKeyPress: disabled ? undefined : handleKeyPress,
143147
onClick: disabled ? undefined : handleClick,
144148
},
145149
focusProps,

0 commit comments

Comments
 (0)