Merge pull request 'Unify create-flow gutters, viewport height, and action-bar width so tablet and wide layouts stay aligned with step content.' (#78) from adilallo/fix/CR-182-builder-layout into main

Reviewed-on: #78
This commit was merged in pull request #78.
This commit is contained in:
2026-09-10 20:03:44 +00:00
20 changed files with 387 additions and 253 deletions
+14 -4
View File
@@ -76,7 +76,13 @@ import {
CREATE_FLOW_FOOTER_BUTTON_CLASS, CREATE_FLOW_FOOTER_BUTTON_CLASS,
CREATE_FLOW_FOOTER_BUTTON_ON_DARK_CLASS, CREATE_FLOW_FOOTER_BUTTON_ON_DARK_CLASS,
} from "./utils/createFlowFooterClassNames"; } from "./utils/createFlowFooterClassNames";
import { CREATE_FLOW_MD_CENTERED_MAIN_CLASS } from "./components/createFlowLayoutTokens"; import {
CREATE_FLOW_MD_CENTERED_MAIN_CLASS,
CREATE_FLOW_PAGE_GUTTER_CLASS,
CREATE_FLOW_SCROLL_REGION_CLASS,
CREATE_FLOW_VIEWPORT_FRAME_CLASS,
getCreateFlowContentMaxClass,
} from "./components/createFlowLayoutTokens";
import { import {
CUSTOM_RULE_CONFIRM_FOOTER_STEP_BY_STEP, CUSTOM_RULE_CONFIRM_FOOTER_STEP_BY_STEP,
methodCardFacetSectionForConfirmStep, methodCardFacetSectionForConfirmStep,
@@ -668,10 +674,10 @@ function CreateFlowLayoutContent({
].filter((b): b is NonNullable<typeof b> => b !== null); ].filter((b): b is NonNullable<typeof b> => b !== null);
return ( return (
<div className="relative flex h-screen min-h-0 flex-col overflow-hidden bg-black"> <div className={`${CREATE_FLOW_VIEWPORT_FRAME_CLASS} bg-black`}>
{topBanners.length > 0 ? ( {topBanners.length > 0 ? (
<div <div
className="pointer-events-none fixed left-0 right-0 top-0 z-[200] flex flex-col gap-2 px-[var(--spacing-measures-spacing-500,20px)] pt-[var(--spacing-measures-spacing-300,12px)] md:px-[var(--measures-spacing-1800,64px)]" className={`pointer-events-none fixed left-0 right-0 top-0 z-[200] flex flex-col gap-2 ${CREATE_FLOW_PAGE_GUTTER_CLASS} pt-[var(--spacing-measures-spacing-300,12px)]`}
aria-live="polite" aria-live="polite"
> >
{topBanners.map((b) => ( {topBanners.map((b) => (
@@ -798,13 +804,17 @@ function CreateFlowLayoutContent({
/> />
) : null} ) : null}
<main <main
className={`flex min-h-0 min-w-0 flex-1 w-full max-w-full overflow-x-hidden ${mainContentClass} ${mainResponsiveLayout}`} className={`flex min-h-0 min-w-0 flex-1 w-full max-w-full overflow-x-hidden ${CREATE_FLOW_SCROLL_REGION_CLASS} ${mainContentClass} ${mainResponsiveLayout}`}
> >
{children} {children}
</main> </main>
{!isCompletedStep && ( {!isCompletedStep && (
<CreateFlowFooter <CreateFlowFooter
className="shrink-0" className="shrink-0"
contentMaxClass={getCreateFlowContentMaxClass({
step: currentStep,
isTemplateReview: isTemplateReviewRoute,
})}
progressBar={ progressBar={
!isTemplateReviewRoute && !isTemplateReviewRoute &&
!isFinalReviewLike && !isFinalReviewLike &&
+2 -1
View File
@@ -11,6 +11,7 @@ import {
import { useCreateFlow } from "./context/CreateFlowContext"; import { useCreateFlow } from "./context/CreateFlowContext";
import { fetchDraftFromServer } from "../../../lib/create/api"; import { fetchDraftFromServer } from "../../../lib/create/api";
import messages from "../../../messages/en/index"; import messages from "../../../messages/en/index";
import { CREATE_FLOW_PAGE_GUTTER_CLASS } from "./components/createFlowLayoutTokens";
import Alert from "../../components/modals/Alert"; import Alert from "../../components/modals/Alert";
import { import {
isValidStep, isValidStep,
@@ -161,7 +162,7 @@ export function SignedInDraftHydration({
if (!loadingHydration) return null; if (!loadingHydration) return null;
return ( return (
<div className="pointer-events-none fixed left-0 right-0 top-14 z-[170] flex justify-center px-[var(--spacing-measures-spacing-500,20px)] pt-2 md:top-16 md:px-[var(--measures-spacing-1800,64px)]"> <div className={`pointer-events-none fixed left-0 right-0 top-14 z-[170] flex justify-center ${CREATE_FLOW_PAGE_GUTTER_CLASS} pt-2 md:top-16`}>
<div className="pointer-events-auto w-full max-w-[960px]"> <div className="pointer-events-auto w-full max-w-[960px]">
<Alert <Alert
type="banner" type="banner"
@@ -0,0 +1,56 @@
"use client";
import type { ReactNode } from "react";
import { CreateFlowHeaderLockup } from "./CreateFlowHeaderLockup";
import { CreateFlowStepShell } from "./CreateFlowStepShell";
import {
CREATE_FLOW_CARD_STACK_AREA_MAX_CLASS,
CREATE_FLOW_MD_CENTERED_SHELL_CLASS,
CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS,
CREATE_FLOW_WIDE_MAX_CLASS,
} from "./createFlowLayoutTokens";
type CreateFlowCardStackStepShellProps = {
lockupTitle: string;
lockupDescription?: ReactNode;
/** When true, the stack region exposes `aria-busy` while recommendations load. */
stackBusy?: boolean;
children: ReactNode;
};
/**
* Centered lockup + card-stack layout shared by communication, membership, and
* conflict-management steps (Figma compact card stack).
*/
export function CreateFlowCardStackStepShell({
lockupTitle,
lockupDescription,
stackBusy = false,
children,
}: CreateFlowCardStackStepShellProps) {
return (
<CreateFlowStepShell
variant="wideGridLoosePadding"
contentTopBelowMd="space-800"
className={CREATE_FLOW_MD_CENTERED_SHELL_CLASS}
>
<div
className={`flex w-full min-w-0 flex-col items-center gap-6 ${CREATE_FLOW_WIDE_MAX_CLASS}`}
>
<div className={CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS}>
<CreateFlowHeaderLockup
title={lockupTitle}
description={lockupDescription}
justification="center"
/>
</div>
<div
className={CREATE_FLOW_CARD_STACK_AREA_MAX_CLASS}
aria-busy={stackBusy}
>
{children}
</div>
</div>
</CreateFlowStepShell>
);
}
@@ -1,6 +1,7 @@
"use client"; "use client";
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import { CREATE_FLOW_PAGE_GUTTER_CLASS } from "./createFlowLayoutTokens";
export type CreateFlowStepShellVariant = export type CreateFlowStepShellVariant =
| "centeredNarrow" | "centeredNarrow"
@@ -13,15 +14,12 @@ export type CreateFlowStepShellVariant =
export type CreateFlowContentTopBelowMd = "none" | "space-1400" | "space-800"; export type CreateFlowContentTopBelowMd = "none" | "space-1400" | "space-800";
const outerByVariant: Record<CreateFlowStepShellVariant, string> = { const outerByVariant: Record<CreateFlowStepShellVariant, string> = {
centeredNarrow: centeredNarrow: `flex w-full min-w-0 flex-col items-center ${CREATE_FLOW_PAGE_GUTTER_CLASS}`,
"flex w-full min-w-0 flex-col items-center px-5 md:px-16", centeredNarrowBottomPad: `flex w-full min-w-0 flex-col items-center ${CREATE_FLOW_PAGE_GUTTER_CLASS} pb-28 md:pb-32`,
centeredNarrowBottomPad: /** Wide two-column steps; inner content supplies the 1328px cap. */
"flex w-full min-w-0 flex-col items-center px-5 pb-28 md:px-[var(--measures-spacing-1800,64px)] md:pb-32", wideGrid: `flex w-full min-w-0 shrink-0 flex-col items-center ${CREATE_FLOW_PAGE_GUTTER_CLASS}`,
/** Wide two-column steps; 1328px = two 640px columns + 48px gutter. */ /** Create Community review + card grid; inner content supplies the 1440px cap. */
wideGrid: "w-full min-w-0 max-w-[1328px] shrink-0 px-5 md:px-12", wideGridLoosePadding: `flex w-full min-w-0 shrink-0 flex-col items-center ${CREATE_FLOW_PAGE_GUTTER_CLASS}`,
/** Create Community review + card grid (Figma Flow — Review `19706:12135`): max width 1440. */
wideGridLoosePadding:
"w-full min-w-0 max-w-[1440px] shrink-0 px-5 md:px-16",
bare: "w-full min-w-0", bare: "w-full min-w-0",
}; };
@@ -41,7 +39,8 @@ interface CreateFlowStepShellProps {
/** /**
* Shared horizontal padding and width constraints for create-flow step pages. * Shared horizontal padding and width constraints for create-flow step pages.
* Horizontal padding uses Tailwind `md:` so it tracks `--breakpoint-md` (640px in `app/tailwind.css`). * Gutters come from {@link CREATE_FLOW_PAGE_GUTTER_CLASS} (`md` / `lg` track
* `--breakpoint-md` and `--breakpoint-lg` in `app/tailwind.css`).
*/ */
export function CreateFlowStepShell({ export function CreateFlowStepShell({
children, children,
@@ -5,7 +5,11 @@ import {
CreateFlowStepShell, CreateFlowStepShell,
type CreateFlowContentTopBelowMd, type CreateFlowContentTopBelowMd,
} from "./CreateFlowStepShell"; } from "./CreateFlowStepShell";
import { CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS } from "./createFlowLayoutTokens"; import {
CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS,
CREATE_FLOW_SCROLL_REGION_CLASS,
CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS,
} from "./createFlowLayoutTokens";
export type CreateFlowSelectShellLgVerticalAlign = "center" | "start"; export type CreateFlowSelectShellLgVerticalAlign = "center" | "start";
@@ -26,10 +30,10 @@ interface CreateFlowTwoColumnSelectShellProps {
} }
/** /**
* Two-column layout for create-flow select steps (community size/structure, core values) and * Question-and-options layout for create-flow select steps (community size/structure,
* {@link DecisionApproachesScreen} (decision approaches). Below `lg` (1024px), one column + main scrolls. * core values, stakeholders) and {@link DecisionApproachesScreen}.
* At `lg+`, mirrors {@link CompletedScreen}: static header column + scrollable controls column * Below `lg` (1024px), one column uses available width and main scrolls.
* (`min-h-0` + `overflow-y-auto` height chain; see completed page right rail). * At `lg+`, static header column + scrollable options column (`min-h-0` + overflow).
*/ */
export function CreateFlowTwoColumnSelectShell({ export function CreateFlowTwoColumnSelectShell({
header, header,
@@ -56,24 +60,24 @@ export function CreateFlowTwoColumnSelectShell({
> >
<div <div
className={ className={
"flex w-full min-w-0 flex-col items-start gap-[var(--measures-spacing-400,16px)] md:max-w-[640px] " + "flex w-full min-w-0 flex-col items-start gap-[var(--measures-spacing-400,16px)] " +
"max-lg:flex-none lg:max-h-full lg:max-w-[1328px] lg:min-h-0 lg:flex-1 lg:flex-row lg:flex-nowrap " + "max-lg:flex-none lg:max-h-full lg:min-h-0 lg:flex-1 lg:flex-row lg:flex-nowrap " +
`${rowLgCrossAlignClass} lg:justify-center lg:gap-[var(--measures-spacing-1200,48px)] lg:overflow-hidden` `${rowLgCrossAlignClass} lg:justify-center lg:gap-[var(--measures-spacing-1200,48px)] lg:overflow-hidden ${CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS}`
} }
> >
<div <div
className={ className={
`flex w-full min-w-0 shrink-0 flex-col items-start gap-[var(--measures-spacing-200,8px)] ` + `flex w-full min-w-0 shrink-0 flex-col items-start gap-[var(--measures-spacing-200,8px)] ` +
`lg:flex-1 ${leftLgMainJustifyClass} lg:py-[12px] lg:max-w-[640px] ${CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS}` `lg:flex-1 ${leftLgMainJustifyClass} lg:py-[12px] ${CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS}`
} }
> >
{header} {header}
</div> </div>
<div <div
className={ className={
`scrollbar-hide relative flex w-full min-w-0 flex-col items-start gap-[var(--measures-spacing-800,32px)] ` + `relative flex w-full min-w-0 flex-col items-start gap-[var(--measures-spacing-800,32px)] ` +
`overflow-x-hidden lg:min-h-0 lg:flex-1 lg:overflow-y-auto lg:pb-[var(--measures-spacing-300,12px)] ` + `overflow-x-hidden lg:min-h-0 lg:flex-1 lg:overflow-y-auto lg:pb-[var(--measures-spacing-300,12px)] ` +
CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS `${CREATE_FLOW_SCROLL_REGION_CLASS} ${CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS}`
} }
> >
{children} {children}
@@ -1,13 +1,47 @@
/** Single column/section: full width under `md`, max 640px from `--breakpoint-md` up. */ import type { CreateFlowStep } from "../types";
import { CREATE_FLOW_SCREEN_REGISTRY } from "../utils/createFlowScreenRegistry";
/**
* Shared page gutters for create-flow chrome and step shells.
* Six viewport bands collapse to three values: 20px below `md`, 48px from `md`,
* 64px from `lg` (320639 / 6401023 / 1024+).
*/
export const CREATE_FLOW_PAGE_GUTTER_CLASS = "px-5 md:px-12 lg:px-16";
/**
* Wizard viewport frame. `dvh` tracks mobile browser chrome so the action bar
* stays on-screen; `max-h-dvh` keeps the flex column from overflowing it.
*/
export const CREATE_FLOW_VIEWPORT_FRAME_CLASS =
"relative flex h-dvh max-h-dvh min-h-0 flex-col overflow-hidden";
/** Design-system scrollbar for create-flow scroll regions (not `scrollbar-hide`). */
export const CREATE_FLOW_SCROLL_REGION_CLASS = "scrollbar-design";
/**
* Fade above the fixed action bar so content that continues below the fold is
* visible as a continuation cue (Figma Scrim / footer overlay).
*/
export const CREATE_FLOW_FOOTER_SCRIM_CLASS =
"pointer-events-none absolute inset-x-0 top-0 z-[1] h-8 -translate-y-full bg-gradient-to-t from-[var(--color-surface-default-primary,#000)] to-transparent";
/**
* Single column: full width below `lg` so tablet widths use available space;
* 640px from `--breakpoint-lg` up (two-column layouts start at `lg`).
*/
export const CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS = export const CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS =
"w-full min-w-0 md:max-w-[640px]"; "w-full min-w-0 lg:max-w-[640px]";
/** Grid cell: same cap as column max, centered when the track is wider than 640px. */ /** Grid cell: same cap as column max, centered when the track is wider than 640px. */
export const CREATE_FLOW_MD_UP_GRID_CELL_CLASS = export const CREATE_FLOW_MD_UP_GRID_CELL_CLASS =
"w-full min-w-0 md:mx-auto md:max-w-[640px]"; "w-full min-w-0 lg:mx-auto lg:max-w-[640px]";
/** Two 640px columns + `--measures-spacing-1200` (48px) gutter. */ /** Two 640px columns + `--measures-spacing-1200` (48px) gutter. */
export const CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS = "md:max-w-[1328px]"; export const CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS =
"w-full min-w-0 lg:max-w-[1328px]";
/** Review / card-stack frames (Figma Flow — Review max 1440). */
export const CREATE_FLOW_WIDE_MAX_CLASS = "w-full min-w-0 lg:max-w-[1440px]";
/** /**
* Lockup+card and card-stack `<main>`: keep `items-start` so a tall card can * Lockup+card and card-stack `<main>`: keep `items-start` so a tall card can
@@ -29,3 +63,30 @@ export const CREATE_FLOW_MD_CENTERED_SHELL_CLASS = "md:my-auto md:pt-0";
*/ */
export const CREATE_FLOW_CARD_STACK_AREA_MAX_CLASS = export const CREATE_FLOW_CARD_STACK_AREA_MAX_CLASS =
"w-full min-w-0 md:max-w-[min(100%,860px)]"; "w-full min-w-0 md:max-w-[min(100%,860px)]";
/**
* Inner max-width for step content and the action bar, so the primary button
* lines up with the nearest content column above 1440.
*/
export function getCreateFlowContentMaxClass(options: {
step: CreateFlowStep | null | undefined;
isTemplateReview?: boolean;
}): string {
if (options.isTemplateReview) {
return CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS;
}
if (!options.step) {
return CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS;
}
switch (CREATE_FLOW_SCREEN_REGISTRY[options.step].layoutKind) {
case "select":
case "right-rail":
case "completed":
case "review":
return CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS;
case "card":
return CREATE_FLOW_WIDE_MAX_CLASS;
default:
return CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS;
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
export default function CreateFlowRouteLoading() { export default function CreateFlowRouteLoading() {
return ( return (
<div <div
className="flex h-screen min-h-0 flex-col overflow-hidden bg-[var(--color-surface-default-primary)]" className="flex h-dvh max-h-dvh min-h-0 flex-col overflow-hidden bg-[var(--color-surface-default-primary)]"
aria-busy="true" aria-busy="true"
aria-live="polite" aria-live="polite"
> >
@@ -6,7 +6,7 @@
* *
* Lives under `screens/card/` (not `select/`): Figma **card stack** layout is a distinct shell from * Lives under `screens/card/` (not `select/`): Figma **card stack** layout is a distinct shell from
* two-column chip **select** frames. Future card-stack steps get their own `*Screen.tsx` here and * two-column chip **select** frames. Future card-stack steps get their own `*Screen.tsx` here and
* reuse `CardStack` / `CreateFlowStepShell` as needed. * reuse `CardStack` / `CreateFlowCardStackStepShell` as needed.
* *
* Card click opens the Figma create modal (node `20246-15829`) with three * Card click opens the Figma create modal (node `20246-15829`) with three
* editable sections rendered by {@link CommunicationMethodEditFields}. The * editable sections rendered by {@link CommunicationMethodEditFields}. The
@@ -22,16 +22,10 @@ import { useCreateFlowMdUp } from "../../hooks/useCreateFlowMdUp";
import { useBeforeUnloadGuard } from "../../../../hooks/useBeforeUnloadGuard"; import { useBeforeUnloadGuard } from "../../../../hooks/useBeforeUnloadGuard";
import { useDiscardCustomizeConfirm } from "../../hooks/useDiscardCustomizeConfirm"; import { useDiscardCustomizeConfirm } from "../../hooks/useDiscardCustomizeConfirm";
import { useMethodCardDeckOrdering } from "../../hooks/useMethodCardDeckOrdering"; import { useMethodCardDeckOrdering } from "../../hooks/useMethodCardDeckOrdering";
import { CreateFlowHeaderLockup } from "../../components/CreateFlowHeaderLockup"; import { CreateFlowCardStackStepShell } from "../../components/CreateFlowCardStackStepShell";
import CardStack from "../../../../components/cards/CardStack"; import CardStack from "../../../../components/cards/CardStack";
import Create from "../../../../components/modals/Create"; import Create from "../../../../components/modals/Create";
import InlineTextButton from "../../../../components/buttons/InlineTextButton"; import InlineTextButton from "../../../../components/buttons/InlineTextButton";
import { CreateFlowStepShell } from "../../components/CreateFlowStepShell";
import {
CREATE_FLOW_CARD_STACK_AREA_MAX_CLASS,
CREATE_FLOW_MD_CENTERED_SHELL_CLASS,
CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS,
} from "../../components/createFlowLayoutTokens";
import { CommunicationMethodEditFields } from "../../components/methodEditFields"; import { CommunicationMethodEditFields } from "../../components/methodEditFields";
import CustomMethodCardWizard from "../../components/CustomMethodCardWizard"; import CustomMethodCardWizard from "../../components/CustomMethodCardWizard";
import { uploadCreateFlowFile } from "../../../../../lib/create/uploadToServer"; import { uploadCreateFlowFile } from "../../../../../lib/create/uploadToServer";
@@ -700,22 +694,10 @@ export function CommunicationMethodsScreen() {
return ( return (
<> <>
<CreateFlowStepShell <CreateFlowCardStackStepShell
variant="wideGridLoosePadding" lockupTitle={title}
contentTopBelowMd="space-800" lockupDescription={description}
className={CREATE_FLOW_MD_CENTERED_SHELL_CLASS} stackBusy={!recommendationsReady}
>
<div className="flex w-full min-w-0 flex-col items-center gap-6">
<div className={CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS}>
<CreateFlowHeaderLockup
title={title}
description={description}
justification="center"
/>
</div>
<div
className={CREATE_FLOW_CARD_STACK_AREA_MAX_CLASS}
aria-busy={!recommendationsReady}
> >
{recommendationsReady && ( {recommendationsReady && (
<CardStack <CardStack
@@ -735,9 +717,7 @@ export function CommunicationMethodsScreen() {
headerLockupSize={mdUp ? "L" : "M"} headerLockupSize={mdUp ? "L" : "M"}
/> />
)} )}
</div> </CreateFlowCardStackStepShell>
</div>
<Create <Create
isOpen={createModalOpen} isOpen={createModalOpen}
onClose={handleCreateModalClose} onClose={handleCreateModalClose}
@@ -780,7 +760,6 @@ export function CommunicationMethodsScreen() {
) )
) : null} ) : null}
</Create> </Create>
</CreateFlowStepShell>
<CustomMethodCardWizard <CustomMethodCardWizard
isOpen={addCustomWizardOpen} isOpen={addCustomWizardOpen}
onClose={handleCloseAddWizard} onClose={handleCloseAddWizard}
@@ -19,16 +19,10 @@ import { useCreateFlowMdUp } from "../../hooks/useCreateFlowMdUp";
import { useBeforeUnloadGuard } from "../../../../hooks/useBeforeUnloadGuard"; import { useBeforeUnloadGuard } from "../../../../hooks/useBeforeUnloadGuard";
import { useDiscardCustomizeConfirm } from "../../hooks/useDiscardCustomizeConfirm"; import { useDiscardCustomizeConfirm } from "../../hooks/useDiscardCustomizeConfirm";
import { useMethodCardDeckOrdering } from "../../hooks/useMethodCardDeckOrdering"; import { useMethodCardDeckOrdering } from "../../hooks/useMethodCardDeckOrdering";
import { CreateFlowHeaderLockup } from "../../components/CreateFlowHeaderLockup"; import { CreateFlowCardStackStepShell } from "../../components/CreateFlowCardStackStepShell";
import CardStack from "../../../../components/cards/CardStack"; import CardStack from "../../../../components/cards/CardStack";
import Create from "../../../../components/modals/Create"; import Create from "../../../../components/modals/Create";
import InlineTextButton from "../../../../components/buttons/InlineTextButton"; import InlineTextButton from "../../../../components/buttons/InlineTextButton";
import { CreateFlowStepShell } from "../../components/CreateFlowStepShell";
import {
CREATE_FLOW_CARD_STACK_AREA_MAX_CLASS,
CREATE_FLOW_MD_CENTERED_SHELL_CLASS,
CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS,
} from "../../components/createFlowLayoutTokens";
import { ConflictManagementEditFields } from "../../components/methodEditFields"; import { ConflictManagementEditFields } from "../../components/methodEditFields";
import CustomMethodCardWizard from "../../components/CustomMethodCardWizard"; import CustomMethodCardWizard from "../../components/CustomMethodCardWizard";
import { uploadCreateFlowFile } from "../../../../../lib/create/uploadToServer"; import { uploadCreateFlowFile } from "../../../../../lib/create/uploadToServer";
@@ -701,22 +695,10 @@ export function ConflictManagementScreen() {
return ( return (
<> <>
<CreateFlowStepShell <CreateFlowCardStackStepShell
variant="wideGridLoosePadding" lockupTitle={title}
contentTopBelowMd="space-800" lockupDescription={description}
className={CREATE_FLOW_MD_CENTERED_SHELL_CLASS} stackBusy={!recommendationsReady}
>
<div className="flex w-full min-w-0 flex-col items-center gap-6">
<div className={CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS}>
<CreateFlowHeaderLockup
title={title}
description={description}
justification="center"
/>
</div>
<div
className={CREATE_FLOW_CARD_STACK_AREA_MAX_CLASS}
aria-busy={!recommendationsReady}
> >
{recommendationsReady && ( {recommendationsReady && (
<CardStack <CardStack
@@ -736,9 +718,7 @@ export function ConflictManagementScreen() {
headerLockupSize={mdUp ? "L" : "M"} headerLockupSize={mdUp ? "L" : "M"}
/> />
)} )}
</div> </CreateFlowCardStackStepShell>
</div>
<Create <Create
isOpen={createModalOpen} isOpen={createModalOpen}
onClose={handleCreateModalClose} onClose={handleCreateModalClose}
@@ -781,7 +761,6 @@ export function ConflictManagementScreen() {
) )
) : null} ) : null}
</Create> </Create>
</CreateFlowStepShell>
<CustomMethodCardWizard <CustomMethodCardWizard
isOpen={addCustomWizardOpen} isOpen={addCustomWizardOpen}
onClose={handleCloseAddWizard} onClose={handleCloseAddWizard}
@@ -20,16 +20,10 @@ import { useCreateFlowMdUp } from "../../hooks/useCreateFlowMdUp";
import { useBeforeUnloadGuard } from "../../../../hooks/useBeforeUnloadGuard"; import { useBeforeUnloadGuard } from "../../../../hooks/useBeforeUnloadGuard";
import { useDiscardCustomizeConfirm } from "../../hooks/useDiscardCustomizeConfirm"; import { useDiscardCustomizeConfirm } from "../../hooks/useDiscardCustomizeConfirm";
import { useMethodCardDeckOrdering } from "../../hooks/useMethodCardDeckOrdering"; import { useMethodCardDeckOrdering } from "../../hooks/useMethodCardDeckOrdering";
import { CreateFlowHeaderLockup } from "../../components/CreateFlowHeaderLockup"; import { CreateFlowCardStackStepShell } from "../../components/CreateFlowCardStackStepShell";
import CardStack from "../../../../components/cards/CardStack"; import CardStack from "../../../../components/cards/CardStack";
import Create from "../../../../components/modals/Create"; import Create from "../../../../components/modals/Create";
import InlineTextButton from "../../../../components/buttons/InlineTextButton"; import InlineTextButton from "../../../../components/buttons/InlineTextButton";
import { CreateFlowStepShell } from "../../components/CreateFlowStepShell";
import {
CREATE_FLOW_CARD_STACK_AREA_MAX_CLASS,
CREATE_FLOW_MD_CENTERED_SHELL_CLASS,
CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS,
} from "../../components/createFlowLayoutTokens";
import { MembershipMethodEditFields } from "../../components/methodEditFields"; import { MembershipMethodEditFields } from "../../components/methodEditFields";
import CustomMethodCardWizard from "../../components/CustomMethodCardWizard"; import CustomMethodCardWizard from "../../components/CustomMethodCardWizard";
import { uploadCreateFlowFile } from "../../../../../lib/create/uploadToServer"; import { uploadCreateFlowFile } from "../../../../../lib/create/uploadToServer";
@@ -694,22 +688,10 @@ export function MembershipMethodsScreen() {
return ( return (
<> <>
<CreateFlowStepShell <CreateFlowCardStackStepShell
variant="wideGridLoosePadding" lockupTitle={title}
contentTopBelowMd="space-800" lockupDescription={description}
className={CREATE_FLOW_MD_CENTERED_SHELL_CLASS} stackBusy={!recommendationsReady}
>
<div className="flex w-full min-w-0 flex-col items-center gap-6">
<div className={CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS}>
<CreateFlowHeaderLockup
title={title}
description={description}
justification="center"
/>
</div>
<div
className={CREATE_FLOW_CARD_STACK_AREA_MAX_CLASS}
aria-busy={!recommendationsReady}
> >
{recommendationsReady && ( {recommendationsReady && (
<CardStack <CardStack
@@ -729,9 +711,7 @@ export function MembershipMethodsScreen() {
headerLockupSize={mdUp ? "L" : "M"} headerLockupSize={mdUp ? "L" : "M"}
/> />
)} )}
</div> </CreateFlowCardStackStepShell>
</div>
<Create <Create
isOpen={createModalOpen} isOpen={createModalOpen}
onClose={handleCreateModalClose} onClose={handleCreateModalClose}
@@ -774,7 +754,6 @@ export function MembershipMethodsScreen() {
) )
) : null} ) : null}
</Create> </Create>
</CreateFlowStepShell>
<CustomMethodCardWizard <CustomMethodCardWizard
isOpen={addCustomWizardOpen} isOpen={addCustomWizardOpen}
onClose={handleCloseAddWizard} onClose={handleCloseAddWizard}
@@ -16,6 +16,8 @@ import { useCreateFlowMdUp } from "../../hooks/useCreateFlowMdUp";
import { CreateFlowHeaderLockup } from "../../components/CreateFlowHeaderLockup"; import { CreateFlowHeaderLockup } from "../../components/CreateFlowHeaderLockup";
import { import {
CREATE_FLOW_MD_UP_GRID_CELL_CLASS, CREATE_FLOW_MD_UP_GRID_CELL_CLASS,
CREATE_FLOW_PAGE_GUTTER_CLASS,
CREATE_FLOW_SCROLL_REGION_CLASS,
CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS, CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS,
} from "../../components/createFlowLayoutTokens"; } from "../../components/createFlowLayoutTokens";
import { import {
@@ -163,7 +165,7 @@ export function CompletedScreen() {
<> <>
<div className="flex min-h-0 w-full flex-1 flex-col overflow-hidden bg-[var(--color-teal-teal50,#c9fef9)] md:h-full"> <div className="flex min-h-0 w-full flex-1 flex-col overflow-hidden bg-[var(--color-teal-teal50,#c9fef9)] md:h-full">
<div <div
className={`mx-auto grid min-h-0 w-full grid-cols-1 gap-4 px-5 max-md:max-w-[639px] max-md:overflow-y-auto max-md:overscroll-y-contain max-md:pt-[var(--space-800)] max-md:pb-8 md:h-full md:grid-cols-2 md:grid-rows-1 md:items-stretch md:justify-items-center md:gap-[var(--measures-spacing-1200,48px)] md:overflow-hidden md:px-12 md:py-0 ${CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS}`} className={`mx-auto grid min-h-0 w-full grid-cols-1 gap-4 ${CREATE_FLOW_PAGE_GUTTER_CLASS} max-md:overflow-y-auto max-md:overscroll-y-contain max-md:pt-[var(--space-800)] max-md:pb-8 md:h-full md:grid-cols-2 md:grid-rows-1 md:items-stretch md:justify-items-center md:gap-[var(--measures-spacing-1200,48px)] md:overflow-hidden md:py-0 ${CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS}`}
> >
<div <div
className={`flex flex-col justify-start max-md:min-h-min max-md:overflow-visible min-h-0 overflow-hidden md:justify-center md:pb-8 ${CREATE_FLOW_MD_UP_GRID_CELL_CLASS}`} className={`flex flex-col justify-start max-md:min-h-min max-md:overflow-visible min-h-0 overflow-hidden md:justify-center md:pb-8 ${CREATE_FLOW_MD_UP_GRID_CELL_CLASS}`}
@@ -177,7 +179,7 @@ export function CompletedScreen() {
/> />
</div> </div>
<div <div
className={`scrollbar-hide relative flex min-h-0 flex-col self-stretch overflow-x-hidden md:max-h-full md:overflow-y-auto ${CREATE_FLOW_MD_UP_GRID_CELL_CLASS}`} className={`relative flex min-h-0 flex-col self-stretch overflow-x-hidden md:max-h-full md:overflow-y-auto ${CREATE_FLOW_SCROLL_REGION_CLASS} ${CREATE_FLOW_MD_UP_GRID_CELL_CLASS}`}
> >
<div <div
className="pointer-events-none sticky top-0 z-10 hidden h-5 shrink-0 bg-gradient-to-b from-[var(--color-teal-teal50,#c9fef9)]/55 from-0% via-[var(--color-teal-teal50,#c9fef9)]/20 via-50% to-transparent md:block" className="pointer-events-none sticky top-0 z-10 hidden h-5 shrink-0 bg-gradient-to-b from-[var(--color-teal-teal50,#c9fef9)]/55 from-0% via-[var(--color-teal-teal50,#c9fef9)]/20 via-50% to-transparent md:block"
@@ -9,8 +9,7 @@ import { useTranslation } from "../../../../contexts/MessagesContext";
import { MAX_STAKEHOLDER_EMAILS } from "../../../../../lib/create/stakeholderLimits"; import { MAX_STAKEHOLDER_EMAILS } from "../../../../../lib/create/stakeholderLimits";
import { useCreateFlow } from "../../context/CreateFlowContext"; import { useCreateFlow } from "../../context/CreateFlowContext";
import { CreateFlowHeaderLockup } from "../../components/CreateFlowHeaderLockup"; import { CreateFlowHeaderLockup } from "../../components/CreateFlowHeaderLockup";
import { CreateFlowStepShell } from "../../components/CreateFlowStepShell"; import { CreateFlowTwoColumnSelectShell } from "../../components/CreateFlowTwoColumnSelectShell";
import { CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS } from "../../components/createFlowLayoutTokens";
import { import {
CREATE_FLOW_MANAGE_STAKEHOLDERS_QUERY, CREATE_FLOW_MANAGE_STAKEHOLDERS_QUERY,
CREATE_FLOW_MANAGE_STAKEHOLDERS_VALUE, CREATE_FLOW_MANAGE_STAKEHOLDERS_VALUE,
@@ -131,42 +130,31 @@ export function ConfirmStakeholdersScreen() {
if (managePublishedMode) { if (managePublishedMode) {
return ( return (
<CreateFlowStepShell <CreateFlowTwoColumnSelectShell
variant="centeredNarrowBottomPad" header={
contentTopBelowMd="space-1400"
>
<div
className={`flex flex-col items-start gap-[var(--measures-spacing-300,12px)] ${CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS}`}
>
<div className="flex w-full flex-col gap-[var(--measures-spacing-200,8px)] py-[12px]">
<CreateFlowHeaderLockup <CreateFlowHeaderLockup
title={t("managePublished.lockupTitle")} title={t("managePublished.lockupTitle")}
description={t("managePublished.lockupDescription")} description={t("managePublished.lockupDescription")}
justification="left" justification="left"
/> />
</div> }
>
<PublishedStakeholdersManagePanel ruleId={editingPublishedRuleId} /> <PublishedStakeholdersManagePanel ruleId={editingPublishedRuleId} />
</div> </CreateFlowTwoColumnSelectShell>
</CreateFlowStepShell>
); );
} }
return ( return (
<> <>
<CreateFlowStepShell <CreateFlowTwoColumnSelectShell
variant="centeredNarrowBottomPad" header={
contentTopBelowMd="space-1400"
>
<div
className={`flex flex-col items-start gap-[var(--measures-spacing-300,12px)] ${CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS}`}
>
<div className="flex w-full flex-col gap-[var(--measures-spacing-200,8px)] py-[12px]">
<CreateFlowHeaderLockup <CreateFlowHeaderLockup
title={t("title")} title={t("title")}
description={t("description")} description={t("description")}
justification="left" justification="left"
/> />
</div> }
>
{chipError ? ( {chipError ? (
<p <p
className="text-small-paragraph text-[var(--color-border-default-utility-negative)]" className="text-small-paragraph text-[var(--color-border-default-utility-negative)]"
@@ -187,8 +175,7 @@ export function ConfirmStakeholdersScreen() {
addButton addButton
addButtonText={t("addStakeholder")} addButtonText={t("addStakeholder")}
/> />
</div> </CreateFlowTwoColumnSelectShell>
</CreateFlowStepShell>
{!toastDismissed && ( {!toastDismissed && (
<div <div
+2 -1
View File
@@ -195,13 +195,14 @@ export function shouldOfferCreateFlowSaveAndExit(
/** /**
* Steps where below `lg` the main column scrolls with split layout * Steps where below `lg` the main column scrolls with split layout
* (`CreateFlowLayoutClient` — Linear CR-92 §4). * (`CreateFlowLayoutClient`).
*/ */
export const CREATE_FLOW_SELECT_SPLIT_SCROLL_STEPS: readonly CreateFlowStep[] = [ export const CREATE_FLOW_SELECT_SPLIT_SCROLL_STEPS: readonly CreateFlowStep[] = [
"community-size", "community-size",
"community-structure", "community-structure",
"core-values", "core-values",
"decision-approaches", "decision-approaches",
"confirm-stakeholders",
] as const; ] as const;
export function createFlowStepUsesSelectSplitScroll( export function createFlowStepUsesSelectSplitScroll(
@@ -17,6 +17,7 @@ const CreateFlowFooterContainer = memo<CreateFlowFooterProps>(
proportionBarVariant, proportionBarVariant,
onBackClick, onBackClick,
className = "", className = "",
contentMaxClass,
footerAriaLabel, footerAriaLabel,
}) => { }) => {
const t = useTranslation("controlsChrome"); const t = useTranslation("controlsChrome");
@@ -29,6 +30,7 @@ const CreateFlowFooterContainer = memo<CreateFlowFooterProps>(
proportionBarVariant={proportionBarVariant} proportionBarVariant={proportionBarVariant}
onBackClick={onBackClick} onBackClick={onBackClick}
className={className} className={className}
contentMaxClass={contentMaxClass}
footerAriaLabel={footerAriaLabel ?? t("createFlowFooterAriaLabel")} footerAriaLabel={footerAriaLabel ?? t("createFlowFooterAriaLabel")}
/> />
); );
@@ -36,6 +36,11 @@ export interface CreateFlowFooterProps {
* Additional CSS classes * Additional CSS classes
*/ */
className?: string; className?: string;
/**
* Inner max-width for the progress bar and actions, matching the step
* content column so the primary button does not drift at wide viewports.
*/
contentMaxClass?: string;
/** /**
* Accessible name for the footer landmark. * Accessible name for the footer landmark.
*/ */
@@ -1,6 +1,11 @@
import ProportionBar from "../../progress/ProportionBar"; import ProportionBar from "../../progress/ProportionBar";
import Button from "../../buttons/Button"; import Button from "../../buttons/Button";
import type { CreateFlowFooterProps } from "./CreateFlowFooter.types"; import type { CreateFlowFooterProps } from "./CreateFlowFooter.types";
import {
CREATE_FLOW_FOOTER_SCRIM_CLASS,
CREATE_FLOW_PAGE_GUTTER_CLASS,
CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS,
} from "../../../(app)/create/components/createFlowLayoutTokens";
export function CreateFlowFooterView({ export function CreateFlowFooterView({
secondButton, secondButton,
@@ -9,18 +14,21 @@ export function CreateFlowFooterView({
proportionBarVariant: proportionBarVariantProp, proportionBarVariant: proportionBarVariantProp,
onBackClick, onBackClick,
className = "", className = "",
contentMaxClass = CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS,
footerAriaLabel, footerAriaLabel,
}: CreateFlowFooterProps) { }: CreateFlowFooterProps) {
const proportionBarVariant = proportionBarVariantProp ?? "default"; const proportionBarVariant = proportionBarVariantProp ?? "default";
return ( return (
<footer <footer
className={`bg-black w-full ${className}`} className={`relative bg-black w-full pb-[env(safe-area-inset-bottom,0px)] ${className}`}
role="contentinfo" role="contentinfo"
aria-label={footerAriaLabel} aria-label={footerAriaLabel}
> >
{/* Progress Bar - Top */} <div className={CREATE_FLOW_FOOTER_SCRIM_CLASS} aria-hidden />
<div className={CREATE_FLOW_PAGE_GUTTER_CLASS}>
<div className={`mx-auto w-full ${contentMaxClass}`}>
{progressBar && ( {progressBar && (
<div className="px-[var(--spacing-measures-spacing-500,20px)] md:px-[var(--spacing-measures-spacing-1200,48px)] pt-[var(--spacing-measures-spacing-300,12px)]"> <div className="pt-[var(--spacing-measures-spacing-300,12px)]">
<ProportionBar <ProportionBar
progress={proportionBarProgress} progress={proportionBarProgress}
variant={proportionBarVariant} variant={proportionBarVariant}
@@ -28,9 +36,7 @@ export function CreateFlowFooterView({
</div> </div>
)} )}
{/* Buttons Container */} <div className="flex items-center justify-between py-[var(--spacing-measures-spacing-300,12px)] gap-[var(--spacing-measures-spacing-300,12px)]">
<div className="flex items-center justify-between mx-auto max-w-[639px] md:max-w-[1920px] px-[var(--spacing-measures-spacing-500,20px)] md:px-[var(--spacing-measures-spacing-1200,48px)] py-[var(--spacing-measures-spacing-300,12px)] gap-[var(--spacing-measures-spacing-300,12px)]">
{/* Back Button - Left */}
<Button <Button
buttonType="ghost" buttonType="ghost"
palette="default" palette="default"
@@ -42,9 +48,10 @@ export function CreateFlowFooterView({
Back Back
</Button> </Button>
{/* Second Button - Right */}
{secondButton && <div className="flex-shrink-0">{secondButton}</div>} {secondButton && <div className="flex-shrink-0">{secondButton}</div>}
</div> </div>
</div>
</div>
</footer> </footer>
); );
} }
@@ -5,6 +5,7 @@ import Button from "../../buttons/Button";
import ListItem from "../../layout/ListItem"; import ListItem from "../../layout/ListItem";
import Popover from "../../modals/Popover"; import Popover from "../../modals/Popover";
import type { CreateFlowTopNavViewProps } from "./CreateFlowTopNav.types"; import type { CreateFlowTopNavViewProps } from "./CreateFlowTopNav.types";
import { CREATE_FLOW_PAGE_GUTTER_CLASS } from "../../../(app)/create/components/createFlowLayoutTokens";
const outlineButtonClass = const outlineButtonClass =
"md:!text-x-small-label !text-xx-small-label !px-[var(--spacing-scale-006,6px)] md:!px-[var(--spacing-scale-008,8px)] !py-[6px] md:!py-[8px] !border md:!border-[1.5px]"; "md:!text-x-small-label !text-xx-small-label !px-[var(--spacing-scale-006,6px)] md:!px-[var(--spacing-scale-008,8px)] !py-[6px] md:!py-[8px] !border md:!border-[1.5px]";
@@ -229,7 +230,7 @@ export function CreateFlowTopNavView({
aria-label={bannerAriaLabel} aria-label={bannerAriaLabel}
> >
<nav <nav
className="flex items-center justify-between mx-auto max-w-[639px] md:max-w-[1920px] px-[var(--spacing-measures-spacing-500,20px)] md:px-[48px] py-[var(--spacing-measures-spacing-300,12px)] md:py-[var(--spacing-measures-spacing-016,16px)]" className={`flex items-center justify-between mx-auto w-full max-w-[1920px] ${CREATE_FLOW_PAGE_GUTTER_CLASS} py-[var(--spacing-measures-spacing-300,12px)] md:py-[var(--spacing-measures-spacing-016,16px)]`}
role="navigation" role="navigation"
aria-label={navAriaLabel} aria-label={navAriaLabel}
> >
@@ -86,4 +86,17 @@ describe("CreateFlowFooter (behavioral tests)", () => {
expect(buttons).toHaveLength(1); expect(buttons).toHaveLength(1);
expect(buttons[0]).toHaveTextContent("Back"); expect(buttons[0]).toHaveTextContent("Back");
}); });
it("constrains the action row to the step content max-width", () => {
const { container } = render(
<CreateFlowFooter contentMaxClass="w-full min-w-0 lg:max-w-[640px]" />,
);
const footer = screen.getByRole("contentinfo", {
name: "Create Flow Footer",
});
expect(footer.className).toContain("pb-[env(safe-area-inset-bottom,0px)]");
expect(
container.querySelector('[class*="lg:max-w-[640px]"]'),
).not.toBeNull();
});
}); });
+40 -3
View File
@@ -3,19 +3,35 @@ import {
CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS, CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS,
CREATE_FLOW_MD_UP_GRID_CELL_CLASS, CREATE_FLOW_MD_UP_GRID_CELL_CLASS,
CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS, CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS,
CREATE_FLOW_WIDE_MAX_CLASS,
CREATE_FLOW_MD_CENTERED_MAIN_CLASS, CREATE_FLOW_MD_CENTERED_MAIN_CLASS,
CREATE_FLOW_MD_CENTERED_SHELL_CLASS, CREATE_FLOW_MD_CENTERED_SHELL_CLASS,
CREATE_FLOW_PAGE_GUTTER_CLASS,
CREATE_FLOW_VIEWPORT_FRAME_CLASS,
getCreateFlowContentMaxClass,
} from "../../app/(app)/create/components/createFlowLayoutTokens"; } from "../../app/(app)/create/components/createFlowLayoutTokens";
describe("createFlowLayoutTokens", () => { describe("createFlowLayoutTokens", () => {
it("exports create-flow column and two-column max class strings", () => { it("exports create-flow column and two-column max class strings", () => {
expect(CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS).toBe( expect(CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS).toBe(
"w-full min-w-0 md:max-w-[640px]", "w-full min-w-0 lg:max-w-[640px]",
); );
expect(CREATE_FLOW_MD_UP_GRID_CELL_CLASS).toBe( expect(CREATE_FLOW_MD_UP_GRID_CELL_CLASS).toBe(
"w-full min-w-0 md:mx-auto md:max-w-[640px]", "w-full min-w-0 lg:mx-auto lg:max-w-[640px]",
); );
expect(CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS).toBe("md:max-w-[1328px]"); expect(CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS).toBe(
"w-full min-w-0 lg:max-w-[1328px]",
);
expect(CREATE_FLOW_WIDE_MAX_CLASS).toBe("w-full min-w-0 lg:max-w-[1440px]");
});
it("uses one gutter scale across the six viewport bands", () => {
expect(CREATE_FLOW_PAGE_GUTTER_CLASS).toBe("px-5 md:px-12 lg:px-16");
});
it("sizes the wizard frame with dynamic viewport height", () => {
expect(CREATE_FLOW_VIEWPORT_FRAME_CLASS).toContain("h-dvh");
expect(CREATE_FLOW_VIEWPORT_FRAME_CLASS).toContain("max-h-dvh");
}); });
it("centers lockup+card and card-stack steps in the navfooter band from md", () => { it("centers lockup+card and card-stack steps in the navfooter band from md", () => {
@@ -24,4 +40,25 @@ describe("createFlowLayoutTokens", () => {
); );
expect(CREATE_FLOW_MD_CENTERED_SHELL_CLASS).toBe("md:my-auto md:pt-0"); expect(CREATE_FLOW_MD_CENTERED_SHELL_CLASS).toBe("md:my-auto md:pt-0");
}); });
it("aligns the action bar max-width with the step content column", () => {
expect(
getCreateFlowContentMaxClass({ step: "community-name" }),
).toBe(CREATE_FLOW_MD_UP_COLUMN_MAX_CLASS);
expect(
getCreateFlowContentMaxClass({ step: "community-structure" }),
).toBe(CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS);
expect(
getCreateFlowContentMaxClass({ step: "communication-methods" }),
).toBe(CREATE_FLOW_WIDE_MAX_CLASS);
expect(
getCreateFlowContentMaxClass({ step: "final-review" }),
).toBe(CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS);
expect(
getCreateFlowContentMaxClass({
step: "informational",
isTemplateReview: true,
}),
).toBe(CREATE_FLOW_TWO_COLUMN_MAX_WIDTH_CLASS);
});
}); });
+11
View File
@@ -10,6 +10,7 @@ import {
resolveCreateFlowBackTarget, resolveCreateFlowBackTarget,
shouldOfferCreateFlowSaveAndExit, shouldOfferCreateFlowSaveAndExit,
isDirectTemplateReviewEntry, isDirectTemplateReviewEntry,
createFlowStepUsesSelectSplitScroll,
TEMPLATES_FACET_RECOMMEND_QUERY, TEMPLATES_FACET_RECOMMEND_QUERY,
TEMPLATES_FACET_RECOMMEND_VALUE, TEMPLATES_FACET_RECOMMEND_VALUE,
TEMPLATE_REVIEW_FROM_CREATE_FLOW_QUERY, TEMPLATE_REVIEW_FROM_CREATE_FLOW_QUERY,
@@ -221,4 +222,14 @@ describe("flowSteps", () => {
).toBe(false); ).toBe(false);
expect(isDirectTemplateReviewEntry(null)).toBe(false); expect(isDirectTemplateReviewEntry(null)).toBe(false);
}); });
it("uses split-column scroll for select, right-rail, and stakeholders", () => {
expect(createFlowStepUsesSelectSplitScroll("community-structure")).toBe(
true,
);
expect(createFlowStepUsesSelectSplitScroll("confirm-stakeholders")).toBe(
true,
);
expect(createFlowStepUsesSelectSplitScroll("community-name")).toBe(false);
});
}); });