Skip to content

Commit

Permalink
Fix VoiceOver bug for Listbox component in Chrome (#2824)
Browse files Browse the repository at this point in the history
* fix VoiceOver bug for Listbox in Chrome

Chrome currently has a bug if you use a `Listbox` with a `Label` and use
the `aria-multiselectable` attribute. This combination will cause
VoiceOver to _not_ announce the `role="option"` elements when
interacting with them.

If we drop the `aria-multiselectable` OR the `aria-labelledby` it starts
working. Alternatively replacing `aria-labelledby` with `aria-label`
won't work either.

I filed a Chrome bug report about this here: https://bugs.chromium.org/p/chromium/issues/detail?id=1498261

---

Luckily there is a workaround in our `Listbox` implementation. Right now
we always require the `Listbox.Button` to be there. The
`Listbox.Options` component doesn't work on its own in our
implementation.

This means that whenever we open the `Listbox` that we have to go via
the `Listbox.Button`. This `Listbox.Button` is already labelled by the
`Listbox.Label` if there is one.

This also means that we can safely drop the `id` of the label inside the
`aria-labelledby` from the `Listbox.Options`.

This wouldn't have worked if our `Listbox.Options` could be used in a
standalone way without the `Listbox.Button`.

At the end of the day the hierarchy looks like this:

- Options is labelled by the Button
   - Button is labelled by the Label
      - Label

Fixes: #2817

* update changelog
  • Loading branch information
RobinMalfait authored Nov 2, 2023
1 parent 1469b85 commit bfacb64
Show file tree
Hide file tree
Showing 6 changed files with 4 additions and 10 deletions.
1 change: 1 addition & 0 deletions packages/@headlessui-react/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Allow changes to the `className` prop when the `<Transition />` component is currently not transitioning ([#2722](https://github.com/tailwindlabs/headlessui/pull/2722))
- Export (internal-only) component interfaces for TypeScript compiler ([#2313](https://github.com/tailwindlabs/headlessui/pull/2313))
- Fix infinite render-loop for `<Disclosure.Panel>` and `<Popover.Panel>` when `as={Fragment}` ([#2760](https://github.com/tailwindlabs/headlessui/pull/2760))
- Fix VoiceOver bug for `Listbox` component in Chrome ([#2824](https://github.com/tailwindlabs/headlessui/pull/2824))

### Added

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
assertListboxButtonLinkedWithListbox,
assertListboxButtonLinkedWithListboxLabel,
assertListboxLabel,
assertListboxLabelLinkedWithListbox,
assertListboxOption,
assertNoActiveListboxOption,
assertNoSelectedListboxOption,
Expand Down Expand Up @@ -515,7 +514,6 @@ describe('Rendering', () => {
textContent: JSON.stringify({ open: true, disabled: false }),
})
assertListbox({ state: ListboxState.Visible })
assertListboxLabelLinkedWithListbox()
assertListboxButtonLinkedWithListboxLabel()
})
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -857,11 +857,7 @@ function OptionsFn<TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG>(
}
})

let labelledby = useComputed(
() => data.labelRef.current?.id ?? data.buttonRef.current?.id,
[data.labelRef.current, data.buttonRef.current]
)

let labelledby = useComputed(() => data.buttonRef.current?.id, [data.buttonRef.current])
let slot = useMemo<OptionsRenderPropArg>(
() => ({ open: data.listboxState === ListboxStates.Open }),
[data]
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 @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Allow `<button>` to be in nested components in `<PopoverButton>` ([#2715](https://github.com/tailwindlabs/headlessui/pull/2715))
- Don't overwrite user-defined template refs when rendering ([#2720](https://github.com/tailwindlabs/headlessui/pull/2720))
- Fix missing `data-headlessui-state` attribute when `as="template"` ([#2787](https://github.com/tailwindlabs/headlessui/pull/2787))
- Fix VoiceOver bug for `Listbox` component in Chrome ([#2824](https://github.com/tailwindlabs/headlessui/pull/2824))

### Added

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
assertListboxButtonLinkedWithListbox,
assertListboxButtonLinkedWithListboxLabel,
assertListboxLabel,
assertListboxLabelLinkedWithListbox,
assertListboxOption,
assertNoActiveListboxOption,
assertNoSelectedListboxOption,
Expand Down Expand Up @@ -560,7 +559,6 @@ describe('Rendering', () => {
textContent: JSON.stringify({ open: true, disabled: false }),
})
assertListbox({ state: ListboxState.Visible })
assertListboxLabelLinkedWithListbox()
assertListboxButtonLinkedWithListboxLabel()
})
)
Expand Down
2 changes: 1 addition & 1 deletion packages/@headlessui-vue/src/components/listbox/listbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@ export let ListboxOptions = defineComponent({
? undefined
: api.options.value[api.activeOptionIndex.value]?.id,
'aria-multiselectable': api.mode.value === ValueMode.Multi ? true : undefined,
'aria-labelledby': dom(api.labelRef)?.id ?? dom(api.buttonRef)?.id,
'aria-labelledby': dom(api.buttonRef)?.id,
'aria-orientation': api.orientation.value,
id,
onKeydown: handleKeyDown,
Expand Down

2 comments on commit bfacb64

@vercel
Copy link

@vercel vercel bot commented on bfacb64 Nov 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

headlessui-vue – ./packages/playground-vue

headlessui-vue.vercel.app
headlessui-vue-git-main-tailwindlabs.vercel.app
headlessui-vue-tailwindlabs.vercel.app

@vercel
Copy link

@vercel vercel bot commented on bfacb64 Nov 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

headlessui-react – ./packages/playground-react

headlessui-react-git-main-tailwindlabs.vercel.app
headlessui-react.vercel.app
headlessui-react-tailwindlabs.vercel.app

Please sign in to comment.