diff --git a/src/components/Stepper/Step/Step.scss b/src/components/Stepper/Step/Step.scss index 518cd2524..bffcdbec9 100644 --- a/src/components/Stepper/Step/Step.scss +++ b/src/components/Stepper/Step/Step.scss @@ -29,6 +29,10 @@ text-decoration: underline; } +.step-enabled { + @include vf-focus-themed; +} + .step-disabled { color: $colors--theme--text-muted; pointer-events: none; diff --git a/src/components/Stepper/Step/Step.test.tsx b/src/components/Stepper/Step/Step.test.tsx index 750ad4b87..99cd777ea 100644 --- a/src/components/Stepper/Step/Step.test.tsx +++ b/src/components/Stepper/Step/Step.test.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { render, screen } from "@testing-library/react"; +import { createEvent, fireEvent, render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import Step from "./Step"; import type { Props } from "./Step"; @@ -14,6 +14,10 @@ describe("Step component", () => { handleClick: jest.fn(), }; + beforeEach(() => { + jest.clearAllMocks(); + }); + it("renders the step with the required props", () => { render(); expect(screen.getByText("Title")).toBeInTheDocument(); @@ -47,6 +51,55 @@ describe("Step component", () => { expect(props.handleClick).toHaveBeenCalled(); }); + it("exposes the title as a button role", () => { + render(); + expect(screen.getByRole("button", { name: "Title" })).toBeInTheDocument(); + }); + + it("is keyboard focusable and not aria-disabled when enabled", () => { + render(); + const title = screen.getByText("Title"); + expect(title).toHaveAttribute("tabindex", "0"); + expect(title).toHaveAttribute("aria-disabled", "false"); + }); + + it("calls handleClick when Enter is pressed and enabled", async () => { + render(); + screen.getByText("Title").focus(); + await userEvent.keyboard("{Enter}"); + expect(props.handleClick).toHaveBeenCalled(); + }); + + it("calls handleClick and prevents default when Space is pressed and enabled", () => { + render(); + const title = screen.getByText("Title"); + const event = createEvent.keyDown(title, { key: " " }); + fireEvent(title, event); + expect(props.handleClick).toHaveBeenCalled(); + expect(event.defaultPrevented).toBe(true); + }); + + it("is not focusable and is aria-disabled when disabled", () => { + render(); + const title = screen.getByText("Title"); + expect(title).toHaveAttribute("tabindex", "-1"); + expect(title).toHaveAttribute("aria-disabled", "true"); + }); + + it("does not call handleClick on keyboard activation when disabled", () => { + render(); + const title = screen.getByText("Title"); + fireEvent.keyDown(title, { key: "Enter" }); + fireEvent.keyDown(title, { key: " " }); + expect(props.handleClick).not.toHaveBeenCalled(); + }); + + it("does not call handleClick when clicked while disabled", async () => { + render(); + await userEvent.click(screen.getByText("Title")); + expect(props.handleClick).not.toHaveBeenCalled(); + }); + it("can display optional label", () => { render(); diff --git a/src/components/Stepper/Step/Step.tsx b/src/components/Stepper/Step/Step.tsx index 36d474e62..e34215ef5 100644 --- a/src/components/Stepper/Step/Step.tsx +++ b/src/components/Stepper/Step/Step.tsx @@ -63,6 +63,23 @@ const Step = ({ }: Props): React.JSX.Element => { const stepStatusClass = enabled ? "step-enabled" : "step-disabled"; + const handleKeyDown = (event: React.KeyboardEvent) => { + if (!enabled) { + return; + } + const isEnter = event.key === "Enter"; + const isSpace = event.key === " " || event.key === "Spacebar"; + if (isEnter || isSpace) { + if (event.repeat) { + return; + } + if (isSpace) { + event.preventDefault(); + } + handleClick(); + } + }; + return (
)}
- + {title} {label && ( diff --git a/src/components/Stepper/__snapshots__/Stepper.test.tsx.snap b/src/components/Stepper/__snapshots__/Stepper.test.tsx.snap index 81ebea07e..b04bdddeb 100644 --- a/src/components/Stepper/__snapshots__/Stepper.test.tsx.snap +++ b/src/components/Stepper/__snapshots__/Stepper.test.tsx.snap @@ -1,4 +1,4 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing +// Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Stepper component renders the stepper 1`] = `
    Step 1 @@ -42,7 +45,10 @@ exports[`Stepper component renders the stepper 1`] = ` class="step-content" > Step 2 @@ -69,7 +75,10 @@ exports[`Stepper component renders the stepper 1`] = ` class="step-content" > Step 3 @@ -102,7 +111,10 @@ exports[`Stepper component renders the stepper 1`] = ` class="step-content" > Step 4