Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: tailwindlabs/headlessui
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: @headlessui/react@v1.7.18
Choose a base ref
...
head repository: tailwindlabs/headlessui
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: @headlessui/react@v1.7.19
Choose a head ref

Commits on Jan 8, 2024

  1. Verified

    This commit was signed with the committer’s verified signature.
    RobinMalfait Robin Malfait
    Copy the full SHA
    d5c2c0a View commit details
  2. Verified

    This commit was signed with the committer’s verified signature.
    RobinMalfait Robin Malfait
    Copy the full SHA
    7ca6174 View commit details
  3. Verified

    This commit was signed with the committer’s verified signature.
    RobinMalfait Robin Malfait
    Copy the full SHA
    7c6fa3d View commit details

Commits on Feb 2, 2024

  1. Allow users customize ID generation (#2959)

    * Move `useId` calls inside setup()
    
    * Allow injecting custom id generator
    
    Vue does not currently have a native `useId` helper. However, Nuxt has created their own that they ensure works across the client/server boundary.
    
    Now a user can use `provide()` in their app to inject a custom useId generation function which, for Nuxt users, can defer to the one provided by Nuxt.
    
    * Add tests
    
    * Export a `provideUseId` helper
    thecrypticace authored Feb 2, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    9a24198 View commit details
  2. Update changelog

    thecrypticace committed Feb 2, 2024
    Copy the full SHA
    10c1659 View commit details
  3. prepare patch release

    thecrypticace committed Feb 2, 2024
    Copy the full SHA
    9a1ae60 View commit details
  4. Update comment

    thecrypticace committed Feb 2, 2024
    Copy the full SHA
    1e03db2 View commit details
  5. Copy the full SHA
    9cd8f7b View commit details
  6. Update tests

    thecrypticace committed Feb 2, 2024
    Copy the full SHA
    1d1046e View commit details
  7. Cleanup code a bit

    thecrypticace committed Feb 2, 2024
    Copy the full SHA
    e6136aa View commit details

Commits on Feb 7, 2024

  1. Fix Combobox activeOption render prop (#2973)

    * Fix Combobox `activeOption` render prop
    
    * Update changelog
    reinink authored Feb 7, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    a3ca52e View commit details
  2. Copy the full SHA
    b2300ca View commit details

Commits on Apr 12, 2024

  1. Make sure panels re-register when IDs are calculated in React < 18 (#…

    …2883)
    
    * Make sure panels re-register when IDs are calculated in React < 18
    
    * Update changelog
    
    * Add comment
    thecrypticace authored Apr 12, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    2b161b3 View commit details

Commits on Apr 15, 2024

  1. Expose disabled state on <Tab /> component (#2918)

    * expose `disabled` on `<Tab/>` component
    
    This will expose it such that you can use it with `ui-disabled`. In the
    Alpha version of React, you can also use `data-[disabled]` because it
    will be exposed as `data-disabled` over there as well.
    
    Fixes: #2864
    
    * update changelog
    RobinMalfait committed Apr 15, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    RobinMalfait Robin Malfait
    Copy the full SHA
    369be39 View commit details
  2. Prevent default behaviour when clicking outside of a Dialog.Panel (#…

    …2919)
    
    * use `event.preventDefault()` in the `useOutsideClick` on Dialog's
    
    When using a `Dialog`, we should prevent the default behaviour of the
    event that triggered the "close" in the `useOutsideClick` call.
    
    We recently made improvements to improve outside click behaviour on
    touch devices (#2572) but
    due to the `touchend` event, the touch is still forwarded and therefore
    a potential button _behind_ the "backdrop" will also be clicked. This is
    not what we want.
    
    Added the `event.preventDefault()` for the Dialog specifically because
    there are other places where we use `useOutsideClick` and where we _do_
    want the behaviour where the click just continues. A concrete example of
    this is 2 `Menu`'s next to eachother where you open the first one, and
    then click on the second one. This should close first one (outside
    click) and open the second one (by not preventing the event)
    
    * update changelog
    RobinMalfait committed Apr 15, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    RobinMalfait Robin Malfait
    Copy the full SHA
    39210df View commit details
  3. Don’t override explicit disabled prop for components inside `<MenuI…

    …tem>` (#2929)
    
    * Don’t override explicit disabled prop inside `<MenuItem>`
    
    * Update changelog
    thecrypticace authored and RobinMalfait committed Apr 15, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    RobinMalfait Robin Malfait
    Copy the full SHA
    46a9b32 View commit details
  4. Add hidden attribute to internal <Hidden /> component when the `F…

    …eatures.Hidden` feature is used (#2955)
    
    * add `hidden` attribute for `<Hidden features={Features.Hidden}>`
    
    * update changelog
    RobinMalfait committed Apr 15, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    RobinMalfait Robin Malfait
    Copy the full SHA
    04695b2 View commit details
  5. Allow setting custom tabIndex on the <Switch /> component (#2966)

    * allow setting a custom `tabIndex` on the `<Switch />` component
    
    * update changelog
    RobinMalfait committed Apr 15, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    RobinMalfait Robin Malfait
    Copy the full SHA
    e9df8dd View commit details
  6. 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
    RobinMalfait committed Apr 15, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    RobinMalfait Robin Malfait
    Copy the full SHA
    2fd9d1c View commit details
  7. Respect selectedIndex for controlled <Tab/> components (#3037)

    * ensure controlled `<Tab>` components respect the `selectedIndex`
    
    * update changelog
    
    * use older syntax in tests
    
    * run prettier to fix lint step
    RobinMalfait committed Apr 15, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    RobinMalfait Robin Malfait
    Copy the full SHA
    6784a73 View commit details
  8. 1.7.19 - @headlessui/react

    RobinMalfait committed Apr 15, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    RobinMalfait Robin Malfait
    Copy the full SHA
    8599e49 View commit details
Showing with 886 additions and 162 deletions.
  1. +18 −2 packages/@headlessui-react/CHANGELOG.md
  2. +1 −1 packages/@headlessui-react/package.json
  3. +42 −0 packages/@headlessui-react/src/components/combobox/combobox.test.tsx
  4. +1 −0 packages/@headlessui-react/src/components/combobox/combobox.tsx
  5. +8 −1 packages/@headlessui-react/src/components/dialog/dialog.tsx
  6. +41 −0 packages/@headlessui-react/src/components/listbox/listbox.test.tsx
  7. +1 −0 packages/@headlessui-react/src/components/listbox/listbox.tsx
  8. +41 −0 packages/@headlessui-react/src/components/menu/menu.test.tsx
  9. +35 −0 packages/@headlessui-react/src/components/radio-group/radio-group.test.tsx
  10. +1 −0 packages/@headlessui-react/src/components/radio-group/radio-group.tsx
  11. +74 −0 packages/@headlessui-react/src/components/switch/switch.test.tsx
  12. +7 −7 packages/@headlessui-react/src/components/switch/switch.tsx
  13. +53 −6 packages/@headlessui-react/src/components/tabs/tabs.test.tsx
  14. +33 −5 packages/@headlessui-react/src/components/tabs/tabs.tsx
  15. +1 −0 packages/@headlessui-react/src/internal/hidden.tsx
  16. +8 −1 packages/@headlessui-react/src/test-utils/accessibility-assertions.ts
  17. +26 −1 packages/@headlessui-vue/CHANGELOG.md
  18. +1 −1 packages/@headlessui-vue/package.json
  19. +43 −0 packages/@headlessui-vue/src/components/combobox/combobox.test.ts
  20. +11 −7 packages/@headlessui-vue/src/components/combobox/combobox.ts
  21. +4 −20 packages/@headlessui-vue/src/components/description/description.test.ts
  22. +5 −3 packages/@headlessui-vue/src/components/description/description.ts
  23. +18 −12 packages/@headlessui-vue/src/components/dialog/dialog.ts
  24. +4 −20 packages/@headlessui-vue/src/components/label/label.test.ts
  25. +4 −3 packages/@headlessui-vue/src/components/label/label.ts
  26. +42 −0 packages/@headlessui-vue/src/components/listbox/listbox.test.tsx
  27. +21 −16 packages/@headlessui-vue/src/components/listbox/listbox.ts
  28. +47 −1 packages/@headlessui-vue/src/components/menu/menu.test.tsx
  29. +14 −13 packages/@headlessui-vue/src/components/menu/menu.ts
  30. +8 −6 packages/@headlessui-vue/src/components/popover/popover.ts
  31. +37 −0 packages/@headlessui-vue/src/components/radio-group/radio-group.test.ts
  32. +12 −7 packages/@headlessui-vue/src/components/radio-group/radio-group.ts
  33. +67 −0 packages/@headlessui-vue/src/components/switch/switch.test.tsx
  34. +8 −3 packages/@headlessui-vue/src/components/switch/switch.ts
  35. +41 −3 packages/@headlessui-vue/src/components/tabs/tabs.ssr.test.ts
  36. +45 −6 packages/@headlessui-vue/src/components/tabs/tabs.test.ts
  37. +25 −12 packages/@headlessui-vue/src/components/tabs/tabs.ts
  38. +20 −4 packages/@headlessui-vue/src/hooks/use-id.ts
  39. +3 −0 packages/@headlessui-vue/src/index.test.ts
  40. +1 −0 packages/@headlessui-vue/src/index.ts
  41. +1 −0 packages/@headlessui-vue/src/internal/hidden.ts
  42. +8 −1 packages/@headlessui-vue/src/test-utils/accessibility-assertions.ts
  43. +5 −0 scripts/release-channel.js
20 changes: 18 additions & 2 deletions packages/@headlessui-react/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -7,7 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- Nothing yet!
### Added

- Add `immediate` prop to `<Combobox />` for immediately opening the Combobox when the `input` receives focus ([#2686](https://github.com/tailwindlabs/headlessui/pull/2686))
- Add `virtual` prop to `Combobox` component ([#2779](https://github.com/tailwindlabs/headlessui/pull/2779))

## [1.7.19] - 2024-04-15

### Fixed

- Make sure panels re-register when IDs are calculated in React < 18 ([#2883](https://github.com/tailwindlabs/headlessui/pull/2883))
- Expose `disabled` state on `<Tab />` component ([#2918](https://github.com/tailwindlabs/headlessui/pull/2918))
- 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))
- Respect `selectedIndex` for controlled `<Tab/>` components ([#3037](https://github.com/tailwindlabs/headlessui/pull/3037))

## [1.7.18] - 2024-01-08

@@ -553,7 +568,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Everything!

[unreleased]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.18...HEAD
[unreleased]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.19...HEAD
[1.7.19]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.18...@headlessui/react@v1.7.19
[1.7.18]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.17...@headlessui/react@v1.7.18
[1.7.17]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.16...v1.7.17
[1.7.16]: https://github.com/tailwindlabs/headlessui/compare/@headlessui/react@v1.7.15...@headlessui/react@v1.7.16
2 changes: 1 addition & 1 deletion packages/@headlessui-react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@headlessui/react",
"version": "1.7.18",
"version": "1.7.19",
"description": "A set of completely unstyled, fully accessible UI components for React, designed to integrate beautifully with Tailwind CSS.",
"main": "dist/index.cjs",
"typings": "dist/index.d.ts",
Original file line number Diff line number Diff line change
@@ -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 = [
Original file line number Diff line number Diff line change
@@ -942,6 +942,7 @@ function ComboboxFn<TValue, TTag extends ElementType = typeof DEFAULT_COMBOBOX_T
hidden: true,
readOnly: true,
form: formName,
disabled,
name,
value,
})}
9 changes: 8 additions & 1 deletion packages/@headlessui-react/src/components/dialog/dialog.tsx
Original file line number Diff line number Diff line change
@@ -299,7 +299,14 @@ function DialogFn<TTag extends ElementType = typeof DEFAULT_DIALOG_TAG>(
if (hasNestedDialogs) return false
return true
})()
useOutsideClick(resolveRootContainers, close, outsideClickEnabled)
useOutsideClick(
resolveRootContainers,
(event) => {
event.preventDefault()
close()
},
outsideClickEnabled
)

// Handle `Escape` to close
let escapeToCloseEnabled = (() => {
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
@@ -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 = [
Original file line number Diff line number Diff line change
@@ -566,6 +566,7 @@ function ListboxFn<
hidden: true,
readOnly: true,
form: formName,
disabled,
name,
value,
})}
41 changes: 41 additions & 0 deletions packages/@headlessui-react/src/components/menu/menu.test.tsx
Original file line number Diff line number Diff line change
@@ -421,6 +421,47 @@ describe('Rendering', () => {
assertMenu({ state: MenuState.InvisibleUnmounted })
})
)

it('should not override an explicit disabled prop on MenuItems child', async () => {
render(
<Menu>
<Menu.Button>Trigger</Menu.Button>
<Menu.Items>
<Menu.Item>{({ disabled }) => <button disabled={disabled}>Item A</button>}</Menu.Item>
<Menu.Item>{({ disabled }) => <button disabled={disabled}>Item B</button>}</Menu.Item>
<Menu.Item disabled>
{({ disabled }) => <button disabled={disabled}>Item C</button>}
</Menu.Item>
</Menu.Items>
</Menu>
)

assertMenuButton({
state: MenuState.InvisibleUnmounted,
})
assertMenu({ state: MenuState.InvisibleUnmounted })

getMenuButton()?.focus()

await press(Keys.Enter)

assertMenuButton({
state: MenuState.Visible,
})
assertMenu({ state: MenuState.Visible })
assertMenuItem(getMenuItems()[0], {
tag: 'button',
attributes: {},
})
assertMenuItem(getMenuItems()[1], {
tag: 'button',
attributes: {},
})
assertMenuItem(getMenuItems()[2], {
tag: 'button',
attributes: { disabled: '' },
})
})
})

it('should guarantee the order of DOM nodes when performing actions', async () => {
Original file line number Diff line number Diff line change
@@ -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 () => {
Original file line number Diff line number Diff line change
@@ -350,6 +350,7 @@ function RadioGroupFn<TTag extends ElementType = typeof DEFAULT_RADIO_GROUP_TAG,
hidden: true,
readOnly: true,
form: formName,
disabled,
name,
value,
})}
74 changes: 74 additions & 0 deletions packages/@headlessui-react/src/components/switch/switch.test.tsx
Original file line number Diff line number Diff line change
@@ -59,6 +59,47 @@ describe('Rendering', () => {
assertSwitch({ state: SwitchState.Off, label: 'Enable notifications' })
})

describe('`tabIndex` attribute', () => {
it('should have a default tabIndex of `0`', () => {
render(
<Switch checked={false} onChange={console.log}>
<span>Enable notifications</span>
</Switch>
)
assertSwitch({
state: SwitchState.Off,
label: 'Enable notifications',
attributes: { tabindex: '0' },
})
})

it('should be possible to override the `tabIndex`', () => {
render(
<Switch checked={false} onChange={console.log} tabIndex={3}>
<span>Enable notifications</span>
</Switch>
)
assertSwitch({
state: SwitchState.Off,
label: 'Enable notifications',
attributes: { tabindex: '3' },
})
})

it('should not be possible to override the `tabIndex` to `-1`', () => {
render(
<Switch checked={false} onChange={console.log} tabIndex={-1}>
<span>Enable notifications</span>
</Switch>
)
assertSwitch({
state: SwitchState.Off,
label: 'Enable notifications',
attributes: { tabindex: '0' },
})
})
})

describe('`type` attribute', () => {
it('should set the `type` to "button" by default', async () => {
render(
@@ -769,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
])
})
})
Loading