Skip to content

Commit

Permalink
Fix Tile flip focus (#5336)
Browse files Browse the repository at this point in the history
* initial commit

* Add refocus behaviour with useEffect

* Fix focus theft on initial render

* Add tests

* Update metadata to be more specific

* Update state naming to be consistent
  • Loading branch information
Zystix authored Dec 3, 2024
1 parent bc3f702 commit 8bf1435
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 9 deletions.
5 changes: 5 additions & 0 deletions .changeset/two-bugs-march.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@kaizen/components": patch
---

Add infoButtonLabel prop to GenericTile and internationalise default label.
49 changes: 41 additions & 8 deletions packages/components/src/Tile/TileGrid/_docs/TileGrid.stories.tsx
Original file line number Diff line number Diff line change
@@ -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"

Expand All @@ -10,21 +11,21 @@ const meta = {
children: (
<>
<InformationTile
title="Title"
title="Title A"
metadata="Side A"
information="Side B"
information="Side A - Back"
footer={<>Footer</>}
/>
<InformationTile
title="Title"
metadata="Side A"
information="Side B"
title="Title B"
metadata="Side B"
information="Side B - Back"
footer={<>Footer</>}
/>
<InformationTile
title="Title"
metadata="Side A"
information="Side B"
title="Title C"
metadata="Side C"
information="Side C - Back"
footer={<>Footer</>}
/>
</>
Expand All @@ -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()
})
})
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
})
})
},
}
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -57,7 +57,28 @@ export const GenericTile = ({
...restProps
}: GenericTileProps): JSX.Element => {
const [isFlipped, setIsFlipped] = useState<boolean>(false)
const [isDocumentReady, setIsDocumentReady] = useState<boolean>(false)

const { formatMessage } = useIntl()
const infoButtonRef = useRef<HTMLButtonElement>(null)
const infoButtonReturnRef = useRef<HTMLButtonElement>(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",
Expand Down Expand Up @@ -97,6 +118,7 @@ export const GenericTile = ({
onClick={(): void => setIsFlipped(true)}
disabled={isFlipped}
aria-hidden={isFlipped}
ref={infoButtonRef}
/>
</div>
)}
Expand Down Expand Up @@ -162,6 +184,7 @@ export const GenericTile = ({
onClick={(): void => setIsFlipped(false)}
disabled={!isFlipped}
aria-hidden={!isFlipped}
ref={infoButtonReturnRef}
/>
</div>
<div className={styles.information}>
Expand Down

0 comments on commit 8bf1435

Please sign in to comment.