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 3539af2
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: lib/experimental/dashboard-widgets/class-wp-rest-widget-modules-controller.php
+18Lines changed: 18 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -223,6 +223,10 @@ public function prepare_item_for_response( $item, $request ) {
223
223
$data['attributes'] = $widget_type->attributes;
224
224
}
225
225
226
+
if ( rest_is_field_included( 'example', $fields ) ) {
227
+
$data['example'] = $widget_type->example;
228
+
}
229
+
226
230
if ( rest_is_field_included( 'keywords', $fields ) ) {
227
231
$data['keywords'] = $widget_type->keywords;
228
232
}
@@ -394,6 +398,20 @@ public function get_item_schema() {
394
398
'readonly' => true,
395
399
),
396
400
401
+
'example' => array(
402
+
'description' => __( 'Structured example: the attributes hosts preview the widget type with, and apply to a new instance created without any.', 'gutenberg' ),
403
+
'type' => array( 'object', 'null' ),
404
+
'properties' => array(
405
+
'attributes' => array(
406
+
'description' => __( 'Attribute values by attribute name.', 'gutenberg' ),
407
+
'type' => 'object',
408
+
'additionalProperties' => true,
409
+
),
410
+
),
411
+
'context' => array( 'view', 'edit', 'embed' ),
412
+
'readonly' => true,
413
+
),
414
+
397
415
'keywords' => array(
398
416
'description' => __( 'Alternative terms used to match the widget type when searching, e.g. "calendar" for an events widget. Translatable.', 'gutenberg' ),
Copy file name to clipboardExpand all lines: packages/widget-primitives/CHANGELOG.md
+4Lines changed: 4 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,6 +2,10 @@
2
2
3
3
## Unreleased
4
4
5
+
### New Features
6
+
7
+
-`WidgetModuleRecord.example`: a record carries the `example``widget.json` declares, in the shape of the runtime field; `useWidgetTypes` reads it for a record without a metadata module, and when the module declares no `example` of its own ([#85240](https://github.com/WordPress/gutenberg/pull/85240)).
Copy file name to clipboardExpand all lines: packages/widget-primitives/README.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -29,7 +29,7 @@ This package ships no stylesheets; there is nothing to enqueue or import.
29
29
The contract types, `<WidgetRender>`, and `useWidgetTypes()` work in any
30
30
React application. The host fetches the widget-module records however it
31
31
wants and passes them in; `useWidgetTypes( records )` imports each record's
32
-
metadata module and returns the resolved `WidgetType[]`.
32
+
metadata module, when it declares one, and returns the resolved `WidgetType[]`.
33
33
34
34
On a WordPress site the records come from the `/wp/v2/widget-modules` REST
35
35
endpoint, exposed while the `gutenberg-dashboard-widgets` experiment is
@@ -51,7 +51,7 @@ Error handling and chrome stay with the host, which wraps the lazy render in a `
51
51
52
52
### `useWidgetTypes( records )`
53
53
54
-
It takes host-supplied records (`WidgetModuleRecord[]`, or `null` while loading) and imports each one's metadata module. It returns `[ widgetTypes, isResolvingWidgetTypes ]`; the flag stays `true` until they resolve.
54
+
It takes host-supplied records (`WidgetModuleRecord[]`, or `null` while loading) and imports each one's metadata module when it declares one. A record without one resolves from its own fields. It returns `[ widgetTypes, isResolvingWidgetTypes ]`; the flag stays `true` until they resolve.
0 commit comments