Skip to content

Commit bfacb64

Browse files
authored
Fix VoiceOver bug for Listbox component in Chrome (#2824)
* 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
1 parent 1469b85 commit bfacb64

File tree

6 files changed

+4
-10
lines changed

6 files changed

+4
-10
lines changed

packages/@headlessui-react/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1717
- Allow changes to the `className` prop when the `<Transition />` component is currently not transitioning ([#2722](https://github.com/tailwindlabs/headlessui/pull/2722))
1818
- Export (internal-only) component interfaces for TypeScript compiler ([#2313](https://github.com/tailwindlabs/headlessui/pull/2313))
1919
- Fix infinite render-loop for `<Disclosure.Panel>` and `<Popover.Panel>` when `as={Fragment}` ([#2760](https://github.com/tailwindlabs/headlessui/pull/2760))
20+
- Fix VoiceOver bug for `Listbox` component in Chrome ([#2824](https://github.com/tailwindlabs/headlessui/pull/2824))
2021

2122
### Added
2223

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

-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
assertListboxButtonLinkedWithListbox,
99
assertListboxButtonLinkedWithListboxLabel,
1010
assertListboxLabel,
11-
assertListboxLabelLinkedWithListbox,
1211
assertListboxOption,
1312
assertNoActiveListboxOption,
1413
assertNoSelectedListboxOption,
@@ -515,7 +514,6 @@ describe('Rendering', () => {
515514
textContent: JSON.stringify({ open: true, disabled: false }),
516515
})
517516
assertListbox({ state: ListboxState.Visible })
518-
assertListboxLabelLinkedWithListbox()
519517
assertListboxButtonLinkedWithListboxLabel()
520518
})
521519
)

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

+1-5
Original file line numberDiff line numberDiff line change
@@ -857,11 +857,7 @@ function OptionsFn<TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG>(
857857
}
858858
})
859859

860-
let labelledby = useComputed(
861-
() => data.labelRef.current?.id ?? data.buttonRef.current?.id,
862-
[data.labelRef.current, data.buttonRef.current]
863-
)
864-
860+
let labelledby = useComputed(() => data.buttonRef.current?.id, [data.buttonRef.current])
865861
let slot = useMemo<OptionsRenderPropArg>(
866862
() => ({ open: data.listboxState === ListboxStates.Open }),
867863
[data]

packages/@headlessui-vue/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1717
- Allow `<button>` to be in nested components in `<PopoverButton>` ([#2715](https://github.com/tailwindlabs/headlessui/pull/2715))
1818
- Don't overwrite user-defined template refs when rendering ([#2720](https://github.com/tailwindlabs/headlessui/pull/2720))
1919
- Fix missing `data-headlessui-state` attribute when `as="template"` ([#2787](https://github.com/tailwindlabs/headlessui/pull/2787))
20+
- Fix VoiceOver bug for `Listbox` component in Chrome ([#2824](https://github.com/tailwindlabs/headlessui/pull/2824))
2021

2122
### Added
2223

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

-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
assertListboxButtonLinkedWithListbox,
99
assertListboxButtonLinkedWithListboxLabel,
1010
assertListboxLabel,
11-
assertListboxLabelLinkedWithListbox,
1211
assertListboxOption,
1312
assertNoActiveListboxOption,
1413
assertNoSelectedListboxOption,
@@ -560,7 +559,6 @@ describe('Rendering', () => {
560559
textContent: JSON.stringify({ open: true, disabled: false }),
561560
})
562561
assertListbox({ state: ListboxState.Visible })
563-
assertListboxLabelLinkedWithListbox()
564562
assertListboxButtonLinkedWithListboxLabel()
565563
})
566564
)

packages/@headlessui-vue/src/components/listbox/listbox.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,7 @@ export let ListboxOptions = defineComponent({
663663
? undefined
664664
: api.options.value[api.activeOptionIndex.value]?.id,
665665
'aria-multiselectable': api.mode.value === ValueMode.Multi ? true : undefined,
666-
'aria-labelledby': dom(api.labelRef)?.id ?? dom(api.buttonRef)?.id,
666+
'aria-labelledby': dom(api.buttonRef)?.id,
667667
'aria-orientation': api.orientation.value,
668668
id,
669669
onKeydown: handleKeyDown,

0 commit comments

Comments
 (0)