JS: findWidgetById
File: tests/js/unit/page-builder/findWidgetById.test.js
Tests: findWidgetById in admin/assets/js/src/page-builder/utils/findWidgetById.js
What this function does
findWidgetById( sections, widgetId ) walks the entire page layout tree and returns the widget object with the matching ID, or null if it is not found.
It is called whenever the builder needs to act on a specific widget — opening its settings panel, deleting it, or moving it during drag-and-drop.
The column model: items[]
Top-level columns now use a unified items[] array that can hold widget items and nested row items in any order:
sections[]
└─ rows[]
└─ columns[]
└─ items[]
├─ { kind: 'widget', id, type, values } ← widget at top-level
└─ { kind: 'nestedRow', id, columns[] }
└─ nestedColumn.widgets[] ← widgets inside nested rows
findWidgetById must search both paths:
- Iterate
col.items[]and return any item wherekind === 'widget'andidmatches. - For
kind === 'nestedRow'items, iterate the nested row'scolumns[].widgets[]and check for a match there.
A simple flat search over a single array would silently miss widgets inside nested rows. That is the specific bug this test is designed to catch if the traversal logic is ever simplified incorrectly.
Tests
Finds a widget in a flat column
const widget = { kind: 'widget', id: 'w-flat', type: 'text_block', values: {} };
const sections = [ section( [ row( [ column( [ widget ] ) ] ) ] ) ];
expect( findWidgetById( sections, 'w-flat' ) ).toBe( widget );
The straightforward case — widget item is directly inside column.items[].
Finds a widget inside a nested row
const widget = { id: 'w-nested', type: 'text_block', values: {} };
const nestedRowItem = {
kind: 'nestedRow',
id: 'nr-1',
columns: [ { id: 'nc-1', widgets: [ widget ] } ],
};
const sections = [ section( [ row( [ column( [ nestedRowItem ] ) ] ) ] ) ];
expect( findWidgetById( sections, 'w-nested' ) ).toBe( widget );
The column's items[] contains a nestedRow item, not a direct widget. The function must follow the nestedRow.columns[].widgets[] path to find the widget.
Finds a widget above a nested row in the same column
const widgetAbove = { kind: 'widget', id: 'w-above', ... };
const widgetInside = { id: 'w-inside', ... };
const nestedRowItem = { kind: 'nestedRow', id: 'nr-2', columns: [ { id: 'nc-2', widgets: [ widgetInside ] } ] };
// Column has a widget item first, then a nested row item.
const sections = [ section( [ row( [ column( [ widgetAbove, nestedRowItem ] ) ] ) ] ) ];
expect( findWidgetById( sections, 'w-above' ) ).toBe( widgetAbove );
expect( findWidgetById( sections, 'w-inside' ) ).toBe( widgetInside );
Confirms the function correctly handles columns where widgets and nested rows are mixed together — both paths are reachable within the same items[] iteration.
Returns null when the ID does not exist
const sections = [ section( [ row( [ column( [ makeWidget( 'w-other' ) ] ) ] ) ] ) ];
expect( findWidgetById( sections, 'does-not-exist' ) ).toBeNull();
Confirms the function returns null cleanly rather than throwing when the ID is not present anywhere in the tree.