Close method-card Save like first add, keep key-resource chips on the approach they were chosen for, and leave expanded See-all stacks in catalog order.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -161,6 +161,44 @@ describe("CommunicationMethodsScreen — Add Platform persistence", () => {
|
||||
expect(textareas[2].value).toBe("Saved coc");
|
||||
});
|
||||
|
||||
it("persists edits and closes when Save is clicked on an already-selected platform", async () => {
|
||||
let latest: CreateFlowState = {};
|
||||
render(
|
||||
<ScreenWithStateProbe
|
||||
onState={(s) => {
|
||||
latest = s;
|
||||
}}
|
||||
initial={{
|
||||
selectedCommunicationMethodIds: ["signal"],
|
||||
communicationMethodDetailsById: {
|
||||
signal: {
|
||||
corePrinciple: "Saved principle",
|
||||
logisticsAdmin: "Saved logistics",
|
||||
codeOfConduct: "Saved coc",
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(
|
||||
screen.getAllByRole("button", { name: /Signal: Encrypted messaging/ })[0],
|
||||
);
|
||||
const dialog = await screen.findByRole("dialog");
|
||||
const textareas = within(dialog).getAllByRole(
|
||||
"textbox",
|
||||
) as HTMLTextAreaElement[];
|
||||
fireEvent.change(textareas[0], { target: { value: "Edited principle" } });
|
||||
fireEvent.click(within(dialog).getByRole("button", { name: "Save" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
expect(
|
||||
latest.communicationMethodDetailsById?.signal?.corePrinciple,
|
||||
).toBe("Edited principle");
|
||||
});
|
||||
|
||||
it("keeps catalog section editors when a title override exists and there are no custom fields", async () => {
|
||||
const details = communicationPresetFor("video-meetings");
|
||||
const noFieldsHint =
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
screen,
|
||||
cleanup,
|
||||
within,
|
||||
waitFor,
|
||||
} from "../utils/test-utils";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, test, expect, afterEach } from "vitest";
|
||||
@@ -161,6 +162,45 @@ describe("Create flow decision-approaches page", () => {
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("selecting an approach in the expanded list does not move it to the top", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<DecisionApproachesScreen />);
|
||||
|
||||
await user.click(
|
||||
screen.getByRole("button", { name: "See all decision approaches" }),
|
||||
);
|
||||
|
||||
const cardLabels = () =>
|
||||
screen
|
||||
.getAllByRole("button")
|
||||
.map((el) => el.getAttribute("aria-label") || "")
|
||||
.filter((label) => label.includes(": "));
|
||||
|
||||
const labelsBefore = cardLabels();
|
||||
expect(labelsBefore[0]).toMatch(/^Lazy Consensus:/);
|
||||
expect(labelsBefore.some((label) => label.startsWith("Sociocracy:"))).toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
await user.click(screen.getByRole("button", { name: /^Sociocracy:/ }));
|
||||
await user.click(
|
||||
within(await screen.findByRole("dialog")).getByRole("button", {
|
||||
name: "Add Approach",
|
||||
}),
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(
|
||||
screen.getByRole("button", { name: "Show less" }),
|
||||
).toBeInTheDocument();
|
||||
expect(cardLabels()).toEqual(labelsBefore);
|
||||
expect(
|
||||
screen.getByRole("button", { name: /^Sociocracy:/ }),
|
||||
).toHaveTextContent("SELECTED");
|
||||
});
|
||||
|
||||
test("clicking a card opens the create modal and confirming selects it", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<DecisionApproachesScreen />);
|
||||
@@ -212,6 +252,33 @@ describe("Create flow decision-approaches page", () => {
|
||||
expect(screen.getByRole("menuitem", { name: "Remove" })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("Save on a selected approach persists the edit and closes", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<DecisionApproachesScreen />);
|
||||
|
||||
const card = screen.getByRole("button", {
|
||||
name: /Lazy Consensus: A decision is assumed approved/,
|
||||
});
|
||||
await user.click(card);
|
||||
const dialog = await screen.findByRole("dialog");
|
||||
await user.click(
|
||||
within(dialog).getByRole("button", { name: "Add Approach" }),
|
||||
);
|
||||
|
||||
await user.click(card);
|
||||
const dialogAgain = await screen.findByRole("dialog");
|
||||
const principleField = within(dialogAgain).getByRole("textbox", {
|
||||
name: /core principle/i,
|
||||
});
|
||||
await user.clear(principleField);
|
||||
await user.type(principleField, "Edited principle");
|
||||
await user.click(within(dialogAgain).getByRole("button", { name: "Save" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
test("Remove from the kebab deselects the approach", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<DecisionApproachesScreen />);
|
||||
@@ -303,7 +370,7 @@ describe("Create flow decision-approaches page", () => {
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("checking a key-resource box selects that chip on every approach", async () => {
|
||||
test("checking a key-resource box does not highlight that chip on an unselected approach", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<DecisionApproachesScreen />);
|
||||
|
||||
@@ -319,10 +386,35 @@ describe("Create flow decision-approaches page", () => {
|
||||
const lazyDialog = await screen.findByRole("dialog");
|
||||
expect(
|
||||
within(lazyDialog).getByRole("button", {
|
||||
name: "Deselect Steward finances",
|
||||
name: "Select Steward finances",
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
await user.click(within(lazyDialog).getByRole("button", { name: "Close dialog" }));
|
||||
});
|
||||
|
||||
test("applicable-scope chips stay on the approach they were chosen for", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<DecisionApproachesScreen />);
|
||||
|
||||
await user.click(
|
||||
screen.getByRole("button", {
|
||||
name: /Lazy Consensus: A decision is assumed approved/,
|
||||
}),
|
||||
);
|
||||
const lazyDialog = await screen.findByRole("dialog");
|
||||
await user.click(
|
||||
within(lazyDialog).getByRole("button", {
|
||||
name: "Select Steward finances",
|
||||
}),
|
||||
);
|
||||
expect(
|
||||
screen.getByRole("checkbox", { name: "Steward finances" }),
|
||||
).toBeChecked();
|
||||
await user.click(
|
||||
within(lazyDialog).getByRole("button", { name: "Add Approach" }),
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
await user.click(
|
||||
screen.getByRole("button", {
|
||||
@@ -332,12 +424,45 @@ describe("Create flow decision-approaches page", () => {
|
||||
const doocracyDialog = await screen.findByRole("dialog");
|
||||
expect(
|
||||
within(doocracyDialog).getByRole("button", {
|
||||
name: "Select Steward finances",
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
within(doocracyDialog).queryByRole("button", {
|
||||
name: "Deselect Steward finances",
|
||||
}),
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("checkbox", { name: "Steward finances" }),
|
||||
).not.toBeChecked();
|
||||
await user.click(
|
||||
within(doocracyDialog).getByRole("button", { name: "Close dialog" }),
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(
|
||||
screen.getByRole("checkbox", { name: "Steward finances" }),
|
||||
).toBeChecked();
|
||||
|
||||
await user.click(
|
||||
screen.getByRole("button", {
|
||||
name: /Lazy Consensus: A decision is assumed approved/,
|
||||
}),
|
||||
);
|
||||
const lazyAgain = await screen.findByRole("dialog");
|
||||
expect(
|
||||
within(lazyAgain).getByRole("button", {
|
||||
name: "Deselect Steward finances",
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole("checkbox", { name: "Steward finances" }),
|
||||
).toBeChecked();
|
||||
});
|
||||
|
||||
test("choosing a key-resource chip checks the matching sidebar box", async () => {
|
||||
test("checking a key-resource box with a modal open selects that chip on the open approach only", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<DecisionApproachesScreen />);
|
||||
|
||||
@@ -346,17 +471,148 @@ describe("Create flow decision-approaches page", () => {
|
||||
name: /Lazy Consensus: A decision is assumed approved/,
|
||||
}),
|
||||
);
|
||||
const dialog = await screen.findByRole("dialog");
|
||||
const lazyDialog = await screen.findByRole("dialog");
|
||||
await user.click(
|
||||
within(dialog).getByRole("button", {
|
||||
name: "Select Discipline and member termination",
|
||||
within(lazyDialog).getByRole("button", { name: "Add Approach" }),
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
await user.click(
|
||||
screen.getByRole("button", {
|
||||
name: /Do-ocracy: Decisions are made by those who take initiative/,
|
||||
}),
|
||||
);
|
||||
const doocracyDialog = await screen.findByRole("dialog");
|
||||
await user.click(
|
||||
screen.getByRole("checkbox", { name: "Steward finances" }),
|
||||
);
|
||||
expect(
|
||||
within(doocracyDialog).getByRole("button", {
|
||||
name: "Deselect Steward finances",
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
await user.click(
|
||||
within(doocracyDialog).getByRole("button", { name: "Close dialog" }),
|
||||
);
|
||||
await user.click(await screen.findByRole("button", { name: "Discard" }));
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
await user.click(
|
||||
screen.getByRole("button", {
|
||||
name: /Lazy Consensus: A decision is assumed approved/,
|
||||
}),
|
||||
);
|
||||
const lazyAgain = await screen.findByRole("dialog");
|
||||
expect(
|
||||
within(lazyAgain).getByRole("button", {
|
||||
name: "Select Steward finances",
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("highlighting a key-resource chip on a second approach does not rewrite the first", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<DecisionApproachesScreen />);
|
||||
|
||||
await user.click(
|
||||
screen.getByRole("button", {
|
||||
name: /Lazy Consensus: A decision is assumed approved/,
|
||||
}),
|
||||
);
|
||||
const lazyDialog = await screen.findByRole("dialog");
|
||||
await user.click(
|
||||
within(lazyDialog).getByRole("button", {
|
||||
name: "Select Steward finances",
|
||||
}),
|
||||
);
|
||||
expect(
|
||||
screen.getByRole("checkbox", { name: "Steward finances" }),
|
||||
).toBeChecked();
|
||||
await user.click(
|
||||
within(lazyDialog).getByRole("button", { name: "Add Approach" }),
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
await user.click(
|
||||
screen.getByRole("button", {
|
||||
name: /Do-ocracy: Decisions are made by those who take initiative/,
|
||||
}),
|
||||
);
|
||||
const doocracyDialog = await screen.findByRole("dialog");
|
||||
await user.click(
|
||||
within(doocracyDialog).getByRole("button", {
|
||||
name: "Select Project level decisions",
|
||||
}),
|
||||
);
|
||||
expect(
|
||||
screen.getByRole("checkbox", { name: "Project level decisions" }),
|
||||
).toBeChecked();
|
||||
expect(
|
||||
screen.getByRole("checkbox", { name: "Steward finances" }),
|
||||
).not.toBeChecked();
|
||||
expect(
|
||||
within(doocracyDialog).getByRole("button", {
|
||||
name: "Select Steward finances",
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
await user.click(
|
||||
within(doocracyDialog).getByRole("button", { name: "Add Approach" }),
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(
|
||||
screen.getByRole("checkbox", {
|
||||
name: "Discipline and member termination",
|
||||
}),
|
||||
screen.getByRole("checkbox", { name: "Steward finances" }),
|
||||
).toBeChecked();
|
||||
expect(
|
||||
screen.getByRole("checkbox", { name: "Project level decisions" }),
|
||||
).toBeChecked();
|
||||
|
||||
await user.click(
|
||||
screen.getByRole("button", {
|
||||
name: /Lazy Consensus: A decision is assumed approved/,
|
||||
}),
|
||||
);
|
||||
const lazyAgain = await screen.findByRole("dialog");
|
||||
expect(
|
||||
within(lazyAgain).getByRole("button", {
|
||||
name: "Deselect Steward finances",
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
within(lazyAgain).getByRole("button", {
|
||||
name: "Select Project level decisions",
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
await user.click(
|
||||
within(lazyAgain).getByRole("button", { name: "Close dialog" }),
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
await user.click(
|
||||
screen.getByRole("button", {
|
||||
name: /Do-ocracy: Decisions are made by those who take initiative/,
|
||||
}),
|
||||
);
|
||||
const doocracyAgain = await screen.findByRole("dialog");
|
||||
expect(
|
||||
within(doocracyAgain).getByRole("button", {
|
||||
name: "Deselect Project level decisions",
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
within(doocracyAgain).getByRole("button", {
|
||||
name: "Select Steward finances",
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
applyDecisionApproachKeyResources,
|
||||
decisionApproachKeyResourceCheckboxIds,
|
||||
decisionApproachKeyResourceIdsFromLabels,
|
||||
decisionApproachScopeForPublish,
|
||||
decisionApproachKeyResourceItems,
|
||||
@@ -96,6 +97,37 @@ describe("decisionApproachKeyResources", () => {
|
||||
).toEqual(["amend", "project"]);
|
||||
});
|
||||
|
||||
it("derives sidebar checks from the open draft or selected approaches", () => {
|
||||
const detailsById = {
|
||||
"lazy-consensus": {
|
||||
...emptyEntry(),
|
||||
selectedApplicableScope: ["Steward finances"],
|
||||
},
|
||||
"do-ocracy": {
|
||||
...emptyEntry(),
|
||||
selectedApplicableScope: ["Project level decisions"],
|
||||
},
|
||||
};
|
||||
expect(
|
||||
decisionApproachKeyResourceCheckboxIds({
|
||||
detailsById,
|
||||
selectedApproachIds: ["lazy-consensus", "do-ocracy"],
|
||||
reminderIds: ["discipline"],
|
||||
}),
|
||||
).toEqual(["finances", "project", "discipline"]);
|
||||
expect(
|
||||
decisionApproachKeyResourceCheckboxIds({
|
||||
detailsById,
|
||||
selectedApproachIds: ["lazy-consensus", "do-ocracy"],
|
||||
reminderIds: ["discipline"],
|
||||
openDraft: {
|
||||
...emptyEntry(),
|
||||
selectedApplicableScope: ["Amend your CommunityRule"],
|
||||
},
|
||||
}),
|
||||
).toEqual(["amend"]);
|
||||
});
|
||||
|
||||
it("syncs existing and selected approach details", () => {
|
||||
const next = syncDecisionApproachKeyResourceDetails(
|
||||
{ "lazy-consensus": emptyEntry() },
|
||||
|
||||
Reference in New Issue
Block a user