Fixes on create component tests

This commit is contained in:
adilallo
2026-02-02 13:08:55 -07:00
parent a8eb9e192b
commit 3b8f2e791f
10 changed files with 54 additions and 72 deletions
-1
View File
@@ -9,7 +9,6 @@ import Progress from "../components/Progress";
import Create from "../components/Create"; import Create from "../components/Create";
import Input from "../components/Input"; import Input from "../components/Input";
import InputWithCounter from "../components/InputWithCounter"; import InputWithCounter from "../components/InputWithCounter";
import { getAssetPath } from "../../lib/assetUtils";
export default function ComponentsPreview() { export default function ComponentsPreview() {
const [alertVisible, setAlertVisible] = useState({ const [alertVisible, setAlertVisible] = useState({
@@ -99,6 +99,8 @@ const ContentLockupContainer = memo<ContentLockupProps>(
titleContainer: "flex items-center justify-start w-full", titleContainer: "flex items-center justify-start w-full",
title: title:
"font-bricolage-grotesque font-bold text-[28px] leading-[36px] tracking-[0] text-[var(--color-content-default-primary)] text-left", "font-bricolage-grotesque font-bold text-[28px] leading-[36px] tracking-[0] text-[var(--color-content-default-primary)] text-left",
subtitle:
"font-inter font-normal text-[16px] leading-[24px] tracking-[0] text-[var(--color-content-default-tertiary)] text-left",
description: description:
"font-inter font-normal text-[16px] leading-[24px] tracking-[0] text-[var(--color-content-default-tertiary)] text-left", "font-inter font-normal text-[16px] leading-[24px] tracking-[0] text-[var(--color-content-default-tertiary)] text-left",
shape: "w-[16px] h-[16px]", shape: "w-[16px] h-[16px]",
+2 -3
View File
@@ -50,8 +50,7 @@ const CreateContainer = memo<CreateProps>(
if (!isOpen) return; if (!isOpen) return;
// Store previous active element // Store previous active element
previousActiveElementRef.current = previousActiveElementRef.current = document.activeElement as HTMLElement;
document.activeElement as HTMLElement;
// Lock body scroll // Lock body scroll
document.body.style.overflow = "hidden"; document.body.style.overflow = "hidden";
@@ -108,13 +107,13 @@ const CreateContainer = memo<CreateProps>(
}; };
}, [isOpen]); }, [isOpen]);
return ( return (
<CreateView <CreateView
isOpen={isOpen} isOpen={isOpen}
onClose={onClose} onClose={onClose}
title={title} title={title}
description={description} description={description}
// eslint-disable-next-line react/no-children-prop
children={children} children={children}
footerContent={footerContent} footerContent={footerContent}
showBackButton={showBackButton} showBackButton={showBackButton}
@@ -1,3 +1,4 @@
/* eslint-disable no-unused-vars, @typescript-eslint/no-unused-vars */
export interface InputWithCounterProps { export interface InputWithCounterProps {
label?: string; label?: string;
placeholder?: string; placeholder?: string;
@@ -8,3 +9,4 @@ export interface InputWithCounterProps {
className?: string; className?: string;
inputClassName?: string; inputClassName?: string;
} }
/* eslint-enable no-unused-vars, @typescript-eslint/no-unused-vars */
@@ -1,4 +1,3 @@
import { getAssetPath } from "../../../lib/assetUtils";
import type { InputWithCounterProps } from "./InputWithCounter.types"; import type { InputWithCounterProps } from "./InputWithCounter.types";
export function InputWithCounterView({ export function InputWithCounterView({
@@ -30,11 +30,7 @@ export function ModalFooterView({
{/* Back Button - Absolutely positioned bottom left */} {/* Back Button - Absolutely positioned bottom left */}
{showBackButton && ( {showBackButton && (
<div className="absolute left-[16px] top-[12px]"> <div className="absolute left-[16px] top-[12px]">
<Button <Button variant="outlined" size="medium" onClick={onBack}>
variant="outlined"
size="medium"
onClick={onBack}
>
{defaultBackText} {defaultBackText}
</Button> </Button>
</div> </div>
+1 -1
View File
@@ -1,4 +1,4 @@
export type StepperActive = 1 | 2 | 3 | 4 | 5; export type StepperActive = number;
export interface StepperProps { export interface StepperProps {
active?: StepperActive; active?: StepperActive;
+8
View File
@@ -176,6 +176,14 @@ const eslintConfig = [
"react-hooks/exhaustive-deps": "off", "react-hooks/exhaustive-deps": "off",
}, },
}, },
// Type definition files - interface properties are used in implementation files
{
files: ["**/*.types.ts"],
rules: {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": "off",
},
},
]; ];
export default eslintConfig; export default eslintConfig;
+8 -28
View File
@@ -65,15 +65,10 @@ export const Default = Template.bind({});
Default.args = { Default.args = {
isOpen: true, isOpen: true,
title: "What do you call your group's new policy?", title: "What do you call your group's new policy?",
description: description: "You can also combine or add new approaches to the list",
"You can also combine or add new approaches to the list",
children: ( children: (
<div className="space-y-4"> <div className="space-y-4">
<Input <Input label="Label" placeholder="Policy name" value="" />
label="Label"
placeholder="Policy name"
value=""
/>
<p className="text-[12px] text-[var(--color-content-default-tertiary)]"> <p className="text-[12px] text-[var(--color-content-default-tertiary)]">
0/48 0/48
</p> </p>
@@ -90,15 +85,10 @@ export const WithStepper = Template.bind({});
WithStepper.args = { WithStepper.args = {
isOpen: true, isOpen: true,
title: "What do you call your group's new policy?", title: "What do you call your group's new policy?",
description: description: "You can also combine or add new approaches to the list",
"You can also combine or add new approaches to the list",
children: ( children: (
<div className="space-y-4"> <div className="space-y-4">
<Input <Input label="Label" placeholder="Policy name" value="" />
label="Label"
placeholder="Policy name"
value=""
/>
<p className="text-[12px] text-[var(--color-content-default-tertiary)]"> <p className="text-[12px] text-[var(--color-content-default-tertiary)]">
0/48 0/48
</p> </p>
@@ -117,15 +107,10 @@ export const Step2 = Template.bind({});
Step2.args = { Step2.args = {
isOpen: true, isOpen: true,
title: "How should conflicts be resolved?", title: "How should conflicts be resolved?",
description: description: "You can also combine or add new approaches to the list",
"You can also combine or add new approaches to the list",
children: ( children: (
<div className="space-y-4"> <div className="space-y-4">
<Input <Input label="Label" placeholder="Enter text" value="" />
label="Label"
placeholder="Enter text"
value=""
/>
</div> </div>
), ),
showBackButton: true, showBackButton: true,
@@ -178,15 +163,10 @@ export const NextButtonDisabled = Template.bind({});
NextButtonDisabled.args = { NextButtonDisabled.args = {
isOpen: true, isOpen: true,
title: "What do you call your group's new policy?", title: "What do you call your group's new policy?",
description: description: "You can also combine or add new approaches to the list",
"You can also combine or add new approaches to the list",
children: ( children: (
<div className="space-y-4"> <div className="space-y-4">
<Input <Input label="Label" placeholder="Policy name" value="" />
label="Label"
placeholder="Policy name"
value=""
/>
<p className="text-[12px] text-[var(--color-content-default-tertiary)]"> <p className="text-[12px] text-[var(--color-content-default-tertiary)]">
0/48 0/48
</p> </p>
+30 -33
View File
@@ -1,7 +1,8 @@
import React from "react"; import React from "react";
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, fireEvent, waitFor } from "@testing-library/react"; import { screen, fireEvent, waitFor } from "@testing-library/react";
import "@testing-library/jest-dom/vitest"; import "@testing-library/jest-dom/vitest";
import { renderWithProviders } from "../utils/test-utils";
import Create from "../../app/components/Create"; import Create from "../../app/components/Create";
import Input from "../../app/components/Input"; import Input from "../../app/components/Input";
@@ -20,20 +21,22 @@ describe("Create", () => {
}); });
it("renders when isOpen is true", () => { it("renders when isOpen is true", () => {
render(<Create {...defaultProps}>Create dialog content</Create>); renderWithProviders(
<Create {...defaultProps}>Create dialog content</Create>,
);
expect(screen.getByRole("dialog")).toBeInTheDocument(); expect(screen.getByRole("dialog")).toBeInTheDocument();
expect(screen.getByText("Test Create Dialog")).toBeInTheDocument(); expect(screen.getByText("Test Create Dialog")).toBeInTheDocument();
expect(screen.getByText("Create dialog content")).toBeInTheDocument(); expect(screen.getByText("Create dialog content")).toBeInTheDocument();
}); });
it("does not render when isOpen is false", () => { it("does not render when isOpen is false", () => {
render(<Create {...defaultProps} isOpen={false} />); renderWithProviders(<Create {...defaultProps} isOpen={false} />);
expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
}); });
it("calls onClose when close button is clicked", () => { it("calls onClose when close button is clicked", () => {
const onClose = vi.fn(); const onClose = vi.fn();
render(<Create {...defaultProps} onClose={onClose} />); renderWithProviders(<Create {...defaultProps} onClose={onClose} />);
const closeButton = screen.getByLabelText("Close dialog"); const closeButton = screen.getByLabelText("Close dialog");
fireEvent.click(closeButton); fireEvent.click(closeButton);
expect(onClose).toHaveBeenCalledTimes(1); expect(onClose).toHaveBeenCalledTimes(1);
@@ -41,14 +44,14 @@ describe("Create", () => {
it("calls onClose when ESC key is pressed", () => { it("calls onClose when ESC key is pressed", () => {
const onClose = vi.fn(); const onClose = vi.fn();
render(<Create {...defaultProps} onClose={onClose} />); renderWithProviders(<Create {...defaultProps} onClose={onClose} />);
fireEvent.keyDown(document, { key: "Escape" }); fireEvent.keyDown(document, { key: "Escape" });
expect(onClose).toHaveBeenCalledTimes(1); expect(onClose).toHaveBeenCalledTimes(1);
}); });
it("calls onClose when overlay is clicked", () => { it("calls onClose when overlay is clicked", () => {
const onClose = vi.fn(); const onClose = vi.fn();
render(<Create {...defaultProps} onClose={onClose} />); renderWithProviders(<Create {...defaultProps} onClose={onClose} />);
const overlay = document.querySelector(".fixed.inset-0"); const overlay = document.querySelector(".fixed.inset-0");
if (overlay) { if (overlay) {
fireEvent.click(overlay); fireEvent.click(overlay);
@@ -59,7 +62,7 @@ describe("Create", () => {
it("renders footer buttons when provided", () => { it("renders footer buttons when provided", () => {
const onBack = vi.fn(); const onBack = vi.fn();
const onNext = vi.fn(); const onNext = vi.fn();
render( renderWithProviders(
<Create <Create
{...defaultProps} {...defaultProps}
showBackButton={true} showBackButton={true}
@@ -76,7 +79,7 @@ describe("Create", () => {
it("calls onBack when back button is clicked", () => { it("calls onBack when back button is clicked", () => {
const onBack = vi.fn(); const onBack = vi.fn();
render( renderWithProviders(
<Create <Create
{...defaultProps} {...defaultProps}
showBackButton={true} showBackButton={true}
@@ -91,7 +94,7 @@ describe("Create", () => {
it("calls onNext when next button is clicked", () => { it("calls onNext when next button is clicked", () => {
const onNext = vi.fn(); const onNext = vi.fn();
render( renderWithProviders(
<Create <Create
{...defaultProps} {...defaultProps}
showNextButton={true} showNextButton={true}
@@ -105,7 +108,7 @@ describe("Create", () => {
}); });
it("disables next button when nextButtonDisabled is true", () => { it("disables next button when nextButtonDisabled is true", () => {
render( renderWithProviders(
<Create <Create
{...defaultProps} {...defaultProps}
showNextButton={true} showNextButton={true}
@@ -118,28 +121,20 @@ describe("Create", () => {
}); });
it("renders stepper when currentStep and totalSteps are provided", () => { it("renders stepper when currentStep and totalSteps are provided", () => {
render( renderWithProviders(
<Create <Create {...defaultProps} currentStep={2} totalSteps={5} />,
{...defaultProps}
currentStep={2}
totalSteps={5}
/>,
); );
const steppers = screen.getAllByRole("progressbar"); // Find the stepper by its aria-label
// Find the stepper in the footer (not the progress bar if any) const stepper = screen.getByRole("progressbar", {
const footerStepper = steppers.find((stepper) => { name: "Step 2 of 5",
const parent = stepper.closest(".absolute.bottom-0");
return parent !== null;
}); });
expect(footerStepper).toBeInTheDocument(); expect(stepper).toBeInTheDocument();
if (footerStepper) { expect(stepper).toHaveAttribute("aria-valuenow", "2");
expect(footerStepper).toHaveAttribute("aria-valuenow", "2"); expect(stepper).toHaveAttribute("aria-valuemax", "5");
expect(footerStepper).toHaveAttribute("aria-valuemax", "5");
}
}); });
it("renders custom footer content", () => { it("renders custom footer content", () => {
render( renderWithProviders(
<Create <Create
{...defaultProps} {...defaultProps}
footerContent={<button>Custom Footer</button>} footerContent={<button>Custom Footer</button>}
@@ -149,33 +144,35 @@ describe("Create", () => {
}); });
it("has proper ARIA attributes", () => { it("has proper ARIA attributes", () => {
render(<Create {...defaultProps} ariaLabel="Test create dialog" />); renderWithProviders(
<Create {...defaultProps} ariaLabel="Test create dialog" />,
);
const dialog = screen.getByRole("dialog"); const dialog = screen.getByRole("dialog");
expect(dialog).toHaveAttribute("aria-modal", "true"); expect(dialog).toHaveAttribute("aria-modal", "true");
expect(dialog).toHaveAttribute("aria-label", "Test create dialog"); expect(dialog).toHaveAttribute("aria-label", "Test create dialog");
}); });
it("locks body scroll when open", () => { it("locks body scroll when open", () => {
render(<Create {...defaultProps} />); renderWithProviders(<Create {...defaultProps} />);
expect(document.body.style.overflow).toBe("hidden"); expect(document.body.style.overflow).toBe("hidden");
}); });
it("restores body scroll when closed", () => { it("restores body scroll when closed", () => {
const { rerender } = render(<Create {...defaultProps} />); const { rerender } = renderWithProviders(<Create {...defaultProps} />);
expect(document.body.style.overflow).toBe("hidden"); expect(document.body.style.overflow).toBe("hidden");
rerender(<Create {...defaultProps} isOpen={false} />); rerender(<Create {...defaultProps} isOpen={false} />);
expect(document.body.style.overflow).toBe(""); expect(document.body.style.overflow).toBe("");
}); });
it("traps focus within create dialog", async () => { it("traps focus within create dialog", async () => {
render( renderWithProviders(
<Create {...defaultProps}> <Create {...defaultProps}>
<Input label="Test Input" /> <Input label="Test Input" />
</Create>, </Create>,
); );
const closeButton = screen.getByLabelText("Close dialog"); const closeButton = screen.getByLabelText("Close dialog");
const input = screen.getByLabelText("Test Input"); screen.getByLabelText("Test Input"); // Verify input is rendered
// Focus should start on first focusable element (close button) // Focus should start on first focusable element (close button)
await waitFor(() => { await waitFor(() => {