Show a footer Remove on selected create-flow modules so removal is not kebab-only.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
adilallo
2026-09-02 14:45:56 -06:00
co-authored by Cursor
parent df7051f33a
commit 84ef943df4
23 changed files with 251 additions and 16 deletions
@@ -27,6 +27,9 @@ describe("ConflictManagementScreen", () => {
for (const field of fields) {
expect(field).toBeEnabled();
}
expect(
within(dialog).queryByRole("button", { name: "Remove" }),
).not.toBeInTheDocument();
fireEvent.click(
within(dialog).getByRole("button", { name: "More options" }),
);
@@ -26,6 +26,9 @@ describe("CoreValuesSelectScreen", () => {
expect(
within(dialog).getByRole("button", { name: "Add Value" }),
).toBeInTheDocument();
expect(
within(dialog).queryByRole("button", { name: "Remove" }),
).not.toBeInTheDocument();
fireEvent.click(within(dialog).getByRole("button", { name: "More options" }));
expect(
screen.getByRole("menuitem", { name: "Customize" }),
@@ -124,6 +127,9 @@ describe("CoreValuesSelectScreen", () => {
});
fireEvent.click(screen.getByText("Accessibility"));
const editing = await screen.findByRole("dialog");
expect(
within(editing).getByRole("button", { name: "Remove" }),
).toBeInTheDocument();
fireEvent.click(
within(editing).getByRole("button", { name: "More options" }),
);
+35
View File
@@ -169,6 +169,41 @@ describe("Create", () => {
expect(screen.getByText("Custom Footer")).toBeInTheDocument();
});
it("renders a danger Remove in the left footer when showRemoveButton is true", () => {
const onRemove = vi.fn();
renderWithProviders(
<Create
{...defaultProps}
showBackButton={false}
showRemoveButton
onRemove={onRemove}
showNextButton
nextButtonText="Save"
/>,
);
const removeButton = screen.getByRole("button", { name: "Remove" });
expect(removeButton).toBeInTheDocument();
expect(screen.queryByRole("button", { name: "Back" })).not.toBeInTheDocument();
fireEvent.click(removeButton);
expect(onRemove).toHaveBeenCalledTimes(1);
});
it("prefers Remove over Back when both would occupy the left slot", () => {
renderWithProviders(
<Create
{...defaultProps}
showBackButton
showRemoveButton
onBack={vi.fn()}
onRemove={vi.fn()}
showNextButton
nextButtonText="Save"
/>,
);
expect(screen.getByRole("button", { name: "Remove" })).toBeInTheDocument();
expect(screen.queryByRole("button", { name: "Back" })).not.toBeInTheDocument();
});
it("uses responsive width at baseline (matches Login modal)", () => {
renderWithProviders(
<Create {...defaultProps}>Create dialog content</Create>,
+31 -2
View File
@@ -519,7 +519,7 @@ describe("FinalReviewScreen — chip detail modal", () => {
).not.toBeInTheDocument();
});
it("closes the chip edit modal when Back is pressed", async () => {
it("closes the chip edit modal when the close control is pressed", async () => {
render(
<FinalReviewWithStateProbe
onState={() => {}}
@@ -532,12 +532,41 @@ describe("FinalReviewScreen — chip detail modal", () => {
fireEvent.click(await screen.findByRole("button", { name: "Signal" }));
const dialog = await screen.findByRole("dialog");
fireEvent.click(within(dialog).getByRole("button", { name: "Back" }));
expect(
within(dialog).getByRole("button", { name: "Remove" }),
).toBeInTheDocument();
expect(
within(dialog).queryByRole("button", { name: "Back" }),
).not.toBeInTheDocument();
fireEvent.click(within(dialog).getByLabelText("Close dialog"));
await waitFor(() => {
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
});
});
it("deselects a method chip from the footer Remove", async () => {
let latest: CreateFlowState = {};
render(
<FinalReviewWithStateProbe
onState={(s) => {
latest = s;
}}
initial={{
title: "Oak Park Commons",
selectedCommunicationMethodIds: ["signal"],
}}
/>,
);
fireEvent.click(await screen.findByRole("button", { name: "Signal" }));
const dialog = await screen.findByRole("dialog");
fireEvent.click(within(dialog).getByRole("button", { name: "Remove" }));
await waitFor(() => {
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
});
expect(latest.selectedCommunicationMethodIds ?? []).not.toContain("signal");
});
});
/**
@@ -27,6 +27,9 @@ describe("MembershipMethodsScreen", () => {
for (const field of fields) {
expect(field).toBeEnabled();
}
expect(
within(dialog).queryByRole("button", { name: "Remove" }),
).not.toBeInTheDocument();
fireEvent.click(
within(dialog).getByRole("button", { name: "More options" }),
);
+27 -3
View File
@@ -40,7 +40,7 @@ describe("Create flow communication-methods page", () => {
expect(within(dialog).getByText("Add Platform")).toBeInTheDocument();
});
test("re-opening a selected method shows Save; Remove is in the kebab", async () => {
test("re-opening a selected method shows Save and footer Remove; Remove stays in the kebab", async () => {
const user = userEvent.setup();
render(<CommunicationMethodsScreen />);
@@ -54,8 +54,8 @@ describe("Create flow communication-methods page", () => {
await user.click(signalCards[0]);
const dialogAgain = screen.getByRole("dialog");
expect(
within(dialogAgain).queryByRole("button", { name: "Remove" }),
).not.toBeInTheDocument();
within(dialogAgain).getByRole("button", { name: "Remove" }),
).toBeInTheDocument();
expect(
within(dialogAgain).queryByRole("button", { name: "Add Platform" }),
).not.toBeInTheDocument();
@@ -67,6 +67,30 @@ describe("Create flow communication-methods page", () => {
expect(screen.getByRole("menuitem", { name: "Remove" })).toBeInTheDocument();
});
test("Remove from the footer deselects the method", async () => {
const user = userEvent.setup();
render(<CommunicationMethodsScreen />);
const signalCards = screen.getAllByRole("button", {
name: /Signal: Encrypted messaging/,
});
await user.click(signalCards[0]);
await user.click(
within(screen.getByRole("dialog")).getByRole("button", {
name: "Add Platform",
}),
);
expect(signalCards[0]).toHaveTextContent("SELECTED");
await user.click(signalCards[0]);
await user.click(
within(screen.getByRole("dialog")).getByRole("button", { name: "Remove" }),
);
expect(signalCards[0]).not.toHaveTextContent("SELECTED");
});
test("Remove from the kebab deselects the method", async () => {
const user = userEvent.setup();
render(<CommunicationMethodsScreen />);
+27 -3
View File
@@ -244,7 +244,7 @@ describe("Create flow decision-approaches page", () => {
expect(screen.getByText("SELECTED")).toBeInTheDocument();
});
test("re-opening a selected approach shows Save; Remove is in the kebab", async () => {
test("re-opening a selected approach shows Save and footer Remove; Remove stays in the kebab", async () => {
const user = userEvent.setup();
render(<DecisionApproachesScreen />);
@@ -262,8 +262,8 @@ describe("Create flow decision-approaches page", () => {
await user.click(card);
const dialogAgain = screen.getByRole("dialog");
expect(
within(dialogAgain).queryByRole("button", { name: "Remove" }),
).not.toBeInTheDocument();
within(dialogAgain).getByRole("button", { name: "Remove" }),
).toBeInTheDocument();
expect(
within(dialogAgain).queryByRole("button", { name: "Add Approach" }),
).not.toBeInTheDocument();
@@ -275,6 +275,30 @@ describe("Create flow decision-approaches page", () => {
expect(screen.getByRole("menuitem", { name: "Remove" })).toBeInTheDocument();
});
test("Remove from the footer deselects the approach", async () => {
const user = userEvent.setup();
render(<DecisionApproachesScreen />);
const card = screen.getByRole("button", {
name: /Lazy Consensus: A decision is assumed approved/,
});
await user.click(card);
await user.click(
within(await screen.findByRole("dialog")).getByRole("button", {
name: "Add Approach",
}),
);
expect(card).toHaveTextContent("SELECTED");
await user.click(card);
await user.click(
within(screen.getByRole("dialog")).getByRole("button", { name: "Remove" }),
);
expect(card).not.toHaveTextContent("SELECTED");
});
test("Save on a selected approach persists the edit and closes", async () => {
const user = userEvent.setup();
render(<DecisionApproachesScreen />);