diff --git a/packages/@headlessui-react/src/components/combobox/combobox.test.tsx b/packages/@headlessui-react/src/components/combobox/combobox.test.tsx index fd33475de..34a3eac92 100644 --- a/packages/@headlessui-react/src/components/combobox/combobox.test.tsx +++ b/packages/@headlessui-react/src/components/combobox/combobox.test.tsx @@ -39,6 +39,7 @@ import { assertCombobox, ComboboxMode, assertNotActiveComboboxOption, + assertComboboxInput, } from '../../test-utils/accessibility-assertions' import { Transition } from '../transitions/transition' @@ -296,8 +297,11 @@ describe('Rendering', () => { render() + assertComboboxInput({ state: ComboboxState.InvisibleUnmounted }) + await click(getComboboxButton()) + assertComboboxInput({ state: ComboboxState.Visible }) assertComboboxList({ state: ComboboxState.Visible }) await click(getComboboxOptions()[1]) diff --git a/packages/@headlessui-react/src/test-utils/accessibility-assertions.ts b/packages/@headlessui-react/src/test-utils/accessibility-assertions.ts index 6eba1d4c1..37db4db69 100644 --- a/packages/@headlessui-react/src/test-utils/accessibility-assertions.ts +++ b/packages/@headlessui-react/src/test-utils/accessibility-assertions.ts @@ -330,6 +330,57 @@ export function assertCombobox( } } +export function assertComboboxInput( + options: { + attributes?: Record + state: ComboboxState + }, + input = getComboboxInput() +) { + try { + if (input === null) return expect(input).not.toBe(null) + + // Ensure combobox input has these properties + expect(input).toHaveAttribute('id') + + switch (options.state) { + case ComboboxState.Visible: + expect(input).toHaveAttribute('aria-controls') + expect(input).toHaveAttribute('aria-expanded', 'true') + break + + case ComboboxState.InvisibleHidden: + expect(input).toHaveAttribute('aria-controls') + if (input.hasAttribute('disabled')) { + expect(input).not.toHaveAttribute('aria-expanded') + } else { + expect(input).toHaveAttribute('aria-expanded', 'false') + } + break + + case ComboboxState.InvisibleUnmounted: + expect(input).not.toHaveAttribute('aria-controls') + if (input.hasAttribute('disabled')) { + expect(input).not.toHaveAttribute('aria-expanded') + } else { + expect(input).toHaveAttribute('aria-expanded', 'false') + } + break + + default: + assertNever(options.state) + } + + // Ensure combobox input has the following attributes + for (let attributeName in options.attributes) { + expect(input).toHaveAttribute(attributeName, options.attributes[attributeName]) + } + } catch (err) { + if (err instanceof Error) Error.captureStackTrace(err, assertComboboxInput) + throw err + } +} + export function assertComboboxList( options: { attributes?: Record diff --git a/packages/@headlessui-vue/CHANGELOG.md b/packages/@headlessui-vue/CHANGELOG.md index 3a84ef66f..3e0c20f31 100644 --- a/packages/@headlessui-vue/CHANGELOG.md +++ b/packages/@headlessui-vue/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Only render the `Dialog` on the client ([#1566](https://github.com/tailwindlabs/headlessui/pull/1566)) - Improve Combobox input cursor position ([#1574](https://github.com/tailwindlabs/headlessui/pull/1574)) - Fix scrolling issue in `Tab` component when using arrow keys ([#1584](https://github.com/tailwindlabs/headlessui/pull/1584)) +- Fix missing `aria-expanded` for `ComboboxInput` component ([#1605](https://github.com/tailwindlabs/headlessui/pull/1605)) ## [1.6.4] - 2022-05-29 diff --git a/packages/@headlessui-vue/src/components/combobox/combobox.test.ts b/packages/@headlessui-vue/src/components/combobox/combobox.test.ts index a21a230e9..77c9eb3b3 100644 --- a/packages/@headlessui-vue/src/components/combobox/combobox.test.ts +++ b/packages/@headlessui-vue/src/components/combobox/combobox.test.ts @@ -45,6 +45,7 @@ import { assertCombobox, ComboboxMode, assertNotActiveComboboxOption, + assertComboboxInput, } from '../../test-utils/accessibility-assertions' import { html } from '../../test-utils/html' import { useOpenClosedProvider, State, useOpenClosed } from '../../internal/open-closed' @@ -332,8 +333,11 @@ describe('Rendering', () => { // TODO: Rendering Example directly reveals a vue bug — I think it's been fixed for a while but I can't find the commit renderTemplate(Example) + assertComboboxInput({ state: ComboboxState.InvisibleUnmounted }) + await click(getComboboxButton()) + assertComboboxInput({ state: ComboboxState.Visible }) assertComboboxList({ state: ComboboxState.Visible }) await click(getComboboxOptions()[1]) diff --git a/packages/@headlessui-vue/src/components/combobox/combobox.ts b/packages/@headlessui-vue/src/components/combobox/combobox.ts index 43f66f71c..401ca63a4 100644 --- a/packages/@headlessui-vue/src/components/combobox/combobox.ts +++ b/packages/@headlessui-vue/src/components/combobox/combobox.ts @@ -738,7 +738,9 @@ export let ComboboxInput = defineComponent({ let slot = { open: api.comboboxState.value === ComboboxStates.Open } let ourProps = { 'aria-controls': api.optionsRef.value?.id, - 'aria-expanded': api.disabled ? undefined : api.comboboxState.value === ComboboxStates.Open, + 'aria-expanded': api.disabled.value + ? undefined + : api.comboboxState.value === ComboboxStates.Open, 'aria-activedescendant': api.activeOptionIndex.value === null ? undefined diff --git a/packages/@headlessui-vue/src/test-utils/accessibility-assertions.ts b/packages/@headlessui-vue/src/test-utils/accessibility-assertions.ts index 6eba1d4c1..37db4db69 100644 --- a/packages/@headlessui-vue/src/test-utils/accessibility-assertions.ts +++ b/packages/@headlessui-vue/src/test-utils/accessibility-assertions.ts @@ -330,6 +330,57 @@ export function assertCombobox( } } +export function assertComboboxInput( + options: { + attributes?: Record + state: ComboboxState + }, + input = getComboboxInput() +) { + try { + if (input === null) return expect(input).not.toBe(null) + + // Ensure combobox input has these properties + expect(input).toHaveAttribute('id') + + switch (options.state) { + case ComboboxState.Visible: + expect(input).toHaveAttribute('aria-controls') + expect(input).toHaveAttribute('aria-expanded', 'true') + break + + case ComboboxState.InvisibleHidden: + expect(input).toHaveAttribute('aria-controls') + if (input.hasAttribute('disabled')) { + expect(input).not.toHaveAttribute('aria-expanded') + } else { + expect(input).toHaveAttribute('aria-expanded', 'false') + } + break + + case ComboboxState.InvisibleUnmounted: + expect(input).not.toHaveAttribute('aria-controls') + if (input.hasAttribute('disabled')) { + expect(input).not.toHaveAttribute('aria-expanded') + } else { + expect(input).toHaveAttribute('aria-expanded', 'false') + } + break + + default: + assertNever(options.state) + } + + // Ensure combobox input has the following attributes + for (let attributeName in options.attributes) { + expect(input).toHaveAttribute(attributeName, options.attributes[attributeName]) + } + } catch (err) { + if (err instanceof Error) Error.captureStackTrace(err, assertComboboxInput) + throw err + } +} + export function assertComboboxList( options: { attributes?: Record