Dashboard: layered grid columns + visual layout model picker#78364
Conversation
both surfaces now compose the two props instead of treating them as mutually exclusive: columns caps the count, minColumnWidth enforces a per-tile floor that can reduce the count on narrow containers.
drop the fixed/responsive ternary in widgets.tsx and pass columns and minColumnWidth together. drop the Fixed column count toggle and the min column width field from the layout settings drawer; minColumnWidth becomes an implicit floor configured in the dashboard context.
# Conflicts: # routes/dashboard/widget-dashboard/components/layout-settings/layout-settings.tsx
switch model field to toggleGroup and show autoRowHeight only when not masonry (previously visible-but-disabled).
apply minColumnWidth as a fallback floor in widgets.tsx so the layered model holds even when stored settings predate the refactor and persist minColumnWidth as undefined. drop minColumnWidth from user-facing defaults; it lives with the engine, not the saved preferences.
bring back minColumnWidth as a user-facing field so the per-tile floor is editable alongside the column count. the engine floor in widgets.tsx stays as a safety net for stored settings that predate this refactor.
introduce a user-facing toggle to disable the per-tile width floor. when off, minColumnWidth resolves to 0 and tiles shrink with the container. soften the surrounding copy.
replace the model toggleGroup with a card-style picker that previews the grid and masonry layouts as inline SVG thumbnails. tighten copy on the surrounding descriptions.
move LayoutModelEdit, the SVG thumbnails, and its styles into components/layout-settings/layout-model-edit/. swap raw svg/rect for SVG/Rect from @wordpress/primitives, the inline fieldset/legend for BaseControl + BaseControl.VisualLabel from @wordpress/components, and the option layout for Stack + Text from @wordpress/ui.
swap the raw button element for Button (variant="unstyled") from @wordpress/ui. inherits font reset, box-sizing reset, and the focus ring from the component instead of duplicating them in css.
swap BaseControl + manual aria for Fieldset.Root + Fieldset.Legend + Fieldset.Description from @wordpress/ui (semantic fieldset + legend + auto-wired aria-describedby). compose the option's inner layout with Stack instead of inline flex styles, shrinking the css module to the visual-only rules.
swap the inline 0.15s ease for --wpds-motion-duration-sm and --wpds-motion-easing-subtle so the option's hover and selected state animations follow the design-system motion scale.
match the DataForm naming convention (each Edit control is a field).
destructure getValue/setValue/elements like the package's built-in controls, read disabled via field.isDisabled, and wrap the change handler in useCallback. swap the inline 1px border for --wpds-border-width-xs.
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
Size Change: 0 B Total Size: 7.97 MB ℹ️ View Unchanged
|
|
Flaky tests detected in 359a815. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/25933984616
|
What?
Integrates
columnsandminColumnWidthinto a layered model on@wordpress/gridand overhauls the layout settings drawer to expose the model as a visual card picker with an inline-SVG preview.Part of #77616 #77626
Why?
DashboardGridandDashboardLanestreatedcolumnsandminColumnWidthas mutually exclusive, both at the type level (discriminated union with?: never) and at runtime. The dashboard had to branch its render call into a fixed-mode tree and a responsive-mode tree, and the layout-settings drawer surfaced a "Fixed column count" toggle so the user had to pick a mode.The two props are not duplicates.
columnsis a target count;minColumnWidthis a per-tile width floor. Treating them as XOR lost the most useful semantics: "I want up to N columns, but never tiles narrower than W pixels."In parallel, the layout settings drawer needed a polish pass: the model selector was a plain text toggle and several copy strings were technical. Mikael flagged this in code review on PR #78336 and suggested a visual picker.
How?
Grid package
Collapse the discriminated union in
packages/grid/src/dashboard-grid/types.tsandpackages/grid/src/dashboard-lanes/types.tsinto a single interface wherecolumns?andminColumnWidth?are both optional and independent.Update the
effectiveColumnsmath in both surfaces (packages/grid/src/dashboard-grid/index.tsx,packages/grid/src/dashboard-lanes/index.tsx):Behavior preserved when only one prop is set; new behavior when both are set:
columnscaps the count,minColumnWidthenforces a per-tile floor that can reduce the count on narrow containers.Add a "Layered" Storybook story to both surfaces demonstrating the combined behavior.
Dashboard engine
routes/dashboard/widget-dashboard/components/widgets/widgets.tsx: drop the fixed/responsive ternary and passcolumns+minColumnWidthtogether. ApplygridSettings.minColumnWidth ?? DASHBOARD_MIN_COLUMN_WIDTHso the floor survives stored settings that predate the refactor (whereminColumnWidth: undefinedcould be persisted).routes/dashboard/widget-dashboard/types.ts: update theBaseWidgetGridSettingsJSDoc that described the props as mutually exclusive.routes/dashboard/widget-dashboard/context/dashboard-context.tsxandroutes/dashboard/hooks/use-dashboard-grid-settings/use-dashboard-grid-settings.ts: align the two default-settings copies on{ columns: 6, minColumnWidth: 350, rowHeight: 200 }.Layout settings drawer
Replace the "Fixed column count" toggle and its conditional fields with a flat list:
Layout model,Columns(1-12 stepper),Adjust on narrow screens(toggle),Minimum tile width(visible only when the adaptive toggle is on),Auto-fit row height to content,Row height (px).minColumnWidthsemantics (three states):undefined: stale / migrated state; engine applies its safety-net floor.0: user disabled the floor explicitly; tiles shrink freely.> 0: user-chosen value.Visual layout model picker
New module:
routes/dashboard/widget-dashboard/components/layout-settings/layout-model-edit-field/.index.tsx:LayoutModelEditFieldDataFormEditcontrol. Composed withFieldset.Root/Fieldset.Legend/Fieldset.Descriptionfor semantic grouping (auto-wiredaria-describedby),Stackfor layout (radio group + inner card layout),Buttonvariant="unstyled"for each option (carries the design system focus ring + font/box-sizing reset),Textfor the option label.thumbnail-grid.tsx,thumbnail-masonry.tsx: inline thumbnails usingSVGandRectfrom@wordpress/primitives.style.module.css: only the visual-only rules that no primitive can supply (border, padding, radius, hover, selected). All values are WPDS tokens (--wpds-border-width-xs,--wpds-motion-duration-sm,--wpds-motion-easing-subtle, …).The control follows the DataForm pattern of the package's built-in controls (e.g.
ToggleGroup): destructuresgetValue/setValue/elementsfromfield, readsdisabledviafield.isDisabled, and wraps the change handler inuseCallback.Testing
Columns: 6andMinimum tile width: 350, resize the browser to narrow widths. Column count should drop progressively as the floor is honored.@wordpress/uibuttons.Screen.Recording.2026-05-15.at.7.24.12.PM.mov