Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions core/src/components/alert/alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -584,12 +584,14 @@ export class Alert implements ComponentInterface, OverlayInterface {
const subHdrId = `alert-${overlayIndex}-sub-hdr`;
const msgId = `alert-${overlayIndex}-msg`;
const role = this.inputs.length > 0 || this.buttons.length > 0 ? 'alertdialog' : 'alert';
const defaultAriaLabel = header || subHeader || 'Alert';

return (
<Host
role={role}
aria-modal="true"
tabindex="-1"
aria-label={defaultAriaLabel}
{...(htmlAttributes as any)}
style={{
zIndex: `${20000 + overlayIndex}`,
Expand Down
43 changes: 43 additions & 0 deletions core/src/components/alert/test/a11y/alert.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import AxeBuilder from '@axe-core/playwright';
import { expect } from '@playwright/test';
import type { E2EPage } from '@utils/test/playwright';
import { test } from '@utils/test/playwright';

const testAriaLabel = async (page: E2EPage, buttonID: string, expectedAriaLabel: string) => {
const button = page.locator(`#${buttonID}`);
await button.click();

const alert = page.locator('ion-alert');
await expect(alert).toHaveAttribute('aria-label', expectedAriaLabel);
};

test.describe('alert: a11y', () => {
test.beforeEach(async ({ page, skip }) => {
skip.rtl();
await page.goto(`/src/components/alert/test/a11y`);
});

test('should not have accessibility violations', async ({ page }) => {
const button = page.locator('#customHeader');
await button.click();

const results = await new AxeBuilder({ page }).analyze();
expect(results.violations).toEqual([]);
});

test('should have fallback aria-label when no header or subheader is specified', async ({ page }) => {
await testAriaLabel(page, 'noHeader', 'Alert');
});

test('should inherit aria-label from header', async ({ page }) => {
await testAriaLabel(page, 'customHeader', 'Header');
});

test('should inherit aria-label from subheader if no header is specified', async ({ page }) => {
await testAriaLabel(page, 'subHeaderOnly', 'Subtitle');
});

test('should allow for manually specifying aria-label', async ({ page }) => {
await testAriaLabel(page, 'customAriaLabel', 'Custom alert');
});
});
75 changes: 75 additions & 0 deletions core/src/components/alert/test/a11y/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Alert - A11y</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
</head>
<script type="module">
import { alertController } from '../../../../dist/ionic/index.esm.js';
window.alertController = alertController;
</script>

<body>
<main class="ion-padding">
<h1>Alert - A11y</h1>

<ion-button id="noHeader" expand="block" onclick="presentNoHeader()">Alert With No Header</ion-button>
<ion-button id="customHeader" expand="block" onclick="presentCustomHeader()">Alert With Custom Header</ion-button>
<ion-button id="subHeaderOnly" expand="block" onclick="presentSubHeaderOnly()"
>Alert With Subheader Only</ion-button
>
<ion-button id="customAriaLabel" expand="block" onclick="presentCustomAriaLabel()"
>Alert With Custom Aria Label</ion-button
>
</main>

<script>
async function openAlert(opts) {
const alert = await alertController.create(opts);
await alert.present();
}

function presentNoHeader() {
openAlert({
message: 'This is an alert message.',
buttons: ['OK'],
});
}

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

function presentSubHeaderOnly() {
openAlert({
subHeader: 'Subtitle',
message: 'This is an alert message.',
buttons: ['OK'],
});
}

function presentCustomAriaLabel() {
openAlert({
header: 'Header',
subHeader: 'Subtitle',
message: 'This is an alert message with a custom aria-label.',
buttons: ['OK'],
htmlAttributes: {
'aria-label': 'Custom alert',
},
});
}
</script>
</body>
</html>