Skip to content

Commit

Permalink
Interactivity API: Add JSDoc comments to the public API (#52469)
Browse files Browse the repository at this point in the history
* Add initial JSDoc for `directive`

* Add initial JSDoc for `store`

* Improve example for `store()`

* Improve store docs a little

* Rename Element type to VNode

* Remove selectors and actions from store types

Co-authored-by: Luis Herranz <luisherranz@gmail.com>

* Clarify that `state` is reactive

Co-authored-by: Luis Herranz <luisherranz@gmail.com>

* Replace `onClick` with `onclick` in the JSDoc example

Co-authored-by: Luis Herranz <luisherranz@gmail.com>

* Fix `onclick` in JSDoc explanation

* Add example with custom suffixes

* Fix jslint errors

* Fix a couple of typos

* Remove docs for dubious properties.block

* Add changelog

---------

Co-authored-by: Luis Herranz <luisherranz@gmail.com>
  • Loading branch information
DAreRodz and luisherranz authored Aug 7, 2023
1 parent 776c825 commit b296321
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 1 deletion.
6 changes: 5 additions & 1 deletion packages/interactivity/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
92 changes: 92 additions & 0 deletions packages/interactivity/src/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,104 @@ import { useRef, useCallback } from 'preact/hooks';
*/
import { rawStore as store } from './store';

/** @typedef {import('preact').VNode} VNode */
/** @typedef {typeof context} Context */
/** @typedef {ReturnType<typeof getEvaluate>} 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 {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.
*/

/**
* @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 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`.
*
* 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
* <button data-wp-alert="state.messages.alert">Click me!</button>
* ```
* 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:
*
* ```html
* <button
* data-wp-color--text="state.theme.text"
* data-wp-color--background="state.theme.background"
* >Click me!</button>
* ```
*
* 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.
* @param {DirectiveOptions=} options Options object.
*/
export const directive = ( name, callback, { priority = 10 } = {} ) => {
directiveCallbacks[ name ] = callback;
directivePriorities[ name ] = priority;
Expand Down
42 changes: 42 additions & 0 deletions packages/interactivity/src/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,48 @@ const getSerializedState = () => {
const rawState = getSerializedState();
export const rawStore = { state: deepSignal( rawState ) };

/**
* Extends the Interactivity API global store with the passed properties.
*
* 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
* store({
* state: {
* counter: { value: 0 },
* },
* actions: {
* counter: {
* increment: ({ state }) => {
* state.counter.value += 1;
* },
* },
* },
* });
* ```
*
* The code from the example above allows blocks to subscribe and interact with
* the store by using directives in the HTML, e.g.:
*
* ```html
* <div data-wp-interactive>
* <button
* data-wp-text="state.counter.value"
* data-wp-on--click="actions.counter.increment"
* >
* 0
* </button>
* </div>
* ```
*
* @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.
*/
export const store = ( { state, ...block } ) => {
deepMerge( rawStore, block );
deepMerge( rawState, state );
Expand Down

1 comment on commit b296321

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flaky tests detected in b296321.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/5787635549
📝 Reported issues:

Please sign in to comment.