From 23763abf797f9a4ba8262225760f718e9dcc4782 Mon Sep 17 00:00:00 2001 From: Maria Hutt Date: Thu, 21 Nov 2024 10:57:25 -0800 Subject: [PATCH] fix(header): use aria attributes to hide small title when collapsed (#30027) Issue number: resolves #29347 --------- ## What is the current behavior? Focusable elements like buttons cannot be accessed within the `ion-header` when it's collapsed. They're only accessible once the small title is displayed. ## What is the new behavior? - Moved the `aria-hidden` from the header to `ion-title`, this aligns with native. - Updated existing test. ## Does this introduce a breaking change? - [ ] Yes - [x] No ## Other information Dev build: `8.4.1-dev.11732064156.12837790` --- core/src/components/header/header.utils.ts | 25 +++++++++++++++++-- .../header/test/condense/header.e2e.ts | 14 ++++++++--- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/core/src/components/header/header.utils.ts b/core/src/components/header/header.utils.ts index f1b6273ef5..af2e4b1acd 100644 --- a/core/src/components/header/header.utils.ts +++ b/core/src/components/header/header.utils.ts @@ -167,13 +167,34 @@ export const handleToolbarIntersection = ( export const setHeaderActive = (headerIndex: HeaderIndex, active = true) => { const headerEl = headerIndex.el; + const toolbars = headerIndex.toolbars; + const ionTitles = toolbars.map((toolbar) => toolbar.ionTitleEl); if (active) { headerEl.classList.remove('header-collapse-condense-inactive'); - headerEl.removeAttribute('aria-hidden'); + + ionTitles.forEach((ionTitle) => { + if (ionTitle) { + ionTitle.removeAttribute('aria-hidden'); + } + }); } else { headerEl.classList.add('header-collapse-condense-inactive'); - headerEl.setAttribute('aria-hidden', 'true'); + + /** + * The small title should only be accessed by screen readers + * when the large title collapses into the small title due + * to scrolling. + * + * Originally, the header was given `aria-hidden="true"` + * but this caused issues with screen readers not being + * able to access any focusable elements within the header. + */ + ionTitles.forEach((ionTitle) => { + if (ionTitle) { + ionTitle.setAttribute('aria-hidden', 'true'); + } + }); } }; diff --git a/core/src/components/header/test/condense/header.e2e.ts b/core/src/components/header/test/condense/header.e2e.ts index 791c1b720a..b57d1ee58f 100644 --- a/core/src/components/header/test/condense/header.e2e.ts +++ b/core/src/components/header/test/condense/header.e2e.ts @@ -3,13 +3,19 @@ import { configs, test } from '@utils/test/playwright'; configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { test.describe(title('header: condense'), () => { - test('should be hidden from screen readers when collapsed', async ({ page }) => { + test('should hide small title from screen readers when collapsed', async ({ page }) => { + test.info().annotations.push({ + type: 'issue', + description: 'https://github.com/ionic-team/ionic-framework/issues/29347', + }); + await page.goto('/src/components/header/test/condense', config); const largeTitleHeader = page.locator('#largeTitleHeader'); const smallTitleHeader = page.locator('#smallTitleHeader'); + const smallTitle = smallTitleHeader.locator('ion-title'); const content = page.locator('ion-content'); - await expect(smallTitleHeader).toHaveAttribute('aria-hidden', 'true'); + await expect(smallTitle).toHaveAttribute('aria-hidden', 'true'); await expect(largeTitleHeader).toHaveScreenshot(screenshot(`header-condense-large-title-initial-diff`)); @@ -24,7 +30,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, screenshot, c * Playwright can't do .not.toHaveAttribute() because a value is expected, * and toHaveAttribute can't accept a value of type null. */ - const ariaHidden = await smallTitleHeader.getAttribute('aria-hidden'); + const ariaHidden = await smallTitle.getAttribute('aria-hidden'); expect(ariaHidden).toBeNull(); await content.evaluate(async (el: HTMLIonContentElement) => { @@ -32,7 +38,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, screenshot, c }); await page.locator('#smallTitleHeader.header-collapse-condense-inactive').waitFor(); - await expect(smallTitleHeader).toHaveAttribute('aria-hidden', 'true'); + await expect(smallTitle).toHaveAttribute('aria-hidden', 'true'); }); }); });