Repository: tailwindlabs/headlessui
Pull request #3004 (2024-02-21 13:08:31)

Forward `disabled` state to hidden inputs in form-like components
This PR fixes an issue where form-like components are disabled then the hidden input fields should be disabled as well otherwise they will be submitted to the form.

We will now forward the `disabled` state to the hidden inputs.

Fixes: #2988


vercel[bot]: [vc]: #XFMtm9bVIaVWoByZM1t5TdWDXTP4a4CGwywr3CEXsxs=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJoZWFkbGVzc3VpLXJlYWN0Iiwicm9vdERpcmVjdG9yeSI6InBsYXlncm91bmRzL3JlYWN0IiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3RhaWx3aW5kbGFicy9oZWFkbGVzc3VpLXJlYWN0L0ZON2VGMVd0bTE3djc4eHhoWFhFQ1hiZHlTQkYiLCJwcmV2aWV3VXJsIjoiaGVhZGxlc3N1aS1yZWFjdC1naXQtZml4LWlzc3VlLTI5ODgtdGFpbHdpbmRsYWJzLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiUEVORElORyIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJoZWFkbGVzc3VpLXJlYWN0LWdpdC1maXgtaXNzdWUtMjk4OC10YWlsd2luZGxhYnMudmVyY2VsLmFwcCJ9fSx7Im5hbWUiOiJoZWFkbGVzc3VpLXZ1ZSIsInJvb3REaXJlY3RvcnkiOiJwbGF5Z3JvdW5kcy92dWUiLCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vdGFpbHdpbmRsYWJzL2hlYWRsZXNzdWktdnVlLzh4M1VOWVFlS3BGa2VHajl5ZktlanlBVzZwRmQiLCJwcmV2aWV3VXJsIjoiaGVhZGxlc3N1aS12dWUtZ2l0LWZpeC1pc3N1ZS0yOTg4LXRhaWx3aW5kbGFicy52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IlBFTkRJTkciLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiaGVhZGxlc3N1aS12dWUtZ2l0LWZpeC1pc3N1ZS0yOTg4LXRhaWx3aW5kbGFicy52ZXJjZWwuYXBwIn19XX0=
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **headlessui-react** | 🔄 Building ([Inspect](https://vercel.com/tailwindlabs/headlessui-react/FN7eF1Wtm17v78xxhXXECXbdySBF)) | [Visit Preview](https://vercel.live/open-feedback/headlessui-react-git-fix-issue-2988-tailwindlabs.vercel.app?via=pr-comment-visit-preview-link&passThrough=1) | 💬 [**Add feedback**](https://vercel.live/open-feedback/headlessui-react-git-fix-issue-2988-tailwindlabs.vercel.app?via=pr-comment-feedback-link) | Feb 21, 2024 1:08pm |
| **headlessui-vue** | 🔄 Building ([Inspect](https://vercel.com/tailwindlabs/headlessui-vue/8x3UNYQeKpFkeGj9yfKejyAW6pFd)) | [Visit Preview](https://vercel.live/open-feedback/headlessui-vue-git-fix-issue-2988-tailwindlabs.vercel.app?via=pr-comment-visit-preview-link&passThrough=1) | 💬 [**Add feedback**](https://vercel.live/open-feedback/headlessui-vue-git-fix-issue-2988-tailwindlabs.vercel.app?via=pr-comment-feedback-link) | Feb 21, 2024 1:08pm |




Patch:
diff --git a/packages/@headlessui-react/CHANGELOG.md b/packages/@headlessui-react/CHANGELOG.md
index 14cc2f7..1035c32 100644
--- a/packages/@headlessui-react/CHANGELOG.md
+++ b/packages/@headlessui-react/CHANGELOG.md
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Add `hidden` attribute to internal `<Hidden />` component when the `Features.Hidden` feature is used ([#2955](https://github.com/tailwindlabs/headlessui/pull/2955))
 - Attempt form submission when pressing `Enter` on `Checkbox` component ([#2962](https://github.com/tailwindlabs/headlessui/pull/2962))
 - 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))
 
 ### Changed
 
diff --git a/packages/@headlessui-react/src/components/checkbox/checkbox.tsx b/packages/@headlessui-react/src/components/checkbox/checkbox.tsx
index aaa7ad2..c67e965 100644
--- a/packages/@headlessui-react/src/components/checkbox/checkbox.tsx
+++ b/packages/@headlessui-react/src/components/checkbox/checkbox.tsx
@@ -173,7 +173,12 @@ function CheckboxFn<TTag extends ElementType = typeof DEFAULT_CHECKBOX_TAG, TTyp
   return (
     <>
       {name != null && (
-        <FormFields data={checked ? { [name]: value || 'on' } : {}} form={form} onReset={reset} />
+        <FormFields
+          disabled={disabled}
+          data={checked ? { [name]: value || 'on' } : {}}
+          form={form}
+          onReset={reset}
+        />
       )}
       {render({
         ourProps,
diff --git a/packages/@headlessui-react/src/components/combobox/combobox.tsx b/packages/@headlessui-react/src/components/combobox/combobox.tsx
index 8bfd8a7..7971322 100644
--- a/packages/@headlessui-react/src/components/combobox/combobox.tsx
+++ b/packages/@headlessui-react/src/components/combobox/combobox.tsx
@@ -907,6 +907,7 @@ function ComboboxFn<TValue, TTag extends ElementType = typeof DEFAULT_COMBOBOX_T
             >
               {name != null && (
                 <FormFields
+                  disabled={disabled}
                   data={value != null ? { [name]: value } : {}}
                   form={form}
                   onReset={reset}
diff --git a/packages/@headlessui-react/src/components/listbox/listbox.tsx b/packages/@headlessui-react/src/components/listbox/listbox.tsx
index 63f26e3..129dd74 100644
--- a/packages/@headlessui-react/src/components/listbox/listbox.tsx
+++ b/packages/@headlessui-react/src/components/listbox/listbox.tsx
@@ -670,7 +670,12 @@ function ListboxFn<
               })}
             >
               {name != null && value != null && (
-                <FormFields data={{ [name]: value }} form={form} onReset={reset} />
+                <FormFields
+                  disabled={disabled}
+                  data={{ [name]: value }}
+                  form={form}
+                  onReset={reset}
+                />
               )}
               {render({
                 ourProps,
diff --git a/packages/@headlessui-react/src/components/radio-group/radio-group.tsx b/packages/@headlessui-react/src/components/radio-group/radio-group.tsx
index 2ab3a7a..2fabaa2 100644
--- a/packages/@headlessui-react/src/components/radio-group/radio-group.tsx
+++ b/packages/@headlessui-react/src/components/radio-group/radio-group.tsx
@@ -314,6 +314,7 @@ function RadioGroupFn<TTag extends ElementType = typeof DEFAULT_RADIO_GROUP_TAG,
           <RadioGroupDataContext.Provider value={radioGroupData}>
             {name != null && (
               <FormFields
+                disabled={disabled}
                 data={value != null ? { [name]: value || 'on' } : {}}
                 form={form}
                 onReset={reset}
diff --git a/packages/@headlessui-react/src/components/switch/switch.tsx b/packages/@headlessui-react/src/components/switch/switch.tsx
index 76a4ab8..eedf7a9 100644
--- a/packages/@headlessui-react/src/components/switch/switch.tsx
+++ b/packages/@headlessui-react/src/components/switch/switch.tsx
@@ -237,7 +237,12 @@ function SwitchFn<TTag extends ElementType = typeof DEFAULT_SWITCH_TAG>(
   return (
     <>
       {name != null && (
-        <FormFields data={checked ? { [name]: value || 'on' } : {}} form={form} onReset={reset} />
+        <FormFields
+          disabled={disabled}
+          data={checked ? { [name]: value || 'on' } : {}}
+          form={form}
+          onReset={reset}
+        />
       )}
       {render({ ourProps, theirProps, slot, defaultTag: DEFAULT_SWITCH_TAG, name: 'Switch' })}
     </>
diff --git a/packages/@headlessui-react/src/internal/form-fields.tsx b/packages/@headlessui-react/src/internal/form-fields.tsx
index 3592c57..ab94d98 100644
--- a/packages/@headlessui-react/src/internal/form-fields.tsx
+++ b/packages/@headlessui-react/src/internal/form-fields.tsx
@@ -31,10 +31,12 @@ export function HoistFormFields({ children }: React.PropsWithChildren<{}>) {
 export function FormFields({
   data,
   form: formId,
+  disabled,
   onReset,
 }: {
   data: Record<string, any>
   form?: string
+  disabled?: boolean
   onReset?: (e: Event) => void
 }) {
   let [form, setForm] = useState<HTMLFormElement | null>(null)
@@ -61,6 +63,7 @@ export function FormFields({
               hidden: true,
               readOnly: true,
               form: formId,
+              disabled,
               name,
               value,
             })}
diff --git a/packages/@headlessui-vue/CHANGELOG.md b/packages/@headlessui-vue/CHANGELOG.md
index 25b14de..044bbd1 100644
--- a/packages/@headlessui-vue/CHANGELOG.md
+++ b/packages/@headlessui-vue/CHANGELOG.md
@@ -19,6 +19,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
 
diff --git a/packages/@headlessui-vue/src/components/combobox/combobox.ts b/packages/@headlessui-vue/src/components/combobox/combobox.ts
index 238c92e..e3e4cc3 100644
--- a/packages/@headlessui-vue/src/components/combobox/combobox.ts
+++ b/packages/@headlessui-vue/src/components/combobox/combobox.ts
@@ -735,6 +735,7 @@ export let Combobox = defineComponent({
                   hidden: true,
                   readOnly: true,
                   form,
+                  disabled,
                   name,
                   value,
                 })
diff --git a/packages/@headlessui-vue/src/components/listbox/listbox.ts b/packages/@headlessui-vue/src/components/listbox/listbox.ts
index f49c438..20b1ce7 100644
--- a/packages/@headlessui-vue/src/components/listbox/listbox.ts
+++ b/packages/@headlessui-vue/src/components/listbox/listbox.ts
@@ -392,6 +392,7 @@ export let Listbox = defineComponent({
                   hidden: true,
                   readOnly: true,
                   form,
+                  disabled,
                   name,
                   value,
                 })
diff --git a/packages/@headlessui-vue/src/components/radio-group/radio-group.ts b/packages/@headlessui-vue/src/components/radio-group/radio-group.ts
index 9bf8417..cd63c39 100644
--- a/packages/@headlessui-vue/src/components/radio-group/radio-group.ts
+++ b/packages/@headlessui-vue/src/components/radio-group/radio-group.ts
@@ -262,6 +262,7 @@ export let RadioGroup = defineComponent({
                   hidden: true,
                   readOnly: true,
                   form,
+                  disabled,
                   name,
                   value,
                 })
diff --git a/packages/@headlessui-vue/src/components/switch/switch.ts b/packages/@headlessui-vue/src/components/switch/switch.ts
index 4746dee..239e0df 100644
--- a/packages/@headlessui-vue/src/components/switch/switch.ts
+++ b/packages/@headlessui-vue/src/components/switch/switch.ts
@@ -78,6 +78,7 @@ export let Switch = defineComponent({
     name: { type: String, optional: true },
     value: { type: String, optional: true },
     id: { type: String, default: () => `headlessui-switch-${useId()}` },
+    disabled: { type: Boolean, default: false },
     tabIndex: { type: Number, default: 0 },
   },
   inheritAttrs: false,
@@ -172,6 +173,7 @@ export let Switch = defineComponent({
                 readOnly: true,
                 checked: checked.value,
                 form,
+                disabled: theirProps.disabled,
                 name,
                 value,
               })


Test patch:
diff --git a/packages/@headlessui-react/src/components/combobox/combobox.test.tsx b/packages/@headlessui-react/src/components/combobox/combobox.test.tsx
index 525f345..562a33e 100644
--- a/packages/@headlessui-react/src/components/combobox/combobox.test.tsx
+++ b/packages/@headlessui-react/src/components/combobox/combobox.test.tsx
@@ -5747,6 +5747,48 @@ describe('Form compatibility', () => {
     expect(submits).toHaveBeenLastCalledWith([['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 = [
diff --git a/packages/@headlessui-react/src/components/listbox/listbox.test.tsx b/packages/@headlessui-react/src/components/listbox/listbox.test.tsx
index acab326..b06c177 100644
--- a/packages/@headlessui-react/src/components/listbox/listbox.test.tsx
+++ b/packages/@headlessui-react/src/components/listbox/listbox.test.tsx
@@ -4670,6 +4670,47 @@ describe('Form compatibility', () => {
     expect(submits).toHaveBeenLastCalledWith([['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 = [
diff --git a/packages/@headlessui-react/src/components/radio-group/radio-group.test.tsx b/packages/@headlessui-react/src/components/radio-group/radio-group.test.tsx
index 85f0fd4..436b2e3 100644
--- a/packages/@headlessui-react/src/components/radio-group/radio-group.test.tsx
+++ b/packages/@headlessui-react/src/components/radio-group/radio-group.test.tsx
@@ -1539,6 +1539,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 () => {
diff --git a/packages/@headlessui-react/src/components/switch/switch.test.tsx b/packages/@headlessui-react/src/components/switch/switch.test.tsx
index b6988f2..06d2c46 100644
--- a/packages/@headlessui-react/src/components/switch/switch.test.tsx
+++ b/packages/@headlessui-react/src/components/switch/switch.test.tsx
@@ -810,4 +810,37 @@ describe('Form compatibility', () => {
     // Verify that the form has been submitted
     expect(submits).toHaveBeenLastCalledWith([['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
+    ])
+  })
 })
diff --git a/packages/@headlessui-react/src/test-utils/scenarios.tsx b/packages/@headlessui-react/src/test-utils/scenarios.tsx
index 31ee14f..c576a43 100644
--- a/packages/@headlessui-react/src/test-utils/scenarios.tsx
+++ b/packages/@headlessui-react/src/test-utils/scenarios.tsx
@@ -202,6 +202,35 @@ export function commonFormScenarios(
       expect(formDataMock.mock.calls[0][0].has('foo')).toBe(true)
     })
 
+    it('should not submit the data if the control is disabled', async () => {
+      let submits = jest.fn()
+
+      function Example() {
+        return (
+          <form
+            onSubmit={(event) => {
+              event.preventDefault()
+              submits([...new FormData(event.currentTarget).entries()])
+            }}
+          >
+            <input type="hidden" name="foo" value="bar" />
+            <Control name="bar" disabled />
+            <button>Submit</button>
+          </form>
+        )
+      }
+
+      render(<Example />)
+
+      // Submit the form
+      await click(screen.getByText('Submit'))
+
+      // Verify that the form has been submitted
+      expect(submits).toHaveBeenLastCalledWith([
+        ['foo', 'bar'], // The only available field
+      ])
+    })
+
     it(
       'should reset the control when the form is reset',
       suppressConsoleLogs(async () => {
diff --git a/packages/@headlessui-vue/src/components/combobox/combobox.test.ts b/packages/@headlessui-vue/src/components/combobox/combobox.test.ts
index a3b638f..3222bf1 100644
--- a/packages/@headlessui-vue/src/components/combobox/combobox.test.ts
+++ b/packages/@headlessui-vue/src/components/combobox/combobox.test.ts
@@ -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()
 
diff --git a/packages/@headlessui-vue/src/components/listbox/listbox.test.tsx b/packages/@headlessui-vue/src/components/listbox/listbox.test.tsx
index 5beb32b..b09c3c3 100644
--- a/packages/@headlessui-vue/src/components/listbox/listbox.test.tsx
+++ b/packages/@headlessui-vue/src/components/listbox/listbox.test.tsx
@@ -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()
 
diff --git a/packages/@headlessui-vue/src/components/radio-group/radio-group.test.ts b/packages/@headlessui-vue/src/components/radio-group/radio-group.test.ts
index f491ae1..42d671a 100644
--- a/packages/@headlessui-vue/src/components/radio-group/radio-group.test.ts
+++ b/packages/@headlessui-vue/src/components/radio-group/radio-group.test.ts
@@ -1680,6 +1680,43 @@ describe('Form compatibility', () => {
     expect(submits).lastCalledWith([['delivery', 'pickup']])
   })
 
+  it('should not submit the data if the RadioGroup is disabled', async () => {
+    let submits = jest.fn()
+
+    renderTemplate({
+      template: html`
+        <form @submit="handleSubmit">
+          <input type="hidden" name="foo" value="bar" />
+          <RadioGroup v-model="value" name="delivery" disabled>
+            <RadioGroupLabel>Pizza Delivery</RadioGroupLabel>
+            <RadioGroupOption value="pickup">Pickup</RadioGroupOption>
+            <RadioGroupOption value="home-delivery">Home delivery</RadioGroupOption>
+            <RadioGroupOption value="dine-in">Dine in</RadioGroupOption>
+          </RadioGroup>
+          <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()])
+          },
+        }
+      },
+    })
+
+    // 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()
 
diff --git a/packages/@headlessui-vue/src/components/switch/switch.test.tsx b/packages/@headlessui-vue/src/components/switch/switch.test.tsx
index c4008b4..ce39576 100644
--- a/packages/@headlessui-vue/src/components/switch/switch.test.tsx
+++ b/packages/@headlessui-vue/src/components/switch/switch.test.tsx
@@ -929,4 +929,39 @@ 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()
+
+    renderTemplate({
+      template: html`
+        <form @submit="handleSubmit">
+          <input type="hidden" name="foo" value="bar" />
+          <SwitchGroup>
+            <Switch v-model="checked" name="fruit" value="apple" disabled />
+            <SwitchLabel>Apple</SwitchLabel>
+          </SwitchGroup>
+          <button>Submit</button>
+        </form>
+      `,
+      setup: () => {
+        let checked = ref(true)
+        return {
+          checked,
+          handleSubmit(event: SubmitEvent) {
+            event.preventDefault()
+            submits([...new FormData(event.currentTarget as HTMLFormElement).entries()])
+          },
+        }
+      },
+    })
+
+    // Submit the form
+    await click(getByText('Submit'))
+
+    // Verify that the form has been submitted
+    expect(submits).toHaveBeenLastCalledWith([
+      ['foo', 'bar'], // The only available field
+    ])
+  })
 })