mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-10 00:27:41 +08:00
fix(modal): role attribute can be customized (#25804)
This commit is contained in:
@ -18,7 +18,8 @@ import type {
|
||||
} from '../../interface';
|
||||
import { findIonContent, printIonContentErrorMsg } from '../../utils/content';
|
||||
import { CoreDelegate, attachComponent, detachComponent } from '../../utils/framework-delegate';
|
||||
import { raf } from '../../utils/helpers';
|
||||
import { raf, inheritAttributes } from '../../utils/helpers';
|
||||
import type { Attributes } from '../../utils/helpers';
|
||||
import { KEYBOARD_DID_OPEN } from '../../utils/keyboard/keyboard';
|
||||
import { printIonWarning } from '../../utils/logging';
|
||||
import { BACKDROP, activeAnimations, dismiss, eventMethod, prepareOverlay, present } from '../../utils/overlays';
|
||||
@ -66,6 +67,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
|
||||
private sortedBreakpoints?: number[];
|
||||
private keyboardOpenCallback?: () => void;
|
||||
private moveSheetToBreakpoint?: (options: MoveSheetToBreakpointOptions) => Promise<void>;
|
||||
private inheritedAttributes: Attributes = {};
|
||||
|
||||
private inline = false;
|
||||
private workingDelegate?: FrameworkDelegate;
|
||||
@ -334,7 +336,9 @@ export class Modal implements ComponentInterface, OverlayInterface {
|
||||
}
|
||||
|
||||
componentWillLoad() {
|
||||
const { breakpoints, initialBreakpoint, swipeToClose } = this;
|
||||
const { breakpoints, initialBreakpoint, swipeToClose, el } = this;
|
||||
|
||||
this.inheritedAttributes = inheritAttributes(el, ['role']);
|
||||
|
||||
/**
|
||||
* If user has custom ID set then we should
|
||||
@ -855,7 +859,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { handle, isSheetModal, presentingElement, htmlAttributes, handleBehavior } = this;
|
||||
const { handle, isSheetModal, presentingElement, htmlAttributes, handleBehavior, inheritedAttributes } = this;
|
||||
|
||||
const showHandle = handle !== false && isSheetModal;
|
||||
const mode = getIonMode(this);
|
||||
@ -867,8 +871,10 @@ export class Modal implements ComponentInterface, OverlayInterface {
|
||||
<Host
|
||||
no-router
|
||||
aria-modal="true"
|
||||
role="dialog"
|
||||
tabindex="-1"
|
||||
{...(htmlAttributes as any)}
|
||||
{...inheritedAttributes}
|
||||
style={{
|
||||
zIndex: `${20000 + this.overlayIndex}`,
|
||||
}}
|
||||
@ -896,7 +902,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
|
||||
|
||||
{mode === 'ios' && <div class="modal-shadow"></div>}
|
||||
|
||||
<div role="dialog" class="modal-wrapper ion-overlay-wrapper" part="content" ref={(el) => (this.wrapperEl = el)}>
|
||||
<div class="modal-wrapper ion-overlay-wrapper" part="content" ref={(el) => (this.wrapperEl = el)}>
|
||||
{showHandle && (
|
||||
<button
|
||||
class="modal-handle"
|
||||
|
||||
21
core/src/components/modal/test/a11y/index.html
Normal file
21
core/src/components/modal/test/a11y/index.html
Normal file
@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Modal - 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 type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main>
|
||||
<h1>Modal - a11y</h1>
|
||||
|
||||
<button id="open-modal">Open Modal</button>
|
||||
<ion-modal aria-label="My custom label" trigger="open-modal">My content</ion-modal>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
39
core/src/components/modal/test/a11y/modal.e2e.ts
Normal file
39
core/src/components/modal/test/a11y/modal.e2e.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import AxeBuilder from '@axe-core/playwright';
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('modal: a11y', () => {
|
||||
test.beforeEach(async ({ skip }) => {
|
||||
skip.rtl();
|
||||
skip.mode('md');
|
||||
});
|
||||
|
||||
test('should not have accessibility violations', async ({ page }) => {
|
||||
await page.goto(`/src/components/modal/test/a11y`);
|
||||
|
||||
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
|
||||
const button = page.locator('#open-modal');
|
||||
const modal = page.locator('ion-modal');
|
||||
|
||||
await expect(modal).toHaveAttribute('role', 'dialog');
|
||||
|
||||
await button.click();
|
||||
await ionModalDidPresent.next();
|
||||
|
||||
const results = await new AxeBuilder({ page }).analyze();
|
||||
expect(results.violations).toEqual([]);
|
||||
});
|
||||
|
||||
test('should allow for custom role', async ({ page }) => {
|
||||
/**
|
||||
* Note: This example should not be used in production.
|
||||
* This only serves to check that `role` can be customized.
|
||||
*/
|
||||
await page.setContent(`
|
||||
<ion-modal role="alertdialog"></ion-modal>
|
||||
`);
|
||||
const modal = page.locator('ion-modal');
|
||||
|
||||
await expect(modal).toHaveAttribute('role', 'alertdialog');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user