Let decision-approaches wrap at narrow widths and sync key-resource scopes.
The info-box checkboxes overflowed in a squeezed column; wrapping them and offering those scopes as chips on every approach keeps the sidebar and Applicable Scope selection in sync without publishing unchecked keys as defaults. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -34,4 +34,11 @@ describe("InfoMessageBox", () => {
|
||||
await u.click(checkbox);
|
||||
expect(onCheckboxChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("keeps the checkbox column shrinkable so labels wrap", () => {
|
||||
render(<InfoMessageBox title="Important" items={items} />);
|
||||
const region = screen.getByRole("region", { name: "Important" });
|
||||
expect(region).toHaveClass("min-w-0");
|
||||
expect(region).toHaveClass("w-full");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -272,4 +272,91 @@ describe("Create flow decision-approaches page", () => {
|
||||
await user.click(amendCheckbox);
|
||||
expect(amendCheckbox).toBeChecked();
|
||||
});
|
||||
|
||||
test("each approach modal includes the key-resource applicable-scope chips", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<DecisionApproachesScreen />);
|
||||
|
||||
await user.click(
|
||||
screen.getByRole("button", {
|
||||
name: /Lazy Consensus: A decision is assumed approved/,
|
||||
}),
|
||||
);
|
||||
const dialog = await screen.findByRole("dialog");
|
||||
expect(
|
||||
within(dialog).getByRole("button", {
|
||||
name: "Select Amend your CommunityRule",
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
within(dialog).getByRole("button", { name: "Select Steward finances" }),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
within(dialog).getByRole("button", {
|
||||
name: "Select Project level decisions",
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
within(dialog).getByRole("button", {
|
||||
name: "Select Discipline and member termination",
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("checking a key-resource box selects that chip on every approach", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<DecisionApproachesScreen />);
|
||||
|
||||
await user.click(
|
||||
screen.getByRole("checkbox", { name: "Steward finances" }),
|
||||
);
|
||||
|
||||
await user.click(
|
||||
screen.getByRole("button", {
|
||||
name: /Lazy Consensus: A decision is assumed approved/,
|
||||
}),
|
||||
);
|
||||
const lazyDialog = await screen.findByRole("dialog");
|
||||
expect(
|
||||
within(lazyDialog).getByRole("button", {
|
||||
name: "Deselect Steward finances",
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
await user.click(within(lazyDialog).getByRole("button", { name: "Close dialog" }));
|
||||
|
||||
await user.click(
|
||||
screen.getByRole("button", {
|
||||
name: /Do-ocracy: Decisions are made by those who take initiative/,
|
||||
}),
|
||||
);
|
||||
const doocracyDialog = await screen.findByRole("dialog");
|
||||
expect(
|
||||
within(doocracyDialog).getByRole("button", {
|
||||
name: "Deselect Steward finances",
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("choosing a key-resource chip checks the matching sidebar box", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<DecisionApproachesScreen />);
|
||||
|
||||
await user.click(
|
||||
screen.getByRole("button", {
|
||||
name: /Lazy Consensus: A decision is assumed approved/,
|
||||
}),
|
||||
);
|
||||
const dialog = await screen.findByRole("dialog");
|
||||
await user.click(
|
||||
within(dialog).getByRole("button", {
|
||||
name: "Select Discipline and member termination",
|
||||
}),
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByRole("checkbox", {
|
||||
name: "Discipline and member termination",
|
||||
}),
|
||||
).toBeChecked();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
applyDecisionApproachKeyResources,
|
||||
decisionApproachKeyResourceIdsFromLabels,
|
||||
decisionApproachScopeForPublish,
|
||||
decisionApproachKeyResourceItems,
|
||||
decisionApproachKeyResourceLabels,
|
||||
selectedKeyResourceLabelsFromCheckedIds,
|
||||
stringArraysEqual,
|
||||
syncDecisionApproachKeyResourceDetails,
|
||||
withDecisionApproachKeyResourceScopes,
|
||||
} from "../../lib/create/decisionApproachKeyResources";
|
||||
import type { DecisionApproachDetailEntry } from "../../app/(app)/create/types";
|
||||
|
||||
const emptyEntry = (): DecisionApproachDetailEntry => ({
|
||||
corePrinciple: "p",
|
||||
applicableScope: ["Daily Operations"],
|
||||
selectedApplicableScope: [],
|
||||
stepByStepInstructions: "s",
|
||||
consensusLevel: 75,
|
||||
objectionsDeadlocks: "o",
|
||||
});
|
||||
|
||||
describe("decisionApproachKeyResources", () => {
|
||||
it("reads the four key-resource items from messages", () => {
|
||||
expect(decisionApproachKeyResourceItems()).toEqual([
|
||||
{ id: "amend", label: "Amend your CommunityRule" },
|
||||
{ id: "finances", label: "Steward finances" },
|
||||
{ id: "project", label: "Project level decisions" },
|
||||
{ id: "discipline", label: "Discipline and member termination" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("selects checked key-resource labels without rewriting applicableScope", () => {
|
||||
const next = applyDecisionApproachKeyResources(emptyEntry(), [
|
||||
"Steward finances",
|
||||
]);
|
||||
expect(next.applicableScope).toEqual(["Daily Operations"]);
|
||||
expect(next.selectedApplicableScope).toEqual(["Steward finances"]);
|
||||
});
|
||||
|
||||
it("keeps non-key chip selections when syncing sidebar checks", () => {
|
||||
const next = applyDecisionApproachKeyResources(
|
||||
{
|
||||
...emptyEntry(),
|
||||
applicableScope: ["Steward finances", "Daily Operations"],
|
||||
selectedApplicableScope: ["Daily Operations"],
|
||||
},
|
||||
["Steward finances"],
|
||||
);
|
||||
expect(next.applicableScope).toEqual([
|
||||
"Steward finances",
|
||||
"Daily Operations",
|
||||
]);
|
||||
expect(next.selectedApplicableScope).toEqual([
|
||||
"Daily Operations",
|
||||
"Steward finances",
|
||||
]);
|
||||
});
|
||||
|
||||
it("publishes default scopes plus checked key resources", () => {
|
||||
expect(
|
||||
decisionApproachScopeForPublish(
|
||||
["Daily Operations", "Minor Expenditures"],
|
||||
["Steward finances"],
|
||||
),
|
||||
).toEqual([
|
||||
"Daily Operations",
|
||||
"Minor Expenditures",
|
||||
"Steward finances",
|
||||
]);
|
||||
expect(
|
||||
decisionApproachScopeForPublish(
|
||||
["Daily Operations", "Minor Expenditures"],
|
||||
["Daily Operations", "Steward finances"],
|
||||
),
|
||||
).toEqual(["Daily Operations", "Steward finances"]);
|
||||
expect(
|
||||
decisionApproachScopeForPublish(
|
||||
["Daily Operations", "Minor Expenditures"],
|
||||
[],
|
||||
),
|
||||
).toEqual(["Daily Operations", "Minor Expenditures"]);
|
||||
});
|
||||
|
||||
it("maps checkbox ids to labels and back", () => {
|
||||
expect(selectedKeyResourceLabelsFromCheckedIds(["amend", "project"])).toEqual(
|
||||
["Amend your CommunityRule", "Project level decisions"],
|
||||
);
|
||||
expect(
|
||||
decisionApproachKeyResourceIdsFromLabels([
|
||||
"Project level decisions",
|
||||
"Unknown",
|
||||
"Amend your CommunityRule",
|
||||
]),
|
||||
).toEqual(["amend", "project"]);
|
||||
});
|
||||
|
||||
it("syncs existing and selected approach details", () => {
|
||||
const next = syncDecisionApproachKeyResourceDetails(
|
||||
{ "lazy-consensus": emptyEntry() },
|
||||
["do-ocracy"],
|
||||
["Amend your CommunityRule"],
|
||||
(id) => ({
|
||||
...emptyEntry(),
|
||||
corePrinciple: id,
|
||||
applicableScope: ["Volunteer Tasks"],
|
||||
}),
|
||||
);
|
||||
expect(next["lazy-consensus"]?.selectedApplicableScope).toEqual([
|
||||
"Amend your CommunityRule",
|
||||
]);
|
||||
expect(next["do-ocracy"]?.corePrinciple).toBe("do-ocracy");
|
||||
expect(next["do-ocracy"]?.applicableScope).toEqual(["Volunteer Tasks"]);
|
||||
expect(next["do-ocracy"]?.selectedApplicableScope).toEqual([
|
||||
"Amend your CommunityRule",
|
||||
]);
|
||||
});
|
||||
|
||||
it("compares string arrays by order", () => {
|
||||
expect(stringArraysEqual(["a", "b"], ["a", "b"])).toBe(true);
|
||||
expect(stringArraysEqual(["a", "b"], ["b", "a"])).toBe(false);
|
||||
expect(decisionApproachKeyResourceLabels()).toHaveLength(4);
|
||||
});
|
||||
|
||||
it("unions key-resource labels onto an existing scope list", () => {
|
||||
expect(
|
||||
withDecisionApproachKeyResourceScopes(["Daily Operations"]),
|
||||
).toEqual([
|
||||
"Daily Operations",
|
||||
"Amend your CommunityRule",
|
||||
"Steward finances",
|
||||
"Project level decisions",
|
||||
"Discipline and member termination",
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,12 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { communicationPresetFor } from "../../lib/create/finalReviewChipPresets";
|
||||
import { communicationMethodFacetMatchesPreset } from "../../lib/create/methodCardFacetMatchesPresetForId";
|
||||
import {
|
||||
communicationPresetFor,
|
||||
decisionApproachPresetFor,
|
||||
} from "../../lib/create/finalReviewChipPresets";
|
||||
import {
|
||||
communicationMethodFacetMatchesPreset,
|
||||
decisionApproachFacetMatchesPreset,
|
||||
} from "../../lib/create/methodCardFacetMatchesPresetForId";
|
||||
|
||||
const uuid = "550e8400-e29b-41d4-a716-446655440000";
|
||||
|
||||
@@ -19,4 +25,23 @@ describe("methodCardFacetMatchesPresetForId", () => {
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("decision approaches: ignores key-resource chip selections", () => {
|
||||
const p = decisionApproachPresetFor(uuid);
|
||||
expect(
|
||||
decisionApproachFacetMatchesPreset(
|
||||
{
|
||||
...p,
|
||||
selectedApplicableScope: ["Steward finances"],
|
||||
},
|
||||
uuid,
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
decisionApproachFacetMatchesPreset(
|
||||
{ ...p, corePrinciple: "edited" },
|
||||
uuid,
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ describe("stripCustomRuleSelectionFields", () => {
|
||||
selectedCommunicationMethodIds: ["signal"],
|
||||
selectedMembershipMethodIds: ["x"],
|
||||
selectedDecisionApproachIds: ["y"],
|
||||
selectedDecisionKeyResourceIds: ["amend"],
|
||||
selectedConflictManagementIds: ["z"],
|
||||
methodSectionsPinCommitted: { communication: true },
|
||||
coreValueDetailsByChipId: { "1": { meaning: "", signals: "" } },
|
||||
@@ -38,6 +39,7 @@ describe("stripCustomRuleSelectionFields", () => {
|
||||
expect(out.selectedCommunicationMethodIds).toBeUndefined();
|
||||
expect(out.selectedMembershipMethodIds).toBeUndefined();
|
||||
expect(out.selectedDecisionApproachIds).toBeUndefined();
|
||||
expect(out.selectedDecisionKeyResourceIds).toBeUndefined();
|
||||
expect(out.selectedConflictManagementIds).toBeUndefined();
|
||||
expect(out.methodSectionsPinCommitted).toBeUndefined();
|
||||
expect(out.coreValueDetailsByChipId).toBeUndefined();
|
||||
|
||||
Reference in New Issue
Block a user