From 8d9b9d6ff31e6482431e65b85cd4ab3f39ae90af Mon Sep 17 00:00:00 2001 From: adilallo <39313955+adilallo@users.noreply.github.com> Date: Sat, 7 Feb 2026 23:35:22 -0700 Subject: [PATCH] Create tests and stories for createflownav --- stories/icons/Logo.stories.js | 152 ++++---------------- stories/utility/CreateFlowFooter.stories.js | 56 ++++++++ stories/utility/CreateFlowTopNav.stories.js | 65 +++++++++ tests/components/CreateFlowFooter.test.tsx | 70 +++++++++ tests/components/CreateFlowTopNav.test.tsx | 119 +++++++++++++++ tests/components/Logo.test.tsx | 15 +- 6 files changed, 348 insertions(+), 129 deletions(-) create mode 100644 stories/utility/CreateFlowFooter.stories.js create mode 100644 stories/utility/CreateFlowTopNav.stories.js create mode 100644 tests/components/CreateFlowFooter.test.tsx create mode 100644 tests/components/CreateFlowTopNav.test.tsx diff --git a/stories/icons/Logo.stories.js b/stories/icons/Logo.stories.js index 91762f6..e3c86cc 100644 --- a/stories/icons/Logo.stories.js +++ b/stories/icons/Logo.stories.js @@ -17,17 +17,10 @@ export default { control: { type: "select" }, options: [ "default", - "homeHeaderXsmall", - "homeHeaderSm", - "homeHeaderMd", - "homeHeaderLg", - "homeHeaderXl", - "header", - "headerMd", - "headerLg", - "headerXl", "footer", - "footerLg", + "createFlow", + "topNavFolderTop", + "topNavHeader", ], description: "The size variant of the logo", }, @@ -53,40 +46,19 @@ export const Sizes = { render: (args) => (
-

Default Sizes

+

Standard Sizes

-
-

Header Sizes

+

Responsive Sizes

- - - - -
-
- -
-

Home Header Sizes

-
- - - - - -
-
- -
-

Footer Sizes

-
- - + + +
@@ -95,7 +67,7 @@ export const Sizes = { docs: { description: { story: - "Different size variants available for the logo component across different contexts.", + "Different size variants available for the logo component. Responsive sizes adapt to breakpoints.", }, }, }, @@ -110,37 +82,22 @@ export const IconOnly = {

- Icon Only - Default Sizes + Icon Only - Standard Sizes

-

- Icon Only - Header Sizes + Icon Only - Responsive Sizes

- - - - -
-
- -
-

- Icon Only - Home Header Sizes -

-
- - - - - + + +
@@ -155,34 +112,22 @@ export const IconOnly = { }, }; -export const HomeHeaderContext = { +export const TopNavContext = { args: {}, render: () => (

- Home Header Context (White Text) + TopNav Context (Responsive)

- XSmall: - + FolderTop: +
- Small: - -
-
- Medium: - -
-
- Large: - -
-
- XLarge: - + Header: +
@@ -192,36 +137,24 @@ export const HomeHeaderContext = { docs: { description: { story: - "Home header context showing white text variants. These are used on dark/transparent backgrounds.", + "TopNav context showing responsive logo sizes. Text hides on smallest breakpoint, shows on larger breakpoints.", }, }, }, }; -export const HeaderContext = { +export const CreateFlowContext = { args: {}, render: () => ( -
+

- Header Context (Dark Text) + Create Flow Context

- Default: - -
-
- Medium: - -
-
- Large: - -
-
- XLarge: - + CreateFlow: +
@@ -231,38 +164,7 @@ export const HeaderContext = { docs: { description: { story: - "Header context showing dark text variants. These are used on light backgrounds.", - }, - }, - }, -}; - -export const FooterContext = { - args: {}, - render: () => ( -
-
-

- Footer Context (Larger Sizes) -

-
-
- Default: - -
-
- Large: - -
-
-
-
- ), - parameters: { - docs: { - description: { - story: - "Footer context showing larger size variants for footer placement.", + "Create flow context showing responsive logo. Used in CreateFlowTopNav component.", }, }, }, diff --git a/stories/utility/CreateFlowFooter.stories.js b/stories/utility/CreateFlowFooter.stories.js new file mode 100644 index 0000000..4943f23 --- /dev/null +++ b/stories/utility/CreateFlowFooter.stories.js @@ -0,0 +1,56 @@ +import CreateFlowFooter from "../../app/components/utility/CreateFlowFooter"; +import Button from "../../app/components/buttons/Button"; + +export default { + title: "Components/Utility/CreateFlowFooter", + component: CreateFlowFooter, + parameters: { + layout: "fullscreen", + docs: { + description: { + component: + "Footer component for the create rule flow with progress bar and buttons.", + }, + }, + }, + argTypes: { + progressBar: { + control: "boolean", + description: "Whether to show the progress bar", + }, + secondButton: { + control: false, + description: "The second button (typically Next) to display on the right side", + }, + }, + tags: ["autodocs"], +}; + +export const Default = { + args: { + progressBar: true, + secondButton: ( + + ), + }, +}; + +export const WithoutProgressBar = { + args: { + progressBar: false, + secondButton: ( + + ), + }, +}; + +export const WithoutSecondButton = { + args: { + progressBar: true, + secondButton: undefined, + }, +}; diff --git a/stories/utility/CreateFlowTopNav.stories.js b/stories/utility/CreateFlowTopNav.stories.js new file mode 100644 index 0000000..918876d --- /dev/null +++ b/stories/utility/CreateFlowTopNav.stories.js @@ -0,0 +1,65 @@ +import CreateFlowTopNav from "../../app/components/utility/CreateFlowTopNav"; + +export default { + title: "Components/Utility/CreateFlowTopNav", + component: CreateFlowTopNav, + parameters: { + layout: "fullscreen", + docs: { + description: { + component: + "Top navigation bar for the create rule flow. Includes logo and action buttons (Share, Export, Edit, Exit/Save & Exit).", + }, + }, + }, + argTypes: { + hasShare: { + control: "boolean", + description: "Whether to show the Share button", + }, + hasExport: { + control: "boolean", + description: "Whether to show the Export button", + }, + hasEdit: { + control: "boolean", + description: "Whether to show the Edit button", + }, + loggedIn: { + control: "boolean", + description: "Whether the user is logged in (affects Exit button text)", + }, + onShare: { action: "share clicked" }, + onExport: { action: "export clicked" }, + onEdit: { action: "edit clicked" }, + onExit: { action: "exit clicked" }, + }, + tags: ["autodocs"], +}; + +export const Default = { + args: { + hasShare: false, + hasExport: false, + hasEdit: false, + loggedIn: false, + }, +}; + +export const AllButtons = { + args: { + hasShare: true, + hasExport: true, + hasEdit: true, + loggedIn: false, + }, +}; + +export const LoggedIn = { + args: { + hasShare: true, + hasExport: true, + hasEdit: true, + loggedIn: true, + }, +}; diff --git a/tests/components/CreateFlowFooter.test.tsx b/tests/components/CreateFlowFooter.test.tsx new file mode 100644 index 0000000..971c0c8 --- /dev/null +++ b/tests/components/CreateFlowFooter.test.tsx @@ -0,0 +1,70 @@ +import React from "react"; +import { describe, it, expect } from "vitest"; +import { render, screen } from "@testing-library/react"; +import "@testing-library/jest-dom/vitest"; +import CreateFlowFooter from "../../app/components/utility/CreateFlowFooter"; +import Button from "../../app/components/buttons/Button"; +import { + componentTestSuite, + ComponentTestSuiteConfig, +} from "../utils/componentTestSuite"; + +type CreateFlowFooterProps = React.ComponentProps; + +const baseProps: CreateFlowFooterProps = {}; + +const config: ComponentTestSuiteConfig = { + component: CreateFlowFooter, + name: "CreateFlowFooter", + props: baseProps, + requiredProps: [], + optionalProps: { + secondButton: , + progressBar: true, + className: "test-class", + }, + primaryRole: "contentinfo", + testCases: { + renders: true, + accessibility: true, + keyboardNavigation: false, + disabledState: false, + errorState: false, + }, +}; + +componentTestSuite(config); + +describe("CreateFlowFooter (behavioral tests)", () => { + it("renders Back button", () => { + render(); + const backButton = screen.getByRole("button", { name: "Back" }); + expect(backButton).toBeInTheDocument(); + }); + + it("renders progress bar when progressBar is true", () => { + render(); + const footer = screen.getByRole("contentinfo", { name: "Create Flow Footer" }); + expect(footer).toBeInTheDocument(); + }); + + it("does not render progress bar when progressBar is false", () => { + const { container } = render(); + const progressBar = container.querySelector('[role="progressbar"]'); + expect(progressBar).not.toBeInTheDocument(); + }); + + it("renders secondButton when provided", () => { + const secondButton = ; + render(); + const nextButton = screen.getByRole("button", { name: "Next" }); + expect(nextButton).toBeInTheDocument(); + }); + + it("does not render secondButton when not provided", () => { + render(); + const buttons = screen.getAllByRole("button"); + expect(buttons).toHaveLength(1); + expect(buttons[0]).toHaveTextContent("Back"); + }); +}); diff --git a/tests/components/CreateFlowTopNav.test.tsx b/tests/components/CreateFlowTopNav.test.tsx new file mode 100644 index 0000000..6cd3b2e --- /dev/null +++ b/tests/components/CreateFlowTopNav.test.tsx @@ -0,0 +1,119 @@ +import React from "react"; +import { describe, it, expect, vi } from "vitest"; +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import "@testing-library/jest-dom/vitest"; +import CreateFlowTopNav from "../../app/components/utility/CreateFlowTopNav"; +import { + componentTestSuite, + ComponentTestSuiteConfig, +} from "../utils/componentTestSuite"; + +// Mock next/navigation +vi.mock("next/navigation", () => ({ + useRouter: () => ({ + push: vi.fn(), + replace: vi.fn(), + prefetch: vi.fn(), + back: vi.fn(), + forward: vi.fn(), + refresh: vi.fn(), + }), +})); + +type CreateFlowTopNavProps = React.ComponentProps; + +const baseProps: CreateFlowTopNavProps = {}; + +const config: ComponentTestSuiteConfig = { + component: CreateFlowTopNav, + name: "CreateFlowTopNav", + props: baseProps, + requiredProps: [], + optionalProps: { + hasShare: true, + hasExport: true, + hasEdit: true, + loggedIn: true, + onShare: vi.fn(), + onExport: vi.fn(), + onEdit: vi.fn(), + onExit: vi.fn(), + className: "test-class", + }, + primaryRole: "banner", + testCases: { + renders: true, + accessibility: true, + keyboardNavigation: false, + disabledState: false, + errorState: false, + }, +}; + +componentTestSuite(config); + +describe("CreateFlowTopNav (behavioral tests)", () => { + it("renders Exit button by default", () => { + render(); + const exitButton = screen.getByRole("button", { name: "Exit" }); + expect(exitButton).toBeInTheDocument(); + }); + + it("shows Save & Exit when loggedIn is true", () => { + render(); + const exitButton = screen.getByRole("button", { name: "Save & Exit" }); + expect(exitButton).toBeInTheDocument(); + }); + + it("shows Exit when loggedIn is false", () => { + render(); + const exitButton = screen.getByRole("button", { name: "Exit" }); + expect(exitButton).toBeInTheDocument(); + }); + + it("renders Share button when hasShare is true", () => { + render(); + const shareButton = screen.getByRole("button", { name: "Share" }); + expect(shareButton).toBeInTheDocument(); + }); + + it("does not render Share button when hasShare is false", () => { + render(); + expect(screen.queryByRole("button", { name: "Share" })).not.toBeInTheDocument(); + }); + + it("renders Export button when hasExport is true", () => { + render(); + const exportButton = screen.getByRole("button", { name: "Export" }); + expect(exportButton).toBeInTheDocument(); + }); + + it("renders Edit button when hasEdit is true", () => { + render(); + const editButton = screen.getByRole("button", { name: "Edit" }); + expect(editButton).toBeInTheDocument(); + }); + + it("calls onExit when Exit button is clicked", async () => { + const user = userEvent.setup(); + const handleExit = vi.fn(); + render(); + + const exitButton = screen.getByRole("button", { name: "Exit" }); + await user.click(exitButton); + + expect(handleExit).toHaveBeenCalledTimes(1); + }); + + it("calls onShare when Share button is clicked", async () => { + const user = userEvent.setup(); + const handleShare = vi.fn(); + render(); + + const shareButton = screen.getByRole("button", { name: "Share" }); + await user.click(shareButton); + + expect(handleShare).toHaveBeenCalledTimes(1); + }); +}); diff --git a/tests/components/Logo.test.tsx b/tests/components/Logo.test.tsx index 5ade664..ef846d0 100644 --- a/tests/components/Logo.test.tsx +++ b/tests/components/Logo.test.tsx @@ -46,19 +46,26 @@ describe("Logo (behavioral tests)", () => { }); it("hides text when showText is false", () => { - render(); - expect(screen.queryByText("CommunityRule")).not.toBeInTheDocument(); + const { container } = render(); + const textElement = container.querySelector('.hidden'); + expect(textElement).toBeInTheDocument(); expect(screen.getByAltText("CommunityRule Logo Icon")).toBeInTheDocument(); }); it("renders with different size variants", () => { - const { rerender } = render(); + const { rerender } = render(); expect(screen.getByRole("link")).toBeInTheDocument(); rerender(); expect(screen.getByRole("link")).toBeInTheDocument(); - rerender(); + rerender(); + expect(screen.getByRole("link")).toBeInTheDocument(); + + rerender(); + expect(screen.getByRole("link")).toBeInTheDocument(); + + rerender(); expect(screen.getByRole("link")).toBeInTheDocument(); }); });