You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit e2c3df7
Browse filesBrowse the repository at this point in the historyBrowse files
├── widget.ts optional metadata module: default-exports the attribute props and example values that need code
27
27
├── render.tsx render module: default-exports the React component
28
28
├── style.module.css optional, injected at runtime by the build
29
29
└── report.csv optional static asset; action `href`s resolve it only where `widgets/` ships
@@ -39,7 +39,9 @@ Unlike the other translatable strings, `help` is an object: `content` plus optio
39
39
40
40
`attributes`: the schema hosts render settings from, one entry per attribute with the JSON-expressible subset of a DataViews `Field` (`id`, `type`, `label`, `elements`, `isValid` without `custom`, ...) plus an optional `relevance` hint. `type` may name a registered field type. Labels, headers, descriptions, placeholders, and option labels and descriptions are translated server-side.
41
41
42
-
`widget.ts` is the live half of the metadata: values that only exist in JavaScript, such as `example` data, or an attribute's `Edit` component or custom validator. It is optional: a widget declared entirely by its manifest ships none, and resolves from its record alone.
42
+
`example`: the attributes hosts preview the widget with, and apply to a new instance created without any, as `{ "attributes": { … } }`. JSON values only, and not translated: example values are data.
43
+
44
+
`widget.ts` is the live half of the metadata: values that only exist in JavaScript, such as an attribute's `Edit` component or custom validator, or `example` values that need code. It is optional: a widget declared entirely by its manifest ships none, and resolves from its record alone.
43
45
44
46
Its render component receives the widget's `attributes` and, optionally, `setAttributes`:
`WP_Widget_Type_Registry` (`lib/experimental/dashboard-widgets/`) is a singleton, hydrated at `init` from the manifest. Each entry becomes a `WP_Widget_Type` with `name`, `render_module`, `widget_module`, `presentation`, `category`, `icon`, and the translatable `title`, `description`, `help`, `actions`, `attributes`, and `keywords` (localized at registration time using the widget's `textdomain`).
63
+
`WP_Widget_Type_Registry` (`lib/experimental/dashboard-widgets/`) is a singleton, hydrated at `init` from the manifest. Each entry becomes a `WP_Widget_Type` with `name`, `render_module`, `widget_module`, `presentation`, `category`, `icon`, `example`, and the translatable `title`, `description`, `help`, `actions`, `attributes`, and `keywords` (localized at registration time using the widget's `textdomain`).
62
64
63
65
The hydration is a deterministic copy, with no filters in between. The `widgets/` folder is the single source of widget authorship in this codebase.
64
66
65
67
The registry is the server's authoritative list of widget types for the site. Two consumers read it:
66
68
67
-
- The REST controller (`WP_REST_Widget_Modules_Controller`) exposes it at `/wp/v2/widget-modules`, returning `{ name, render_module, widget_module, presentation, category, title, description, help, icon, actions, attributes, keywords }` per record.
69
+
- The REST controller (`WP_REST_Widget_Modules_Controller`) exposes it at `/wp/v2/widget-modules`, returning `{ name, render_module, widget_module, presentation, category, title, description, help, icon, actions, attributes, example, keywords }` per record.
68
70
- The dashboard page hooks its `dashboard-wp-admin_boot_dependencies` filter, a per-page instance of the generic `{page-slug}-wp-admin_boot_dependencies`, and adds every registered module to its import map as a `dynamic` dependency. A dynamic dependency is reachable by `import()` but never executed eagerly.
69
71
70
72
Registration only makes the modules known to WordPress; loading them is a separate, per-host decision. Dynamic `import()` against the import map is how the dashboard loads widgets today. A host can load them another way: enqueue a module eagerly (`wp_enqueue_script_module()`), declare it as a `static` dependency of its own module, or, outside WordPress, skip the import map and resolve modules through its own `ResolveWidgetModule`.
@@ -75,7 +77,7 @@ The registry exists as a class, rather than having REST read the manifest direct
75
77
76
78
Everything after the REST record is the job of [`@wordpress/widget-primitives`](https://github.com/WordPress/gutenberg/tree/HEAD/packages/widget-primitives), the contract both widget authors and hosts share. Its full surface (the contract types, the discovery hook, and the render component) is covered in the _Widget Primitives / Introduction_ story. In the pipeline it does two things.
77
79
78
-
`useWidgetTypes( records )` takes the host-supplied records, imports each record's `widget_module` for the live metadata, and merges it with the record into `WidgetType[]`. A record without a metadata module resolves from its own fields, as long as it carries a `render_module`. The record's `presentation`, `category`, `title`, `description`, `help`, `actions`, `attributes`, and `keywords`, all sourced from `widget.json` (with `title`, `description`, `help`, `actions`, `attributes`, and `keywords` localized server-side), win over the module's value. `attributes` merges per entry by `id`: the record wins a shared key, a module entry adds what JSON cannot express, `isValid` merges rule by rule, and module-only entries follow. The record's `icon` is a registered icon name: the hook resolves it through the application-registered resolver (`registerIconResolver`), and the resolved element wins over a module's element. The hook reaches for no store or endpoint; a host such as the dashboard reads its own `widgetModule` core-data entity (backed by `/wp/v2/widget-modules`) and passes the records in.
80
+
`useWidgetTypes( records )` takes the host-supplied records, imports each record's `widget_module` for the live metadata, and merges it with the record into `WidgetType[]`. A record without a metadata module resolves from its own fields, as long as it carries a `render_module`. The record's `presentation`, `category`, `title`, `description`, `help`, `actions`, `attributes`, and `keywords`, all sourced from `widget.json` (with `title`, `description`, `help`, `actions`, `attributes`, and `keywords` localized server-side), win over the module's value. `attributes` merges per entry by `id`: the record wins a shared key, a module entry adds what JSON cannot express, `isValid` merges rule by rule, and module-only entries follow. `example` is the exception: the module's wins, and the record's stands in only when the module declares none, so values that need code stay in the module. The record's `icon` is a registered icon name: the hook resolves it through the application-registered resolver (`registerIconResolver`), and the resolved element wins over a module's element. The hook reaches for no store or endpoint; a host such as the dashboard reads its own `widgetModule` core-data entity (backed by `/wp/v2/widget-modules`) and passes the records in.
79
81
80
82
An attribute may also reference a field type by name (`"type": "location"`). The application registers those definitions up front through `registerFieldType()` (the dashboard route registers its own on boot), and `useWidgetTypes` resolves every named reference through that registry while building each `WidgetType`: the registered definition supplies the field's behavior on top of its DataViews `baseType`, and hosts receive plain DataViews fields. The widget declaration stays serializable; resolution happens once, at this boundary.
Copy file name to clipboardExpand all lines: packages/wp-build/README.md
+9-10Lines changed: 9 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -613,9 +613,10 @@ Widgets use a dual-entry pattern, similar in spirit to how blocks split metadata
613
613
| Translatable metadata (`title`, `description`, `help`, `keywords`) |`widget.json`| translated server-side via `textdomain`, so the host receives localized strings without a JS runtime |
614
614
| Framing (`category`, `presentation`) |`widget.json`| plain JSON the host reads without a JS runtime |
615
615
| Attribute schema (types, options, labels, `relevance`) |`widget.json`| plain JSON; labels translated server-side via `textdomain`; a `widget.ts` entry with the same `id` adds what JSON cannot express |
616
-
|`example`, attribute props that need code |`widget.ts`| runtime values |
616
+
|`example` attributes |`widget.json`| plain JSON; a `widget.ts``example` wins when its values need code |
617
+
| Attribute props that need code |`widget.ts`| runtime values |
617
618
618
-
Rule of thumb: anything the host needs before loading a widget's module — identity, translatable copy, framing, attribute schema— goes in `widget.json`, which the build forwards to `build/widgets/registry.php` and exposes through the REST API. Anything that needs code (`example`, an attribute's `Edit` component or custom validator) goes in `widget.ts`.
619
+
Rule of thumb: anything the host needs before loading a widget's module — identity, translatable copy, framing, attribute schema, example attributes — goes in `widget.json`, which the build forwards to `build/widgets/registry.php` and exposes through the REST API. Anything that needs code (an attribute's `Edit` component or custom validator, an `example` with runtime values) goes in `widget.ts`.
619
620
620
621
### `widget.json` — static discovery metadata
621
622
@@ -627,6 +628,9 @@ Rule of thumb: anything the host needs before loading a widget's module — iden
627
628
"help": {
628
629
"content": "A short note for compact surfaces such as tooltips."
@@ -640,13 +644,14 @@ Rule of thumb: anything the host needs before loading a widget's module — iden
640
644
-**`help`** (optional): Contextual help note for compact surfaces. An object with `content` (may carry `<em>`/`<strong>`) and optional `links` (`label`, `href`). Translated server-side using `textdomain`.
641
645
-**`keywords`** (optional): Search aliases. Translated server-side using `textdomain`.
642
646
-**`attributes`** (optional): Attribute schema, one entry per attribute with the JSON-expressible subset of a DataViews `Field` (`id`, `type`, `label`, `elements`, `isValid` without `custom`, ...) plus an optional `relevance` (`high`, `medium`, `low`). `type` may name a registered field type. `label`, `header`, `description`, `placeholder`, and option labels and descriptions are translated server-side using `textdomain`.
647
+
-**`example`** (optional): Structured example, `{ "attributes": { … } }`: the attributes hosts preview the widget with, and apply to a new instance created without any. JSON values only; an `example` in `widget.ts` wins over it.
643
648
-**`category`** (optional): Grouping category for filtering
-**`textdomain`** (optional): Gettext text domain for translating `title`, `description`, `help`, `keywords`, and attribute labels
646
651
647
652
### `widget.ts` — runtime values
648
653
649
-
Optional. Exports a default object with what needs code: `example` data, and attribute entries that add an `Edit` component or a custom validator to a `widget.json` attribute with the same `id`. The build system injects the `render_module` handle at registration time, so authors don't need to declare it.
654
+
Optional. Exports a default object with what needs code: attribute entries that add an `Edit` component or a custom validator to a `widget.json` attribute with the same `id`, and an `example` whose values only exist at runtime, which wins over the `widget.json` one. The build system injects the `render_module` handle at registration time, so authors don't need to declare it.
Entries merge by `id` with the `widget.json` schema: the JSON value wins a shared key, the module adds the rest, and `isValid` merges rule by rule so JSON rules and a module `custom` validator coexist. A widget whose attributes are plain data needs no `widget.ts`.
675
+
Entries merge by `id` with the `widget.json` schema: the JSON value wins a shared key, the module adds the rest, and `isValid` merges rule by rule so JSON rules and a module `custom` validator coexist. A widget whose attributes and example are plain data needs no `widget.ts`.
0 commit comments