Skip to content

Commit

Permalink
fix(menu-full-screen, modal): ensure the call to action element is fo…
Browse files Browse the repository at this point in the history
…cused on close

Ensures the element which opens `Modal` or a fullscreen `Menu` is focused when the
respective component closes.
  • Loading branch information
tomdavies73 committed Oct 7, 2024
1 parent 37acd17 commit 720eeef
Show file tree
Hide file tree
Showing 11 changed files with 603 additions and 3 deletions.
87 changes: 87 additions & 0 deletions src/components/dialog-full-screen/components.test-pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,93 @@ export const DialogFullScreenComponent = ({
);
};

export const DefaultStory = ({
children = "This is an example",
...props
}: Partial<DialogFullScreenProps>) => {
const [isDialogFullScreenOpen, setIsDialogFullScreenOpen] = useState(false);

return (
<>
<Button onClick={() => setIsDialogFullScreenOpen(true)}>
Open Dialog Full Screen
</Button>
<DialogFullScreen
open={isDialogFullScreenOpen}
showCloseIcon
title="First Dialog Full Screen"
aria-label="aria-label"
onCancel={() => setIsDialogFullScreenOpen(false)}
{...props}
>
{children}
</DialogFullScreen>
</>
);
};

export const DefaultOpenStory = ({
children = "This is an example",
...props
}: Partial<DialogFullScreenProps>) => {
const [isDialogFullScreenOpen, setIsDialogFullScreenOpen] = useState(true);

return (
<>
<Button onClick={() => setIsDialogFullScreenOpen(true)}>
Open Dialog Full Screen
</Button>
<DialogFullScreen
open={isDialogFullScreenOpen}
showCloseIcon
title="First Dialog Full Screen"
aria-label="aria-label"
onCancel={() => setIsDialogFullScreenOpen(false)}
{...props}
>
{children}
</DialogFullScreen>
</>
);
};

export const DefaultNestedStory = ({
children = "This is an example",
...props
}: Partial<DialogFullScreenProps>) => {
const [isFirstDialogOpen, setIsFirstDialogOpen] = useState(false);
const [isNestedDialogOpen, setIsNestedDialogOpen] = useState(false);
return (
<>
<Button onClick={() => setIsFirstDialogOpen(true)}>
Open First Dialog Full Screen
</Button>
<DialogFullScreen
open={isFirstDialogOpen}
showCloseIcon
title="First Dialog Full Screen"
aria-label="aria-label"
onCancel={() => setIsFirstDialogOpen(false)}
{...props}
>
<Button onClick={() => setIsNestedDialogOpen(true)}>
Open Nested Dialog Full Screen
</Button>
<DialogFullScreen
open={isNestedDialogOpen}
showCloseIcon
title="Nested Dialog Full Screen"
aria-label="aria-label"
onCancel={() => setIsNestedDialogOpen(false)}
{...props}
>
<Textbox label="Nested Dialog Textbox" />
</DialogFullScreen>
</DialogFullScreen>
</>
);
};

export const NestedDialog = () => {
const [mainDialogOpen, setMainDialogOpen] = React.useState(false);
const [nestedDialogOpen, setNestedDialogOpen] = React.useState(false);
Expand Down
79 changes: 79 additions & 0 deletions src/components/dialog-full-screen/dialog-full-screen.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { expect, test } from "@playwright/experimental-ct-react17";
import type { Page } from "@playwright/test";
import {
DialogFullScreenComponent,
DefaultStory,
DefaultOpenStory,
DefaultNestedStory,
NestedDialog,
MultipleDialogsInDifferentProviders,
DialogFullScreenWithHeaderChildren,
Expand Down Expand Up @@ -303,6 +306,82 @@ test.describe("render DialogFullScreen component and check properties", () => {
);
});

test("when Dialog Full Screen is opened and then closed, the call to action element should be focused", async ({
mount,
page,
}) => {
await mount(<DefaultStory />);

const button = page
.getByRole("button")
.filter({ hasText: "Open Dialog Full Screen" });
const dialogFullScreen = page.getByRole("dialog");
await expect(button).not.toBeFocused();
await expect(dialogFullScreen).not.toBeVisible();

await button.click();
await expect(dialogFullScreen).toBeVisible();
const closeButton = page.getByLabel("Close");
await closeButton.click();
await expect(button).toBeFocused();
await expect(dialogFullScreen).not.toBeVisible();
});

test("when Dialog Full Screen is open on render, then closed, opened and then closed again, the call to action element should be focused", async ({
mount,
page,
}) => {
await mount(<DefaultOpenStory />);

const dialogFullScreen = page.getByRole("dialog");
await expect(dialogFullScreen).toBeVisible();
const closeButton = page.getByLabel("Close");
await closeButton.click();

const button = page
.getByRole("button")
.filter({ hasText: "Open Dialog Full Screen" });
await expect(button).not.toBeFocused();
await expect(dialogFullScreen).not.toBeVisible();

await button.click();
await expect(dialogFullScreen).toBeVisible();
await closeButton.click();
await expect(button).toBeFocused();
});

test("when nested Dialog's are open/closed their respective call to action elements should be focused correctly", async ({
mount,
page,
}) => {
await mount(<DefaultNestedStory />);

const firstButton = page
.getByRole("button")
.filter({ hasText: "Open First Dialog Full Screen" });
const firstDialog = page.getByRole("dialog").first();
await expect(firstButton).not.toBeFocused();
await expect(firstDialog).not.toBeVisible();

await firstButton.click();
await expect(firstDialog).toBeVisible();
const secondButton = page
.getByRole("button")
.filter({ hasText: "Open Nested Dialog Full Screen" });
await expect(secondButton).not.toBeFocused();
await secondButton.click();
const secondDialog = page.getByRole("dialog").last();
await expect(secondDialog).toBeVisible();

const secondCloseButton = page.getByLabel("Close").last();
await secondCloseButton.click();
await expect(secondButton).toBeFocused();

const firstCloseButton = page.getByLabel("Close").first();
await firstCloseButton.click();
await expect(firstButton).toBeFocused();
});

test("should render component with autofocus disabled", async ({
mount,
page,
Expand Down
73 changes: 73 additions & 0 deletions src/components/dialog/components.test-pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,79 @@ export const DefaultStory = () => {
);
};

export const DefaultOpenStory = () => {
const [isOpen, setIsOpen] = useState(true);
return (
<>
<Button onClick={() => setIsOpen(true)}>Open Dialog</Button>
<Dialog
open={isOpen}
onCancel={() => setIsOpen(false)}
title="Title"
subtitle="Subtitle"
>
<Form
stickyFooter
height="500px"
leftSideButtons={
<Button onClick={() => setIsOpen(false)}>Cancel</Button>
}
saveButton={
<Button buttonType="primary" type="submit">
Save
</Button>
}
>
<Typography>
This is an example of a dialog with a Form as content
</Typography>
<Textbox label="First Name" />
<Textbox label="Middle Name" />
<Textbox label="Surname" />
<Textbox label="Birth Place" />
<Textbox label="Favourite Colour" />
<Textbox label="Address" />
<Textbox label="First Name" />
<Textbox label="Middle Name" />
<Textbox label="Surname" />
<Textbox label="Birth Place" />
<Textbox label="Favourite Colour" />
<Textbox label="Address" />
</Form>
</Dialog>
</>
);
};

export const DefaultNestedStory = () => {
const [isFirstDialogOpen, setIsFirstDialogOpen] = useState(false);
const [isNestedDialogOpen, setIsNestedDialogOpen] = useState(false);

return (
<>
<Button onClick={() => setIsFirstDialogOpen(true)}>
Open First Dialog
</Button>
<Dialog
open={isFirstDialogOpen}
onCancel={() => setIsFirstDialogOpen(false)}
title="First Dialog"
>
<Button onClick={() => setIsNestedDialogOpen(true)}>
Open Nested Dialog
</Button>
<Dialog
open={isNestedDialogOpen}
onCancel={() => setIsNestedDialogOpen(false)}
title="Nested Dialog"
>
<Textbox label="Nested Dialog Textbox" />
</Dialog>
</Dialog>
</>
);
};

export const Editable = () => {
const [isOpen, setIsOpen] = useState(defaultOpenState);
const [isDisabled, setIsDisabled] = useState(true);
Expand Down
74 changes: 74 additions & 0 deletions src/components/dialog/dialog.pw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
DialogWithAutoFocusSelect,
DialogComponentFocusableSelectors,
DefaultStory,
DefaultOpenStory,
DefaultNestedStory,
Editable,
WithHelp,
LoadingContent,
Expand Down Expand Up @@ -249,6 +251,78 @@ test.describe("Testing Dialog component properties", () => {
).toBeFocused();
});

test("when Dialog is opened and then closed, the call to action element should be focused", async ({
mount,
page,
}) => {
await mount(<DefaultStory />);

const button = page.getByRole("button").filter({ hasText: "Open Dialog" });
const dialog = page.getByRole("dialog");
await expect(button).not.toBeFocused();
await expect(dialog).not.toBeVisible();

await button.click();
await expect(dialog).toBeVisible();
const closeButton = page.getByLabel("Close");
await closeButton.click();
await expect(button).toBeFocused();
await expect(dialog).not.toBeVisible();
});

test("when Dialog is open on render, then closed, opened and then closed again, the call to action element should be focused", async ({
mount,
page,
}) => {
await mount(<DefaultOpenStory />);

const dialog = page.getByRole("dialog");
await expect(dialog).toBeVisible();
const closeButton = page.getByLabel("Close");
await closeButton.click();

const button = page.getByRole("button").filter({ hasText: "Open Dialog" });
await expect(button).not.toBeFocused();
await expect(dialog).not.toBeVisible();

await button.click();
await expect(dialog).toBeVisible();
await closeButton.click();
await expect(button).toBeFocused();
});

test("when nested Dialog's are open/closed their respective call to action elements should be focused correctly", async ({
mount,
page,
}) => {
await mount(<DefaultNestedStory />);

const firstButton = page
.getByRole("button")
.filter({ hasText: "Open First Dialog" });
const firstDialog = page.getByRole("dialog").first();
await expect(firstButton).not.toBeFocused();
await expect(firstDialog).not.toBeVisible();

await firstButton.click();
await expect(firstDialog).toBeVisible();
const secondButton = page
.getByRole("button")
.filter({ hasText: "Open Nested Dialog" });
await expect(secondButton).not.toBeFocused();
await secondButton.click();
const secondDialog = page.getByRole("dialog").last();
await expect(secondDialog).toBeVisible();

const secondCloseButton = page.getByLabel("Close").last();
await secondCloseButton.click();
await expect(secondButton).toBeFocused();

const firstCloseButton = page.getByLabel("Close").first();
await firstCloseButton.click();
await expect(firstButton).toBeFocused();
});

test("when disableAutoFocus prop is passed, the first focusable element should not be focused", async ({
mount,
page,
Expand Down
Loading

0 comments on commit 720eeef

Please sign in to comment.