From bfacb648dc5f45129eab53bc815bcee3001f0c9f Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Thu, 2 Nov 2023 17:52:43 +0100 Subject: [PATCH] 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 --- packages/@headlessui-react/CHANGELOG.md | 1 + .../src/components/listbox/listbox.test.tsx | 2 -- .../@headlessui-react/src/components/listbox/listbox.tsx | 6 +----- packages/@headlessui-vue/CHANGELOG.md | 1 + .../@headlessui-vue/src/components/listbox/listbox.test.tsx | 2 -- packages/@headlessui-vue/src/components/listbox/listbox.ts | 2 +- 6 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/@headlessui-react/CHANGELOG.md b/packages/@headlessui-react/CHANGELOG.md index 521b008..a349a2c 100644 --- a/packages/@headlessui-react/CHANGELOG.md +++ b/packages/@headlessui-react/CHANGELOG.md @@ -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 `` 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 `` and `` 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 diff --git a/packages/@headlessui-react/src/components/listbox/listbox.test.tsx b/packages/@headlessui-react/src/components/listbox/listbox.test.tsx index 737445a..e2e3568 100644 --- a/packages/@headlessui-react/src/components/listbox/listbox.test.tsx +++ b/packages/@headlessui-react/src/components/listbox/listbox.test.tsx @@ -8,7 +8,6 @@ import { assertListboxButtonLinkedWithListbox, assertListboxButtonLinkedWithListboxLabel, assertListboxLabel, - assertListboxLabelLinkedWithListbox, assertListboxOption, assertNoActiveListboxOption, assertNoSelectedListboxOption, @@ -515,7 +514,6 @@ describe('Rendering', () => { textContent: JSON.stringify({ open: true, disabled: false }), }) assertListbox({ state: ListboxState.Visible }) - assertListboxLabelLinkedWithListbox() assertListboxButtonLinkedWithListboxLabel() }) ) diff --git a/packages/@headlessui-react/src/components/listbox/listbox.tsx b/packages/@headlessui-react/src/components/listbox/listbox.tsx index 9c3eedf..154883f 100644 --- a/packages/@headlessui-react/src/components/listbox/listbox.tsx +++ b/packages/@headlessui-react/src/components/listbox/listbox.tsx @@ -857,11 +857,7 @@ function OptionsFn( } }) - 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( () => ({ open: data.listboxState === ListboxStates.Open }), [data] diff --git a/packages/@headlessui-vue/CHANGELOG.md b/packages/@headlessui-vue/CHANGELOG.md index 2e0de2c..60be506 100644 --- a/packages/@headlessui-vue/CHANGELOG.md +++ b/packages/@headlessui-vue/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Allow `