Skip to content

Commit a50be92

Browse files
authored
Forward disabled state to hidden inputs in form-like components (#3004)
* make hidden inputs disabled if the wrapping component is disabled * add tests to verify disabled hidden form elements * update changelog
1 parent 08baf09 commit a50be92

File tree

21 files changed

+367
-3
lines changed

21 files changed

+367
-3
lines changed

packages/@headlessui-react/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
- Add `hidden` attribute to internal `<Hidden />` component when the `Features.Hidden` feature is used ([#2955](https://github.com/tailwindlabs/headlessui/pull/2955))
1717
- Attempt form submission when pressing `Enter` on `Checkbox` component ([#2962](https://github.com/tailwindlabs/headlessui/pull/2962))
1818
- Allow setting custom `tabIndex` on the `<Switch />` component ([#2966](https://github.com/tailwindlabs/headlessui/pull/2966))
19+
- Forward `disabled` state to hidden inputs in form-like components ([#3004](https://github.com/tailwindlabs/headlessui/pull/3004))
1920

2021
### Changed
2122

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,12 @@ function CheckboxFn<TTag extends ElementType = typeof DEFAULT_CHECKBOX_TAG, TTyp
173173
return (
174174
<>
175175
{name != null && (
176-
<FormFields data={checked ? { [name]: value || 'on' } : {}} form={form} onReset={reset} />
176+
<FormFields
177+
disabled={disabled}
178+
data={checked ? { [name]: value || 'on' } : {}}
179+
form={form}
180+
onReset={reset}
181+
/>
177182
)}
178183
{render({
179184
ourProps,

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5747,6 +5747,48 @@ describe('Form compatibility', () => {
57475747
expect(submits).toHaveBeenLastCalledWith([['delivery', 'pickup']])
57485748
})
57495749

5750+
it('should not submit the data if the Combobox is disabled', async () => {
5751+
let submits = jest.fn()
5752+
5753+
function Example() {
5754+
let [value, setValue] = useState('home-delivery')
5755+
return (
5756+
<form
5757+
onSubmit={(event) => {
5758+
event.preventDefault()
5759+
submits([...new FormData(event.currentTarget).entries()])
5760+
}}
5761+
>
5762+
<input type="hidden" name="foo" value="bar" />
5763+
<Combobox value={value} onChange={setValue} name="delivery" disabled>
5764+
<Combobox.Input onChange={NOOP} />
5765+
<Combobox.Button>Trigger</Combobox.Button>
5766+
<Combobox.Label>Pizza Delivery</Combobox.Label>
5767+
<Combobox.Options>
5768+
<Combobox.Option value="pickup">Pickup</Combobox.Option>
5769+
<Combobox.Option value="home-delivery">Home delivery</Combobox.Option>
5770+
<Combobox.Option value="dine-in">Dine in</Combobox.Option>
5771+
</Combobox.Options>
5772+
</Combobox>
5773+
<button>Submit</button>
5774+
</form>
5775+
)
5776+
}
5777+
5778+
render(<Example />)
5779+
5780+
// Open combobox
5781+
await click(getComboboxButton())
5782+
5783+
// Submit the form
5784+
await click(getByText('Submit'))
5785+
5786+
// Verify that the form has been submitted
5787+
expect(submits).toHaveBeenLastCalledWith([
5788+
['foo', 'bar'], // The only available field
5789+
])
5790+
})
5791+
57505792
it('should be possible to submit a form with a complex value object', async () => {
57515793
let submits = jest.fn()
57525794
let options = [

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,7 @@ function ComboboxFn<TValue, TTag extends ElementType = typeof DEFAULT_COMBOBOX_T
907907
>
908908
{name != null && (
909909
<FormFields
910+
disabled={disabled}
910911
data={value != null ? { [name]: value } : {}}
911912
form={form}
912913
onReset={reset}

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4670,6 +4670,47 @@ describe('Form compatibility', () => {
46704670
expect(submits).toHaveBeenLastCalledWith([['delivery', 'pickup']])
46714671
})
46724672

4673+
it('should not submit the data if the Listbox is disabled', async () => {
4674+
let submits = jest.fn()
4675+
4676+
function Example() {
4677+
let [value, setValue] = useState('home-delivery')
4678+
return (
4679+
<form
4680+
onSubmit={(event) => {
4681+
event.preventDefault()
4682+
submits([...new FormData(event.currentTarget).entries()])
4683+
}}
4684+
>
4685+
<input type="hidden" name="foo" value="bar" />
4686+
<Listbox value={value} onChange={setValue} name="delivery" disabled>
4687+
<Listbox.Button>Trigger</Listbox.Button>
4688+
<Listbox.Label>Pizza Delivery</Listbox.Label>
4689+
<Listbox.Options>
4690+
<Listbox.Option value="pickup">Pickup</Listbox.Option>
4691+
<Listbox.Option value="home-delivery">Home delivery</Listbox.Option>
4692+
<Listbox.Option value="dine-in">Dine in</Listbox.Option>
4693+
</Listbox.Options>
4694+
</Listbox>
4695+
<button>Submit</button>
4696+
</form>
4697+
)
4698+
}
4699+
4700+
render(<Example />)
4701+
4702+
// Open listbox
4703+
await click(getListboxButton())
4704+
4705+
// Submit the form
4706+
await click(getByText('Submit'))
4707+
4708+
// Verify that the form has been submitted
4709+
expect(submits).toHaveBeenLastCalledWith([
4710+
['foo', 'bar'], // The only available field
4711+
])
4712+
})
4713+
46734714
it('should be possible to submit a form with a complex value object', async () => {
46744715
let submits = jest.fn()
46754716
let options = [

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,12 @@ function ListboxFn<
670670
})}
671671
>
672672
{name != null && value != null && (
673-
<FormFields data={{ [name]: value }} form={form} onReset={reset} />
673+
<FormFields
674+
disabled={disabled}
675+
data={{ [name]: value }}
676+
form={form}
677+
onReset={reset}
678+
/>
674679
)}
675680
{render({
676681
ourProps,

packages/@headlessui-react/src/components/radio-group/radio-group.test.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1539,6 +1539,41 @@ describe('Form compatibility', () => {
15391539
})
15401540
)
15411541

1542+
it('should not submit the data if the RadioGroup is disabled', async () => {
1543+
let submits = jest.fn()
1544+
1545+
function Example() {
1546+
let [value, setValue] = useState('home-delivery')
1547+
return (
1548+
<form
1549+
onSubmit={(event) => {
1550+
event.preventDefault()
1551+
submits([...new FormData(event.currentTarget).entries()])
1552+
}}
1553+
>
1554+
<input type="hidden" name="foo" value="bar" />
1555+
<RadioGroup value={value} onChange={setValue} name="delivery" disabled>
1556+
<RadioGroup.Label>Pizza Delivery</RadioGroup.Label>
1557+
<RadioGroup.Option value="pickup">Pickup</RadioGroup.Option>
1558+
<RadioGroup.Option value="home-delivery">Home delivery</RadioGroup.Option>
1559+
<RadioGroup.Option value="dine-in">Dine in</RadioGroup.Option>
1560+
</RadioGroup>
1561+
<button>Submit</button>
1562+
</form>
1563+
)
1564+
}
1565+
1566+
render(<Example />)
1567+
1568+
// Submit the form
1569+
await click(getByText('Submit'))
1570+
1571+
// Verify that the form has been submitted
1572+
expect(submits).toHaveBeenLastCalledWith([
1573+
['foo', 'bar'], // The only available field
1574+
])
1575+
})
1576+
15421577
it(
15431578
'should be possible to submit a form with a complex value object',
15441579
suppressConsoleLogs(async () => {

packages/@headlessui-react/src/components/radio-group/radio-group.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ function RadioGroupFn<TTag extends ElementType = typeof DEFAULT_RADIO_GROUP_TAG,
314314
<RadioGroupDataContext.Provider value={radioGroupData}>
315315
{name != null && (
316316
<FormFields
317+
disabled={disabled}
317318
data={value != null ? { [name]: value || 'on' } : {}}
318319
form={form}
319320
onReset={reset}

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,4 +810,37 @@ describe('Form compatibility', () => {
810810
// Verify that the form has been submitted
811811
expect(submits).toHaveBeenLastCalledWith([['fruit', 'apple']])
812812
})
813+
814+
it('should not submit the data if the Switch is disabled', async () => {
815+
let submits = jest.fn()
816+
817+
function Example() {
818+
let [state, setState] = useState(true)
819+
return (
820+
<form
821+
onSubmit={(event) => {
822+
event.preventDefault()
823+
submits([...new FormData(event.currentTarget).entries()])
824+
}}
825+
>
826+
<input type="hidden" name="foo" value="bar" />
827+
<Switch.Group>
828+
<Switch checked={state} onChange={setState} name="fruit" value="apple" disabled />
829+
<Switch.Label>Apple</Switch.Label>
830+
</Switch.Group>
831+
<button>Submit</button>
832+
</form>
833+
)
834+
}
835+
836+
render(<Example />)
837+
838+
// Submit the form
839+
await click(getByText('Submit'))
840+
841+
// Verify that the form has been submitted
842+
expect(submits).toHaveBeenLastCalledWith([
843+
['foo', 'bar'], // The only available field
844+
])
845+
})
813846
})

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,12 @@ function SwitchFn<TTag extends ElementType = typeof DEFAULT_SWITCH_TAG>(
237237
return (
238238
<>
239239
{name != null && (
240-
<FormFields data={checked ? { [name]: value || 'on' } : {}} form={form} onReset={reset} />
240+
<FormFields
241+
disabled={disabled}
242+
data={checked ? { [name]: value || 'on' } : {}}
243+
form={form}
244+
onReset={reset}
245+
/>
241246
)}
242247
{render({ ourProps, theirProps, slot, defaultTag: DEFAULT_SWITCH_TAG, name: 'Switch' })}
243248
</>

0 commit comments

Comments
 (0)