Skip to content

Grid: per-item tile size limits - #81899

Merged
retrofox merged 26 commits into
trunkfrom
update/grid-tile-limits
Aug 28, 2026
Merged

retrofox merged 26 commits into
trunkfrom
update/grid-tile-limits

Conversation

@retrofox

@retrofox retrofox commented Aug 21, 2026 •

Copy link
Copy Markdown
Contributor

What

DashboardGrid and DashboardLanes accept a new itemLimits prop: per-item minimum and maximum tile sizes, in pixels, keyed by layout item key.

Each surface quantizes the limits to whole tracks of its current geometry (minimums round up so the span covers the pixels, maximums round down so it fits inside them) and enforces the result on rendered spans and on the resize gesture. The limits are never written into the layout: stored data stays untouched, and a limit change re-renders every existing layout under the new rule.

Part of #81795.

Why

Tiles can currently be resized down to a single cell, or grown past the point where their content stays useful. Hosts need per-tile floors and ceilings the gesture cannot cross, without contaminating the persisted layout with derived constraints: the layout is user state, while limits are recomputable facts that change on their own clock.

This PR ships the capability at the grid level. Translating widget-type declarations into concrete limits is host work, tracked separately in #81795.

How

  • New GridItemLimits type ({ minWidth, minHeight, maxWidth, maxHeight }, in pixels) and an itemLimits?: Record< string, GridItemLimits > prop on both surfaces.
  • pixelLimitsToSpanBounds() converts pixel limits to span bounds with the surface's own track math; width bounds saturate at the column count, and a minimum that exceeds a maximum after quantization wins over it.
  • Rendered spans are bounded before placement, and the resize gesture is absorbed at both bounds: the continuous pixel clamp (clampResizeDelta gains a maxSize), the snap preview, and the committed spans all stay inside the limits.
  • 'full' and 'fill' respect a maximum: 'full' renders at the item's capped span, and resolveFillWidths() plans rows around each fill's bounds, reserving at least its floor, wrapping it to a wide-enough free run, and capping the result.
  • Limits travel through a dedicated prop instead of the layout items, so onChangeLayout cannot persist them into consumer state.
  • Lanes apply the width axis only; heights there are content-driven.
  • One "Per-Item Size Limits" story per grid model, with per-tile full/fill toggles and a committed-state panel.

Testing

Run Storybook:

npm run storybook:dev
image
  1. Open Grid / DashboardGrid / Per-Item Size Limits.
  2. On load, the constrained tiles render lifted to their floors while the Committed panel still shows the smaller stored spans.
  3. Shrink the min 320 × 200 px tile below its floor: the tile and the snap preview pin at the bound, and the release commits inside it.
  4. Grow the max 500 × 400 px tile past its ceiling: the gesture is absorbed the same way.
  5. Toggle full width on the capped tile: it stops at its maximum span instead of taking the whole row.
  6. Toggle fill width on a constrained tile: the resolved span honors the floor, pushing row neighbors out when needed, and never exceeds the ceiling.
  7. Open Grid / DashboardLanes / Per-Item Size Limits and repeat the horizontal cases: the floored tile refuses to shrink, the capped tile refuses to grow.
  8. Confirm the Committed panel never records a span outside the limits, and that the free tiles keep the full range.

Run the unit tests:

npx jest --config test/unit/jest.config.js packages/grid/src

Screenshots and videos

Min width and height

Screen.Recording.2026-08-21.at.1.20.45.PM.mov

Max widget and height

Screen.Recording.2026-08-21.at.1.21.19.PM.mov

Min / Max -> width / heigh

Screen.Recording.2026-08-23.at.11.20.54.AM.mov

fill width + min width

Screen.Recording.2026-08-21.at.1.22.32.PM.mov

...

Follow-ups

@retrofox retrofox added the [Type] Enhancement A suggestion for improvement. label Aug 21, 2026
@retrofox retrofox self-assigned this Aug 21, 2026
@github-actions

github-actions Bot commented Aug 21, 2026 •

Copy link
Copy Markdown

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 props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: retrofox <retrofox@git.wordpress.org>
Co-authored-by: chihsuan <chihsuan@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions

github-actions Bot commented Aug 21, 2026 •

Copy link
Copy Markdown

Size Change: 0 B

Total Size: 7.92 MB

compressed-size-action

@github-actions

github-actions Bot commented Aug 21, 2026 •

Copy link
Copy Markdown

Flaky tests detected in b440755.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/32970005850
📝 Reported tests:

Top level navigation links inherit the text color from the theme/group but do not apply to the submenu or overlay text in /test/e2e/specs/editor/blocks/navigation-colors.spec.js, passed after 1 failed attempt.
Error: apiRequestContext.fetch: socket hang up
Call log:
  - → GET http://localhost:8889/wp-json/wp/v2/pages?per_page=100&status=publish%2Cfuture%2Cdraft%2Cpending%2Cprivate%2Ctrash
    - user-agent: Playwright/1.62.1 (x64; ubuntu 24.04) node/20.20 CI/1
    - accept: */*
    - accept-encoding: gzip,deflate,br
    - X-WP-Nonce: 087e897fa8
    - cookie: wordpress_test_cookie=WP%20Cookie%20check; wordpress_logged_in_23778236db82f19306f247e20a353a99=admin%7C1787921277%7CGUHLBSPeiIcyLEEuGP6kjTS0CyAZD4hK4YvdFbCrbnu%7C2d4f238a100f38a5c68a9878f66edab50d93940faae2dc9a4a4241ac2f7db4e2; wp-settings-time-1=1787748544

    at RequestUtils.rest (/home/runner/work/gutenberg/gutenberg/packages/e2e-test-utils-playwright/src/request-utils/rest.ts:112:39)
    at RequestUtils.deleteAllPages (/home/runner/work/gutenberg/gutenberg/packages/e2e-test-utils-playwright/src/request-utils/pages.ts:44:27)
    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/blocks/navigation-colors.spec.js:38:17

@chihsuan chihsuan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice API overall. @retrofox I found two interaction-state issues and one smaller Lanes type-safety issue.

I think issues mainly come from the same state mismatch. Grid can save render-only bounded sizes, and Lanes can resize from a different width than the user sees. Otherwise, tested well.

} );
return changed ? bounded : layout;
}, [ layout, spanBoundsByKey ] );
const activeLayout = temporaryLayout ?? boundedLayout;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we keep boundedLayout render-only here?

Currently, resizing or reordering one tile also saves the clamped size of every other limited tile, including ones the user never touched. For example, someone sets a widget to 4 columns. Later they work in a narrower window, where the same pixel floor quantizes to 6, so it renders at 6.

Then they drag an unrelated tile, and the 6 gets written back. Back on the wide screen the widget stays at 6, and relaxing the limit later won't bring the 4 back.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, and that is the rule now (73e323d).

The bounded view is render-only. Both gestures commit from the stored layout or from the pending commit while a gesture is in flight, so a commit is the stored layout plus the change to the touched tile.

A reorder rewrites only order; a resize writes the new span of the resized tile, and every other tile keeps its stored span.

The scenario is covered by a regression test: a tile stored at 1 column with a floor that renders it at 2 still reports width: 1 in onChangeLayout after another tile is resized and after a reorder (dashboard-grid/test/item-limits.test.tsx).

One deliberate exception: the tile being resized commits at the span on screen because the gesture starts from what is rendered, and a gesture that ends at the rendered span commits nothing. This matches how 'fill' and 'full' convert to a numeric width on resize.

Comment on lines 27 to 622
import { LanesItem } from './lanes-item';
import { useLanePlacement } from './use-lane-placement';
import { GridOverlay } from '../shared/grid-overlay';
import { gridSpanToPixelSize } from '../shared/resize-snap';
import { clampSpan, gridSpanToPixelSize } from '../shared/resize-snap';
import { useResizePixelLimits, useSpanBounds } from '../shared/use-span-bounds';
import layoutAnimationStyles from '../shared/layout-shift-animation.module.css';
import { ItemExitOverlay } from '../shared/item-exit-overlay';
import {
getLayoutFingerprint,
getPlacementFingerprint,
useLayoutShiftAnimation,
} from '../shared/use-layout-shift-animation';
import { useItemExitAnimation } from '../shared/use-item-exit-animation';
import type { DashboardLanesLayoutItem, DashboardLanesProps } from './types';
import type { ResizeSnapSize } from '../shared/resize-snap';
import type { ResizeDelta } from '../shared/types';
import { createDashboardDragDropAnimation } from '../shared/drag-overlay-drop-animation';
import styles from './lanes.module.css';

const dashboardDragDropAnimation = createDashboardDragDropAnimation(
styles[ 'drag-preview-frame' ],
styles.dragPreviewFrameExiting
);

// Fallback gap in pixels for math that runs before the computed gap
// can be read from the DOM. Matches the `'xl'` step the surface
// resolves to in CSS (`--wpds-dimension-gap-xl`); the next layout
// effect overwrites this with the actual computed value.
const FALLBACK_GAP_PX = 24;

// Default lane cap when no explicit `columns` or `minColumnWidth` is
// supplied. Layered semantics: `columns` acts as a cap and
// `minColumnWidth` as a per-tile floor; if neither is set we still
// need a finite count to render against.
const DEFAULT_COLUMNS = 6;

const NO_SORT_STRATEGY = () => null;

/**
* Masonry-style surface aligned with `display: grid-lanes`. Items
* declare a column span; heights are driven by content; placement
* follows the source-ordered, shortest-lane algorithm with
* `flow-tolerance` tiebreaking.
*
* On browsers that support `display: grid-lanes` natively, the
* component emits the spec's CSS and lets the engine handle layout.
* Otherwise, `useLanePlacement` measures item heights and assigns
* explicit `grid-column-start` / `grid-row-start` values that
* approximate the same result inside CSS Grid.
*
* Each child's `key` must match an entry in the `layout` array;
* children without a match render at the end of the surface without
* explicit placement and fall through the lanes auto-flow.
*
* @example
* ```jsx
* <DashboardLanes
* layout={ [
* { key: 'a' },
* { key: 'b', width: 2 },
* { key: 'c' },
* ] }
* columns={ 3 }
* editMode
* onChangeLayout={ setLayout }
* >
* <Tile key="a">A</Tile>
* <Tile key="b">B</Tile>
* <Tile key="c">C</Tile>
* </DashboardLanes>
* ```
*
* @param props Component props.
* @param ref Forwarded to the surface's root `<div>`.
*/
export const DashboardLanes = forwardRef< HTMLDivElement, DashboardLanesProps >(
function DashboardLanes( props, ref ) {
const {
layout,
columns,
children,
className,
style,
flowTolerance = 16,
rowUnit = 4,
minColumnWidth,
itemLimits,
editMode = false,
onChangeLayout,
onPreviewLayout,
renderResizeHandle,
renderDragPreview,
renderGridOverlay,
...divProps
} = props;

const [ temporaryLayout, setTemporaryLayout ] = useState<
DashboardLanesLayoutItem[] | undefined
>();
const [ activeId, setActiveId ] = useState< string | null >( null );
const [ isResizing, setIsResizing ] = useState( false );
const [ resizeSnapPreview, setResizeSnapPreview ] = useState< {
id: string;
snap: ResizeSnapSize;
} | null >( null );
const latestLayoutRef = useRef<
DashboardLanesLayoutItem[] | undefined
>();
const lastReorderCursorRef = useRef< {
x: number;
y: number;
} | null >( null );
const resizeBaselineRef = useRef< number | null >( null );
const captureLayoutSnapshotRef = useRef< () => void >( () => {} );
const childrenCacheRef = useRef< Map< string, React.ReactElement > >(
new Map()
);
const activeLayout = temporaryLayout ?? layout;

const [ container, setContainer ] = useState< HTMLDivElement | null >(
null
);
const [ containerWidth, setContainerWidth ] = useState( 0 );
const [ gapPx, setGapPx ] = useState( FALLBACK_GAP_PX );
const resizeObserverRef = useResizeObserver(
( [ { contentRect } ] ) => {
setContainerWidth( contentRect.width );
}
);
const mergedRootRef = useMergeRefs( [
setContainer,
resizeObserverRef,
ref,
] );

// Measure synchronously before paint and snapshot the computed
// `column-gap` so the placement math tracks the design-system
// token under any density.
useLayoutEffect( () => {
if ( ! container ) {
return;
}
const { width } = container.getBoundingClientRect();
if ( width > 0 ) {
setContainerWidth( width );
}
const parsed = Number.parseFloat(
window.getComputedStyle( container ).columnGap
);
if ( Number.isFinite( parsed ) && parsed > 0 ) {
setGapPx( parsed );
}
}, [ container ] );
const effectiveColumns = useMemo( () => {
if ( ! minColumnWidth ) {
return columns ?? DEFAULT_COLUMNS;
}
const totalWidthPerColumn = minColumnWidth + gapPx;
const maxFit = Math.max(
1,
Math.floor( ( containerWidth + gapPx ) / totalWidthPerColumn )
);
return columns !== undefined ? Math.min( columns, maxFit ) : maxFit;
}, [ minColumnWidth, gapPx, containerWidth, columns ] );
const columnWidth =
( containerWidth - ( effectiveColumns - 1 ) * gapPx ) /
effectiveColumns;
const minResizeWidthPx = gridSpanToPixelSize(
1,
1,
columnWidth,
gapPx,
null
).widthPx;

// Height limits do not apply: lane heights are content-driven.
const widthBoundsByKey = useSpanBounds(
itemLimits,
columnWidth,
gapPx,
null,
effectiveColumns
);
const resizeLimitsByKey = useResizePixelLimits(
widthBoundsByKey,
columnWidth,
gapPx,
null
);

const layoutMap = useMemo( () => {
const map = new Map< string, DashboardLanesLayoutItem >();
activeLayout.forEach( ( item ) => map.set( item.key, item ) );
return map;
}, [ activeLayout ] );

// Stable-identity key set for the children walk (see grid.tsx).
const layoutKeys = useMemo(
() => new Set( layout.map( ( item ) => item.key ) ),
[ layout ]
);

// Sorted item keys, identity-stable when the resulting sequence
// is unchanged (avoids invalidating SortableContext).
const sortedItems = useMemo(
() =>
activeLayout
.map( ( item, index ) => ( { item, index } ) )
.sort(
( a, b ) =>
( a.item.order ?? a.index ) -
( b.item.order ?? b.index )
)
.map( ( { item } ) => item.key ),
[ activeLayout ]
);
const items = sortedItems;

// Placement input for the hook: each item with its clamped span
// in source (sorted) order. `lane` forwards the optional explicit
// pin from the layout item; the algorithm clamps out-of-range
// values, so no surface-level guard is needed.
const placementItems = useMemo( () => {
return items.map( ( key ) => {
const item = layoutMap.get( key );
const width = item?.width;
const span =
typeof width === 'number'
? Math.max( 1, Math.min( width, effectiveColumns ) )
: 1;
return { key, span, lane: item?.lane };
const bounds = widthBoundsByKey.get( key );
return {
key,
span: bounds
? clampSpan( span, bounds.minWidth, bounds.maxWidth )
: span,
lane: item?.lane,
};
} );
}, [ items, layoutMap, effectiveColumns ] );
}, [ items, layoutMap, effectiveColumns, widthBoundsByKey ] );

const { itemStyles } = useLanePlacement( container, {
items: placementItems,
lanes: effectiveColumns,
gap: gapPx,
flowTolerance,
rowUnit,
} );

const [ childrenMap, actionableAreaMap, remaining, renderedByKey ] =
useMemo( () => {
const childMap = new Map< string, React.ReactElement >();
const actionableMap = new Map< string, React.ReactNode >();
const rest: React.ReactNode[] = [];
const byKey = new Map< string, React.ReactElement >();

Children.forEach( children, ( child ) => {
if ( ! isValidElement( child ) ) {
rest.push( child );
return;
}
const key = child.key?.toString();
if ( ! key ) {
rest.push( child );
return;
}

// Strip `actionableArea` so it does not leak to the DOM;
// the grid lifts it to a slot separately.
const { actionableArea } = child.props as {
actionableArea?: React.ReactNode;
};
const stripped =
actionableArea !== undefined
? cloneElement(
child as React.ReactElement< {
actionableArea?: React.ReactNode;
} >,
{ actionableArea: undefined }
)
: ( child as React.ReactElement );

byKey.set( key, stripped );

if ( layoutKeys.has( key ) ) {
if ( actionableArea !== undefined ) {
actionableMap.set( key, actionableArea );
}
childMap.set( key, stripped );
} else {
rest.push( child );
}
} );

return [ childMap, actionableMap, rest, byKey ];
}, [ children, layoutKeys ] );

// Persist the latest rendered children so a removed tile's content
// is still available for its exit overlay. Filled from an effect so a
// discarded render never writes to the cache.
useLayoutEffect( () => {
for ( const [ key, child ] of renderedByKey ) {
childrenCacheRef.current.set( key, child );
}
}, [ renderedByKey ] );

const sensors = useSensors(
useSensor( PointerSensor ),
useSensor( KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
} )
);

const handleDragStart = useEvent( ( event: DragStartEvent ) => {
setActiveId( String( event.active.id ) );
lastReorderCursorRef.current = null;
} );

const handleDragCancel = useEvent( () => {
setActiveId( null );
latestLayoutRef.current = undefined;
lastReorderCursorRef.current = null;
resizeBaselineRef.current = null;
setIsResizing( false );
setResizeSnapPreview( null );
setTemporaryLayout( undefined );
} );

const handleDragMove = useEvent( ( event: DragMoveEvent ) => {
const { active, over } = event;
if ( ! over || active.id === over.id ) {
return;
}
const activeRect = active.rect.current.translated;
if ( ! activeRect ) {
return;
}
const activeCenterX = activeRect.left + activeRect.width / 2;
const activeCenterY = activeRect.top + activeRect.height / 2;

const lastCursor = lastReorderCursorRef.current;
if ( lastCursor ) {
const dx = activeCenterX - lastCursor.x;
const dy = activeCenterY - lastCursor.y;
if ( dx * dx + dy * dy < 100 ) {
return;
}
}

const overCenterX = over.rect.left + over.rect.width / 2;
const insertAfter = activeCenterX > overCenterX;

const currentIndex = items.indexOf( String( active.id ) );
const overIndex = items.indexOf( String( over.id ) );
let newIndex: number;
if ( insertAfter ) {
newIndex = currentIndex > overIndex ? overIndex + 1 : overIndex;
} else {
newIndex = currentIndex > overIndex ? overIndex : overIndex - 1;
}
newIndex = Math.max( 0, Math.min( newIndex, items.length - 1 ) );

if ( newIndex === currentIndex ) {
return;
}

const updatedItems = arrayMove( items, currentIndex, newIndex );
// Build a key→index lookup so the .map below is O(n)
// instead of O(n²) from per-item `indexOf` calls.
const orderByKey = new Map< string, number >();
updatedItems.forEach( ( key, index ) => {
orderByKey.set( key, index );
} );
const updatedLayout = activeLayout.map( ( item ) => ( {
...item,
order: orderByKey.get( item.key ) ?? 0,
} ) );

lastReorderCursorRef.current = {
x: activeCenterX,
y: activeCenterY,
};
latestLayoutRef.current = updatedLayout;
captureLayoutSnapshotRef.current();
setTemporaryLayout( updatedLayout );
onPreviewLayout?.( updatedLayout );
} );

const persistTemporaryLayout = useEvent( () => {
const latest = latestLayoutRef.current;
latestLayoutRef.current = undefined;
resizeBaselineRef.current = null;
setIsResizing( false );
setResizeSnapPreview( null );
if ( ! onChangeLayout || ! latest ) {
setTemporaryLayout( undefined );
return;
}

onChangeLayout( latest );
setTemporaryLayout( undefined );
} );

const handleResize = useEvent( ( id: string, delta: ResizeDelta ) => {
if ( ! editMode ) {
return;
}
if ( ! isResizing ) {
setIsResizing( true );
}

const relativeDelta = Math.round(
delta.width / ( columnWidth + gapPx )
);

if ( resizeBaselineRef.current === null ) {
const baseItem = layoutMap.get( id );
const baseWidth =
typeof baseItem?.width === 'number' ? baseItem.width : 1;
resizeBaselineRef.current = baseWidth;
}
const baseline = resizeBaselineRef.current;
const newWidth = Math.max(
1,
Math.min( baseline + relativeDelta, effectiveColumns )
const bounds = widthBoundsByKey.get( id );
const newWidth = clampSpan(
baseline + relativeDelta,
bounds?.minWidth ?? 1,
bounds?.maxWidth ?? effectiveColumns
);

setResizeSnapPreview( {
id,
snap: gridSpanToPixelSize(
newWidth,
1,
columnWidth,
gapPx,
null
),
} );

const pendingItem = latestLayoutRef.current?.find(
( item ) => item.key === id
);
const currentItem = pendingItem ?? layoutMap.get( id );
if ( currentItem && currentItem.width === newWidth ) {
return;
}

const updatedLayout = activeLayout.map( ( item ) =>
item.key === id ? { ...item, width: newWidth } : item
);

latestLayoutRef.current = updatedLayout;
captureLayoutSnapshotRef.current();
setTemporaryLayout( updatedLayout );
onPreviewLayout?.( updatedLayout );
} );

const interacting = activeId !== null || isResizing;

// Drag-overlay clone composition: the surface always wraps with a
// thin functional frame (lift, cursor, pointer pass-through). When
// `renderDragPreview` is supplied, the consumer's wrapper sits
// inside the frame around the cloned children; otherwise the
// cloned children render directly so any persistent chrome on
// them carries through unchanged.
const activeClone = activeId ? childrenMap.get( activeId ) : null;
const DragPreview = renderDragPreview;
const dragOverlayContent =
activeId && activeClone ? (
<div className={ styles[ 'drag-preview-frame' ] }>
<div className={ styles[ 'drag-preview-frame__lift' ] }>
{ DragPreview ? (
<DragPreview itemId={ activeId }>
{ activeClone }
</DragPreview>
) : (
activeClone
) }
</div>
</div>
) : null;

// Edit-mode background. Lanes are content-driven vertically, so
// the overlay mirrors columns only. Rendered unconditionally so
// it can cross-fade on edit-mode toggles (`isActive` drives the
// transition); memoized so drag/resize re-renders skip it.
const Overlay = renderGridOverlay ?? GridOverlay;
const gridOverlay = useMemo(
() => (
<Overlay columns={ effectiveColumns } isActive={ editMode } />
),
[ Overlay, editMode, effectiveColumns ]
);

const layoutFingerprint = useMemo( () => {
const layoutSig = getLayoutFingerprint( activeLayout );
const placementSig = getPlacementFingerprint( itemStyles );
return `${ layoutSig }\0${ placementSig }`;
}, [ activeLayout, itemStyles ] );
const excludeLayoutAnimationKey =
activeId ?? ( isResizing ? resizeSnapPreview?.id : null );
const { captureLayoutSnapshot, getPositionsBeforeLastChange } =
useLayoutShiftAnimation( {
container,
enabled: editMode,
layoutFingerprint,
excludeItemKey: excludeLayoutAnimationKey,
} );
const { exitingItems, clearExitingItem } = useItemExitAnimation( {
container,
enabled: editMode,
layoutKeys,
getPositionsBeforeLastChange,
childrenCacheRef,
} );
const layoutAnimating = editMode;
useLayoutEffect( () => {
captureLayoutSnapshotRef.current = captureLayoutSnapshot;
}, [ captureLayoutSnapshot ] );

return (
<DndContext
sensors={ sensors }
onDragStart={ handleDragStart }
onDragCancel={ handleDragCancel }
onDragMove={ handleDragMove }
onDragEnd={ () => {
persistTemporaryLayout();
lastReorderCursorRef.current = null;
setActiveId( null );
} }
>
<SortableContext items={ items } strategy={ NO_SORT_STRATEGY }>
<div
{ ...divProps }
ref={ mergedRootRef }
className={ clsx(
styles.lanes,
layoutAnimating &&
layoutAnimationStyles[ 'layout-animating' ],
className
) }
data-wp-grid-dragging={ activeId || undefined }
data-wp-grid-resizing={ isResizing || undefined }
style={
{
...style,
gridTemplateColumns: `repeat(${ effectiveColumns }, minmax(0, 1fr))`,
// `column-gap` and `row-gap` are set in
// `lanes.module.css` from the
// design-system gap token, with an
// `@supports` block that zeroes `row-gap`
// in polyfill mode (the skyline already
// encodes vertical spacing in each tile's
// `top`). Driving the toggle from CSS
// keeps SSR and client output identical
// regardless of native support.
'--wp-grid-lane-row-unit': `${ Math.max(
1,
rowUnit
) }px`,
} as React.CSSProperties
}
>
{ gridOverlay }
{ items.map( ( id ) => {
const child = childrenMap.get( id );
if ( ! child ) {
return null;
}
const limitsPx = resizeLimitsByKey.get( id );
return (
<LanesItem
key={ id }
itemKey={ id }
placementStyle={
itemStyles.get( id ) ?? {}
}
disabled={ ! editMode }
interacting={ interacting }
dragging={ activeId !== null }
onResize={ handleResize }
onResizeEnd={ persistTemporaryLayout }
resizeSnapPreview={
resizeSnapPreview?.id === id
? resizeSnapPreview.snap
: null
}
minResizeWidthPx={ minResizeWidthPx }
minResizeWidthPx={
limitsPx?.minWidthPx ?? minResizeWidthPx
}
maxResizeWidthPx={ limitsPx?.maxWidthPx }
actionableArea={ actionableAreaMap.get(
id
) }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the resize start from the size on screen instead of the stored one?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Since 23d42d7, the lanes baseline is the on-screen span: resizeBaselineRef starts from renderedSpanByKey, the same map that feeds placement, so the delta is added to what the user sees.

The bail check uses the same reference: it compares the snap target with the span staged for commit, or with the rendered span when nothing is staged, so a gesture that stays within the rendered span commits nothing.

Covered by two regression tests in dashboard-lanes/test/item-limits.test.tsx: a tile stored at 1 lane that renders at 2 commits width: 3 after one lane to the right, and a gesture that ends within the rendered span produces no onChangeLayout call.

* Per-item width limits in pixels, keyed by layout item key. Height
* limits are ignored; lane heights are content-driven.
*/
itemLimits?: Record< string, GridItemLimits >;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Would it make sense to narrow this to the width axis? DashboardLanes ignores height limits, but TypeScript currently accepts minHeight and maxHeight, which may make developers expect them to work.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in b440755. DashboardLanes now takes GridItemWidthLimits (only minWidth and maxWidth), and GridItemLimits extends it with minHeight and maxHeight for the grid, so a height limit on lanes is a type error.

Both types are exported, and the README documents the split.

bounded spans stay render-only for untouched tiles
baseline and commit check use the span on screen, not the stored width
add GridItemWidthLimits; GridItemLimits extends it with heights
# Conflicts:
#	packages/grid/CHANGELOG.md
#	packages/grid/src/dashboard-grid/index.tsx
@github-actions github-actions Bot added the [Package] Grid /packages/grid label Aug 27, 2026

@chihsuan chihsuan left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @retrofox LGTM! 🚀

I left a few inline notes for docs. None of them are blocking.

Comment thread packages/grid/README.md
<DashboardGrid
layout={ layout }
itemLimits={ {
chart: { minWidth: 320, minHeight: 200 },

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Should the example set a numeric rowHeight? I noticed height limits are skipped when rowHeight is 'auto', which is the default, so minHeight: 200 here may be a no-op.

Comment thread packages/grid/README.md Outdated
retrofox and others added 4 commits August 28, 2026 09:37
@retrofox
retrofox enabled auto-merge (squash) August 28, 2026 08:25
@retrofox
retrofox merged commit dad66c5 into trunk Aug 28, 2026
60 checks passed
@retrofox
retrofox deleted the update/grid-tile-limits branch August 28, 2026 08:55
@github-actions github-actions Bot added this to the Gutenberg 24.0 milestone Aug 28, 2026
wporg-sync pushed a commit to WordPress/WordPress that referenced this pull request Sep 8, 2026
This updates the pinned commit hash of the Gutenberg repository from `658c8cbe89db5d90279051d3bb816ca210fe4646 ` (version `23.8.0`) to `5a4cc502d43016f1efa66f8f6d9bd3bb4bb24d85`, which contains the latest changes up until the Node.js version was bumped from 20.x to 24.x.

A full list of changes included in this commit can be found on GitHub: https://github.com/WordPress/gutenberg/compare/658c8cbe89db5d90279051d3bb816ca210fe4646..5a4cc502d43016f1efa66f8f6d9bd3bb4bb24d85.

- Menu: Stabilize iframe reload test (WordPress/gutenberg#82081)
- Packages: Recover August 26 npm release metadata (WordPress/gutenberg#82085)
- Storybook: Demonstrate light, dark, and nested themes (WordPress/gutenberg#82039)
- Project Management: Add validation for missing GitHub labels configuration (WordPress/gutenberg#76263)
- Automated Testing: Enforce `.tsx` for all new React files (WordPress/gutenberg#80123)
- UI: Use root theme for portaled overlays (WordPress/gutenberg#82038)
- Menu: Stabilize portaled iframe test (WordPress/gutenberg#82092)
- Theme: Move post-release changelog entry to Unreleased (WordPress/gutenberg#82093)
- Media: Refuse a multi-file drop on a placeholder that takes one file (WordPress/gutenberg#82046)
- Font Library: Keep CSS custom properties unquoted in font previews (WordPress/gutenberg#82010)
- Add "reading settings" link to Front Page template (WordPress/gutenberg#81987)
- Docs: Fix grammar in documentation (WordPress/gutenberg#82032)
- Media Editor: refactor the panel layout (WordPress/gutenberg#81840)
- DataViews layout storybook: action names and primary status (WordPress/gutenberg#82104)
- Make `@wordpress/reusable-blocks` a no-op compatibility package (WordPress/gutenberg#79186)
- Widget Dashboard: add a Policy provider to govern what users may do (WordPress/gutenberg#81967)
- Build Package: Restore Core Boot layout compatibility (WordPress/gutenberg#82112)
- Edit Widgets: Guard canInsertBlockInWidgetArea against an empty block list (WordPress/gutenberg#82107)
- Build: Enforce NodeNext resolution for ESM packages (WordPress/gutenberg#82088)
- UI: Remove unused outset-ring--focus-within-visible class. (WordPress/gutenberg#82077)
- Inserter: Keep the block preview inside the viewport (WordPress/gutenberg#82060)
- Test: Enforce Vitest conventions and CI guardrails (WordPress/gutenberg#81040)
- Editor: Apply the showListViewByDefault preference in the shared editor (WordPress/gutenberg#82119)
- Release: Use CLI runtime for package publishing (WordPress/gutenberg#82122)
- Reusable Blocks: Remove leftover merge conflict markers from the changelog (WordPress/gutenberg#82126)
- chore: Update grep settings and remove comments (WordPress/gutenberg#82121)
- Fix: Content Guidelines: UX/UI Accessibility issues (WordPress/gutenberg#80306)
- Extensible Site Editor: Let menu items be added from the navigation screen (WordPress/gutenberg#82125)
- Navigation: Change WCText to Text (WordPress/gutenberg#82130)
- Storybook: Persist sidebar theme in URL (WordPress/gutenberg#82111)
- Editor assets endpoint: prime the theme JSON resolver before capturing (WordPress/gutenberg#82134)
- Remove unused dependencies across the monorepo (WordPress/gutenberg#82103)
- Update changelog instructions for agents (WordPress/gutenberg#82097)
- Build: Normalize JSX file extensions (WordPress/gutenberg#80990)
- Fix pattern export filenames by normalizing special characters (WordPress/gutenberg#77033)
- Site editor v2: navigation takes view config from endpoint (WordPress/gutenberg#82138)
- Extensible Site Editor: Load media assets before the media fields open (WordPress/gutenberg#82135)
- Site editor v2: pages take view config from endpoint (WordPress/gutenberg#82140)
- Extensible site editor: Add a revisions entry point to the Styles route (WordPress/gutenberg#82142)
- List: outdent an empty middle item without adding an item (WordPress/gutenberg#82011)
- Extensible site editor: Fall back to the raw title in edit route document titles (WordPress/gutenberg#82143)
- Blocks: Make BlockTransform a discriminated union over its type (WordPress/gutenberg#81831)
- Theme: add LightningCSS WPDS token fallback plugin (WordPress/gutenberg#80401)
- UI: Add popupWidth prop to item popup components (WordPress/gutenberg#82087)
- Snackbar: Restart timeout when a notice is recreated (WordPress/gutenberg#81764)
- Media: Report server upload failures in plain language (WordPress/gutenberg#81735)
- Format library: visualise non-breaking spaces (WordPress/gutenberg#74040)
- Bump the github-actions group across 1 directory with 2 updates (WordPress/gutenberg#81932)
- Try adding a Grid block variation to Gallery (WordPress/gutenberg#81909)
- Components: Keep foreign items out of a ToolsPanel menu (WordPress/gutenberg#82127)
- Enable Gallery columns and crop controls in viewport states (WordPress/gutenberg#82003)
- Query: Don't write excludeCurrent into blocks that never had it (WordPress/gutenberg#82147)
- Site title: Add fit-text support. (WordPress/gutenberg#82074)
- Accordion Panel: Reset padding-block when panel is hidden (WordPress/gutenberg#81782)
- Replace npm-run-all with native npm script chaining (WordPress/gutenberg#82166)
- Grid: per-item tile size limits (WordPress/gutenberg#81899)
- Fix: Tabs block: Anchor links in tab panels are not functional (WordPress/gutenberg#81744)
- Validate tsconfig references against declared dependencies (WordPress/gutenberg#82106)
- Performance tests: Build once and shard the suites across parallel CI jobs (WordPress/gutenberg#82160)
- Site Health widget: route the body's review link through the host (WordPress/gutenberg#82066)
- Build: allow wp-theme 2.x peer deps (WordPress/gutenberg#82139)
- Bundled packages: Add a check for transitive private API usage (WordPress/gutenberg#82027)
- List: try to preserve indentation level of children after outdenting+merging their parent (WordPress/gutenberg#82145)
- Block Library: Fix categories block handling of invalid taxonomy (WordPress/gutenberg#82153)
- useViewConfig: don't request the view config twice (WordPress/gutenberg#82141)
- Documentation: Remove invalid references to Babel in packages README (WordPress/gutenberg#82176)
- Automated Testing: Fix failing categories test on trunk (WordPress/gutenberg#82182)
- Jest: Update to 30.5.0 (WordPress/gutenberg#82181)
- Fix: Retain focus and caret position after Backspace undo of a prefix block transform (WordPress/gutenberg#82116)
- Scaffold e2e tests for the editor inspector consolidation experiment (WordPress/gutenberg#82178)
- Package lock: Deduplicate Jest dependencies (WordPress/gutenberg#82187)
- Components: Migrate remaining JSX files to TypeScript (WordPress/gutenberg#82132)
- Text: Use pretty wrapping by default (WordPress/gutenberg#82133)
- Build Tooling: Add browserslist config for build targeting (WordPress/gutenberg#82179)
- Columns: Fix appender drop zone visibility when dragging into empty column (WordPress/gutenberg#77852)
- Build: Require explicit JSX file extensions (WordPress/gutenberg#82189)
- Icons: Redraw 35 prominent icons with consistent stroke widths (WordPress/gutenberg#78808)
- Test: Classify and enforce unit test environments by filename (WordPress/gutenberg#80991)
- Test: Fix CI failures on trunk (WordPress/gutenberg#82201)
- Fix editor link colour cascade order for nested blocks (WordPress/gutenberg#77833)
- Add New Template: Show the design system focus ring instead of legacy box-shadow rings (WordPress/gutenberg#82164)
- List View: Don’t focus the last Table cell on keyboard activation (WordPress/gutenberg#81964)
- Fix spelling typos in docs and inline comments (WordPress/gutenberg#82217)
- CI: Pin npm version in the private API check job (WordPress/gutenberg#82222)
- Private APIs: Add back '@wordpress/dataviews' to the allowed core modules (WordPress/gutenberg#82221)
- Editor: Mark __unstableSaveForPreview options as optional (WordPress/gutenberg#81858)
- Docs: Explain when the Cover block's overlay controls appear (WordPress/gutenberg#82234)
- Menu: Use Text for item labels and descriptions (WordPress/gutenberg#82237)
- Test: Type pipe's supported array inputs (WordPress/gutenberg#82210)
- Widget Dashboard: keep the plain anchor for an empty-string download (WordPress/gutenberg#82073)
- CI: Read the required npm version from a single source (WordPress/gutenberg#82235)
- Components: Add `defaultShown`, `onShownChange` to `ToolsPanelItem` (WordPress/gutenberg#78010)
- Storybook: Drop unused support for js-as-jsx (WordPress/gutenberg#82192)
-  Math: Declare clientNavigation interactivity support (WordPress/gutenberg#82248)
- Point to docmumentation about release assets (WordPress/gutenberg#82250)
- Change stray `ubuntu-latest` to `ubuntu-24.04`. (WordPress/gutenberg#82156)
- Performance tests: Reuse the plugin an earlier trunk run already built (WordPress/gutenberg#82223)
- Breadcrumb: Add UI component (WordPress/gutenberg#80425)
- Components: Cut the ToolsPanel render cascade (WordPress/gutenberg#82180)
- Fix: alert dialog box cancel button color (WordPress/gutenberg#82261)
- UI: Add Form best practices Storybook page (WordPress/gutenberg#82197)
- Media: Stop claiming "Upload complete" when upload failed (WordPress/gutenberg#81397)
- Boot, Site Editor: Seed content surfaces with default background color (WordPress/gutenberg#81646)
- Format Library: Fix Edit as HTML crash with background-only highlight (WordPress/gutenberg#82215)
- Remove the template activation experiment (WordPress/gutenberg#82241)
- Packages: Add cross-version compatibility skill (WordPress/gutenberg#81890)
- Show the block outline where the grab cursor shows (WordPress/gutenberg#81798)
- DataViews: Scope the search field's fixed width to the default UI search row (WordPress/gutenberg#82128)
- Background image: support setting the image from a URL (WordPress/gutenberg#82230)
- Border Box Control: Try moving the unlink button to the label row, and rename the panel to Borders (WordPress/gutenberg#82163)
- Widget Dashboard: govern Reset to default through the policy (WordPress/gutenberg#82255)
- Media: Attach unattached media to the current post on save/publish (WordPress/gutenberg#81977)
- Widget Dashboard: make the column count a host decision (WordPress/gutenberg#82204)
- Global styles: resolve theme-relative background image URLs for display (WordPress/gutenberg#82242)
- CI: Unify the automation comments into a single PR comment (WordPress/gutenberg#82249)
- Core Data: Convert hooks tests to renderHook and drop JSX (WordPress/gutenberg#82276)
- Release: Map [Type] Flaky Test to the Tools changelog section (WordPress/gutenberg#82078)
- DateTime tests: freeze the clock in the empty-value calendar test (WordPress/gutenberg#82282)
- DataForm: treat combined form fields purely as layout containers (WordPress/gutenberg#82175)
- Global styles engine: return a copy from getResolvedValue instead of mutating (WordPress/gutenberg#82278)
- Test: Migrate Node unit tests to Vitest (WordPress/gutenberg#82211)
- Icons: Redraw 64 icons to be stroke-based. (WordPress/gutenberg#78812)
- Tests: Fix Vitest setup and stale jest types under non-hoisting installs (WordPress/gutenberg#82290)
- UI: Rename Popup `popupWidth` prop to `width` (WordPress/gutenberg#82193)
- Query: Replace the "Reload full page enabled" modal with a snackbar notice (WordPress/gutenberg#82246)
- Project Management: Update CODEOWNERS to reflect current participation (WordPress/gutenberg#82262)
- Editor: Refactor the Options menu to use the Menu component (WordPress/gutenberg#81564)
- UI: Add Field.VisualLabel (WordPress/gutenberg#82095)
- Update Stylelint to v17 and related packages (WordPress/gutenberg#80738)
- Media: keep indexed PNG sub-sizes indexed (WordPress/gutenberg#81884)
- UI: Add Combobox and Autocomplete Status (WordPress/gutenberg#82195)
- UI: Drop Status and Empty live region test assertions (WordPress/gutenberg#82300)
- Project Management: Remove empty entries from CODEOWNERS (WordPress/gutenberg#82292)
- Documentation: Add recommended bulk suppressions configuration for ESLint (WordPress/gutenberg#82303)
- UI: Hide Input field ring when a slot control is focused (WordPress/gutenberg#82257)
- Widgets: Surface debugging details in the ErrorBoundary (WordPress/gutenberg#82099)
- TypeScript: Migrate format-library package to TS (WordPress/gutenberg#79486)
- BorderBoxControl: Make sure the group of controls is associated with the label (WordPress/gutenberg#82279)
- Image block cropping: Sync image size and link destination settings (WordPress/gutenberg#82316)
- Menu: Support multiple item descriptions (WordPress/gutenberg#81825)
- CI: Do not fail the type label check on a newly opened pull request (WordPress/gutenberg#82322)
- UI: Align overlay Trigger props with Base UI (WordPress/gutenberg#81824)
- Gallery block: Fix 'Crop images to fit' broken in editor (WordPress/gutenberg#82318)
- Widget Dashboard: enforce the policy on the staging layer for every instance operation (WordPress/gutenberg#82256)
- DataForm: Communicate the timezone in the datetime control (WordPress/gutenberg#82291)
- Add more e2e tests for the editor inspector consolidation experiment (WordPress/gutenberg#82239)
- Framework: Error on npm versions older than the required floor (WordPress/gutenberg#82320)
- Document every type for DataViews/DataForm/Field API (WordPress/gutenberg#82326)
- ESLint plugin: remove usage of Babel parser (WordPress/gutenberg#82144)
- DataViews: remove the rich text options from the control config type (WordPress/gutenberg#82330)
- Meta Boxes: Move meta box markup with moveBefore to keep classic editors alive (WordPress/gutenberg#82243)
- Add more e2e tests for the editor inspector consolidation experiment (WordPress/gutenberg#82327)
- Test: Enforce JavaScript test runner ownership (WordPress/gutenberg#82299)
- DataViews: clamp page after delete (WordPress/gutenberg#82244)
- Stylelint tools: Resolve test config extends from the workspace (WordPress/gutenberg#82336)
- Editor: Deprecate the `as` prop of the plugin menu item components (WordPress/gutenberg#82319)
- Icons: Convert Search icon to strokes (WordPress/gutenberg#82338)
- DataViews: Document that table column styles do not apply to the primary column (WordPress/gutenberg#82238)
- DataViews: add hierarchical levels story (WordPress/gutenberg#82344)
- CI: Add "Required changes from trunk" PR check (WordPress/gutenberg#82272)
- feat: Improve TypeScript definitions in the getEntityRecord function (WordPress/gutenberg#81863)
- Framework: Update Node.js to v24 LTS and npm to v11 (WordPress/gutenberg#80395)
- Revert "Framework: Update Node.js to v24 LTS and npm to v11 (WordPress/gutenberg#80395)" (WordPress/gutenberg#82355)
- UI: Remove ValidityIndicator outer margin (WordPress/gutenberg#82267)
- Span the writing flow focus capture elements over the canvas (WordPress/gutenberg#82354)
- Fields: Hide the slug field for posts without a permalink (WordPress/gutenberg#82341)
- Fix: Responsive horizontal orientation does not override a vertical layout. (WordPress/gutenberg#82364)
- View Config: Move the table column style descriptions to the 7.2 compat layer (WordPress/gutenberg#82372)
- Block Library: Allow themes to override the padding added via background color (WordPress/gutenberg#82024)
- DataViews: Append an ellipsis to action labels that open a dialog (WordPress/gutenberg#81994)
- Pages: require authentication and a capability to render generated standalone pages (WordPress/gutenberg#82254)
- Build/Test Tools: Replace npx with npm exec --no (WordPress/gutenberg#82331)
- Navigation: restore flex-grow on container when accessible label is present (WordPress/gutenberg#78447)
- DOM Ready: Exclude src from published package files (WordPress/gutenberg#77302)
- UI: Support custom targets on Link and Menu.LinkItem (WordPress/gutenberg#82347)
- Escape HTML: Exclude src from published package files (WordPress/gutenberg#77304)
- keycodes: Exclude src from published files (WordPress/gutenberg#77306)
- Packages: Exclude src from block-serialization-default-parser published package (WordPress/gutenberg#77307)
- A11y: Exclude src from published package files (WordPress/gutenberg#77309)
- Packages: Exclude src from autop published package (WordPress/gutenberg#77283)
- Blob: Exclude src from published package files (WordPress/gutenberg#77289)
- Packages: Exclude src from deprecated published package (WordPress/gutenberg#77303)
- fix(list-reusable-blocks): exclude src from published files (WordPress/gutenberg#77305)
- Admin UI: Exclude src from published files and sideEffects (WordPress/gutenberg#77285)
- Standardize 'Back' and 'Go back' labels (WordPress/gutenberg#79211)
- Audio: Update autoplay help text for clarity (WordPress/gutenberg#69978)
- Theme JSON: Avoid rebuilding identical block schemas during sanitization (WordPress/gutenberg#82203)
- Docs: Improve setAttributes updater function wording (WordPress/gutenberg#82405)
- Query Loop: Rename the "Keyword" filter to "Search terms" and add help text (WordPress/gutenberg#82387)
- CI: add a per-workflow status check job and filter paths (WordPress/gutenberg#81015)
- ESLint: Thin use-recommended-components tests (WordPress/gutenberg#82407)
- UI: Fix enabled input placeholder contrast (WordPress/gutenberg#82304)
- Tab trap escape hatch: leave/enter the canvas with Escape/Enter (WordPress/gutenberg#82314)
- List: preserve client IDs on indent, just like outdent (WordPress/gutenberg#59216)
- Templates REST API: prevent fatal error when a null template reaches prepare_item_for_response (WordPress/gutenberg#82374)
- Editor: Refactor the View menu to use the Menu component (WordPress/gutenberg#82321)
- CSS Styling: Cleanup focus outline override for grid component (WordPress/gutenberg#82337)
- E2E: Run the site editor suite against the extensible site editor too (WordPress/gutenberg#82117)
- Fields: Remove unused CSS rule from slug field control (WordPress/gutenberg#77961)
- Fix: Footnote IDs can start with a digit and break CSS selectors (WordPress/gutenberg#82398)
- feat: Introduce 'Add Media' in the block controls for media-text block (WordPress/gutenberg#82420)
- fix: Audio element to have it's own name (WordPress/gutenberg#82389)
- Docs: Update Playground CLI wording (WordPress/gutenberg#77579)
- Upgrade React 19 to 19.2.8, read version from package.json (WordPress/gutenberg#82439)
- Tab Panel: Show focus with outline instead of legacy box-shadow ring (WordPress/gutenberg#82421)
- Editor: Fix disabled and link items of the more menu adapter (WordPress/gutenberg#82428)
- UI: Fix Menu.LinkItem target detection (WordPress/gutenberg#82442)
- SearchableSelect: Add form primitive to @wordpress/ui (WordPress/gutenberg#80961)
- Validated form controls: Use design token for invalid focus rings (WordPress/gutenberg#82410)
- feat: Update layout icons (WordPress/gutenberg#82025)
- Widgets: Replace {TODO} placeholder with PR number in changelog entries (WordPress/gutenberg#82450)
- UI: Remove ESLint ref suppressions and refactor AlertDialog state (WordPress/gutenberg#82131)
- Menu: Align selection indicators and prefix icons with item labels (WordPress/gutenberg#82346)
- ToggleGroupControl: Honor the root disabled prop (WordPress/gutenberg#82259)
- Block Library: Remove the form blocks experiment (WordPress/gutenberg#82451)
- List: indent and outdent multi-selected items with Tab (WordPress/gutenberg#82411)
- CodeRabbit: Only review on request, keep PR descriptions untouched (WordPress/gutenberg#82448)
- Privacy policy page badge: make it visible in Site Editor > Pages and Editor Inspector (WordPress/gutenberg#82422)
- Support `isAny` and `isNone` filter operators for numeric fields. (WordPress/gutenberg#77942)
- UI: Keep focus rings visible in overlay content (WordPress/gutenberg#82443)
- Menu: Correct alignment documentation and changelog (WordPress/gutenberg#82455)
- UI: Name unlabeled form primitive stories (WordPress/gutenberg#82311)
- UI: Re-read the native validity on blur in ControlWithError (WordPress/gutenberg#82376)
- Media Utils: Preserve arrays in multipart form data (WordPress/gutenberg#82353)
- fix: Remove stories from performance tests (WordPress/gutenberg#82459)
- Add the template e2e tests for the editor inspector consolidation experiment (WordPress/gutenberg#82394)
- UI: Fix Link target detection (WordPress/gutenberg#82447)
- Fix privacy policy page setting unit test on multisite (WordPress/gutenberg#82467)
- UI, DataViews: Give input and selection controls solid backgrounds (WordPress/gutenberg#82391)
- Fix LocationPicker popover alignment with InputGroup (WordPress/gutenberg#82090)
- List: indent and outdent a fully selected list item with Tab (WordPress/gutenberg#82460)
- UI: Fix Minimal Select popup width (WordPress/gutenberg#82461)
- Rich text: resolve owned event listeners once instead of per subscriber (WordPress/gutenberg#80605)
- UI: Keep borderless InputLayout chrome when disabled (WordPress/gutenberg#82468)
- Theme: Cache luminance for contrast checks (WordPress/gutenberg#82445)
- Add promptfoo for testing agent development setup and skills (WordPress/gutenberg#80812)
- wp-env: Fix Docker build errors with Debian Bullseye repositories (WordPress/gutenberg#82478)
- Query Loop: Fall back to `post` when the query has no `postType` (WordPress/gutenberg#82465)
- Block Supports: Bail early in state styles when a block has no style attribute (WordPress/gutenberg#81908)
- CSS Styling: Represent focus with outline for list view component (WordPress/gutenberg#82129)
- Icons: Remove the obsolete forced colors mode hack (WordPress/gutenberg#82481)
- Docs: Remove invalid `@return` tag from the `block_core_social_link_get_services` hook docblock (WordPress/gutenberg#82488)
- Site Editor: Add a root error boundary to prevent a blank screen (WordPress/gutenberg#82486)
- Writing Flow: Bail out of Enter handling when no block is selected (WordPress/gutenberg#82424)
- Docs: Remove the blank line between @PARAM and @return in test docblocks (WordPress/gutenberg#81907)
- List View: Disable block icon colors while the block is selected (WordPress/gutenberg#82498)
- Fix: Restore layout styles for block style variations (WordPress/gutenberg#82335)
- Post slug: prefer server sanitization in the editor (WordPress/gutenberg#78135)
- Media Modal Experiment: Allow filtering by attached to the post, or unattached (WordPress/gutenberg#81974)
- Media editor: Lock the editing tools while a save is running (WordPress/gutenberg#82417)
- DataViews: Restrict the isAll filter operator to array fields (WordPress/gutenberg#82463)
- Media Fields: Pass the field's disabled state to the textarea controls (WordPress/gutenberg#82516)
- Theme: Make chroma capacity caching deterministic (WordPress/gutenberg#82505)
- Global Styles: Remove the color randomizer experiment (WordPress/gutenberg#82452)
- DataViews: Fix Field.sort TypeScript type definition (WordPress/gutenberg#82162)
- Migrate __experimentalText and __experimentalHeading to @wordpress/ui Text in Inspector Popover Header (WordPress/gutenberg#77449)
- Migrate __experimentalText to @wordpress/ui Text in Block switcher bindings hint (WordPress/gutenberg#77366)
- UI: Add Radio form primitive (WordPress/gutenberg#82214)
- Migrate __experimentalText to @wordpress/ui Text in Allowed Blocks Modal (WordPress/gutenberg#78119)
- UI: Add CheckboxControl (WordPress/gutenberg#82213)
- Core Data: Scope revisionId to the entity it is provided for (WordPress/gutenberg#82517)
- Migrate __experimentalText to @wordpress/ui Text in Pattern Overrides Dropdown (WordPress/gutenberg#77492)
- ProgressBar: Respect reduced motion preferences (WordPress/gutenberg#82490)
- Theme: Clarify color seed and warning guarantees (WordPress/gutenberg#82526)
- Theme: Build default ramps before token artifacts (WordPress/gutenberg#82525)
- CodeRabbit: Disable the review status comment (WordPress/gutenberg#82534)
- Project Management: Restore CODEOWNERS exclusions for suppression lists (WordPress/gutenberg#82538)
- Editor: merge duplicate "Enhancements" changelog sections (WordPress/gutenberg#82533)
- UI: Validate compound component context (WordPress/gutenberg#82510)
- Docs: Document isolated worktree setup (WordPress/gutenberg#82524)
- DataForm: render untyped fields without an edit control that are read-only (WordPress/gutenberg#82514)
- API Fetch: Add unregister to remove a registered middleware (WordPress/gutenberg#82408)
- Editor: Show revision data in Post Title component (WordPress/gutenberg#82536)
- Components: Add compound component composition diagnostics (WordPress/gutenberg#82509)
- Fieldset: Fix gap token to match spec (WordPress/gutenberg#75479)
- Test: Migrate deterministic JSDOM tests and harden Vitest isolation (WordPress/gutenberg#82212)
- Release: Make npm publication verification resumable (WordPress/gutenberg#82089)
- Block Library: Use 'useEntityProp' in Site Title and Tagline blocks (WordPress/gutenberg#82518)
- Theme: Migrate Terrazzo modes to resolvers (WordPress/gutenberg#82537)
- Fix auto-capitalization after Enter on iOS Safari (WordPress/gutenberg#82475)
- Update navigation link block name, use a default variation for custom link (WordPress/gutenberg#82375)
- API Fetch: Expose defaultFetchHandler so overrides can restore it (WordPress/gutenberg#82553)
- Core Data: Return a stable record for equivalent `_fields` queries (WordPress/gutenberg#82552)
- API Fetch: Rename defaultFetchHandler parameter to options (WordPress/gutenberg#82588)
- Fields: remove unused custom sort from the author fields (WordPress/gutenberg#82559)
- Fields: Declare the caption, alt text, description and ping status controls through the Field API (WordPress/gutenberg#82539)
- Docs: Align @PARAM tag columns in phpunit/ docblocks (WordPress/gutenberg#81904)
- Docs: Align @PARAM tag columns in lib/ docblocks (WordPress/gutenberg#81902)
- Docs: Align @PARAM tag columns in packages/ docblocks (WordPress/gutenberg#81903)
- Fix help paragraph color contrast ratio in the Connectors page. (WordPress/gutenberg#82378)
- DataViews: Declare transitive dependencies imported by the ./wp bundle (WordPress/gutenberg#81843)
- DataForm: Add a `showPlaceholderIfEmpty` option to the panel layout (WordPress/gutenberg#82527)
- Global Styles UI: migrate revisions Active Badge to UI Badge (WordPress/gutenberg#82560)
- UI: Put overflow on Select.List instead of a nested wrapper (WordPress/gutenberg#82470)
- Editor: migrate post-card-panel Badge to UI Badge (WordPress/gutenberg#82500)
- Fields: migrate page-title Badge to UI Badge (WordPress/gutenberg#82499)
- DataViews: Skip table columns for field ids without a field definition (WordPress/gutenberg#82601)
- View config: remove stale fields from the wp_template default view (WordPress/gutenberg#82602)
- Rich text: restore contenteditable on pointercancel (WordPress/gutenberg#82598)
- Components: Add an Emotion-to-SCSS migration skill and guide (WordPress/gutenberg#82567)

See #66070.
Built from https://develop.svn.wordpress.org/trunk@63538


git-svn-id: http://core.svn.wordpress.org/trunk@62714 1a063a9b-81f0-0310-95a4-ce76da25c4cd
desrosj added a commit to desrosj/wordpress-develop that referenced this pull request Sep 8, 2026
This updates the pinned commit hash of the Gutenberg repository from `658c8cbe89db5d90279051d3bb816ca210fe4646 ` (version `23.8.0`) to `5a4cc502d43016f1efa66f8f6d9bd3bb4bb24d85`, which contains the latest changes up until the Node.js version was bumped from 20.x to 24.x.

A full list of changes included in this commit can be found on GitHub: https://github.com/WordPress/gutenberg/compare/658c8cbe89db5d90279051d3bb816ca210fe4646..5a4cc502d43016f1efa66f8f6d9bd3bb4bb24d85.

- Menu: Stabilize iframe reload test (WordPress/gutenberg#82081)
- Packages: Recover August 26 npm release metadata (WordPress/gutenberg#82085)
- Storybook: Demonstrate light, dark, and nested themes (WordPress/gutenberg#82039)
- Project Management: Add validation for missing GitHub labels configuration (WordPress/gutenberg#76263)
- Automated Testing: Enforce `.tsx` for all new React files (WordPress/gutenberg#80123)
- UI: Use root theme for portaled overlays (WordPress/gutenberg#82038)
- Menu: Stabilize portaled iframe test (WordPress/gutenberg#82092)
- Theme: Move post-release changelog entry to Unreleased (WordPress/gutenberg#82093)
- Media: Refuse a multi-file drop on a placeholder that takes one file (WordPress/gutenberg#82046)
- Font Library: Keep CSS custom properties unquoted in font previews (WordPress/gutenberg#82010)
- Add "reading settings" link to Front Page template (WordPress/gutenberg#81987)
- Docs: Fix grammar in documentation (WordPress/gutenberg#82032)
- Media Editor: refactor the panel layout (WordPress/gutenberg#81840)
- DataViews layout storybook: action names and primary status (WordPress/gutenberg#82104)
- Make `@wordpress/reusable-blocks` a no-op compatibility package (WordPress/gutenberg#79186)
- Widget Dashboard: add a Policy provider to govern what users may do (WordPress/gutenberg#81967)
- Build Package: Restore Core Boot layout compatibility (WordPress/gutenberg#82112)
- Edit Widgets: Guard canInsertBlockInWidgetArea against an empty block list (WordPress/gutenberg#82107)
- Build: Enforce NodeNext resolution for ESM packages (WordPress/gutenberg#82088)
- UI: Remove unused outset-ring--focus-within-visible class. (WordPress/gutenberg#82077)
- Inserter: Keep the block preview inside the viewport (WordPress/gutenberg#82060)
- Test: Enforce Vitest conventions and CI guardrails (WordPress/gutenberg#81040)
- Editor: Apply the showListViewByDefault preference in the shared editor (WordPress/gutenberg#82119)
- Release: Use CLI runtime for package publishing (WordPress/gutenberg#82122)
- Reusable Blocks: Remove leftover merge conflict markers from the changelog (WordPress/gutenberg#82126)
- chore: Update grep settings and remove comments (WordPress/gutenberg#82121)
- Fix: Content Guidelines: UX/UI Accessibility issues (WordPress/gutenberg#80306)
- Extensible Site Editor: Let menu items be added from the navigation screen (WordPress/gutenberg#82125)
- Navigation: Change WCText to Text (WordPress/gutenberg#82130)
- Storybook: Persist sidebar theme in URL (WordPress/gutenberg#82111)
- Editor assets endpoint: prime the theme JSON resolver before capturing (WordPress/gutenberg#82134)
- Remove unused dependencies across the monorepo (WordPress/gutenberg#82103)
- Update changelog instructions for agents (WordPress/gutenberg#82097)
- Build: Normalize JSX file extensions (WordPress/gutenberg#80990)
- Fix pattern export filenames by normalizing special characters (WordPress/gutenberg#77033)
- Site editor v2: navigation takes view config from endpoint (WordPress/gutenberg#82138)
- Extensible Site Editor: Load media assets before the media fields open (WordPress/gutenberg#82135)
- Site editor v2: pages take view config from endpoint (WordPress/gutenberg#82140)
- Extensible site editor: Add a revisions entry point to the Styles route (WordPress/gutenberg#82142)
- List: outdent an empty middle item without adding an item (WordPress/gutenberg#82011)
- Extensible site editor: Fall back to the raw title in edit route document titles (WordPress/gutenberg#82143)
- Blocks: Make BlockTransform a discriminated union over its type (WordPress/gutenberg#81831)
- Theme: add LightningCSS WPDS token fallback plugin (WordPress/gutenberg#80401)
- UI: Add popupWidth prop to item popup components (WordPress/gutenberg#82087)
- Snackbar: Restart timeout when a notice is recreated (WordPress/gutenberg#81764)
- Media: Report server upload failures in plain language (WordPress/gutenberg#81735)
- Format library: visualise non-breaking spaces (WordPress/gutenberg#74040)
- Bump the github-actions group across 1 directory with 2 updates (WordPress/gutenberg#81932)
- Try adding a Grid block variation to Gallery (WordPress/gutenberg#81909)
- Components: Keep foreign items out of a ToolsPanel menu (WordPress/gutenberg#82127)
- Enable Gallery columns and crop controls in viewport states (WordPress/gutenberg#82003)
- Query: Don't write excludeCurrent into blocks that never had it (WordPress/gutenberg#82147)
- Site title: Add fit-text support. (WordPress/gutenberg#82074)
- Accordion Panel: Reset padding-block when panel is hidden (WordPress/gutenberg#81782)
- Replace npm-run-all with native npm script chaining (WordPress/gutenberg#82166)
- Grid: per-item tile size limits (WordPress/gutenberg#81899)
- Fix: Tabs block: Anchor links in tab panels are not functional (WordPress/gutenberg#81744)
- Validate tsconfig references against declared dependencies (WordPress/gutenberg#82106)
- Performance tests: Build once and shard the suites across parallel CI jobs (WordPress/gutenberg#82160)
- Site Health widget: route the body's review link through the host (WordPress/gutenberg#82066)
- Build: allow wp-theme 2.x peer deps (WordPress/gutenberg#82139)
- Bundled packages: Add a check for transitive private API usage (WordPress/gutenberg#82027)
- List: try to preserve indentation level of children after outdenting+merging their parent (WordPress/gutenberg#82145)
- Block Library: Fix categories block handling of invalid taxonomy (WordPress/gutenberg#82153)
- useViewConfig: don't request the view config twice (WordPress/gutenberg#82141)
- Documentation: Remove invalid references to Babel in packages README (WordPress/gutenberg#82176)
- Automated Testing: Fix failing categories test on trunk (WordPress/gutenberg#82182)
- Jest: Update to 30.5.0 (WordPress/gutenberg#82181)
- Fix: Retain focus and caret position after Backspace undo of a prefix block transform (WordPress/gutenberg#82116)
- Scaffold e2e tests for the editor inspector consolidation experiment (WordPress/gutenberg#82178)
- Package lock: Deduplicate Jest dependencies (WordPress/gutenberg#82187)
- Components: Migrate remaining JSX files to TypeScript (WordPress/gutenberg#82132)
- Text: Use pretty wrapping by default (WordPress/gutenberg#82133)
- Build Tooling: Add browserslist config for build targeting (WordPress/gutenberg#82179)
- Columns: Fix appender drop zone visibility when dragging into empty column (WordPress/gutenberg#77852)
- Build: Require explicit JSX file extensions (WordPress/gutenberg#82189)
- Icons: Redraw 35 prominent icons with consistent stroke widths (WordPress/gutenberg#78808)
- Test: Classify and enforce unit test environments by filename (WordPress/gutenberg#80991)
- Test: Fix CI failures on trunk (WordPress/gutenberg#82201)
- Fix editor link colour cascade order for nested blocks (WordPress/gutenberg#77833)
- Add New Template: Show the design system focus ring instead of legacy box-shadow rings (WordPress/gutenberg#82164)
- List View: Don’t focus the last Table cell on keyboard activation (WordPress/gutenberg#81964)
- Fix spelling typos in docs and inline comments (WordPress/gutenberg#82217)
- CI: Pin npm version in the private API check job (WordPress/gutenberg#82222)
- Private APIs: Add back '@wordpress/dataviews' to the allowed core modules (WordPress/gutenberg#82221)
- Editor: Mark __unstableSaveForPreview options as optional (WordPress/gutenberg#81858)
- Docs: Explain when the Cover block's overlay controls appear (WordPress/gutenberg#82234)
- Menu: Use Text for item labels and descriptions (WordPress/gutenberg#82237)
- Test: Type pipe's supported array inputs (WordPress/gutenberg#82210)
- Widget Dashboard: keep the plain anchor for an empty-string download (WordPress/gutenberg#82073)
- CI: Read the required npm version from a single source (WordPress/gutenberg#82235)
- Components: Add `defaultShown`, `onShownChange` to `ToolsPanelItem` (WordPress/gutenberg#78010)
- Storybook: Drop unused support for js-as-jsx (WordPress/gutenberg#82192)
-  Math: Declare clientNavigation interactivity support (WordPress/gutenberg#82248)
- Point to docmumentation about release assets (WordPress/gutenberg#82250)
- Change stray `ubuntu-latest` to `ubuntu-24.04`. (WordPress/gutenberg#82156)
- Performance tests: Reuse the plugin an earlier trunk run already built (WordPress/gutenberg#82223)
- Breadcrumb: Add UI component (WordPress/gutenberg#80425)
- Components: Cut the ToolsPanel render cascade (WordPress/gutenberg#82180)
- Fix: alert dialog box cancel button color (WordPress/gutenberg#82261)
- UI: Add Form best practices Storybook page (WordPress/gutenberg#82197)
- Media: Stop claiming "Upload complete" when upload failed (WordPress/gutenberg#81397)
- Boot, Site Editor: Seed content surfaces with default background color (WordPress/gutenberg#81646)
- Format Library: Fix Edit as HTML crash with background-only highlight (WordPress/gutenberg#82215)
- Remove the template activation experiment (WordPress/gutenberg#82241)
- Packages: Add cross-version compatibility skill (WordPress/gutenberg#81890)
- Show the block outline where the grab cursor shows (WordPress/gutenberg#81798)
- DataViews: Scope the search field's fixed width to the default UI search row (WordPress/gutenberg#82128)
- Background image: support setting the image from a URL (WordPress/gutenberg#82230)
- Border Box Control: Try moving the unlink button to the label row, and rename the panel to Borders (WordPress/gutenberg#82163)
- Widget Dashboard: govern Reset to default through the policy (WordPress/gutenberg#82255)
- Media: Attach unattached media to the current post on save/publish (WordPress/gutenberg#81977)
- Widget Dashboard: make the column count a host decision (WordPress/gutenberg#82204)
- Global styles: resolve theme-relative background image URLs for display (WordPress/gutenberg#82242)
- CI: Unify the automation comments into a single PR comment (WordPress/gutenberg#82249)
- Core Data: Convert hooks tests to renderHook and drop JSX (WordPress/gutenberg#82276)
- Release: Map [Type] Flaky Test to the Tools changelog section (WordPress/gutenberg#82078)
- DateTime tests: freeze the clock in the empty-value calendar test (WordPress/gutenberg#82282)
- DataForm: treat combined form fields purely as layout containers (WordPress/gutenberg#82175)
- Global styles engine: return a copy from getResolvedValue instead of mutating (WordPress/gutenberg#82278)
- Test: Migrate Node unit tests to Vitest (WordPress/gutenberg#82211)
- Icons: Redraw 64 icons to be stroke-based. (WordPress/gutenberg#78812)
- Tests: Fix Vitest setup and stale jest types under non-hoisting installs (WordPress/gutenberg#82290)
- UI: Rename Popup `popupWidth` prop to `width` (WordPress/gutenberg#82193)
- Query: Replace the "Reload full page enabled" modal with a snackbar notice (WordPress/gutenberg#82246)
- Project Management: Update CODEOWNERS to reflect current participation (WordPress/gutenberg#82262)
- Editor: Refactor the Options menu to use the Menu component (WordPress/gutenberg#81564)
- UI: Add Field.VisualLabel (WordPress/gutenberg#82095)
- Update Stylelint to v17 and related packages (WordPress/gutenberg#80738)
- Media: keep indexed PNG sub-sizes indexed (WordPress/gutenberg#81884)
- UI: Add Combobox and Autocomplete Status (WordPress/gutenberg#82195)
- UI: Drop Status and Empty live region test assertions (WordPress/gutenberg#82300)
- Project Management: Remove empty entries from CODEOWNERS (WordPress/gutenberg#82292)
- Documentation: Add recommended bulk suppressions configuration for ESLint (WordPress/gutenberg#82303)
- UI: Hide Input field ring when a slot control is focused (WordPress/gutenberg#82257)
- Widgets: Surface debugging details in the ErrorBoundary (WordPress/gutenberg#82099)
- TypeScript: Migrate format-library package to TS (WordPress/gutenberg#79486)
- BorderBoxControl: Make sure the group of controls is associated with the label (WordPress/gutenberg#82279)
- Image block cropping: Sync image size and link destination settings (WordPress/gutenberg#82316)
- Menu: Support multiple item descriptions (WordPress/gutenberg#81825)
- CI: Do not fail the type label check on a newly opened pull request (WordPress/gutenberg#82322)
- UI: Align overlay Trigger props with Base UI (WordPress/gutenberg#81824)
- Gallery block: Fix 'Crop images to fit' broken in editor (WordPress/gutenberg#82318)
- Widget Dashboard: enforce the policy on the staging layer for every instance operation (WordPress/gutenberg#82256)
- DataForm: Communicate the timezone in the datetime control (WordPress/gutenberg#82291)
- Add more e2e tests for the editor inspector consolidation experiment (WordPress/gutenberg#82239)
- Framework: Error on npm versions older than the required floor (WordPress/gutenberg#82320)
- Document every type for DataViews/DataForm/Field API (WordPress/gutenberg#82326)
- ESLint plugin: remove usage of Babel parser (WordPress/gutenberg#82144)
- DataViews: remove the rich text options from the control config type (WordPress/gutenberg#82330)
- Meta Boxes: Move meta box markup with moveBefore to keep classic editors alive (WordPress/gutenberg#82243)
- Add more e2e tests for the editor inspector consolidation experiment (WordPress/gutenberg#82327)
- Test: Enforce JavaScript test runner ownership (WordPress/gutenberg#82299)
- DataViews: clamp page after delete (WordPress/gutenberg#82244)
- Stylelint tools: Resolve test config extends from the workspace (WordPress/gutenberg#82336)
- Editor: Deprecate the `as` prop of the plugin menu item components (WordPress/gutenberg#82319)
- Icons: Convert Search icon to strokes (WordPress/gutenberg#82338)
- DataViews: Document that table column styles do not apply to the primary column (WordPress/gutenberg#82238)
- DataViews: add hierarchical levels story (WordPress/gutenberg#82344)
- CI: Add "Required changes from trunk" PR check (WordPress/gutenberg#82272)
- feat: Improve TypeScript definitions in the getEntityRecord function (WordPress/gutenberg#81863)
- Framework: Update Node.js to v24 LTS and npm to v11 (WordPress/gutenberg#80395)
- Revert "Framework: Update Node.js to v24 LTS and npm to v11 (WordPress/gutenberg#80395)" (WordPress/gutenberg#82355)
- UI: Remove ValidityIndicator outer margin (WordPress/gutenberg#82267)
- Span the writing flow focus capture elements over the canvas (WordPress/gutenberg#82354)
- Fields: Hide the slug field for posts without a permalink (WordPress/gutenberg#82341)
- Fix: Responsive horizontal orientation does not override a vertical layout. (WordPress/gutenberg#82364)
- View Config: Move the table column style descriptions to the 7.2 compat layer (WordPress/gutenberg#82372)
- Block Library: Allow themes to override the padding added via background color (WordPress/gutenberg#82024)
- DataViews: Append an ellipsis to action labels that open a dialog (WordPress/gutenberg#81994)
- Pages: require authentication and a capability to render generated standalone pages (WordPress/gutenberg#82254)
- Build/Test Tools: Replace npx with npm exec --no (WordPress/gutenberg#82331)
- Navigation: restore flex-grow on container when accessible label is present (WordPress/gutenberg#78447)
- DOM Ready: Exclude src from published package files (WordPress/gutenberg#77302)
- UI: Support custom targets on Link and Menu.LinkItem (WordPress/gutenberg#82347)
- Escape HTML: Exclude src from published package files (WordPress/gutenberg#77304)
- keycodes: Exclude src from published files (WordPress/gutenberg#77306)
- Packages: Exclude src from block-serialization-default-parser published package (WordPress/gutenberg#77307)
- A11y: Exclude src from published package files (WordPress/gutenberg#77309)
- Packages: Exclude src from autop published package (WordPress/gutenberg#77283)
- Blob: Exclude src from published package files (WordPress/gutenberg#77289)
- Packages: Exclude src from deprecated published package (WordPress/gutenberg#77303)
- fix(list-reusable-blocks): exclude src from published files (WordPress/gutenberg#77305)
- Admin UI: Exclude src from published files and sideEffects (WordPress/gutenberg#77285)
- Standardize 'Back' and 'Go back' labels (WordPress/gutenberg#79211)
- Audio: Update autoplay help text for clarity (WordPress/gutenberg#69978)
- Theme JSON: Avoid rebuilding identical block schemas during sanitization (WordPress/gutenberg#82203)
- Docs: Improve setAttributes updater function wording (WordPress/gutenberg#82405)
- Query Loop: Rename the "Keyword" filter to "Search terms" and add help text (WordPress/gutenberg#82387)
- CI: add a per-workflow status check job and filter paths (WordPress/gutenberg#81015)
- ESLint: Thin use-recommended-components tests (WordPress/gutenberg#82407)
- UI: Fix enabled input placeholder contrast (WordPress/gutenberg#82304)
- Tab trap escape hatch: leave/enter the canvas with Escape/Enter (WordPress/gutenberg#82314)
- List: preserve client IDs on indent, just like outdent (WordPress/gutenberg#59216)
- Templates REST API: prevent fatal error when a null template reaches prepare_item_for_response (WordPress/gutenberg#82374)
- Editor: Refactor the View menu to use the Menu component (WordPress/gutenberg#82321)
- CSS Styling: Cleanup focus outline override for grid component (WordPress/gutenberg#82337)
- E2E: Run the site editor suite against the extensible site editor too (WordPress/gutenberg#82117)
- Fields: Remove unused CSS rule from slug field control (WordPress/gutenberg#77961)
- Fix: Footnote IDs can start with a digit and break CSS selectors (WordPress/gutenberg#82398)
- feat: Introduce 'Add Media' in the block controls for media-text block (WordPress/gutenberg#82420)
- fix: Audio element to have it's own name (WordPress/gutenberg#82389)
- Docs: Update Playground CLI wording (WordPress/gutenberg#77579)
- Upgrade React 19 to 19.2.8, read version from package.json (WordPress/gutenberg#82439)
- Tab Panel: Show focus with outline instead of legacy box-shadow ring (WordPress/gutenberg#82421)
- Editor: Fix disabled and link items of the more menu adapter (WordPress/gutenberg#82428)
- UI: Fix Menu.LinkItem target detection (WordPress/gutenberg#82442)
- SearchableSelect: Add form primitive to @wordpress/ui (WordPress/gutenberg#80961)
- Validated form controls: Use design token for invalid focus rings (WordPress/gutenberg#82410)
- feat: Update layout icons (WordPress/gutenberg#82025)
- Widgets: Replace {TODO} placeholder with PR number in changelog entries (WordPress/gutenberg#82450)
- UI: Remove ESLint ref suppressions and refactor AlertDialog state (WordPress/gutenberg#82131)
- Menu: Align selection indicators and prefix icons with item labels (WordPress/gutenberg#82346)
- ToggleGroupControl: Honor the root disabled prop (WordPress/gutenberg#82259)
- Block Library: Remove the form blocks experiment (WordPress/gutenberg#82451)
- List: indent and outdent multi-selected items with Tab (WordPress/gutenberg#82411)
- CodeRabbit: Only review on request, keep PR descriptions untouched (WordPress/gutenberg#82448)
- Privacy policy page badge: make it visible in Site Editor > Pages and Editor Inspector (WordPress/gutenberg#82422)
- Support `isAny` and `isNone` filter operators for numeric fields. (WordPress/gutenberg#77942)
- UI: Keep focus rings visible in overlay content (WordPress/gutenberg#82443)
- Menu: Correct alignment documentation and changelog (WordPress/gutenberg#82455)
- UI: Name unlabeled form primitive stories (WordPress/gutenberg#82311)
- UI: Re-read the native validity on blur in ControlWithError (WordPress/gutenberg#82376)
- Media Utils: Preserve arrays in multipart form data (WordPress/gutenberg#82353)
- fix: Remove stories from performance tests (WordPress/gutenberg#82459)
- Add the template e2e tests for the editor inspector consolidation experiment (WordPress/gutenberg#82394)
- UI: Fix Link target detection (WordPress/gutenberg#82447)
- Fix privacy policy page setting unit test on multisite (WordPress/gutenberg#82467)
- UI, DataViews: Give input and selection controls solid backgrounds (WordPress/gutenberg#82391)
- Fix LocationPicker popover alignment with InputGroup (WordPress/gutenberg#82090)
- List: indent and outdent a fully selected list item with Tab (WordPress/gutenberg#82460)
- UI: Fix Minimal Select popup width (WordPress/gutenberg#82461)
- Rich text: resolve owned event listeners once instead of per subscriber (WordPress/gutenberg#80605)
- UI: Keep borderless InputLayout chrome when disabled (WordPress/gutenberg#82468)
- Theme: Cache luminance for contrast checks (WordPress/gutenberg#82445)
- Add promptfoo for testing agent development setup and skills (WordPress/gutenberg#80812)
- wp-env: Fix Docker build errors with Debian Bullseye repositories (WordPress/gutenberg#82478)
- Query Loop: Fall back to `post` when the query has no `postType` (WordPress/gutenberg#82465)
- Block Supports: Bail early in state styles when a block has no style attribute (WordPress/gutenberg#81908)
- CSS Styling: Represent focus with outline for list view component (WordPress/gutenberg#82129)
- Icons: Remove the obsolete forced colors mode hack (WordPress/gutenberg#82481)
- Docs: Remove invalid `@return` tag from the `block_core_social_link_get_services` hook docblock (WordPress/gutenberg#82488)
- Site Editor: Add a root error boundary to prevent a blank screen (WordPress/gutenberg#82486)
- Writing Flow: Bail out of Enter handling when no block is selected (WordPress/gutenberg#82424)
- Docs: Remove the blank line between @PARAM and @return in test docblocks (WordPress/gutenberg#81907)
- List View: Disable block icon colors while the block is selected (WordPress/gutenberg#82498)
- Fix: Restore layout styles for block style variations (WordPress/gutenberg#82335)
- Post slug: prefer server sanitization in the editor (WordPress/gutenberg#78135)
- Media Modal Experiment: Allow filtering by attached to the post, or unattached (WordPress/gutenberg#81974)
- Media editor: Lock the editing tools while a save is running (WordPress/gutenberg#82417)
- DataViews: Restrict the isAll filter operator to array fields (WordPress/gutenberg#82463)
- Media Fields: Pass the field's disabled state to the textarea controls (WordPress/gutenberg#82516)
- Theme: Make chroma capacity caching deterministic (WordPress/gutenberg#82505)
- Global Styles: Remove the color randomizer experiment (WordPress/gutenberg#82452)
- DataViews: Fix Field.sort TypeScript type definition (WordPress/gutenberg#82162)
- Migrate __experimentalText and __experimentalHeading to @wordpress/ui Text in Inspector Popover Header (WordPress/gutenberg#77449)
- Migrate __experimentalText to @wordpress/ui Text in Block switcher bindings hint (WordPress/gutenberg#77366)
- UI: Add Radio form primitive (WordPress/gutenberg#82214)
- Migrate __experimentalText to @wordpress/ui Text in Allowed Blocks Modal (WordPress/gutenberg#78119)
- UI: Add CheckboxControl (WordPress/gutenberg#82213)
- Core Data: Scope revisionId to the entity it is provided for (WordPress/gutenberg#82517)
- Migrate __experimentalText to @wordpress/ui Text in Pattern Overrides Dropdown (WordPress/gutenberg#77492)
- ProgressBar: Respect reduced motion preferences (WordPress/gutenberg#82490)
- Theme: Clarify color seed and warning guarantees (WordPress/gutenberg#82526)
- Theme: Build default ramps before token artifacts (WordPress/gutenberg#82525)
- CodeRabbit: Disable the review status comment (WordPress/gutenberg#82534)
- Project Management: Restore CODEOWNERS exclusions for suppression lists (WordPress/gutenberg#82538)
- Editor: merge duplicate "Enhancements" changelog sections (WordPress/gutenberg#82533)
- UI: Validate compound component context (WordPress/gutenberg#82510)
- Docs: Document isolated worktree setup (WordPress/gutenberg#82524)
- DataForm: render untyped fields without an edit control that are read-only (WordPress/gutenberg#82514)
- API Fetch: Add unregister to remove a registered middleware (WordPress/gutenberg#82408)
- Editor: Show revision data in Post Title component (WordPress/gutenberg#82536)
- Components: Add compound component composition diagnostics (WordPress/gutenberg#82509)
- Fieldset: Fix gap token to match spec (WordPress/gutenberg#75479)
- Test: Migrate deterministic JSDOM tests and harden Vitest isolation (WordPress/gutenberg#82212)
- Release: Make npm publication verification resumable (WordPress/gutenberg#82089)
- Block Library: Use 'useEntityProp' in Site Title and Tagline blocks (WordPress/gutenberg#82518)
- Theme: Migrate Terrazzo modes to resolvers (WordPress/gutenberg#82537)
- Fix auto-capitalization after Enter on iOS Safari (WordPress/gutenberg#82475)
- Update navigation link block name, use a default variation for custom link (WordPress/gutenberg#82375)
- API Fetch: Expose defaultFetchHandler so overrides can restore it (WordPress/gutenberg#82553)
- Core Data: Return a stable record for equivalent `_fields` queries (WordPress/gutenberg#82552)
- API Fetch: Rename defaultFetchHandler parameter to options (WordPress/gutenberg#82588)
- Fields: remove unused custom sort from the author fields (WordPress/gutenberg#82559)
- Fields: Declare the caption, alt text, description and ping status controls through the Field API (WordPress/gutenberg#82539)
- Docs: Align @PARAM tag columns in phpunit/ docblocks (WordPress/gutenberg#81904)
- Docs: Align @PARAM tag columns in lib/ docblocks (WordPress/gutenberg#81902)
- Docs: Align @PARAM tag columns in packages/ docblocks (WordPress/gutenberg#81903)
- Fix help paragraph color contrast ratio in the Connectors page. (WordPress/gutenberg#82378)
- DataViews: Declare transitive dependencies imported by the ./wp bundle (WordPress/gutenberg#81843)
- DataForm: Add a `showPlaceholderIfEmpty` option to the panel layout (WordPress/gutenberg#82527)
- Global Styles UI: migrate revisions Active Badge to UI Badge (WordPress/gutenberg#82560)
- UI: Put overflow on Select.List instead of a nested wrapper (WordPress/gutenberg#82470)
- Editor: migrate post-card-panel Badge to UI Badge (WordPress/gutenberg#82500)
- Fields: migrate page-title Badge to UI Badge (WordPress/gutenberg#82499)
- DataViews: Skip table columns for field ids without a field definition (WordPress/gutenberg#82601)
- View config: remove stale fields from the wp_template default view (WordPress/gutenberg#82602)
- Rich text: restore contenteditable on pointercancel (WordPress/gutenberg#82598)
- Components: Add an Emotion-to-SCSS migration skill and guide (WordPress/gutenberg#82567)

See #66070.

git-svn-id: https://develop.svn.wordpress.org/trunk@63538 602fd350-edb4-49c9-b593-d223f7449a82
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Package] Grid /packages/grid [Type] Enhancement A suggestion for improvement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants