Skip to content

DataViews: Add registerLayout API for custom view types (POC)#77413

Draft
mordeth wants to merge 3 commits intoWordPress:trunkfrom
mordeth:feat/dataviews-register-layout-poc
Draft

DataViews: Add registerLayout API for custom view types (POC)#77413
mordeth wants to merge 3 commits intoWordPress:trunkfrom
mordeth:feat/dataviews-register-layout-poc

Conversation

@mordeth
Copy link
Copy Markdown
Contributor

@mordeth mordeth commented Apr 16, 2026

Description

Proof of concept for a plugin-facing registerLayout API 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-hiding thead, faking column widths via view.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 (matches registerBlockType's behavior).
  • getRegisteredLayout(type) / getRegisteredLayouts() provide read access for the lookup and for introspection.
  • 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 — requiring every consumer to enumerate plugin-defined types would defeat the point of a plugin API.
  • 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 icon integration, view-config menu integration, unregisterLayout, and a filter hook on VIEW_LAYOUTS are 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:

  • Add POC design doc (e7e810d)
  • Add registerLayout API, lookup wire-up, ViewCustom type, exports, unit tests (d8a7e4d)
  • Add Storybook story demonstrating a pocCardRows custom layout (0d01570)

Testing Steps

Unit tests

  • Run the registry tests:
    npm run test:unit -- packages/dataviews/src/test/registry.tsx
  • 9 tests pass, 1 skipped. The skipped one is a DataViewsLayout render-through; it relies on transitive dependencies (@wordpress/components@wordpress/composeclipboard) that need a full npm install, and will run in CI. The Storybook story below is the interactive equivalent.

Storybook demo

  • Build and start Storybook:
    npm run storybook:dev
  • Navigate to DataViews → Register Layout (POC) → PocCardRows.
  • Three "offline payment method" rows render with no table column headers and no pagination chrome.
  • The first row ("Bank transfer") shows an Action needed badge next to the title.
  • Each row with enabled: true shows a visible manage link plus a checkbox; the disabled row only shows the checkbox.
  • Using a screen reader, each row announces its title — verifies the aria-labelledby wiring that compensates for the removed column header.

Regression

  • Run the DataViews unit tests:
    npm run test:unit -- packages/dataviews
  • All existing tests still pass. The lookup change is purely additive — built-ins still resolve through their defaultLayouts gate.

🤖 Generated with Claude Code

@github-actions github-actions Bot added the [Package] DataViews /packages/dataviews label Apr 16, 2026
mordeth added 2 commits April 16, 2026 11:31
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
@mordeth mordeth force-pushed the feat/dataviews-register-layout-poc branch from c1e2800 to 9730df2 Compare April 16, 2026 08:51
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
@mordeth mordeth force-pushed the feat/dataviews-register-layout-poc branch from 9730df2 to 2c195be Compare April 16, 2026 10:26
@oandregal
Copy link
Copy Markdown
Member

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):

Riad and I talked about this, and he shared the lessons learned by the block editor and global registries. There will be situations where consumers want to register/unregister X depending on some context (e.g., different pages), and it becomes a challenge in a SPA.

Given we’re implementing this issue and #74856 to enable BlockFields work (it’s core code we control), we’re scoping down this issue to only make it work with a settings config for the components (DataViews, DataForm, DataViewsPicker). A future next step will be to open this up via registration.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Package] DataViews /packages/dataviews

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants