Create custom flow UI
This commit is contained in:
@@ -40,8 +40,8 @@ describe("ProportionBar (behavioral tests)", () => {
|
||||
it("renders proportion bar with correct progress value", () => {
|
||||
render(<ProportionBar progress="2-1" />);
|
||||
const progressbar = screen.getByRole("progressbar");
|
||||
// 2-1: First section full (1) + second section 1/3 filled = 1 + 1/3 ≈ 1.333
|
||||
expect(progressbar).toHaveAttribute("aria-valuenow", "1.3333333333333333");
|
||||
// 2-1 (Figma `17861:33241`): first section full + second section 1/4 filled = 1.25.
|
||||
expect(progressbar).toHaveAttribute("aria-valuenow", "1.25");
|
||||
expect(progressbar).toHaveAttribute("aria-valuemin", "0");
|
||||
expect(progressbar).toHaveAttribute("aria-valuemax", "3");
|
||||
});
|
||||
@@ -63,7 +63,8 @@ describe("ProportionBar (behavioral tests)", () => {
|
||||
{ progress: "1-0" as const, expected: 1 / 6 }, // First section 1/6 filled
|
||||
{ progress: "1-5" as const, expected: 1 }, // First section 6/6 filled (fully filled)
|
||||
{ progress: "2-0" as const, expected: 1 }, // First section full, second empty
|
||||
{ progress: "2-2" as const, expected: 1 + 2 / 3 }, // First section full, second section 2/3 filled
|
||||
{ progress: "2-2" as const, expected: 1 + 1 / 2 }, // 1 + 1/2 per Figma `18861:15250`
|
||||
{ progress: "2-3" as const, expected: 1 + 3 / 4 }, // 1 + 3/4 per Figma `21434:17632`
|
||||
{ progress: "3-0" as const, expected: 2 }, // First two sections full, third empty
|
||||
{ progress: "3-2" as const, expected: 2 + 2 / 3 }, // First two sections full, third section 2/3 filled
|
||||
];
|
||||
|
||||
@@ -6,16 +6,16 @@ import {
|
||||
} from "../utils/test-utils";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, test, expect, afterEach } from "vitest";
|
||||
import { CardsScreen } from "../../app/create/screens/card/CardsScreen";
|
||||
import { CommunicationMethodsScreen } from "../../app/create/screens/card/CommunicationMethodsScreen";
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
describe("Create flow cards page", () => {
|
||||
describe("Create flow communication-methods page", () => {
|
||||
test("clicking a card opens the Create modal", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<CardsScreen />);
|
||||
render(<CommunicationMethodsScreen />);
|
||||
|
||||
const signalCards = screen.getAllByRole("button", {
|
||||
name: /Signal: Encrypted messaging/,
|
||||
@@ -29,7 +29,7 @@ describe("Create flow cards page", () => {
|
||||
});
|
||||
|
||||
test("renders without error", () => {
|
||||
render(<CardsScreen />);
|
||||
render(<CommunicationMethodsScreen />);
|
||||
|
||||
expect(
|
||||
screen.getByText(
|
||||
@@ -39,13 +39,12 @@ describe("Create flow cards page", () => {
|
||||
});
|
||||
|
||||
test("renders HeaderLockup and CardStack content", () => {
|
||||
render(<CardsScreen />);
|
||||
render(<CommunicationMethodsScreen />);
|
||||
|
||||
expect(
|
||||
screen.getByText(
|
||||
"You can select multiple methods for different needs or add your own",
|
||||
),
|
||||
screen.getByText(/You can select multiple methods for different needs or/),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: "add" })).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("button", { name: "See all communication approaches" }),
|
||||
).toBeInTheDocument();
|
||||
@@ -53,7 +52,7 @@ describe("Create flow cards page", () => {
|
||||
|
||||
test("toggle expands and shows Show less", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<CardsScreen />);
|
||||
render(<CommunicationMethodsScreen />);
|
||||
|
||||
const toggle = screen.getByRole("button", {
|
||||
name: "See all communication approaches",
|
||||
@@ -18,7 +18,7 @@ describe("Create flow right-rail page", () => {
|
||||
|
||||
expect(
|
||||
screen.getByRole("heading", {
|
||||
name: "How should conflicts be resolved?",
|
||||
name: "How should this community make difficult decisions?",
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
@@ -30,9 +30,9 @@ describe("Create flow right-rail page", () => {
|
||||
if (element?.tagName !== "P") return false;
|
||||
const text = element.textContent ?? "";
|
||||
return (
|
||||
text.includes("You can also combine or") &&
|
||||
text.includes("Select as many as you need") &&
|
||||
text.includes("add") &&
|
||||
text.includes("new approaches to the list")
|
||||
text.includes("new decision making approaches")
|
||||
);
|
||||
});
|
||||
expect(description).toBeInTheDocument();
|
||||
@@ -77,17 +77,17 @@ describe("Create flow right-rail page", () => {
|
||||
|
||||
expect(
|
||||
screen.getByRole("button", {
|
||||
name: /Mediation: Collaborative work to reach a resolution/,
|
||||
name: /Lazy Consensus: A decision is assumed approved/,
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("button", {
|
||||
name: /Facilitated dialogue: Structured sessions/,
|
||||
name: /Do-ocracy: Decisions are made by those who take initiative/,
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("button", {
|
||||
name: /Invite-only: Private discussions with selected participants/,
|
||||
name: /Consensus Decision-Making: All members must agree/,
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
@@ -123,10 +123,10 @@ describe("Create flow right-rail page", () => {
|
||||
const user = userEvent.setup();
|
||||
render(<RightRailScreen />);
|
||||
|
||||
const mediationCard = screen.getByRole("button", {
|
||||
name: /Mediation: Collaborative work to reach a resolution/,
|
||||
const card = screen.getByRole("button", {
|
||||
name: /Lazy Consensus: A decision is assumed approved/,
|
||||
});
|
||||
await user.click(mediationCard);
|
||||
await user.click(card);
|
||||
|
||||
expect(screen.getByText("SELECTED")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -26,4 +26,28 @@ describe("getProportionBarProgressForCreateFlowStep", () => {
|
||||
"2-0",
|
||||
);
|
||||
});
|
||||
|
||||
it("uses 2-1 on communication-methods", () => {
|
||||
expect(
|
||||
getProportionBarProgressForCreateFlowStep("communication-methods"),
|
||||
).toBe("2-1");
|
||||
});
|
||||
|
||||
it("uses 2-2 on membership-methods", () => {
|
||||
expect(
|
||||
getProportionBarProgressForCreateFlowStep("membership-methods"),
|
||||
).toBe("2-2");
|
||||
});
|
||||
|
||||
it("uses 2-3 on decision-approaches (Figma Flow — Right Rail)", () => {
|
||||
expect(
|
||||
getProportionBarProgressForCreateFlowStep("decision-approaches"),
|
||||
).toBe("2-3");
|
||||
});
|
||||
|
||||
it("uses 3-0 on conflict-management (start of Review segment)", () => {
|
||||
expect(
|
||||
getProportionBarProgressForCreateFlowStep("conflict-management"),
|
||||
).toBe("3-0");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -56,7 +56,7 @@ describe("createFlowStateSchema", () => {
|
||||
it("accepts known fields and passthrough keys", () => {
|
||||
const r = createFlowStateSchema.safeParse({
|
||||
title: "My rule",
|
||||
currentStep: "cards",
|
||||
currentStep: "communication-methods",
|
||||
customField: { nested: [1, 2] },
|
||||
});
|
||||
expect(r.success).toBe(true);
|
||||
|
||||
@@ -16,7 +16,10 @@ describe("flowSteps", () => {
|
||||
});
|
||||
|
||||
it("getNextStep returns next step in order", () => {
|
||||
expect(getNextStep("right-rail")).toBe("confirm-stakeholders");
|
||||
expect(getNextStep("communication-methods")).toBe("membership-methods");
|
||||
expect(getNextStep("membership-methods")).toBe("decision-approaches");
|
||||
expect(getNextStep("decision-approaches")).toBe("conflict-management");
|
||||
expect(getNextStep("conflict-management")).toBe("confirm-stakeholders");
|
||||
expect(getNextStep("confirm-stakeholders")).toBe("final-review");
|
||||
});
|
||||
|
||||
@@ -67,6 +70,6 @@ describe("flowSteps", () => {
|
||||
const opts = { skipCommunitySave: true } as const;
|
||||
expect(getNextStep("community-size", opts)).toBe("community-upload");
|
||||
expect(getNextStep("review", opts)).toBe("core-values");
|
||||
expect(getPreviousStep("cards", opts)).toBe("core-values");
|
||||
expect(getPreviousStep("communication-methods", opts)).toBe("core-values");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,27 +2,12 @@ import { describe, it, expect } from "vitest";
|
||||
import { migrateLegacyCreateFlowState } from "../../lib/create/migrateLegacyCreateFlowState";
|
||||
|
||||
describe("migrateLegacyCreateFlowState", () => {
|
||||
it("maps communityReflection to communitySaveEmail when save email empty", () => {
|
||||
it("passes through object payloads", () => {
|
||||
const out = migrateLegacyCreateFlowState({
|
||||
title: "T",
|
||||
communityReflection: "old@example.com",
|
||||
});
|
||||
expect(out.communitySaveEmail).toBe("old@example.com");
|
||||
expect("communityReflection" in out).toBe(false);
|
||||
});
|
||||
|
||||
it("does not overwrite existing communitySaveEmail", () => {
|
||||
const out = migrateLegacyCreateFlowState({
|
||||
communityReflection: "old@example.com",
|
||||
communitySaveEmail: "kept@example.com",
|
||||
});
|
||||
expect(out.communitySaveEmail).toBe("kept@example.com");
|
||||
});
|
||||
|
||||
it("rewrites currentStep slug", () => {
|
||||
const out = migrateLegacyCreateFlowState({
|
||||
currentStep: "community-reflection",
|
||||
currentStep: "community-save",
|
||||
});
|
||||
expect(out.title).toBe("T");
|
||||
expect(out.currentStep).toBe("community-save");
|
||||
});
|
||||
|
||||
@@ -30,4 +15,13 @@ describe("migrateLegacyCreateFlowState", () => {
|
||||
expect(migrateLegacyCreateFlowState(null)).toEqual({});
|
||||
expect(migrateLegacyCreateFlowState(undefined)).toEqual({});
|
||||
});
|
||||
|
||||
it("renames legacy right-rail step to decision-approaches", () => {
|
||||
const out = migrateLegacyCreateFlowState({
|
||||
currentStep: "right-rail",
|
||||
title: "T",
|
||||
});
|
||||
expect(out.currentStep).toBe("decision-approaches");
|
||||
expect(out.title).toBe("T");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user