Skip to content

Commit

Permalink
Private experimental cross-module selectors and actions (#44521)
Browse files Browse the repository at this point in the history
Introduces a private selectors APIs in `@wordpress/data` via [the new `@wordpress/experimental`](#43386 (comment)) package:

```js
// Package wordpress/block-data:
import { unlock } from '../experiments';
import { experiments as dataExperiments } from '@wordpress/data';
const { registerPrivateActionsAndSelectors } = unlock( dataExperiments );

import { store as blockEditorStore } from './store';
import { __unstableSelectionHasUnmergeableBlock } from './store/selectors';
registerPrivateActionsAndSelectors( store, {}, {
     __experimentalHasContentRoleAttribute
} );

// plain usage
unlock( registry.select( blockEditorStore ) ).getContentLockingParent();

// usage in React
useSelect( ( select ) => ( {
  parent: privateOf( select( blockEditorStore ) ).__unstableSelectionHasUnmergeableBlock();
} ) );
```
  • Loading branch information
adamziel committed Dec 22, 2022
1 parent 75ec4fd commit 7f1b499
Show file tree
Hide file tree
Showing 16 changed files with 569 additions and 142 deletions.
23 changes: 12 additions & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions packages/block-editor/src/components/block-list/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import BlockHtml from './block-html';
import { useBlockProps } from './use-block-props';
import { store as blockEditorStore } from '../../store';
import { useLayout } from './layout';
import { unlock } from '../../experiments';
export const BlockListBlockContext = createContext();

/**
Expand Down Expand Up @@ -115,10 +116,9 @@ function BlockListBlock( {
!! __unstableGetContentLockingParent( clientId );
return {
themeSupportsLayout: getSettings().supportsLayout,
isContentBlock:
select( blocksStore ).__experimentalHasContentRoleAttribute(
name
),
isContentBlock: unlock(
select( blocksStore )
).__experimentalHasContentRoleAttribute( name ),
hasContentLockedParent: _hasContentLockedParent,
isContentLocking:
getTemplateLock( clientId ) === 'contentOnly' &&
Expand Down
9 changes: 9 additions & 0 deletions packages/block-editor/src/experiments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* WordPress dependencies
*/
import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/experiments';

export const { unlock } = __dangerousOptInToUnstableAPIsOnlyForCoreModules(
'I know using unstable features means my plugin or theme will inevitably break on the next WordPress release.',
'@wordpress/block-editor'
);
1 change: 1 addition & 0 deletions packages/blocks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"@wordpress/deprecated": "file:../deprecated",
"@wordpress/dom": "file:../dom",
"@wordpress/element": "file:../element",
"@wordpress/experiments": "file:../experiments",
"@wordpress/hooks": "file:../hooks",
"@wordpress/html-entities": "file:../html-entities",
"@wordpress/i18n": "file:../i18n",
Expand Down
14 changes: 14 additions & 0 deletions packages/blocks/src/api/raw-handling/test/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ import { getBlockContentSchemaFromTransforms, isPlain } from '../utils';
import { store as mockStore } from '../../../store';
import { STORE_NAME as mockStoreName } from '../../../store/constants';

jest.mock( '@wordpress/experiments', () => {
return {
__dangerousOptInToUnstableAPIsOnlyForCoreModules: () => {
return {
unlock: () => {
return {
registerPrivateActions: () => {},
registerPrivateSelectors: () => {},
};
},
};
},
};
} );
jest.mock( '@wordpress/data', () => {
return {
select: jest.fn( ( store ) => {
Expand Down
12 changes: 12 additions & 0 deletions packages/blocks/src/experiments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* WordPress dependencies
*/
import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/experiments';
import { experiments as dataExperiments } from '@wordpress/data';

export const { unlock } = __dangerousOptInToUnstableAPIsOnlyForCoreModules(
'I know using unstable features means my plugin or theme will inevitably break on the next WordPress release.',
'@wordpress/blocks'
);

export const { registerPrivateSelectors } = unlock( dataExperiments );
7 changes: 6 additions & 1 deletion packages/blocks/src/store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import * as selectors from './selectors';
import * as actions from './actions';
import { STORE_NAME } from './constants';

import { registerPrivateSelectors } from '../experiments';
const { __experimentalHasContentRoleAttribute, ...stableSelectors } = selectors;

/**
* Store definition for the blocks namespace.
*
Expand All @@ -20,8 +23,10 @@ import { STORE_NAME } from './constants';
*/
export const store = createReduxStore( STORE_NAME, {
reducer,
selectors,
selectors: stableSelectors,
actions,
} );

registerPrivateSelectors( store, { __experimentalHasContentRoleAttribute } );

register( store );
20 changes: 20 additions & 0 deletions packages/data/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,26 @@ _Returns_

- `Object`: Object containing the action creators.

### experiments

The experimental APIs exposed by the `@wordpress/data` package.
Only available to core packages. These APIs are not stable and may
change without notice. Do not use outside of core.

_Usage_

```js
import { unlock } from '../experiments';
import { experiments as dataExperiments } from '@wordpress/data';
const { registerPrivateSelectors } = unlock( dataExperiments );

import { store as blockEditorStore } from './store';
import { __unstableSelectionHasUnmergeableBlock } from './store/selectors';
registerPrivateSelectors( store, {
__experimentalHasContentRoleAttribute,
} );
```

### plugins

Object of available plugins to use with a registry.
Expand Down
1 change: 1 addition & 0 deletions packages/data/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@wordpress/compose": "file:../compose",
"@wordpress/deprecated": "file:../deprecated",
"@wordpress/element": "file:../element",
"@wordpress/experiments": "file:../experiments",
"@wordpress/is-shallow-equal": "file:../is-shallow-equal",
"@wordpress/priority-queue": "file:../priority-queue",
"@wordpress/redux-routine": "file:../redux-routine",
Expand Down
104 changes: 104 additions & 0 deletions packages/data/src/experiments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* WordPress dependencies
*/
import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/experiments';

export const { lock, unlock } =
__dangerousOptInToUnstableAPIsOnlyForCoreModules(
'I know using unstable features means my plugin or theme will inevitably break on the next WordPress release.',
'@wordpress/data'
);

/**
* Enables registering private actions and selectors on a store without exposing
* them as public API.
*
* Use it with the store descriptor object:
*
* ```js
* const store = createReduxStore( 'my-store', { ... } );
* registerPrivateActionsAndSelectors( store, {
* __experimentalAction: ( state ) => state.value,
* }, {
* __experimentalSelector: ( state ) => state.value,
* } );
* ```
*
* Once the selectors are registered, they can be accessed using the
* `unlock()` utility:
*
* ```js
* unlock( registry.dispatch( blockEditorStore ) ).__experimentalAction();
* unlock( registry.select( blockEditorStore ) ).__experimentalSelector();
* ```
*
* Note the objects returned by select() and dispatch() have the good old public
* methods, but the modules that opted-in to the private APIs can also "unlock"
* additional private selectors and actions.
*
* @example
*
* ```js
* // In the package exposing the private selectors:
*
* import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/experiments';
* export const { lock, unlock } = __dangerousOptInToUnstableAPIsOnlyForCoreModules( /* ... *\/ );
*
* import { experiments as dataExperiments } from '@wordpress/data';
* const { registerPrivateActionsAndSelectors } = unlock( dataExperiments );
*
* const store = registerStore( 'my-store', { /* ... *\/ } );
* registerPrivateActionsAndSelectors( store, {
* __experimentalAction: ( state ) => state.value,
* }, {
* __experimentalSelector: ( state ) => state.value,
* } );
*
* // In the package using the private selectors:
* import { store as blockEditorStore } from '@wordpress/block-editor';
* unlock( registry.select( blockEditorStore ) ).__experimentalSelector();
*
* // Or in a React component:
* useSelect( ( select ) => (
* unlock( select( blockEditorStore ) ).__experimentalSelector()
* ) );
* useDispatch( ( dispatch ) => {
* unlock( dispatch( blockEditorStore ) ).__experimentalAction()
* } );
* ```
*
* @param {Object} store The store descriptor to register the private selectors on.
* @param {Object} actions The private actions to register in a { actionName: ( ...args ) => action } format.
* @param {Object} selectors The private selectors to register in a { selectorName: ( state, ...args ) => data } format.
*/
export function registerPrivateActionsAndSelectors(
store,
actions = {},
selectors = {}
) {
lock( store, { actions, selectors } );
}

/**
* The experimental APIs exposed by the `@wordpress/data` package.
* Only available to core packages. These APIs are not stable and may
* change without notice. Do not use outside of core.
*
* @example
*
* ```js
* import { unlock } from '../experiments';
* import { experiments as dataExperiments } from '@wordpress/data';
* const { registerPrivateSelectors } = unlock( dataExperiments );
*
* import { store as blockEditorStore } from './store';
* import { __unstableSelectionHasUnmergeableBlock } from './store/selectors';
* registerPrivateSelectors( store, {
* __experimentalHasContentRoleAttribute
* } );
* ```
*/
export const experiments = {};
lock( experiments, {
registerPrivateActionsAndSelectors,
} );
2 changes: 2 additions & 0 deletions packages/data/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export { createRegistrySelector, createRegistryControl } from './factory';
export { controls } from './controls';
export { default as createReduxStore } from './redux-store';

export { experiments } from './experiments';

/**
* Object of available plugins to use with a registry.
*
Expand Down
Loading

0 comments on commit 7f1b499

Please sign in to comment.