From 2ba4ccabfebdd5c2bd3695c17b7a9cc7b0d661f3 Mon Sep 17 00:00:00 2001 From: Edoardo Morandi Date: Thu, 21 Nov 2024 08:47:53 +0100 Subject: [PATCH 01/17] feat(popover): omit aria-controls when closed The `aria-controls` attribute should be set only when an element with the specified id is available in the DOM. --- packages/react/popover/src/Popover.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/popover/src/Popover.tsx b/packages/react/popover/src/Popover.tsx index 215f5173ab..b3bfe10604 100644 --- a/packages/react/popover/src/Popover.tsx +++ b/packages/react/popover/src/Popover.tsx @@ -143,7 +143,7 @@ const PopoverTrigger = React.forwardRef Date: Thu, 21 Nov 2024 08:51:11 +0100 Subject: [PATCH 02/17] test(popover): tests for aria-controls behavior --- packages/react/popover/src/Popover.test.tsx | 39 +++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 packages/react/popover/src/Popover.test.tsx diff --git a/packages/react/popover/src/Popover.test.tsx b/packages/react/popover/src/Popover.test.tsx new file mode 100644 index 0000000000..c3b7a0bd9b --- /dev/null +++ b/packages/react/popover/src/Popover.test.tsx @@ -0,0 +1,39 @@ +import { act, render } from '@testing-library/react'; +import * as Popover from '.'; + +const OPEN_TEXT = 'Open'; +const CONTENT_TEXT = 'Content'; + +function PopoverTest(): React.ReactElement { + return ( + + {OPEN_TEXT} + {CONTENT_TEXT} + + ); +} + +describe('Popover', () => { + describe('aria-controls', () => { + it('should not be defined when the popover is closed', async () => { + const rendered = render(); + + expect(rendered.queryByText(CONTENT_TEXT)).not.toBeInTheDocument(); + const trigger = rendered.getByText(OPEN_TEXT); + expect(trigger.getAttribute('aria-controls')).toBeNull(); + }); + + it('should be valid when the popover is open', () => { + const rendered = render(); + + const trigger = rendered.getByText(OPEN_TEXT); + act(() => trigger.click()); + const ariaControls = trigger.getAttribute('aria-controls'); + expect(ariaControls).not.toBeNull(); + + const dialog = rendered.getByRole('dialog'); + expect(dialog).toBeInTheDocument(); + expect(dialog.id).toBe(ariaControls); + }); + }); +}); From 3c637368ef8db61f3f3d1986616c9e6c8394a091 Mon Sep 17 00:00:00 2001 From: Edoardo Morandi Date: Thu, 21 Nov 2024 11:24:33 +0100 Subject: [PATCH 03/17] chore: add version check for popover --- .yarn/versions/794debf7.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .yarn/versions/794debf7.yml diff --git a/.yarn/versions/794debf7.yml b/.yarn/versions/794debf7.yml new file mode 100644 index 0000000000..73e23957c1 --- /dev/null +++ b/.yarn/versions/794debf7.yml @@ -0,0 +1,2 @@ +releases: + "@radix-ui/react-popover": minor From 6e63965d6c7ef7e6222fb9a0e969aa4c24dd6c76 Mon Sep 17 00:00:00 2001 From: Edoardo Morandi Date: Thu, 21 Nov 2024 09:22:31 +0100 Subject: [PATCH 04/17] feat(dialog): omit aria-controls when closed The `aria-controls` attribute should be set only when an element with the specified id is available in the DOM. --- packages/react/dialog/src/Dialog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/dialog/src/Dialog.tsx b/packages/react/dialog/src/Dialog.tsx index 1a57793360..05994a7148 100644 --- a/packages/react/dialog/src/Dialog.tsx +++ b/packages/react/dialog/src/Dialog.tsx @@ -104,7 +104,7 @@ const DialogTrigger = React.forwardRef type="button" aria-haspopup="dialog" aria-expanded={context.open} - aria-controls={context.contentId} + aria-controls={context.open ? context.contentId : undefined} data-state={getState(context.open)} {...triggerProps} ref={composedTriggerRef} From fca06ab9f90c97f47c567ee5aba8ae5513ff3a4a Mon Sep 17 00:00:00 2001 From: Edoardo Morandi Date: Thu, 21 Nov 2024 09:23:34 +0100 Subject: [PATCH 05/17] test(dialog): tests for aria-controls behavior --- packages/react/dialog/src/Dialog.test.tsx | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/react/dialog/src/Dialog.test.tsx b/packages/react/dialog/src/Dialog.test.tsx index 1b37dab287..b7a5042e31 100644 --- a/packages/react/dialog/src/Dialog.test.tsx +++ b/packages/react/dialog/src/Dialog.test.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { act } from 'react'; import { axe } from 'jest-axe'; import { render, fireEvent, RenderResult, cleanup } from '@testing-library/react'; import * as Dialog from '@radix-ui/react-dialog'; @@ -134,4 +134,21 @@ describe('given a default Dialog', () => { }); }); }); + + describe("it's aria-controls", () => { + it('should not be defined when the dialog is closed', async () => { + expect(rendered.queryByText(TITLE_TEXT)).not.toBeInTheDocument(); + expect(trigger.getAttribute('aria-controls')).toBeNull(); + }); + + it('should be valid when the dialog is open', () => { + act(() => trigger.click()); + const ariaControls = trigger.getAttribute('aria-controls'); + expect(ariaControls).not.toBeNull(); + + const dialog = rendered.getByRole('dialog'); + expect(dialog).toBeInTheDocument(); + expect(dialog.id).toBe(ariaControls); + }); + }); }); From 48c29f64b767e71c9cac0ad9ac9d1305ccb4e249 Mon Sep 17 00:00:00 2001 From: Edoardo Morandi Date: Thu, 21 Nov 2024 11:26:25 +0100 Subject: [PATCH 06/17] chore: add version check for dialog --- .yarn/versions/794debf7.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.yarn/versions/794debf7.yml b/.yarn/versions/794debf7.yml index 73e23957c1..1de1cf2004 100644 --- a/.yarn/versions/794debf7.yml +++ b/.yarn/versions/794debf7.yml @@ -1,2 +1,7 @@ releases: + "@radix-ui/react-alert-dialog": minor + "@radix-ui/react-dialog": minor "@radix-ui/react-popover": minor + +declined: + - primitives From 703e0522e85db74df03a04a48e08ba177fbf288f Mon Sep 17 00:00:00 2001 From: Edoardo Morandi Date: Thu, 21 Nov 2024 09:26:34 +0100 Subject: [PATCH 07/17] feat(select): omit aria-controls when closed The `aria-controls` attribute should be set only when an element with the specified id is available in the DOM. --- packages/react/select/src/Select.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/select/src/Select.tsx b/packages/react/select/src/Select.tsx index 5e848d8caf..1dbbafc6e2 100644 --- a/packages/react/select/src/Select.tsx +++ b/packages/react/select/src/Select.tsx @@ -253,7 +253,7 @@ const SelectTrigger = React.forwardRef Date: Thu, 21 Nov 2024 09:50:01 +0100 Subject: [PATCH 08/17] test(select): tests for aria-controls behavior --- packages/react/select/src/Select.test.tsx | 48 +++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 packages/react/select/src/Select.test.tsx diff --git a/packages/react/select/src/Select.test.tsx b/packages/react/select/src/Select.test.tsx new file mode 100644 index 0000000000..875b704d9c --- /dev/null +++ b/packages/react/select/src/Select.test.tsx @@ -0,0 +1,48 @@ +import { act, render } from '@testing-library/react'; +import * as Select from '.'; + +const VALUE_1 = 'Value 1'; +const VALUE_2 = 'Value 2'; + +function SelectTest(): React.ReactElement { + return ( + + + + + + {VALUE_1} + {VALUE_2} + + + + + ); +} + +describe('Select', () => { + describe('aria-controls', () => { + it('should not be defined when the select is closed', async () => { + const rendered = render(); + + expect(rendered.queryByText(VALUE_1)).not.toBeInTheDocument(); + const trigger = rendered.getByRole('combobox'); + expect(trigger.getAttribute('aria-controls')).toBeNull(); + }); + + it('should be valid when the select is open', () => { + const rendered = render(); + + const trigger = rendered.getByRole('combobox'); + act(() => trigger.click()); + expect(rendered.queryByText(VALUE_1)).toBeVisible(); + + const ariaControls = trigger.getAttribute('aria-controls'); + expect(ariaControls).not.toBeNull(); + + const listbox = rendered.getByRole('listbox'); + expect(listbox).toBeInTheDocument(); + expect(listbox.id).toBe(ariaControls); + }); + }); +}); From c9d50bbc7583384593afda8be1c6233a4269365b Mon Sep 17 00:00:00 2001 From: Edoardo Morandi Date: Thu, 21 Nov 2024 11:27:12 +0100 Subject: [PATCH 09/17] chore: add version check for select --- .yarn/versions/794debf7.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.yarn/versions/794debf7.yml b/.yarn/versions/794debf7.yml index 1de1cf2004..1a7ccac2a2 100644 --- a/.yarn/versions/794debf7.yml +++ b/.yarn/versions/794debf7.yml @@ -2,6 +2,7 @@ releases: "@radix-ui/react-alert-dialog": minor "@radix-ui/react-dialog": minor "@radix-ui/react-popover": minor + "@radix-ui/react-select": minor declined: - primitives From cf6a1ef9b5aa54296235bf338d3acfac69dc8e6c Mon Sep 17 00:00:00 2001 From: Edoardo Morandi Date: Thu, 21 Nov 2024 10:02:29 +0100 Subject: [PATCH 10/17] feat(menu): omit aria-controls when closed The `aria-controls` attribute should be set only when an element with the specified id is available in the DOM. --- packages/react/menu/src/Menu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/menu/src/Menu.tsx b/packages/react/menu/src/Menu.tsx index d8f3406891..1accd4f1a9 100644 --- a/packages/react/menu/src/Menu.tsx +++ b/packages/react/menu/src/Menu.tsx @@ -1047,7 +1047,7 @@ const MenuSubTrigger = React.forwardRef Date: Thu, 21 Nov 2024 11:28:11 +0100 Subject: [PATCH 11/17] chore: add version check for menu --- .yarn/versions/794debf7.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.yarn/versions/794debf7.yml b/.yarn/versions/794debf7.yml index 1a7ccac2a2..7c9cc7e7cf 100644 --- a/.yarn/versions/794debf7.yml +++ b/.yarn/versions/794debf7.yml @@ -1,6 +1,10 @@ releases: "@radix-ui/react-alert-dialog": minor + "@radix-ui/react-context-menu": minor "@radix-ui/react-dialog": minor + "@radix-ui/react-dropdown-menu": minor + "@radix-ui/react-menu": minor + "@radix-ui/react-menubar": minor "@radix-ui/react-popover": minor "@radix-ui/react-select": minor From ffef77030ebc2999aba8e2540f037b876a1cc45b Mon Sep 17 00:00:00 2001 From: Edoardo Morandi Date: Thu, 21 Nov 2024 10:48:26 +0100 Subject: [PATCH 12/17] feat(navigation-menu): no aria-controls if closed The `aria-controls` attribute should be set only when an element with the specified id is available in the DOM. --- packages/react/navigation-menu/src/NavigationMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/navigation-menu/src/NavigationMenu.tsx b/packages/react/navigation-menu/src/NavigationMenu.tsx index e450ce8dba..4863762b69 100644 --- a/packages/react/navigation-menu/src/NavigationMenu.tsx +++ b/packages/react/navigation-menu/src/NavigationMenu.tsx @@ -498,7 +498,7 @@ const NavigationMenuTrigger = React.forwardRef< data-disabled={disabled ? '' : undefined} data-state={getOpenState(open)} aria-expanded={open} - aria-controls={contentId} + aria-controls={open ? contentId : undefined} {...triggerProps} ref={composedRefs} onPointerEnter={composeEventHandlers(props.onPointerEnter, () => { From 13113f2c34065f43a1e781b9c9b2f5fc0eb24ae1 Mon Sep 17 00:00:00 2001 From: Edoardo Morandi Date: Thu, 21 Nov 2024 10:54:56 +0100 Subject: [PATCH 13/17] test(navigation-menu): aria-controls behavior --- .../src/NavigationMenu.test.tsx | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 packages/react/navigation-menu/src/NavigationMenu.test.tsx diff --git a/packages/react/navigation-menu/src/NavigationMenu.test.tsx b/packages/react/navigation-menu/src/NavigationMenu.test.tsx new file mode 100644 index 0000000000..361c2f1d7c --- /dev/null +++ b/packages/react/navigation-menu/src/NavigationMenu.test.tsx @@ -0,0 +1,45 @@ +import { act, render } from '@testing-library/react'; +import * as NavigationMenu from '.'; + +const TRIGGER = 'Trigger'; +const CONTENT = 'Content'; + +function NavigationMenuTest(): React.ReactElement { + return ( + + + + {TRIGGER} + {CONTENT} + + + + ); +} + +describe('NavigationMenu', () => { + describe('aria-controls', () => { + it('should not be defined when the menu is closed', async () => { + const rendered = render(); + + expect(rendered.queryByText(CONTENT)).not.toBeInTheDocument(); + const trigger = rendered.getByText(TRIGGER); + expect(trigger.getAttribute('aria-controls')).toBeNull(); + }); + + it('should be valid when the menu is open', () => { + const rendered = render(); + + const trigger = rendered.getByText(TRIGGER); + act(() => trigger.click()); + const content = rendered.queryByText(CONTENT); + expect(content).toBeVisible(); + + const ariaControls = trigger.getAttribute('aria-controls'); + expect(ariaControls).not.toBeNull(); + if (content !== null) { + expect(content.id).toBe(ariaControls); + } + }); + }); +}); From d3f1d313a237aae382493c4a3a412e6dccd871ff Mon Sep 17 00:00:00 2001 From: Edoardo Morandi Date: Thu, 21 Nov 2024 11:29:01 +0100 Subject: [PATCH 14/17] chore: add version check for navigation-menu --- .yarn/versions/794debf7.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.yarn/versions/794debf7.yml b/.yarn/versions/794debf7.yml index 7c9cc7e7cf..57cabb82e3 100644 --- a/.yarn/versions/794debf7.yml +++ b/.yarn/versions/794debf7.yml @@ -5,6 +5,7 @@ releases: "@radix-ui/react-dropdown-menu": minor "@radix-ui/react-menu": minor "@radix-ui/react-menubar": minor + "@radix-ui/react-navigation-menu": minor "@radix-ui/react-popover": minor "@radix-ui/react-select": minor From 2eddc8d5569bc5a308b909ba560c785dd173a233 Mon Sep 17 00:00:00 2001 From: Edoardo Morandi Date: Thu, 21 Nov 2024 11:10:57 +0100 Subject: [PATCH 15/17] feat(collapsible): omit aria-controls when closed The `aria-controls` attribute should be set only when an element with the specified id is available in the DOM. --- packages/react/collapsible/src/Collapsible.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/collapsible/src/Collapsible.tsx b/packages/react/collapsible/src/Collapsible.tsx index 1ec9829692..411da7aa55 100644 --- a/packages/react/collapsible/src/Collapsible.tsx +++ b/packages/react/collapsible/src/Collapsible.tsx @@ -93,7 +93,7 @@ const CollapsibleTrigger = React.forwardRef Date: Thu, 21 Nov 2024 11:16:40 +0100 Subject: [PATCH 16/17] test(collapsible): test aria-controls behavior --- packages/react/collapsible/src/Collapsible.test.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/react/collapsible/src/Collapsible.test.tsx b/packages/react/collapsible/src/Collapsible.test.tsx index 7faf6f43c8..c620dcd797 100644 --- a/packages/react/collapsible/src/Collapsible.test.tsx +++ b/packages/react/collapsible/src/Collapsible.test.tsx @@ -48,6 +48,10 @@ describe('given a default Collapsible', () => { }); }); }); + + it('should not have an aria-controls attribute', () => { + expect(trigger.getAttribute('aria-controls')).toBeNull(); + }); }); describe('given an open uncontrolled Collapsible', () => { @@ -100,4 +104,11 @@ describe('given an open controlled Collapsible', () => { expect(content).toBeVisible(); }); }); + + it('should have a valid aria-controls attribute', () => { + const trigger = rendered.getByText(TRIGGER_TEXT); + const ariaControls = trigger.getAttribute('aria-controls'); + expect(ariaControls).not.toBeNull(); + expect(ariaControls).toBe(content.id); + }); }); From dda97fd0ab2f80532fa32df4de3a95484c804bd9 Mon Sep 17 00:00:00 2001 From: Edoardo Morandi Date: Thu, 21 Nov 2024 11:29:49 +0100 Subject: [PATCH 17/17] chore: add version check for collapsible --- .yarn/versions/794debf7.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.yarn/versions/794debf7.yml b/.yarn/versions/794debf7.yml index 57cabb82e3..617e8ac6e4 100644 --- a/.yarn/versions/794debf7.yml +++ b/.yarn/versions/794debf7.yml @@ -1,5 +1,7 @@ releases: + "@radix-ui/react-accordion": minor "@radix-ui/react-alert-dialog": minor + "@radix-ui/react-collapsible": minor "@radix-ui/react-context-menu": minor "@radix-ui/react-dialog": minor "@radix-ui/react-dropdown-menu": minor