DataViews: Add registerLayout API for custom view types (POC)#77413
DataViews: Add registerLayout API for custom view types (POC)#77413mordeth wants to merge 3 commits intoWordPress:trunkfrom
Conversation
Design document describing a proof-of-concept `registerLayout` API for the DataViews package. The current layouts array is hardcoded and closed, forcing plugins that want a different visual shape to fall back to CSS overrides against internal class names and prop-shape hacks. Documents the render-only scope, API shape, the files that need to change, a Storybook-based demo, unit tests, and validation against a real downstream migration that currently uses those fallbacks. Refs: none
The built-in layouts (table, grid, list, activity, pickerGrid,
pickerTable) are defined in a hardcoded array and the package exposes
no register function, filter hook, or slot/fill for adding new ones.
Plugins that need a different visual shape fall back to CSS overrides
targeting internal class names.
Introduce a minimal registration API so plugins can own their own
layout components:
- `registerLayout({ type, label, component, icon? })` adds to a
module-level Map. Throws on built-in collision and on duplicate
registration, matching `registerBlockType`'s shape.
- `getRegisteredLayout(type)` / `getRegisteredLayouts()` read access,
used both by the lookup below and by consumers that want to
enumerate registered layouts.
- `DataViewsLayout` consults the registry when the built-in lookup
misses. Built-ins keep their `defaultLayouts[type]` gate so existing
consumer opt-in behavior is unchanged; registered layouts skip the
gate because requiring every consumer to enumerate plugin-defined
types would defeat the point of a plugin API.
- `DataViews` (and `DataViewsPicker` for type compatibility) guards
updated so the view.type widening from ViewCustom typechecks and so
registered layouts aren't filtered out by the defaultLayouts gate.
- `ViewCustom` widens the `View` union with a permissive variant so
`view.type: string` typechecks for plugin code without casts.
Scope is deliberately render-only. View-switcher integration,
view-config menu integration, and `unregisterLayout` are follow-ups.
Design doc: docs/plans/2026-04-16-dataviews-register-layout-design.md
Refs: none
c1e2800 to
9730df2
Compare
A render-only demo of the new registerLayout API. Registers a `pocCardRows` layout that renders each item as a flex row — primary field on the left, secondary field(s) on the right — with no table headers, no borders, and no column-reorder or sort affordances. Fixture is a minimal settings-page "payment methods" list (title, description, toggle) because that is the real-world pattern that motivated the API: an existing migration to DataViews was visually-hiding thead with CSS, faking column widths via view.layout.styles, and overriding internal .dataviews-view-table class names. A registered layout replaces every one of those hacks with a component that just renders what it wants. Accessibility: each row is wired with aria-labelledby so assistive tech still gets row context even though the visual column header is gone. Layout authors that omit headers are responsible for this — the story doubles as a correct example. Refs: none
9730df2 to
2c195be
Compare
|
Thanks for exploring this, Cvetan. One thing I’d like to share is that custom layouts are already possible in DataViews via the “free form” mode. Even though that’s possible, we still want to allow extending DataViews. We discussed a few alternatives for this already and the current view is that we should have component prop for extension instead of using registries (see):
Before jumping into implementation, is there a specific layout/situation you have in mind? I’d like to understand if the “free form” custom layout would be a good fit. |
Description
Proof of concept for a plugin-facing
registerLayoutAPI in@wordpress/dataviews.Today the layouts available to DataViews (
table,grid,list,activity,pickerGrid,pickerTable) are defined in a hardcoded array and the package exposes no registration function, filter hook, or slot/fill for adding new ones. Plugins that need a different visual shape fall back to CSS overrides targeting internal class names — visually-hidingthead, faking column widths viaview.layout.styles, overriding.dataviews-view-table td, etc.This PR introduces a minimal registration API so plugins can own their own layout components:
registerLayout({ type, label, component, icon? })adds to a module-level registry. Throws on built-in collision and on duplicate registration (matchesregisterBlockType's behavior).getRegisteredLayout(type)/getRegisteredLayouts()provide read access for the lookup and for introspection.DataViewsLayoutconsults the registry when the built-in lookup misses. Built-ins keep theirdefaultLayouts[type]gate so existing consumer opt-in behavior is unchanged. Registered layouts skip the gate — requiring every consumer to enumerate plugin-defined types would defeat the point of a plugin API.ViewCustomwidens theViewunion with a permissive variant soview.type: stringtypechecks for plugin code without casts.Scope is deliberately render-only. View-switcher icon integration, view-config menu integration,
unregisterLayout, and a filter hook onVIEW_LAYOUTSare follow-ups. All are additive and wouldn't break the API shipped here.Full design at
docs/plans/2026-04-16-dataviews-register-layout-design.md.Key commits:
registerLayoutAPI, lookup wire-up,ViewCustomtype, exports, unit tests (d8a7e4d)pocCardRowscustom layout (0d01570)Testing Steps
Unit tests
DataViewsLayoutrender-through; it relies on transitive dependencies (@wordpress/components→@wordpress/compose→clipboard) that need a fullnpm install, and will run in CI. The Storybook story below is the interactive equivalent.Storybook demo
enabled: trueshows a visible manage link plus a checkbox; the disabled row only shows the checkbox.aria-labelledbywiring that compensates for the removed column header.Regression
defaultLayoutsgate.🤖 Generated with Claude Code