fix(loading): support custom aria-label (#26581)

resolves #24486
This commit is contained in:
Liam DeBeasi
2023-01-10 08:59:52 -05:00
committed by GitHub
parent 77ce9e066e
commit 2450a1e821
3 changed files with 99 additions and 3 deletions

View File

@ -186,10 +186,20 @@ export class Loading implements ComponentInterface, OverlayInterface {
}; };
render() { render() {
const { message, spinner, htmlAttributes } = this; const { message, spinner, htmlAttributes, overlayIndex } = this;
const mode = getIonMode(this); const mode = getIonMode(this);
const msgId = `loading-${overlayIndex}-msg`;
/**
* If the message is defined, use that as the label.
* Otherwise, don't set aria-labelledby.
*/
const ariaLabelledBy = message !== undefined ? msgId : null;
return ( return (
<Host <Host
role="dialog"
aria-modal="true"
aria-labelledby={ariaLabelledBy}
tabindex="-1" tabindex="-1"
{...(htmlAttributes as any)} {...(htmlAttributes as any)}
style={{ style={{
@ -207,14 +217,16 @@ export class Loading implements ComponentInterface, OverlayInterface {
<div tabindex="0"></div> <div tabindex="0"></div>
<div class="loading-wrapper ion-overlay-wrapper" role="dialog"> <div class="loading-wrapper ion-overlay-wrapper">
{spinner && ( {spinner && (
<div class="loading-spinner"> <div class="loading-spinner">
<ion-spinner name={spinner} aria-hidden="true" /> <ion-spinner name={spinner} aria-hidden="true" />
</div> </div>
)} )}
{message !== undefined && <div class="loading-content" innerHTML={sanitizeDOMString(message)}></div>} {message !== undefined && (
<div class="loading-content" id={msgId} innerHTML={sanitizeDOMString(message)}></div>
)}
</div> </div>
<div tabindex="0"></div> <div tabindex="0"></div>

View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Loading - a11y</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
<link href="../../../../../css/core.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>
<script type="module">
import { loadingController } from '../../../../../dist/ionic/index.esm.js';
window.loadingController = loadingController;
</script>
</head>
<body>
<main>
<h1>Loading - a11y</h1>
<button
id="open-message-loading"
onclick="openLoading({
message: 'Loading'
})"
>
Present Loading with Message
</button>
<button
id="open-label-loading"
onclick="openLoading({
spinner: 'bubbles',
htmlAttributes: {
'aria-label': 'Loading'
}
})"
>
Present Loading with Label
</button>
<script>
async function openLoading(opts) {
const loading = await loadingController.create(opts);
await loading.present();
}
</script>
</main>
</body>
</html>

View File

@ -0,0 +1,34 @@
import AxeBuilder from '@axe-core/playwright';
import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';
test.describe('loading: a11y', () => {
test.beforeEach(({ skip }) => {
skip.mode('md');
skip.rtl();
});
test('should set aria-labelledby with a message', async ({ page }) => {
await page.goto('/src/components/loading/test/a11y');
const ionLoadingDidPresent = await page.spyOnEvent('ionLoadingDidPresent');
await page.click('#open-message-loading');
await ionLoadingDidPresent.next();
const results = await new AxeBuilder({ page }).analyze();
expect(results.violations).toEqual([]);
});
test('should set aria-labelledby with a label', async ({ page }) => {
await page.goto('/src/components/loading/test/a11y');
const ionLoadingDidPresent = await page.spyOnEvent('ionLoadingDidPresent');
await page.click('#open-label-loading');
await ionLoadingDidPresent.next();
const results = await new AxeBuilder({ page }).analyze();
expect(results.violations).toEqual([]);
});
});