Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interactivity API: Add JSDoc comments to the public API #52469

Merged
merged 14 commits into from
Aug 7, 2023
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 ) );
luisherranz marked this conversation as resolved.
Show resolved Hide resolved
* }
* }
* )
* ```
*
* 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
Loading