Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG][EuiModal] Fix VoiceOver + Safari escaping focus trap #7564

Merged
merged 11 commits into from
Mar 12, 2024
3 changes: 3 additions & 0 deletions changelogs/upcoming/7564.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**Bug fixes**

- Fixed `EuiModal` to properly trap Safari + VoiceOver virtual cursor
6 changes: 6 additions & 0 deletions src-docs/src/views/modal/confirm_modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ export default () => {
const showDestroyModal = () => setIsDestroyModalVisible(true);

let modal;
const confirmTitleProps = { id: 'euiModal-confirmModalExample' };

if (isModalVisible) {
modal = (
<EuiConfirmModal
aria-labelledby="euiModal-confirmModalExample"
style={{ width: 600 }}
title="Update subscription to Platinum?"
titleProps={confirmTitleProps}
onCancel={closeModal}
onConfirm={closeModal}
cancelButtonText="Cancel"
Expand All @@ -40,11 +43,14 @@ export default () => {
}

let destroyModal;
const destroyTitleProps = { id: 'euiModal-destroyModalExample' };

if (isDestroyModalVisible) {
destroyModal = (
<EuiConfirmModal
aria-labelledby="euiModal-destroyModalExample"
title="Discard dashboard changes?"
titleProps={destroyTitleProps}
onCancel={closeDestroyModal}
onConfirm={closeDestroyModal}
cancelButtonText="Keep editing"
Expand Down
3 changes: 3 additions & 0 deletions src-docs/src/views/modal/confirm_modal_loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,14 @@ export default () => {
};

let modal;
const confirmTitleProps = { id: 'euiModal-confirmLoadingExample' };

if (isModalVisible) {
modal = (
<EuiConfirmModal
aria-labelledby="euiModal-confirmLoadingExample"
title="Delete the EUI repo?"
titleProps={confirmTitleProps}
onCancel={closeModal}
onConfirm={() => {
closeModal();
Expand Down
6 changes: 4 additions & 2 deletions src-docs/src/views/modal/modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ export default () => {

if (isModalVisible) {
modal = (
<EuiModal onClose={closeModal}>
<EuiModal aria-labelledby="euiModal-example" onClose={closeModal}>
<EuiModalHeader>
<EuiModalHeaderTitle>Modal title</EuiModalHeaderTitle>
<EuiModalHeaderTitle id="euiModal-example">
Modal title
</EuiModalHeaderTitle>
</EuiModalHeader>

<EuiModalBody>
Expand Down
10 changes: 8 additions & 2 deletions src-docs/src/views/modal/modal_form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,15 @@ export default () => {

if (isModalVisible) {
modal = (
<EuiModal onClose={closeModal} initialFocus="[name=popswitch]">
<EuiModal
aria-labelledby="euiModal-formExample"
onClose={closeModal}
initialFocus="[name=popswitch]"
>
<EuiModalHeader>
<EuiModalHeaderTitle>Modal title</EuiModalHeaderTitle>
<EuiModalHeaderTitle id="euiModal-formExample">
Modal title
</EuiModalHeaderTitle>
</EuiModalHeader>

<EuiModalBody>{formSample}</EuiModalBody>
Expand Down
10 changes: 8 additions & 2 deletions src-docs/src/views/modal/modal_width.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,15 @@ export default () => {

if (isModalVisible) {
modal = (
<EuiModal style={{ width: 800 }} onClose={closeModal}>
<EuiModal
aria-labelledby="euiModal-widthExample"
style={{ width: 800 }}
onClose={closeModal}
>
<EuiModalHeader>
<EuiModalHeaderTitle>Modal title</EuiModalHeaderTitle>
<EuiModalHeaderTitle id="euiModal-widthExample">
Modal title
</EuiModalHeaderTitle>
</EuiModalHeader>

<EuiModalBody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ Array [
>
<div
aria-label="aria-label"
aria-modal="true"
class="euiModal euiModal--confirmation testClass1 testClass2 emotion-euiModal-defaultMaxWidth-confirmation-euiTestCss"
data-test-subj="test subject string"
role="alertdialog"
tabindex="0"
1Copenut marked this conversation as resolved.
Show resolved Hide resolved
>
<button
Expand Down Expand Up @@ -110,8 +112,10 @@ Array [
>
<div
aria-label="aria-label"
aria-modal="true"
class="euiModal euiModal--confirmation testClass1 testClass2 emotion-euiModal-defaultMaxWidth-confirmation-euiTestCss"
data-test-subj="test subject string"
role="alertdialog"
tabindex="0"
>
<button
Expand Down
2 changes: 2 additions & 0 deletions src/components/modal/__snapshots__/modal.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ exports[`EuiModal renders 1`] = `
>
<div
aria-label="aria-label"
aria-modal="true"
class="euiModal testClass1 testClass2 emotion-euiModal-defaultMaxWidth-euiTestCss"
data-test-subj="test subject string"
role="dialog"
tabindex="0"
>
<button
Expand Down
8 changes: 7 additions & 1 deletion src/components/modal/confirm_modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,13 @@ export const EuiConfirmModal: FunctionComponent<EuiConfirmModalProps> = ({
}

return (
<EuiModal className={classes} css={cssStyles} onClose={onCancel} {...rest}>
<EuiModal
className={classes}
css={cssStyles}
onClose={onCancel}
role="alertdialog"
{...rest}
>
{modalTitle}

{message && (
Expand Down
8 changes: 8 additions & 0 deletions src/components/modal/modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ export interface EuiModalProps extends HTMLAttributes<HTMLDivElement> {
* Can be a DOM node, or a selector string (which will be passed to document.querySelector() to find the DOM node), or a function that returns a DOM node.
*/
initialFocus?: HTMLElement | (() => HTMLElement) | string;
/**
* Identifies a modal dialog to screen readers. Modal dialogs that confirm destructive actions
* or need a user's attention should use "alertdialog".
*/
role?: 'dialog' | 'alertdialog';
}

export const EuiModal: FunctionComponent<EuiModalProps> = ({
Expand All @@ -51,6 +56,7 @@ export const EuiModal: FunctionComponent<EuiModalProps> = ({
initialFocus,
onClose,
maxWidth = true,
role = 'dialog',
style,
...rest
}) => {
Expand Down Expand Up @@ -88,6 +94,8 @@ export const EuiModal: FunctionComponent<EuiModalProps> = ({
onKeyDown={onKeyDown}
tabIndex={0}
style={newStyle}
role={role}
aria-modal={true}
{...rest}
>
<EuiI18n
Expand Down
1 change: 1 addition & 0 deletions src/components/modal/modal_header_title.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export type EuiModalHeaderTitleProps = FunctionComponent<
* @default h1
*/
component?: ElementType;
id?: String;
cee-chen marked this conversation as resolved.
Show resolved Hide resolved
}
>;

Expand Down
Loading