Route kebab Customize through the prefilled policy wizard so existing methods and values can be renamed, reordered, and kept in that field order on the card after Finalize.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -214,4 +214,39 @@ describe("applyFinalReviewChipEditPatch", () => {
|
||||
"1": { meaning: "m2", signals: "s2" },
|
||||
});
|
||||
});
|
||||
|
||||
it("merges customMethodCardFieldBlocksById for a core values patch", () => {
|
||||
const state: CreateFlowState = {
|
||||
customMethodCardFieldBlocksById: {
|
||||
other: [
|
||||
{
|
||||
kind: "text",
|
||||
id: "x",
|
||||
blockTitle: "T",
|
||||
placeholderText: "keep",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const patch: FinalReviewChipEditPatch = {
|
||||
groupKey: "coreValues",
|
||||
overrideKey: "1",
|
||||
value: { meaning: "m", signals: "s" },
|
||||
customMethodCardFieldBlocks: [
|
||||
{
|
||||
kind: "text",
|
||||
id: "facet-meaning",
|
||||
blockTitle: "Meaning",
|
||||
placeholderText: "m",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = applyFinalReviewChipEditPatch(state, patch);
|
||||
|
||||
expect(result.customMethodCardFieldBlocksById).toEqual({
|
||||
other: state.customMethodCardFieldBlocksById?.other,
|
||||
"1": patch.customMethodCardFieldBlocks,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildMethodCardWizardInitialValues,
|
||||
coreValueDetailsFromWizardFieldBlocks,
|
||||
facetDetailsToWizardFieldBlocks,
|
||||
} from "../../lib/create/methodCardWizardPrefill";
|
||||
import type { CustomMethodCardFieldBlock } from "../../lib/create/customMethodCardFieldBlocks";
|
||||
|
||||
const cardId = "signal";
|
||||
const blocks: CustomMethodCardFieldBlock[] = [
|
||||
{ kind: "text", id: "b1", blockTitle: "Notes", placeholderText: "…" },
|
||||
];
|
||||
|
||||
const commHeadings = {
|
||||
corePrinciple: "Core Principle & Scope",
|
||||
logisticsAdmin: "Logistics, Admin & Norms",
|
||||
codeOfConduct: "Code of Conduct",
|
||||
};
|
||||
|
||||
describe("buildMethodCardWizardInitialValues", () => {
|
||||
it("uses fallback title and description when meta is missing", () => {
|
||||
expect(
|
||||
buildMethodCardWizardInitialValues({
|
||||
cardId,
|
||||
fallbackTitle: "Signal",
|
||||
fallbackDescription: "Encrypted messaging.",
|
||||
meta: {},
|
||||
persistedBlocks: {},
|
||||
draftFieldBlocks: null,
|
||||
}),
|
||||
).toEqual({
|
||||
title: "Signal",
|
||||
description: "Encrypted messaging.",
|
||||
fieldBlocks: [],
|
||||
});
|
||||
});
|
||||
|
||||
it("maps facet body fields onto wizard blocks when none exist yet", () => {
|
||||
expect(
|
||||
buildMethodCardWizardInitialValues({
|
||||
cardId,
|
||||
fallbackTitle: "Signal",
|
||||
fallbackDescription: "Encrypted messaging.",
|
||||
meta: {},
|
||||
persistedBlocks: {},
|
||||
draftFieldBlocks: null,
|
||||
facetPrefill: {
|
||||
group: "communication",
|
||||
draft: {
|
||||
corePrinciple: "Privacy first.",
|
||||
logisticsAdmin: "Admins steward access.",
|
||||
codeOfConduct: "No leaks.",
|
||||
},
|
||||
headings: commHeadings,
|
||||
},
|
||||
}).fieldBlocks,
|
||||
).toEqual([
|
||||
{
|
||||
kind: "text",
|
||||
id: "facet-corePrinciple",
|
||||
blockTitle: "Core Principle & Scope",
|
||||
placeholderText: "Privacy first.",
|
||||
},
|
||||
{
|
||||
kind: "text",
|
||||
id: "facet-logisticsAdmin",
|
||||
blockTitle: "Logistics, Admin & Norms",
|
||||
placeholderText: "Admins steward access.",
|
||||
},
|
||||
{
|
||||
kind: "text",
|
||||
id: "facet-codeOfConduct",
|
||||
blockTitle: "Code of Conduct",
|
||||
placeholderText: "No leaks.",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("prefers persisted meta title and description over fallbacks", () => {
|
||||
expect(
|
||||
buildMethodCardWizardInitialValues({
|
||||
cardId,
|
||||
fallbackTitle: "Signal",
|
||||
fallbackDescription: "Encrypted messaging.",
|
||||
meta: {
|
||||
[cardId]: { label: "Our Signal", supportText: "Private ops." },
|
||||
},
|
||||
persistedBlocks: { [cardId]: blocks },
|
||||
draftFieldBlocks: null,
|
||||
}),
|
||||
).toEqual({
|
||||
title: "Our Signal",
|
||||
description: "Private ops.",
|
||||
fieldBlocks: blocks,
|
||||
});
|
||||
});
|
||||
|
||||
it("overlays current facet field values onto matching persisted blocks", () => {
|
||||
const persisted: CustomMethodCardFieldBlock[] = [
|
||||
{
|
||||
kind: "text",
|
||||
id: "facet-corePrinciple",
|
||||
blockTitle: "Core Principle & Scope",
|
||||
placeholderText: "Stale principle",
|
||||
},
|
||||
{
|
||||
kind: "text",
|
||||
id: "extra-notes",
|
||||
blockTitle: "Notes",
|
||||
placeholderText: "keep",
|
||||
},
|
||||
];
|
||||
expect(
|
||||
buildMethodCardWizardInitialValues({
|
||||
cardId,
|
||||
fallbackTitle: "Signal",
|
||||
fallbackDescription: "Encrypted messaging.",
|
||||
meta: {},
|
||||
persistedBlocks: { [cardId]: persisted },
|
||||
draftFieldBlocks: null,
|
||||
facetPrefill: {
|
||||
group: "communication",
|
||||
draft: {
|
||||
corePrinciple: "Updated principle",
|
||||
logisticsAdmin: "Updated logistics",
|
||||
codeOfConduct: "Updated coc",
|
||||
},
|
||||
headings: commHeadings,
|
||||
},
|
||||
}).fieldBlocks,
|
||||
).toEqual([
|
||||
{
|
||||
kind: "text",
|
||||
id: "facet-corePrinciple",
|
||||
blockTitle: "Core Principle & Scope",
|
||||
placeholderText: "Updated principle",
|
||||
},
|
||||
{
|
||||
kind: "text",
|
||||
id: "extra-notes",
|
||||
blockTitle: "Notes",
|
||||
placeholderText: "keep",
|
||||
},
|
||||
{
|
||||
kind: "text",
|
||||
id: "facet-logisticsAdmin",
|
||||
blockTitle: "Logistics, Admin & Norms",
|
||||
placeholderText: "Updated logistics",
|
||||
},
|
||||
{
|
||||
kind: "text",
|
||||
id: "facet-codeOfConduct",
|
||||
blockTitle: "Code of Conduct",
|
||||
placeholderText: "Updated coc",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("prefers in-modal field-block drafts over persisted blocks", () => {
|
||||
const draft: CustomMethodCardFieldBlock[] = [
|
||||
{ kind: "proportion", id: "p1", blockTitle: "Share", defaultPercent: 40 },
|
||||
];
|
||||
expect(
|
||||
buildMethodCardWizardInitialValues({
|
||||
cardId,
|
||||
fallbackTitle: "Signal",
|
||||
fallbackDescription: "Encrypted messaging.",
|
||||
meta: {
|
||||
[cardId]: { label: "Our Signal", supportText: "Private ops." },
|
||||
},
|
||||
persistedBlocks: { [cardId]: blocks },
|
||||
draftFieldBlocks: draft,
|
||||
}).fieldBlocks,
|
||||
).toEqual(draft);
|
||||
});
|
||||
});
|
||||
|
||||
describe("facetDetailsToWizardFieldBlocks", () => {
|
||||
it("returns no blocks when facet copy is empty", () => {
|
||||
expect(
|
||||
facetDetailsToWizardFieldBlocks({
|
||||
group: "communication",
|
||||
draft: {
|
||||
corePrinciple: " ",
|
||||
logisticsAdmin: "",
|
||||
codeOfConduct: "",
|
||||
},
|
||||
headings: commHeadings,
|
||||
}),
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it("maps decision scope chips to a badge block", () => {
|
||||
const blocksOut = facetDetailsToWizardFieldBlocks({
|
||||
group: "decisionApproaches",
|
||||
draft: {
|
||||
corePrinciple: "Momentum.",
|
||||
applicableScope: ["Daily Operations"],
|
||||
selectedApplicableScope: [],
|
||||
stepByStepInstructions: "Post a deadline.",
|
||||
consensusLevel: 100,
|
||||
objectionsDeadlocks: "Any member can block.",
|
||||
},
|
||||
headings: {
|
||||
corePrinciple: "Core Principle",
|
||||
applicableScope: "Applicable Scope",
|
||||
stepByStepInstructions: "Step-by-Step Instructions",
|
||||
consensusLevel: "Consensus Level",
|
||||
objectionsDeadlocks: "Objections & Deadlocks",
|
||||
},
|
||||
});
|
||||
expect(blocksOut.find((b) => b.kind === "badges")).toMatchObject({
|
||||
kind: "badges",
|
||||
blockTitle: "Applicable Scope",
|
||||
options: ["Daily Operations"],
|
||||
});
|
||||
expect(blocksOut.find((b) => b.kind === "proportion")).toMatchObject({
|
||||
defaultPercent: 100,
|
||||
});
|
||||
});
|
||||
|
||||
it("maps core value meaning and signals onto text blocks", () => {
|
||||
expect(
|
||||
facetDetailsToWizardFieldBlocks({
|
||||
group: "coreValues",
|
||||
draft: {
|
||||
meaning: "Everyone can join.",
|
||||
signals: "Inaccessible venues.",
|
||||
},
|
||||
headings: {
|
||||
meaning: "What does this value mean to your group?",
|
||||
signals: "Signals of Violation",
|
||||
},
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
kind: "text",
|
||||
id: "facet-meaning",
|
||||
blockTitle: "What does this value mean to your group?",
|
||||
placeholderText: "Everyone can join.",
|
||||
},
|
||||
{
|
||||
kind: "text",
|
||||
id: "facet-signals",
|
||||
blockTitle: "Signals of Violation",
|
||||
placeholderText: "Inaccessible venues.",
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("coreValueDetailsFromWizardFieldBlocks", () => {
|
||||
it("reads meaning and signals from facet block ids", () => {
|
||||
expect(
|
||||
coreValueDetailsFromWizardFieldBlocks(
|
||||
[
|
||||
{
|
||||
kind: "text",
|
||||
id: "facet-meaning",
|
||||
blockTitle: "Meaning",
|
||||
placeholderText: "Updated meaning",
|
||||
},
|
||||
{
|
||||
kind: "text",
|
||||
id: "facet-signals",
|
||||
blockTitle: "Signals",
|
||||
placeholderText: "Updated signals",
|
||||
},
|
||||
],
|
||||
{ meaning: "old m", signals: "old s", supportText: "keep" },
|
||||
),
|
||||
).toEqual({
|
||||
meaning: "Updated meaning",
|
||||
signals: "Updated signals",
|
||||
supportText: "keep",
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -113,4 +113,16 @@ describe("usesWizardFieldBlocksModalBody", () => {
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("is true for catalog ids when the wizard draft has blocks", () => {
|
||||
expect(
|
||||
usesWizardFieldBlocksModalBody({
|
||||
methodId: "signal",
|
||||
meta: {},
|
||||
fieldBlocksById: {},
|
||||
modalEditUnlocked: false,
|
||||
draftFieldBlocks: blocks,
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user