diff --git a/core/src/components/alert/alert.tsx b/core/src/components/alert/alert.tsx index ca7efa30fb9..e763d8bcc6f 100644 --- a/core/src/components/alert/alert.tsx +++ b/core/src/components/alert/alert.tsx @@ -730,10 +730,12 @@ export class Alert implements ComponentInterface, OverlayInterface { const role = this.inputs.length > 0 || this.buttons.length > 0 ? 'alertdialog' : 'alert'; /** - * If the header is defined, use that. Otherwise, fall back to the subHeader. - * If neither is defined, don't set aria-labelledby. + * Use both the header and subHeader ids if they are defined. + * If only the header is defined, use the header id. + * If only the subHeader is defined, use the subHeader id. + * If neither are defined, do not set aria-labelledby. */ - const ariaLabelledBy = header ? hdrId : subHeader ? subHdrId : null; + const ariaLabelledBy = header && subHeader ? `${hdrId} ${subHdrId}` : header ? hdrId : subHeader ? subHdrId : null; return ( )} - {subHeader && ( + {/* If no header exists, subHeader should be the highest heading level, h2 */} + {subHeader && !header && (

{subHeader}

)} + {/* If a header exists, subHeader should be one level below it, h3 */} + {subHeader && header && ( +

+ {subHeader} +

+ )} {this.renderAlertMessage(msgId)} diff --git a/core/src/components/alert/test/a11y/alert.e2e.ts b/core/src/components/alert/test/a11y/alert.e2e.ts index 12add8b2eca..56aef1f94a8 100644 --- a/core/src/components/alert/test/a11y/alert.e2e.ts +++ b/core/src/components/alert/test/a11y/alert.e2e.ts @@ -17,6 +17,27 @@ const testAria = async ( const alert = page.locator('ion-alert'); + const header = alert.locator('.alert-title'); + const subHeader = alert.locator('.alert-sub-title'); + + // If a header exists, it should be an h2 element + if ((await header.count()) > 0) { + const headerTagName = await header.evaluate((el) => el.tagName); + expect(headerTagName).toBe('H2'); + } + + // If a header and subHeader exist, the subHeader should be an h3 element + if ((await header.count()) > 0 && (await subHeader.count()) > 0) { + const subHeaderTagName = await subHeader.evaluate((el) => el.tagName); + expect(subHeaderTagName).toBe('H3'); + } + + // If a subHeader exists without a header, the subHeader should be an h2 element + if ((await header.count()) === 0 && (await subHeader.count()) > 0) { + const subHeaderTagName = await subHeader.evaluate((el) => el.tagName); + expect(subHeaderTagName).toBe('H2'); + } + /** * expect().toHaveAttribute() can't check for a null value, so grab and check * the values manually instead. @@ -124,16 +145,24 @@ configs({ directions: ['ltr'] }).forEach(({ config, title }) => { await page.goto(`/src/components/alert/test/a11y`, config); }); - test('should have aria-labelledby when header is set', async ({ page }) => { - await testAria(page, 'noMessage', 'alert-1-hdr', null); + test('should have aria-labelledby set to both when header and subHeader are set', async ({ page }) => { + await testAria(page, 'bothHeadersOnly', 'alert-1-hdr alert-1-sub-hdr', null); + }); + + test('should have aria-labelledby set when only header is set', async ({ page }) => { + await testAria(page, 'headerOnly', 'alert-1-hdr', null); + }); + + test('should fall back to subHeader for aria-labelledby if header is not defined', async ({ page }) => { + await testAria(page, 'subHeaderOnly', 'alert-1-sub-hdr', null); }); test('should have aria-describedby when message is set', async ({ page }) => { await testAria(page, 'noHeaders', null, 'alert-1-msg'); }); - test('should fall back to subHeader for aria-labelledby if header is not defined', async ({ page }) => { - await testAria(page, 'subHeaderOnly', 'alert-1-sub-hdr', 'alert-1-msg'); + test('should have aria-labelledby and aria-describedby when headers and message are set', async ({ page }) => { + await testAria(page, 'headersAndMessage', 'alert-1-hdr alert-1-sub-hdr', 'alert-1-msg'); }); test('should allow for manually specifying aria attributes', async ({ page }) => { diff --git a/core/src/components/alert/test/a11y/index.html b/core/src/components/alert/test/a11y/index.html index 00b3e1d928c..cb137ba7314 100644 --- a/core/src/components/alert/test/a11y/index.html +++ b/core/src/components/alert/test/a11y/index.html @@ -19,10 +19,11 @@

Alert - A11y

- + + - + @@ -34,11 +35,17 @@

Alert - A11y

await alert.present(); } - function presentBothHeaders() { + function presentBothHeadersOnly() { openAlert({ header: 'Header', subHeader: 'Subtitle', - message: 'This is an alert message.', + buttons: ['OK'], + }); + } + + function presentHeaderOnly() { + openAlert({ + header: 'Header', buttons: ['OK'], }); } @@ -46,7 +53,6 @@

Alert - A11y

function presentSubHeaderOnly() { openAlert({ subHeader: 'Subtitle', - message: 'This is an alert message.', buttons: ['OK'], }); } @@ -58,10 +64,11 @@

Alert - A11y

}); } - function presentNoMessage() { + function presentHeadersAndMessage() { openAlert({ header: 'Header', subHeader: 'Subtitle', + message: 'This is an alert message.', buttons: ['OK'], }); }