Keep the template picker in the create flow, make catalog cards real links, and stop showing a fake community when review has no draft.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
adilallo
2026-09-10 15:22:46 -06:00
co-authored by Cursor
parent 3f9a8395be
commit 90db213413
31 changed files with 412 additions and 126 deletions
+18
View File
@@ -76,6 +76,24 @@ describe("Rule Component", () => {
expect(handleClick).toHaveBeenCalledTimes(2);
});
it("renders as a link when href is set", () => {
const handleClick = vi.fn();
render(
<Rule
{...defaultProps}
href="/create/review-template/consensus"
onClick={handleClick}
/>,
);
const card = screen.getByRole("link", {
name: "Learn more about Test Rule governance pattern",
});
expect(card).toHaveAttribute("href", "/create/review-template/consensus");
fireEvent.click(card, { preventDefault() {} });
expect(handleClick).toHaveBeenCalledTimes(1);
});
it("applies hover effects correctly", () => {
render(<Rule {...defaultProps} />);
+11 -8
View File
@@ -226,13 +226,14 @@ describe("RuleStack Component", () => {
render(<RuleStack />);
await waitForRuleStackCards();
const consensusCard = screen.getByText("Consensus").closest("div");
const consensusCard = screen.getByRole("link", { name: /Consensus/i });
expect(consensusCard).toHaveAttribute(
"href",
"/create/review-template/consensus",
);
await user.click(consensusCard);
expect(debugSpy).toHaveBeenCalledWith("consensus template clicked");
expect(testRouter.push).toHaveBeenCalledWith(
"/create/review-template/consensus",
);
debugSpy.mockRestore();
});
@@ -251,7 +252,7 @@ describe("RuleStack Component", () => {
render(<RuleStack />);
await waitForRuleStackCards();
const consensusCard = screen.getByText("Consensus").closest("div");
const consensusCard = screen.getByRole("link", { name: /Consensus/i });
await user.click(consensusCard);
expect(window.localStorage.getItem(CREATE_FLOW_ANONYMOUS_KEY)).toBeNull();
@@ -319,8 +320,10 @@ describe("RuleStack Component", () => {
render(<RuleStack />);
await waitForRuleStackCards();
const buttons = document.querySelectorAll('[role="button"]');
const templateSurfaces = [...buttons].filter((el) =>
const cards = screen.getAllByRole("link").filter((el) =>
el.getAttribute("href")?.includes("/create/review-template/"),
);
const templateSurfaces = [...cards].filter((el) =>
el.className.includes("--color-surface-invert"),
);
expect(templateSurfaces.length).toBe(homeFeatured.length);
@@ -375,7 +378,7 @@ describe("RuleStack Component", () => {
render(<RuleStack />);
await waitForRuleStackCards();
const doOcracyCard = screen.getByText("Do-ocracy").closest("div");
const doOcracyCard = screen.getByRole("link", { name: /Do-ocracy/i });
await user.click(doOcracyCard);
expect(gtagSpy).toHaveBeenCalledWith("event", "template_click", {
@@ -60,5 +60,11 @@ describe("createFlowLayoutTokens", () => {
isTemplateReview: true,
}),
).toBe(CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS);
expect(
getCreateFlowContentMaxClass({
step: null,
isTemplatesPicker: true,
}),
).toBe(CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS);
});
});
+1
View File
@@ -41,6 +41,7 @@ describe("createFlowPaths (CR-92 §2)", () => {
it("CREATE_ROUTES constants", () => {
expect(CREATE_ROUTES.review).toBe("/create/review");
expect(CREATE_ROUTES.templatesPicker).toBe("/create/templates");
expect(CREATE_ROUTES.completed).toBe("/create/completed");
});
+18 -7
View File
@@ -7,14 +7,12 @@ import {
isValidStep,
getStepIndex,
parseReviewReturnSearchParam,
parseCreateFlowScreenFromPathname,
resolveCreateFlowBackTarget,
shouldOfferCreateFlowSaveAndExit,
isDirectTemplateReviewEntry,
isCreateFlowTemplatesPickerPath,
createFlowStepUsesSelectSplitScroll,
TEMPLATES_FACET_RECOMMEND_QUERY,
TEMPLATES_FACET_RECOMMEND_VALUE,
TEMPLATE_REVIEW_FROM_CREATE_FLOW_QUERY,
TEMPLATE_REVIEW_FROM_CREATE_FLOW_VALUE,
} from "../../app/(app)/create/utils/flowSteps";
describe("flowSteps", () => {
@@ -158,10 +156,23 @@ describe("flowSteps", () => {
);
});
it("review Create from template uses fromFlow and recommendTemplates together", () => {
it("isCreateFlowTemplatesPickerPath is true only for /create/templates", () => {
expect(isCreateFlowTemplatesPickerPath("/create/templates")).toBe(true);
expect(isCreateFlowTemplatesPickerPath("/create/templates/")).toBe(true);
expect(isCreateFlowTemplatesPickerPath("/create/review-template/consensus")).toBe(
false,
);
expect(isCreateFlowTemplatesPickerPath("/templates")).toBe(false);
expect(isCreateFlowTemplatesPickerPath("/create/review")).toBe(false);
expect(isCreateFlowTemplatesPickerPath(null)).toBe(false);
});
it("parseCreateFlowScreenFromPathname ignores template auxiliary routes", () => {
expect(parseCreateFlowScreenFromPathname("/create/review")).toBe("review");
expect(parseCreateFlowScreenFromPathname("/create/templates")).toBeNull();
expect(
`/templates?${TEMPLATE_REVIEW_FROM_CREATE_FLOW_QUERY}=${TEMPLATE_REVIEW_FROM_CREATE_FLOW_VALUE}&${TEMPLATES_FACET_RECOMMEND_QUERY}=${TEMPLATES_FACET_RECOMMEND_VALUE}`,
).toBe("/templates?fromFlow=1&recommendTemplates=1");
parseCreateFlowScreenFromPathname("/create/review-template/consensus"),
).toBeNull();
});
it("parseReviewReturnSearchParam accepts only final-review and edit-rule", () => {