diff --git a/app/(app)/create/components/methodEditFields/CoreValueEditFields.tsx b/app/(app)/create/components/methodEditFields/CoreValueEditFields.tsx index 2f331f1..2fc8d23 100644 --- a/app/(app)/create/components/methodEditFields/CoreValueEditFields.tsx +++ b/app/(app)/create/components/methodEditFields/CoreValueEditFields.tsx @@ -15,7 +15,7 @@ import type { CoreValueDetailEntry } from "../../types"; export interface CoreValueEditFieldsProps { value: CoreValueDetailEntry; onChange: (_next: CoreValueDetailEntry) => void; - /** Disable meaning/signals. Create-flow core-values omits this; final-review locks until Customize. */ + /** Disable meaning/signals. Facet and final-review callers omit this so fields stay editable on open. */ readOnly?: boolean; } diff --git a/app/(app)/create/screens/card/CommunicationMethodsScreen.tsx b/app/(app)/create/screens/card/CommunicationMethodsScreen.tsx index e70fd2c..7a46781 100644 --- a/app/(app)/create/screens/card/CommunicationMethodsScreen.tsx +++ b/app/(app)/create/screens/card/CommunicationMethodsScreen.tsx @@ -10,8 +10,8 @@ * * Card click opens the Figma create modal (node `20246-15829`) with three * editable sections rendered by {@link CommunicationMethodEditFields}. The primary - * action is **Add Platform** for an unselected card; a selected card in view mode has - * no footer primary — **Remove** is available from the kebab (same behavior as legacy + * action is **Add Platform** for an unselected card and **Save** for a selected + * card. **Remove** is available from the kebab (same behavior as legacy * footer remove via {@link removeMethodCardFromFacetSelection}). */ @@ -216,7 +216,7 @@ export function CommunicationMethodsScreen() { methodId: pendingCardId, meta: state.customMethodCardMetaById, fieldBlocksById: state.customMethodCardFieldBlocksById, - modalEditUnlocked: false, + modalEditUnlocked: true, draftFieldBlocks, customFacetDetailsMatchPreset, }), diff --git a/app/(app)/create/screens/card/ConflictManagementScreen.tsx b/app/(app)/create/screens/card/ConflictManagementScreen.tsx index 0aa9337..972ce08 100644 --- a/app/(app)/create/screens/card/ConflictManagementScreen.tsx +++ b/app/(app)/create/screens/card/ConflictManagementScreen.tsx @@ -217,7 +217,7 @@ export function ConflictManagementScreen() { methodId: pendingCardId, meta: state.customMethodCardMetaById, fieldBlocksById: state.customMethodCardFieldBlocksById, - modalEditUnlocked: false, + modalEditUnlocked: true, draftFieldBlocks, customFacetDetailsMatchPreset, }), diff --git a/app/(app)/create/screens/card/MembershipMethodsScreen.tsx b/app/(app)/create/screens/card/MembershipMethodsScreen.tsx index 78532c8..1a3dd30 100644 --- a/app/(app)/create/screens/card/MembershipMethodsScreen.tsx +++ b/app/(app)/create/screens/card/MembershipMethodsScreen.tsx @@ -214,7 +214,7 @@ export function MembershipMethodsScreen() { methodId: pendingCardId, meta: state.customMethodCardMetaById, fieldBlocksById: state.customMethodCardFieldBlocksById, - modalEditUnlocked: false, + modalEditUnlocked: true, draftFieldBlocks, customFacetDetailsMatchPreset, }), diff --git a/app/(app)/create/screens/right-rail/DecisionApproachesScreen.tsx b/app/(app)/create/screens/right-rail/DecisionApproachesScreen.tsx index adb23da..7b71481 100644 --- a/app/(app)/create/screens/right-rail/DecisionApproachesScreen.tsx +++ b/app/(app)/create/screens/right-rail/DecisionApproachesScreen.tsx @@ -250,7 +250,7 @@ export function DecisionApproachesScreen() { methodId: pendingCardId, meta: state.customMethodCardMetaById, fieldBlocksById: state.customMethodCardFieldBlocksById, - modalEditUnlocked: false, + modalEditUnlocked: true, draftFieldBlocks, customFacetDetailsMatchPreset, }), diff --git a/app/components/controls/TextArea/TextArea.container.tsx b/app/components/controls/TextArea/TextArea.container.tsx index 3c6f59f..044ee1a 100644 --- a/app/components/controls/TextArea/TextArea.container.tsx +++ b/app/components/controls/TextArea/TextArea.container.tsx @@ -80,7 +80,8 @@ const TextAreaContainer = forwardRef( }, }; - // State styles (embedded: Figma 20736-12668 – borderless, darker grey block, white text) + // Embedded (Figma 20736-12668): borderless grey block; default copy is + // tertiary, primary on focus so seeded modal fields stay muted until edit. const getStateStyles = (): { textarea: string; label: string; @@ -89,13 +90,13 @@ const TextAreaContainer = forwardRef( if (disabled) { return { textarea: - "border-0 bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-primary)] cursor-not-allowed opacity-60", + "border-0 bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-tertiary,#b4b4b4)] cursor-not-allowed opacity-60", label: "text-[var(--color-content-default-secondary)]", }; } return { textarea: - "border-0 bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-default-tertiary)] focus:ring-inset", + "border-0 bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-tertiary,#b4b4b4)] placeholder:text-[var(--color-content-default-tertiary,#b4b4b4)] focus:text-[var(--color-content-default-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-default-tertiary)] focus:ring-inset", label: "text-[var(--color-content-default-secondary)]", }; } diff --git a/app/components/controls/TextArea/TextArea.types.ts b/app/components/controls/TextArea/TextArea.types.ts index 5b8d98a..812a35a 100644 --- a/app/components/controls/TextArea/TextArea.types.ts +++ b/app/components/controls/TextArea/TextArea.types.ts @@ -48,7 +48,8 @@ export interface TextAreaProps extends Omit< showHelpIcon?: boolean; /** * Visual appearance. "embedded" matches Create modal sections (Figma 20736-12668): - * borderless, darker grey background, white text. "default" is standard bordered input. + * borderless, darker grey background, tertiary text in default (primary on + * focus). "default" is the standard bordered input. * @default "default" */ appearance?: TextAreaAppearanceValue; diff --git a/lib/create/usesWizardFieldBlocksModalBody.ts b/lib/create/usesWizardFieldBlocksModalBody.ts index 4afc432..ea6ac57 100644 --- a/lib/create/usesWizardFieldBlocksModalBody.ts +++ b/lib/create/usesWizardFieldBlocksModalBody.ts @@ -29,6 +29,9 @@ import { isCustomMethodCardId } from "./isCustomMethodCardId"; * so meta-only wizard cards show policy copy instead of empty section editors. * Pass `customFacetDetailsMatchPreset: false` when the caller knows facet details * were edited or cloned from a filled preset. + * + * Create-flow facet screens and final-review pass `modalEditUnlocked: true` so + * section fields stay editable on open (Customize is the kebab wizard). */ export function usesWizardFieldBlocksModalBody(args: { methodId: string; diff --git a/tests/components/ConflictManagementScreen.test.tsx b/tests/components/ConflictManagementScreen.test.tsx new file mode 100644 index 0000000..2d2a68b --- /dev/null +++ b/tests/components/ConflictManagementScreen.test.tsx @@ -0,0 +1,37 @@ +import { describe, it, expect, afterEach } from "vitest"; +import { + renderWithProviders as render, + screen, + cleanup, + within, +} from "../utils/test-utils"; +import { fireEvent } from "@testing-library/react"; +import "@testing-library/jest-dom/vitest"; +import { ConflictManagementScreen } from "../../app/(app)/create/screens/card/ConflictManagementScreen"; + +afterEach(() => { + cleanup(); +}); + +describe("ConflictManagementScreen", () => { + it("opens section fields editable without Customize", async () => { + render(); + fireEvent.click( + screen.getAllByRole("button", { + name: /Peer Mediation: Trained members/, + })[0], + ); + const dialog = await screen.findByRole("dialog"); + const fields = within(dialog).getAllByRole("textbox"); + expect(fields.length).toBeGreaterThan(0); + for (const field of fields) { + expect(field).toBeEnabled(); + } + fireEvent.click( + within(dialog).getByRole("button", { name: "More options" }), + ); + expect( + screen.getByRole("menuitem", { name: "Customize" }), + ).toBeInTheDocument(); + }); +}); diff --git a/tests/components/MembershipMethodsScreen.test.tsx b/tests/components/MembershipMethodsScreen.test.tsx new file mode 100644 index 0000000..c10e7c7 --- /dev/null +++ b/tests/components/MembershipMethodsScreen.test.tsx @@ -0,0 +1,37 @@ +import { describe, it, expect, afterEach } from "vitest"; +import { + renderWithProviders as render, + screen, + cleanup, + within, +} from "../utils/test-utils"; +import { fireEvent } from "@testing-library/react"; +import "@testing-library/jest-dom/vitest"; +import { MembershipMethodsScreen } from "../../app/(app)/create/screens/card/MembershipMethodsScreen"; + +afterEach(() => { + cleanup(); +}); + +describe("MembershipMethodsScreen", () => { + it("opens section fields editable without Customize", async () => { + render(); + fireEvent.click( + screen.getAllByRole("button", { + name: /Open Access: Maximum inclusion/, + })[0], + ); + const dialog = await screen.findByRole("dialog"); + const fields = within(dialog).getAllByRole("textbox"); + expect(fields.length).toBeGreaterThan(0); + for (const field of fields) { + expect(field).toBeEnabled(); + } + fireEvent.click( + within(dialog).getByRole("button", { name: "More options" }), + ); + expect( + screen.getByRole("menuitem", { name: "Customize" }), + ).toBeInTheDocument(); + }); +}); diff --git a/tests/components/TextArea.test.tsx b/tests/components/TextArea.test.tsx index 61588b2..96bce2b 100644 --- a/tests/components/TextArea.test.tsx +++ b/tests/components/TextArea.test.tsx @@ -42,4 +42,17 @@ describe("TextArea appearance", () => { expect(textarea).toBeInTheDocument(); expect(textarea).toHaveClass("border-0"); }); + + it("uses tertiary text in the embedded default state and primary on focus", () => { + renderWithProviders( +