From 0d5ed4af6ab766db5482162169fbac0309aaa104 Mon Sep 17 00:00:00 2001 From: David Arenas Date: Fri, 30 Jun 2023 20:18:00 +0200 Subject: [PATCH 01/14] Add initial JSDoc for `directive` --- packages/interactivity/src/hooks.js | 57 +++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/packages/interactivity/src/hooks.js b/packages/interactivity/src/hooks.js index 81e2d0713fd0e..ae1098ef233c1 100644 --- a/packages/interactivity/src/hooks.js +++ b/packages/interactivity/src/hooks.js @@ -8,12 +8,69 @@ import { useRef, useCallback } from 'preact/hooks'; */ import { rawStore as store } from './store'; +/** @typedef {import('preact').VNode} Element */ +/** @typedef {typeof context} Context */ +/** @typedef {ReturnType} Evaluate */ + +/** + * @typedef {Object} DirectiveCallbackParams Callback parameters. + * @property {Object} directives Object map with the defined directives of the element being evaluated. + * @property {Object} props Props present in the current element. + * @property {Element} element Virtual node representing the original element. + * @property {Context} context The inherited context. + * @property {Evaluate} evaluate Function that resolves a given path to a value either in the store or the context. + */ + +/** + * @callback DirectiveCallback Callback that runs the directive logic. + * @param {DirectiveCallbackParams} params Callback parameters. + */ + +/** + * @typedef DirectiveOptions Options object. + * @property {number} [priority=10] Value that specifies the priority to + * evaluate directives of this type. Lower + * numbers correspond with earlier execution. + * Default is `10`. + */ + // Main context. const context = createContext( {} ); // WordPress Directives. const directiveCallbacks = {}; const directivePriorities = {}; + +/** + * Register a new directive type in the Interactivity API runtime. + * + * @example + * ```js + * directive( + * 'alert', // Name without the `data-wp-` prefix. + * ( { directives: { alert }, element, evaluate }) => { + * element.props.onClick = () => { + * alert( evaluate( alert.default ) ); + * } + * } + * ) + * ``` + * + * The previous code register a custom directive type for displaying an alert + * message whenever an element using it is clicked. The message text is obtained + * from the store using `evaluate`. + * + * When the HTML is processed by the Interactivity API, any element containing + * the `data-wp-alert` directive will have the `onClick` event handler, e.g., + * + * ```html + * + * ``` + * + * @param {string} name Directive name, without the `data-wp-` prefix. + * @param {DirectiveCallback} callback Function that runs the directive logic. + * @param {DirectiveOptions=} options Options object. + */ export const directive = ( name, callback, { priority = 10 } = {} ) => { directiveCallbacks[ name ] = callback; directivePriorities[ name ] = priority; From f3ed771a4afd4ba68c3cf101d1e931006fd29697 Mon Sep 17 00:00:00 2001 From: David Arenas Date: Fri, 30 Jun 2023 21:10:14 +0200 Subject: [PATCH 02/14] Add initial JSDoc for `store` --- packages/interactivity/src/store.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/packages/interactivity/src/store.js b/packages/interactivity/src/store.js index 207fd2d58cfc9..f47087ab93efb 100644 --- a/packages/interactivity/src/store.js +++ b/packages/interactivity/src/store.js @@ -38,6 +38,35 @@ const getSerializedState = () => { const rawState = getSerializedState(); export const rawStore = { state: deepSignal( rawState ) }; +/** + * Extends the global store with the passed properties. These props tipically + * consist of `state`, `actions` and `effects` used by interactive blocks, and + * any of them may be accessed by any directive present in the page. + * + * @example + * ```js + * store({ + * state: { + * favoriteMovies: [], + * }, + * actions: { + * addMovie: ({ state, context }) => { + * // We assume that there is a `wp-context` directive + * // on the block which provides the item ID. + * state.favoriteMovies.push(context.item.id); + * }, + * clearFavoriteMovies: ({ state }) => { + * state.favoriteMovies = []; + * }, + * }, + * }); + * ``` + * + * @param {Object} properties Properties to be added to the global store. + * @param {Object} [properties.state] State to be added to the global store. + * @param {Object} [properties.actions] Actions to be added to the global store. + * @param {Object} [properties.effects] Effects to be added to the global store. + */ export const store = ( { state, ...block } ) => { deepMerge( rawStore, block ); deepMerge( rawState, state ); From d234543cb189314c9ac88b124be4bf14282cf33f Mon Sep 17 00:00:00 2001 From: David Arenas Date: Wed, 5 Jul 2023 18:12:03 +0200 Subject: [PATCH 03/14] Improve example for `store()` --- packages/interactivity/src/store.js | 37 +++++++++++++++++++---------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/packages/interactivity/src/store.js b/packages/interactivity/src/store.js index f47087ab93efb..dcfa6a03fe087 100644 --- a/packages/interactivity/src/store.js +++ b/packages/interactivity/src/store.js @@ -40,32 +40,43 @@ export const rawStore = { state: deepSignal( rawState ) }; /** * Extends the global store with the passed properties. These props tipically - * consist of `state`, `actions` and `effects` used by interactive blocks, and + * consist of `state`, `selectors` and `actions` used by interactive blocks, and * any of them may be accessed by any directive present in the page. * * @example * ```js * store({ * state: { - * favoriteMovies: [], + * counter: { value: 0 }, * }, * actions: { - * addMovie: ({ state, context }) => { - * // We assume that there is a `wp-context` directive - * // on the block which provides the item ID. - * state.favoriteMovies.push(context.item.id); - * }, - * clearFavoriteMovies: ({ state }) => { - * state.favoriteMovies = []; + * counter: { + * increment: ({ state }) => { + * state.counter.value += 1; + * }, * }, * }, * }); * ``` * - * @param {Object} properties Properties to be added to the global store. - * @param {Object} [properties.state] State to be added to the global store. - * @param {Object} [properties.actions] Actions to be added to the global store. - * @param {Object} [properties.effects] Effects to be added to the global store. + * The code from the example above allows blocks to subscribe and interact with + * the store by using directives in the HTML, e.g.: + * + * ```html + *
+ * + *
+ * ``` + * + * @param {Object} properties Properties to be added to the global store. + * @param {Object} [properties.state] State to be added to the global store. + * @param {Object} [properties.selectors] Selectors to be added to the global store. + * @param {Object} [properties.actions] Actions to be added to the global store. */ export const store = ( { state, ...block } ) => { deepMerge( rawStore, block ); From ab3784e10989d20b0b7e20f437baaee4f075afa7 Mon Sep 17 00:00:00 2001 From: David Arenas Date: Thu, 6 Jul 2023 14:03:00 +0200 Subject: [PATCH 04/14] Improve store docs a little --- packages/interactivity/src/store.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/interactivity/src/store.js b/packages/interactivity/src/store.js index dcfa6a03fe087..2d0338db57dfa 100644 --- a/packages/interactivity/src/store.js +++ b/packages/interactivity/src/store.js @@ -39,9 +39,10 @@ const rawState = getSerializedState(); export const rawStore = { state: deepSignal( rawState ) }; /** - * Extends the global store with the passed properties. These props tipically - * consist of `state`, `selectors` and `actions` used by interactive blocks, and - * any of them may be accessed by any directive present in the page. + * Extends the Interactivity API global store with the passed properties. + * + * These props tipically consist of `state`, `selectors` and `actions` that can + * be used by any directive, making the HTML reactive and interactive. * * @example * ```js From ee66c1615a73c3235ff6a3411058b1bde8a27acb Mon Sep 17 00:00:00 2001 From: David Arenas Date: Thu, 6 Jul 2023 14:07:44 +0200 Subject: [PATCH 05/14] Rename Element type to VNode --- packages/interactivity/src/hooks.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/interactivity/src/hooks.js b/packages/interactivity/src/hooks.js index ae1098ef233c1..94e67829c1b9f 100644 --- a/packages/interactivity/src/hooks.js +++ b/packages/interactivity/src/hooks.js @@ -8,7 +8,7 @@ import { useRef, useCallback } from 'preact/hooks'; */ import { rawStore as store } from './store'; -/** @typedef {import('preact').VNode} Element */ +/** @typedef {import('preact').VNode} VNode */ /** @typedef {typeof context} Context */ /** @typedef {ReturnType} Evaluate */ @@ -16,7 +16,7 @@ import { rawStore as store } from './store'; * @typedef {Object} DirectiveCallbackParams Callback parameters. * @property {Object} directives Object map with the defined directives of the element being evaluated. * @property {Object} props Props present in the current element. - * @property {Element} element Virtual node representing the original element. + * @property {VNode} element Virtual node representing the original element. * @property {Context} context The inherited context. * @property {Evaluate} evaluate Function that resolves a given path to a value either in the store or the context. */ From d6b9a967cf784834776d8b844d2aeb2e07e6505b Mon Sep 17 00:00:00 2001 From: David Arenas Date: Mon, 7 Aug 2023 13:53:28 +0200 Subject: [PATCH 06/14] Remove selectors and actions from store types Co-authored-by: Luis Herranz --- packages/interactivity/src/store.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/interactivity/src/store.js b/packages/interactivity/src/store.js index 2d0338db57dfa..d976303d6c4c3 100644 --- a/packages/interactivity/src/store.js +++ b/packages/interactivity/src/store.js @@ -76,8 +76,6 @@ export const rawStore = { state: deepSignal( rawState ) }; * * @param {Object} properties Properties to be added to the global store. * @param {Object} [properties.state] State to be added to the global store. - * @param {Object} [properties.selectors] Selectors to be added to the global store. - * @param {Object} [properties.actions] Actions to be added to the global store. */ export const store = ( { state, ...block } ) => { deepMerge( rawStore, block ); From 2ffd416b842eeaed0849911b23bbb7229541b3d9 Mon Sep 17 00:00:00 2001 From: David Arenas Date: Mon, 7 Aug 2023 13:54:08 +0200 Subject: [PATCH 07/14] Clarify that `state` is reactive Co-authored-by: Luis Herranz --- packages/interactivity/src/store.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/interactivity/src/store.js b/packages/interactivity/src/store.js index d976303d6c4c3..8a97a615b90b7 100644 --- a/packages/interactivity/src/store.js +++ b/packages/interactivity/src/store.js @@ -41,8 +41,10 @@ export const rawStore = { state: deepSignal( rawState ) }; /** * Extends the Interactivity API global store with the passed properties. * - * These props tipically consist of `state`, `selectors` and `actions` that can - * be used by any directive, making the HTML reactive and interactive. + * These props typically consist of `state`, which is reactive, and other + * properties like `selectors`, `actions`, `effects`, etc. which can store + * callbacks and derived state. These props can then be referenced by any + * directive to make the HTML interactive. * * @example * ```js From 1de4c838c3aaf44a503bc6a13b9bb125a7c34a7c Mon Sep 17 00:00:00 2001 From: David Arenas Date: Mon, 7 Aug 2023 13:55:17 +0200 Subject: [PATCH 08/14] Replace `onClick` with `onclick` in the JSDoc example Co-authored-by: Luis Herranz --- packages/interactivity/src/hooks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/interactivity/src/hooks.js b/packages/interactivity/src/hooks.js index 94e67829c1b9f..86c43884fed01 100644 --- a/packages/interactivity/src/hooks.js +++ b/packages/interactivity/src/hooks.js @@ -49,7 +49,7 @@ const directivePriorities = {}; * directive( * 'alert', // Name without the `data-wp-` prefix. * ( { directives: { alert }, element, evaluate }) => { - * element.props.onClick = () => { + * element.props.onclick = () => { * alert( evaluate( alert.default ) ); * } * } From b4d605917945cddd16c16c180259b3f320a27f45 Mon Sep 17 00:00:00 2001 From: David Arenas Date: Mon, 7 Aug 2023 14:06:04 +0200 Subject: [PATCH 09/14] Fix `onclick` in JSDoc explanation --- packages/interactivity/src/hooks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/interactivity/src/hooks.js b/packages/interactivity/src/hooks.js index 86c43884fed01..83fe3d72c848d 100644 --- a/packages/interactivity/src/hooks.js +++ b/packages/interactivity/src/hooks.js @@ -61,7 +61,7 @@ const directivePriorities = {}; * from the store using `evaluate`. * * When the HTML is processed by the Interactivity API, any element containing - * the `data-wp-alert` directive will have the `onClick` event handler, e.g., + * the `data-wp-alert` directive will have the `onclick` event handler, e.g., * * ```html * From 4ec8dbb361cbd66659a08dca84536a3b4b7ce6b5 Mon Sep 17 00:00:00 2001 From: David Arenas Date: Mon, 7 Aug 2023 15:58:21 +0200 Subject: [PATCH 10/14] Add example with custom suffixes --- packages/interactivity/src/hooks.js | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/packages/interactivity/src/hooks.js b/packages/interactivity/src/hooks.js index 83fe3d72c848d..396abe6b56be7 100644 --- a/packages/interactivity/src/hooks.js +++ b/packages/interactivity/src/hooks.js @@ -66,6 +66,41 @@ const directivePriorities = {}; * ```html * * ``` + * Note that, in the previous example, you acces `alert.default` in order to + * retrieve the `state.messages.alert` value passed to the directive. You can + * also define custom names by appending `--` to the directive attribute, + * followed by a suffix, like in the following HTML snippet: + * + * ```html + * + * ``` + * + * This could be an hypothetical implementation of the custom directive used in + * the snippet above. + * + * @example + * ```js + * directive( + * 'color', // Name without prefix and suffix. + * ( { directives: { color }, ref, evaluate }) => { + * if ( color.text ) { + * ref.style.setProperty( + * 'color', + * evaluate( color.text ) + * ); + * } + * if ( color.background ) { + * ref.style.setProperty( + * 'background-color', + * evaluate( color.background ) + * ); + * } + * } + * ) + * ``` * * @param {string} name Directive name, without the `data-wp-` prefix. * @param {DirectiveCallback} callback Function that runs the directive logic. From 9fbc666b0aaa3c59d6669dbc97a8e551612bf6b2 Mon Sep 17 00:00:00 2001 From: David Arenas Date: Mon, 7 Aug 2023 16:51:48 +0200 Subject: [PATCH 11/14] Fix jslint errors --- packages/interactivity/src/store.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/interactivity/src/store.js b/packages/interactivity/src/store.js index 8a97a615b90b7..4e11f2879e813 100644 --- a/packages/interactivity/src/store.js +++ b/packages/interactivity/src/store.js @@ -76,8 +76,10 @@ export const rawStore = { state: deepSignal( rawState ) }; * * ``` * - * @param {Object} properties Properties to be added to the global store. - * @param {Object} [properties.state] State to be added to the global store. + * @param {Object} properties Properties to be added to the global store. + * @param {Object} [properties.state] State to be added to the global store. All + * the properties included here become reactive. + * @param {Object} [properties.block] Other properties to be added to the global store. */ export const store = ( { state, ...block } ) => { deepMerge( rawStore, block ); From 67ccbd37733319ca33fa8ed7bd8f3105c92ff7d1 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Mon, 7 Aug 2023 17:42:32 +0200 Subject: [PATCH 12/14] Fix a couple of typos --- packages/interactivity/src/hooks.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/interactivity/src/hooks.js b/packages/interactivity/src/hooks.js index 396abe6b56be7..448060caf2b2e 100644 --- a/packages/interactivity/src/hooks.js +++ b/packages/interactivity/src/hooks.js @@ -56,7 +56,7 @@ const directivePriorities = {}; * ) * ``` * - * The previous code register a custom directive type for displaying an alert + * The previous code registers a custom directive type for displaying an alert * message whenever an element using it is clicked. The message text is obtained * from the store using `evaluate`. * @@ -66,7 +66,7 @@ const directivePriorities = {}; * ```html * * ``` - * Note that, in the previous example, you acces `alert.default` in order to + * Note that, in the previous example, you access `alert.default` in order to * retrieve the `state.messages.alert` value passed to the directive. You can * also define custom names by appending `--` to the directive attribute, * followed by a suffix, like in the following HTML snippet: From 9b686e8eea7c2ecd73ef4863d2614ca617222816 Mon Sep 17 00:00:00 2001 From: David Arenas Date: Mon, 7 Aug 2023 17:49:55 +0200 Subject: [PATCH 13/14] Remove docs for dubious properties.block --- packages/interactivity/src/store.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/interactivity/src/store.js b/packages/interactivity/src/store.js index 4e11f2879e813..f13bba040c8c9 100644 --- a/packages/interactivity/src/store.js +++ b/packages/interactivity/src/store.js @@ -79,7 +79,6 @@ export const rawStore = { state: deepSignal( rawState ) }; * @param {Object} properties Properties to be added to the global store. * @param {Object} [properties.state] State to be added to the global store. All * the properties included here become reactive. - * @param {Object} [properties.block] Other properties to be added to the global store. */ export const store = ( { state, ...block } ) => { deepMerge( rawStore, block ); From 45716a87f03f1f1e609125b25b71a08d5b237bfd Mon Sep 17 00:00:00 2001 From: David Arenas Date: Mon, 7 Aug 2023 17:54:32 +0200 Subject: [PATCH 14/14] Add changelog --- packages/interactivity/CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/interactivity/CHANGELOG.md b/packages/interactivity/CHANGELOG.md index 9188f342ae2f7..c1e2144bfd496 100644 --- a/packages/interactivity/CHANGELOG.md +++ b/packages/interactivity/CHANGELOG.md @@ -4,7 +4,11 @@ ### Bug Fix -- Add support for underscores and leading dashes in the suffix part of the directive. ([#53337](https://github.com/WordPress/gutenberg/pull/53337)) +- Add support for underscores and leading dashes in the suffix part of the directive. ([#53337](https://github.com/WordPress/gutenberg/pull/53337)) + +### Enhancements + +- Add JSDoc comments to `store()` and `directive()` functions. ([#52469](https://github.com/WordPress/gutenberg/pull/52469)) ### Breaking Change