diff --git a/.changeset/two-bugs-march.md b/.changeset/two-bugs-march.md new file mode 100644 index 00000000000..e07d3d44084 --- /dev/null +++ b/.changeset/two-bugs-march.md @@ -0,0 +1,5 @@ +--- +"@kaizen/components": patch +--- + +Add infoButtonLabel prop to GenericTile and internationalise default label. diff --git a/packages/components/src/Tile/TileGrid/_docs/TileGrid.stories.tsx b/packages/components/src/Tile/TileGrid/_docs/TileGrid.stories.tsx index 318fa943f7c..3b620699a65 100644 --- a/packages/components/src/Tile/TileGrid/_docs/TileGrid.stories.tsx +++ b/packages/components/src/Tile/TileGrid/_docs/TileGrid.stories.tsx @@ -1,5 +1,6 @@ import React from "react" import { Meta, StoryObj } from "@storybook/react" +import { expect, waitFor, within } from "@storybook/test" import { InformationTile } from "~components/Tile" import { TileGrid } from "../index" @@ -10,21 +11,21 @@ const meta = { children: ( <> Footer} /> Footer} /> Footer} /> @@ -45,3 +46,35 @@ export const Playground: Story = { }, }, } + +// Test for multiple tiles, flipping one doesn't flip others +export const FlipOneNotOthers: Story = { + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement) + + await step("initial render complete", async () => { + await waitFor(() => { + canvas.getByRole("button", { + name: "View more information: Title A", + }) + }) + }) + + await step("Can focus to button", async () => { + await waitFor(() => { + const buttonWithInfoLabel = canvas.getByRole("button", { + name: "View more information: Title A", + }) + buttonWithInfoLabel.click() + }) + }) + + await step("Check other tiles", async () => { + await waitFor(() => { + expect(canvas.getByText("Side A - Back")).toBeInTheDocument() + expect(canvas.getByText("Title B")).toBeInTheDocument() + expect(canvas.getByText("Title C")).toBeInTheDocument() + }) + }) + }, +} diff --git a/packages/components/src/Tile/subcomponents/GenericTile/GenericTile.spec.stories.tsx b/packages/components/src/Tile/subcomponents/GenericTile/GenericTile.spec.stories.tsx index 85e5ded23d5..4395b846bb4 100644 --- a/packages/components/src/Tile/subcomponents/GenericTile/GenericTile.spec.stories.tsx +++ b/packages/components/src/Tile/subcomponents/GenericTile/GenericTile.spec.stories.tsx @@ -87,3 +87,61 @@ export const InfoButtonLabel: Story = { }) }, } + +export const DoesNotStealFocusOnInitialRender: Story = { + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement) + + await step("initial render complete", async () => { + await waitFor(() => { + canvas.getByRole("button", { + name: "View more information: Title", + }) + }) + }) + + await step("Can focus to button", async () => { + await waitFor(() => { + const buttonWithInfoLabel = canvas.getByRole("button", { + name: "View more information: Title", + }) + expect(buttonWithInfoLabel).not.toHaveFocus() + }) + }) + }, +} + +export const FocusOnFlip: Story = { + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement) + const buttonWithInfoLabel = await canvas.findByRole("button", { + name: "View more information: Title", + }) + + await step("initial render complete", async () => { + expect(buttonWithInfoLabel).toBeInTheDocument() + }) + + await step("Can focus to button", async () => { + await waitFor(() => { + buttonWithInfoLabel.click() + }) + }) + + const returnButton = canvas.getByRole("button", { + name: "Hide information: Title", + }) + + await step("Can click on info button again", async () => { + await waitFor(() => { + returnButton.click() + }) + }) + + await step("Info button has focus again", async () => { + await waitFor(() => { + expect(buttonWithInfoLabel).toHaveFocus() + }) + }) + }, +} diff --git a/packages/components/src/Tile/subcomponents/GenericTile/GenericTile.tsx b/packages/components/src/Tile/subcomponents/GenericTile/GenericTile.tsx index 957e7d592f3..cf064324575 100644 --- a/packages/components/src/Tile/subcomponents/GenericTile/GenericTile.tsx +++ b/packages/components/src/Tile/subcomponents/GenericTile/GenericTile.tsx @@ -1,4 +1,4 @@ -import React, { HTMLAttributes, useState } from "react" +import React, { HTMLAttributes, useState, useRef, useEffect } from "react" import { useIntl } from "@cultureamp/i18n-react-intl" import classnames from "classnames" import { AllowedHeadingTags, Heading } from "~components/Heading" @@ -57,7 +57,28 @@ export const GenericTile = ({ ...restProps }: GenericTileProps): JSX.Element => { const [isFlipped, setIsFlipped] = useState(false) + const [isDocumentReady, setIsDocumentReady] = useState(false) + const { formatMessage } = useIntl() + const infoButtonRef = useRef(null) + const infoButtonReturnRef = useRef(null) + + useEffect(() => { + setIsDocumentReady(true) + }, []) + + useEffect(() => { + if (!isDocumentReady) { + setIsDocumentReady(true) + return + } + + if (isFlipped) { + infoButtonReturnRef.current!.focus() + } else { + infoButtonRef.current!.focus() + } + }, [isFlipped]) const translatedInfoLabel = formatMessage({ id: "kzGenericTile.infoButtonLabel", @@ -97,6 +118,7 @@ export const GenericTile = ({ onClick={(): void => setIsFlipped(true)} disabled={isFlipped} aria-hidden={isFlipped} + ref={infoButtonRef} /> )} @@ -162,6 +184,7 @@ export const GenericTile = ({ onClick={(): void => setIsFlipped(false)} disabled={!isFlipped} aria-hidden={!isFlipped} + ref={infoButtonReturnRef} />