Skip to content

Commit

Permalink
Forward disabled state to hidden inputs in form-like components (#3004
Browse files Browse the repository at this point in the history
)

* make hidden inputs disabled if the wrapping component is disabled

* add tests to verify disabled hidden form elements

* update changelog
  • Loading branch information
RobinMalfait committed Apr 15, 2024
1 parent e9df8dd commit 2fd9d1c
Show file tree
Hide file tree
Showing 18 changed files with 322 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/@headlessui-react/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Prevent default behaviour when clicking outside of a `Dialog.Panel` ([#2919](https://github.com/tailwindlabs/headlessui/pull/2919))
- Add `hidden` attribute to internal `<Hidden />` component when the `Features.Hidden` feature is used ([#2955](https://github.com/tailwindlabs/headlessui/pull/2955))
- Allow setting custom `tabIndex` on the `<Switch />` component ([#2966](https://github.com/tailwindlabs/headlessui/pull/2966))
- Forward `disabled` state to hidden inputs in form-like components ([#3004](https://github.com/tailwindlabs/headlessui/pull/3004))

## [1.7.18] - 2024-01-08

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5655,6 +5655,48 @@ describe('Form compatibility', () => {
expect(submits).lastCalledWith([['delivery', 'pickup']])
})

it('should not submit the data if the Combobox is disabled', async () => {
let submits = jest.fn()

function Example() {
let [value, setValue] = useState('home-delivery')
return (
<form
onSubmit={(event) => {
event.preventDefault()
submits([...new FormData(event.currentTarget).entries()])
}}
>
<input type="hidden" name="foo" value="bar" />
<Combobox value={value} onChange={setValue} name="delivery" disabled>
<Combobox.Input onChange={NOOP} />
<Combobox.Button>Trigger</Combobox.Button>
<Combobox.Label>Pizza Delivery</Combobox.Label>
<Combobox.Options>
<Combobox.Option value="pickup">Pickup</Combobox.Option>
<Combobox.Option value="home-delivery">Home delivery</Combobox.Option>
<Combobox.Option value="dine-in">Dine in</Combobox.Option>
</Combobox.Options>
</Combobox>
<button>Submit</button>
</form>
)
}

render(<Example />)

// Open combobox
await click(getComboboxButton())

// Submit the form
await click(getByText('Submit'))

// Verify that the form has been submitted
expect(submits).toHaveBeenLastCalledWith([
['foo', 'bar'], // The only available field
])
})

it('should be possible to submit a form with a complex value object', async () => {
let submits = jest.fn()
let options = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,7 @@ function ComboboxFn<TValue, TTag extends ElementType = typeof DEFAULT_COMBOBOX_T
hidden: true,
readOnly: true,
form: formName,
disabled,
name,
value,
})}
Expand Down
41 changes: 41 additions & 0 deletions packages/@headlessui-react/src/components/listbox/listbox.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4864,6 +4864,47 @@ describe('Form compatibility', () => {
expect(submits).lastCalledWith([['delivery', 'pickup']])
})

it('should not submit the data if the Listbox is disabled', async () => {
let submits = jest.fn()

function Example() {
let [value, setValue] = useState('home-delivery')
return (
<form
onSubmit={(event) => {
event.preventDefault()
submits([...new FormData(event.currentTarget).entries()])
}}
>
<input type="hidden" name="foo" value="bar" />
<Listbox value={value} onChange={setValue} name="delivery" disabled>
<Listbox.Button>Trigger</Listbox.Button>
<Listbox.Label>Pizza Delivery</Listbox.Label>
<Listbox.Options>
<Listbox.Option value="pickup">Pickup</Listbox.Option>
<Listbox.Option value="home-delivery">Home delivery</Listbox.Option>
<Listbox.Option value="dine-in">Dine in</Listbox.Option>
</Listbox.Options>
</Listbox>
<button>Submit</button>
</form>
)
}

render(<Example />)

// Open listbox
await click(getListboxButton())

// Submit the form
await click(getByText('Submit'))

// Verify that the form has been submitted
expect(submits).toHaveBeenLastCalledWith([
['foo', 'bar'], // The only available field
])
})

it('should be possible to submit a form with a complex value object', async () => {
let submits = jest.fn()
let options = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,7 @@ function ListboxFn<
hidden: true,
readOnly: true,
form: formName,
disabled,
name,
value,
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1485,6 +1485,41 @@ describe('Form compatibility', () => {
})
)

it('should not submit the data if the RadioGroup is disabled', async () => {
let submits = jest.fn()

function Example() {
let [value, setValue] = useState('home-delivery')
return (
<form
onSubmit={(event) => {
event.preventDefault()
submits([...new FormData(event.currentTarget).entries()])
}}
>
<input type="hidden" name="foo" value="bar" />
<RadioGroup value={value} onChange={setValue} name="delivery" disabled>
<RadioGroup.Label>Pizza Delivery</RadioGroup.Label>
<RadioGroup.Option value="pickup">Pickup</RadioGroup.Option>
<RadioGroup.Option value="home-delivery">Home delivery</RadioGroup.Option>
<RadioGroup.Option value="dine-in">Dine in</RadioGroup.Option>
</RadioGroup>
<button>Submit</button>
</form>
)
}

render(<Example />)

// Submit the form
await click(getByText('Submit'))

// Verify that the form has been submitted
expect(submits).toHaveBeenLastCalledWith([
['foo', 'bar'], // The only available field
])
})

it(
'should be possible to submit a form with a complex value object',
suppressConsoleLogs(async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ function RadioGroupFn<TTag extends ElementType = typeof DEFAULT_RADIO_GROUP_TAG,
hidden: true,
readOnly: true,
form: formName,
disabled,
name,
value,
})}
Expand Down
33 changes: 33 additions & 0 deletions packages/@headlessui-react/src/components/switch/switch.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -810,4 +810,37 @@ describe('Form compatibility', () => {
// Verify that the form has been submitted
expect(submits).lastCalledWith([['fruit', 'apple']])
})

it('should not submit the data if the Switch is disabled', async () => {
let submits = jest.fn()

function Example() {
let [state, setState] = useState(true)
return (
<form
onSubmit={(event) => {
event.preventDefault()
submits([...new FormData(event.currentTarget).entries()])
}}
>
<input type="hidden" name="foo" value="bar" />
<Switch.Group>
<Switch checked={state} onChange={setState} name="fruit" value="apple" disabled />
<Switch.Label>Apple</Switch.Label>
</Switch.Group>
<button>Submit</button>
</form>
)
}

render(<Example />)

// Submit the form
await click(getByText('Submit'))

// Verify that the form has been submitted
expect(submits).toHaveBeenLastCalledWith([
['foo', 'bar'], // The only available field
])
})
})
4 changes: 4 additions & 0 deletions packages/@headlessui-react/src/components/switch/switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ export type SwitchProps<TTag extends ElementType> = Props<
onChange?(checked: boolean): void
name?: string
value?: string
disabled?: boolean
form?: string
tabIndex?: number
}
Expand All @@ -129,6 +130,7 @@ function SwitchFn<TTag extends ElementType = typeof DEFAULT_SWITCH_TAG>(
checked: controlledChecked,
defaultChecked = false,
onChange: controlledOnChange,
disabled = false,
name,
value,
form,
Expand Down Expand Up @@ -172,6 +174,7 @@ function SwitchFn<TTag extends ElementType = typeof DEFAULT_SWITCH_TAG>(
'aria-checked': checked,
'aria-labelledby': groupContext?.labelledby,
'aria-describedby': groupContext?.describedby,
disabled,
onClick: handleClick,
onKeyUp: handleKeyUp,
onKeyPress: handleKeyPress,
Expand All @@ -198,6 +201,7 @@ function SwitchFn<TTag extends ElementType = typeof DEFAULT_SWITCH_TAG>(
type: 'checkbox',
hidden: true,
readOnly: true,
disabled,
form,
checked,
name,
Expand Down
1 change: 1 addition & 0 deletions packages/@headlessui-vue/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Don’t override explicit `disabled` prop for components inside `<MenuItem>` ([#2929](https://github.com/tailwindlabs/headlessui/pull/2929))
- Add `hidden` attribute to internal `<Hidden />` component when the `Features.Hidden` feature is used ([#2955](https://github.com/tailwindlabs/headlessui/pull/2955))
- Allow setting custom `tabIndex` on the `<Switch />` component ([#2966](https://github.com/tailwindlabs/headlessui/pull/2966))
- Forward `disabled` state to hidden inputs in form-like components ([#3004](https://github.com/tailwindlabs/headlessui/pull/3004))

## [1.7.19] - 2024-02-07

Expand Down
43 changes: 43 additions & 0 deletions packages/@headlessui-vue/src/components/combobox/combobox.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6146,6 +6146,49 @@ describe('Form compatibility', () => {
expect(submits).lastCalledWith([['delivery', 'pickup']])
})

it('should not submit the data if the Combobox is disabled', async () => {
let submits = jest.fn()

renderTemplate({
template: html`
<form @submit="handleSubmit">
<input type="hidden" name="foo" value="bar" />
<Combobox v-model="value" name="delivery" disabled>
<ComboboxInput />
<ComboboxButton>Trigger</ComboboxButton>
<ComboboxOptions>
<ComboboxOption value="pickup">Pickup</ComboboxOption>
<ComboboxOption value="home-delivery">Home delivery</ComboboxOption>
<ComboboxOption value="dine-in">Dine in</ComboboxOption>
</ComboboxOptions>
</Combobox>
<button>Submit</button>
</form>
`,
setup: () => {
let value = ref('home-delivery')
return {
value,
handleSubmit(event: SubmitEvent) {
event.preventDefault()
submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])
},
}
},
})

// Open combobox
await click(getComboboxButton())

// Submit the form
await click(getByText('Submit'))

// Verify that the form has been submitted
expect(submits).toHaveBeenLastCalledWith([
['foo', 'bar'], // The only available field
])
})

it('should be possible to submit a form with a complex value object', async () => {
let submits = jest.fn()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,7 @@ export let Combobox = defineComponent({
hidden: true,
readOnly: true,
form,
disabled,
name,
value,
})
Expand Down
42 changes: 42 additions & 0 deletions packages/@headlessui-vue/src/components/listbox/listbox.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5071,6 +5071,48 @@ describe('Form compatibility', () => {
expect(submits).lastCalledWith([['delivery', 'pickup']])
})

it('should not submit the data if the Listbox is disabled', async () => {
let submits = jest.fn()

renderTemplate({
template: html`
<form @submit="handleSubmit">
<input type="hidden" name="foo" value="bar" />
<Listbox v-model="value" name="delivery" disabled>
<ListboxButton>Trigger</ListboxButton>
<ListboxOptions>
<ListboxOption value="pickup">Pickup</ListboxOption>
<ListboxOption value="home-delivery">Home delivery</ListboxOption>
<ListboxOption value="dine-in">Dine in</ListboxOption>
</ListboxOptions>
</Listbox>
<button>Submit</button>
</form>
`,
setup: () => {
let value = ref('home-delivery')
return {
value,
handleSubmit(event: SubmitEvent) {
event.preventDefault()
submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])
},
}
},
})

// Open listbox
await click(getListboxButton())

// Submit the form
await click(getByText('Submit'))

// Verify that the form has been submitted
expect(submits).toHaveBeenLastCalledWith([
['foo', 'bar'], // The only available field
])
})

it('should be possible to submit a form with a complex value object', async () => {
let submits = jest.fn()

Expand Down
1 change: 1 addition & 0 deletions packages/@headlessui-vue/src/components/listbox/listbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ export let Listbox = defineComponent({
hidden: true,
readOnly: true,
form,
disabled,
name,
value,
})
Expand Down
Loading

0 comments on commit 2fd9d1c

Please sign in to comment.