From 3e85c43655254f896dda97e326c104a9ec8fc2e9 Mon Sep 17 00:00:00 2001 From: Adam Zielinski Date: Thu, 26 Jan 2023 20:57:30 +0100 Subject: [PATCH] Introduce ExperimentalBlockEditorProvider (#47319) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit introduces `ExperimentalBlockEditorProvider` that prevents mixing experimental editor settings and the public `BlockEditorProvider` component. The actual filtering is handled in a private `__experimentalUpdateSettings` selector in the block-editor store. Experimental settings may still be set via a PHP plugin – limiting this vector would require a separate PR. ## Rationale WordPress extenders cannot update the experimental block settings on their own. The `updateSettings()` actions of the `@wordpress/block-editor` store will filter out all the settings that are **not** a part of the public API. The only way to actually store them is via private action. `__experimentalUpdateSettings()`. To privatize a block editor setting, add it to the `privateSettings` list in [/packages/block-editor/src/store/actions.js](/packages/block-editor/src/store/actions.js): ```js const privateSettings = [ '__unstableInserterMediaCategories', // List a block editor setting here to make it private ]; ``` ## Mobile apps This commit updates a few `.native.js` files. Mobile apps choose to import the `.native.js` files over their `.js` counterparts whenever possible. At the same time, this PR introduced the expectation of having two extra named exports: `ExperimentalEditorProvider` and `ExperimentalBlockEditorProvider`. I updated the native files to export the regular Provider component under the experimental name. This works because the settings filtering is restricted to the web – it doesn't make sense in context of mobile apps anyway. Co-authored-by: ntsekouras --- docs/contributors/code/coding-guidelines.md | 13 ++ package-lock.json | 4 + .../components/block-mover/stories/index.js | 6 +- .../src/components/block-preview/index.js | 16 +- .../src/components/inserter/stories/index.js | 18 +- .../src/components/provider/index.js | 47 ++-- .../src/components/provider/index.native.js | 7 +- .../provider/test/experimental-provider.js | 98 +++++++++ .../provider/test/use-block-sync.js | 10 + packages/block-editor/src/experiments.js | 2 + packages/block-editor/src/store/actions.js | 39 +++- packages/block-editor/src/store/index.js | 9 +- packages/customize-widgets/package.json | 1 + .../sidebar-editor-provider.js | 10 +- packages/customize-widgets/src/experiments.js | 10 + packages/edit-navigation/src/experiments.js | 10 + packages/edit-post/package.json | 1 + packages/edit-post/src/editor.js | 9 +- packages/edit-post/src/experiments.js | 10 + .../src/components/block-editor/index.js | 9 +- packages/edit-widgets/package.json | 1 + .../index.js | 9 +- packages/edit-widgets/src/experiments.js | 10 + packages/editor/package.json | 1 + .../editor/src/components/provider/index.js | 204 ++++++++++-------- .../src/components/provider/index.native.js | 5 +- packages/editor/src/experiments.js | 10 + packages/editor/src/index.js | 1 + packages/editor/src/index.native.js | 1 + packages/editor/src/lockUnlock.js | 9 + packages/experiments/src/implementation.js | 5 + 31 files changed, 445 insertions(+), 140 deletions(-) create mode 100644 packages/block-editor/src/components/provider/test/experimental-provider.js create mode 100644 packages/customize-widgets/src/experiments.js create mode 100644 packages/edit-navigation/src/experiments.js create mode 100644 packages/edit-post/src/experiments.js create mode 100644 packages/edit-widgets/src/experiments.js create mode 100644 packages/editor/src/experiments.js create mode 100644 packages/editor/src/lockUnlock.js diff --git a/docs/contributors/code/coding-guidelines.md b/docs/contributors/code/coding-guidelines.md index 9fc115b8eb9e3a..0404f579de4405 100644 --- a/docs/contributors/code/coding-guidelines.md +++ b/docs/contributors/code/coding-guidelines.md @@ -441,6 +441,19 @@ export function MyComponent() { } ``` +#### Experimental editor settings + +WordPress extenders cannot update the experimental block settings on their own. The `updateSettings()` actions of the `@wordpress/block-editor` store will filter out all the settings that are **not** a part of the public API. The only way to actually store them is via private action. `__experimentalUpdateSettings()`. + +To privatize a block editor setting, add it to the `privateSettings` list in [/packages/block-editor/src/store/actions.js](/packages/block-editor/src/store/actions.js): + +```js +const privateSettings = [ + '__unstableInserterMediaCategories', + // List a block editor setting here to make it private +]; +``` + #### Experimental block.json and theme.json APIs As of today, there is no way to restrict the `block.json` and `theme.json` APIs diff --git a/package-lock.json b/package-lock.json index 25ee3155936ada..ab1314afd8b2ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17568,6 +17568,7 @@ "@wordpress/data": "file:packages/data", "@wordpress/dom": "file:packages/dom", "@wordpress/element": "file:packages/element", + "@wordpress/experiments": "file:packages/experiments", "@wordpress/hooks": "file:packages/hooks", "@wordpress/i18n": "file:packages/i18n", "@wordpress/icons": "file:packages/icons", @@ -17721,6 +17722,7 @@ "@wordpress/deprecated": "file:packages/deprecated", "@wordpress/editor": "file:packages/editor", "@wordpress/element": "file:packages/element", + "@wordpress/experiments": "file:packages/experiments", "@wordpress/hooks": "file:packages/hooks", "@wordpress/i18n": "file:packages/i18n", "@wordpress/icons": "file:packages/icons", @@ -17807,6 +17809,7 @@ "@wordpress/deprecated": "file:packages/deprecated", "@wordpress/dom": "file:packages/dom", "@wordpress/element": "file:packages/element", + "@wordpress/experiments": "file:packages/experiments", "@wordpress/hooks": "file:packages/hooks", "@wordpress/i18n": "file:packages/i18n", "@wordpress/icons": "file:packages/icons", @@ -17840,6 +17843,7 @@ "@wordpress/deprecated": "file:packages/deprecated", "@wordpress/dom": "file:packages/dom", "@wordpress/element": "file:packages/element", + "@wordpress/experiments": "file:packages/experiments", "@wordpress/hooks": "file:packages/hooks", "@wordpress/html-entities": "file:packages/html-entities", "@wordpress/i18n": "file:packages/i18n", diff --git a/packages/block-editor/src/components/block-mover/stories/index.js b/packages/block-editor/src/components/block-mover/stories/index.js index aac98ecc84d6ed..de30260563f91e 100644 --- a/packages/block-editor/src/components/block-mover/stories/index.js +++ b/packages/block-editor/src/components/block-mover/stories/index.js @@ -11,7 +11,7 @@ import { Toolbar } from '@wordpress/components'; * Internal dependencies */ import BlockMover from '../'; -import BlockEditorProvider from '../../provider'; +import { ExperimentalBlockEditorProvider } from '../../provider'; import { store as blockEditorStore } from '../../../store'; registerCoreBlocks(); @@ -35,9 +35,9 @@ function Provider( { children } ) { return (
- + { children } - +
); } diff --git a/packages/block-editor/src/components/block-preview/index.js b/packages/block-editor/src/components/block-preview/index.js index 37ba4d1fe3a4cc..df3a7c174e6466 100644 --- a/packages/block-editor/src/components/block-preview/index.js +++ b/packages/block-editor/src/components/block-preview/index.js @@ -14,7 +14,7 @@ import deprecated from '@wordpress/deprecated'; /** * Internal dependencies */ -import BlockEditorProvider from '../provider'; +import { ExperimentalBlockEditorProvider } from '../provider'; import AutoHeightBlockPreview from './auto'; import { store as blockEditorStore } from '../../store'; import { BlockListItems } from '../block-list'; @@ -66,13 +66,16 @@ export function BlockPreview( { } return ( - + - + ); } @@ -126,12 +129,15 @@ export function useBlockPreview( { ); const children = ( - + - + ); return { diff --git a/packages/block-editor/src/components/inserter/stories/index.js b/packages/block-editor/src/components/inserter/stories/index.js index f6949653c87872..960fd86b3bc8ff 100644 --- a/packages/block-editor/src/components/inserter/stories/index.js +++ b/packages/block-editor/src/components/inserter/stories/index.js @@ -2,7 +2,7 @@ * Internal dependencies */ import BlockLibrary from '../library'; -import BlockEditorProvider from '../../provider'; +import { ExperimentalBlockEditorProvider } from '../../provider'; import { patternCategories, patterns, reusableBlocks } from './utils/fixtures'; import Inserter from '../'; @@ -16,11 +16,11 @@ export const libraryWithoutPatterns = () => { display: 'inline-block', }; return ( - +
-
+ ); }; @@ -32,7 +32,7 @@ export const libraryWithPatterns = () => { display: 'inline-block', }; return ( - {
-
+ ); }; @@ -53,7 +53,7 @@ export const libraryWithPatternsAndReusableBlocks = () => { display: 'inline-block', }; return ( - {
-
+ ); }; @@ -75,7 +75,7 @@ export const quickInserter = () => { display: 'inline-block', }; return ( - {
-
+ ); }; diff --git a/packages/block-editor/src/components/provider/index.js b/packages/block-editor/src/components/provider/index.js index e9a9da86dc15ef..323ce68765c40a 100644 --- a/packages/block-editor/src/components/provider/index.js +++ b/packages/block-editor/src/components/provider/index.js @@ -11,24 +11,43 @@ import withRegistryProvider from './with-registry-provider'; import useBlockSync from './use-block-sync'; import { store as blockEditorStore } from '../../store'; import { BlockRefsProvider } from './block-refs-provider'; +import { unlock } from '../../experiments'; /** @typedef {import('@wordpress/data').WPDataRegistry} WPDataRegistry */ -function BlockEditorProvider( props ) { - const { children, settings } = props; +export const ExperimentalBlockEditorProvider = withRegistryProvider( + ( props ) => { + const { children, settings, stripExperimentalSettings = false } = props; - const { updateSettings } = useDispatch( blockEditorStore ); - useEffect( () => { - updateSettings( { - ...settings, - __internalIsInitialized: true, - } ); - }, [ settings ] ); + const { __experimentalUpdateSettings } = unlock( + useDispatch( blockEditorStore ) + ); + useEffect( () => { + __experimentalUpdateSettings( + { + ...settings, + __internalIsInitialized: true, + }, + stripExperimentalSettings + ); + }, [ settings ] ); - // Syncs the entity provider with changes in the block-editor store. - useBlockSync( props ); + // Syncs the entity provider with changes in the block-editor store. + useBlockSync( props ); - return { children }; -} + return { children }; + } +); -export default withRegistryProvider( BlockEditorProvider ); +export const BlockEditorProvider = ( props ) => { + return ( + + { props.children } + + ); +}; + +export default BlockEditorProvider; diff --git a/packages/block-editor/src/components/provider/index.native.js b/packages/block-editor/src/components/provider/index.native.js index 8851a2b9b47a15..1bb790a20c19d2 100644 --- a/packages/block-editor/src/components/provider/index.native.js +++ b/packages/block-editor/src/components/provider/index.native.js @@ -14,7 +14,7 @@ import { BlockRefsProvider } from './block-refs-provider'; /** @typedef {import('@wordpress/data').WPDataRegistry} WPDataRegistry */ -function BlockEditorProvider( props ) { +const BlockEditorProvider = withRegistryProvider( function ( props ) { const { children, settings } = props; const { updateSettings } = useDispatch( blockEditorStore ); @@ -26,6 +26,7 @@ function BlockEditorProvider( props ) { useBlockSync( props ); return { children }; -} +} ); -export default withRegistryProvider( BlockEditorProvider ); +export default BlockEditorProvider; +export { BlockEditorProvider as ExperimentalBlockEditorProvider }; diff --git a/packages/block-editor/src/components/provider/test/experimental-provider.js b/packages/block-editor/src/components/provider/test/experimental-provider.js new file mode 100644 index 00000000000000..5c0d27602685a5 --- /dev/null +++ b/packages/block-editor/src/components/provider/test/experimental-provider.js @@ -0,0 +1,98 @@ +/** + * External dependencies + */ +import { render } from '@testing-library/react'; +/** + * WordPress dependencies + */ +import { useRegistry } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { BlockEditorProvider, ExperimentalBlockEditorProvider } from '../'; +import { store as blockEditorStore } from '../../../store'; + +const HasEditorSetting = ( props ) => { + const registry = useRegistry(); + if ( props.setRegistry ) { + props.setRegistry( registry ); + } + return

Test.

; +}; + +describe( 'BlockEditorProvider', () => { + let registry; + const setRegistry = ( reg ) => { + registry = reg; + }; + beforeEach( () => { + registry = undefined; + } ); + it( 'should strip experimental settings', async () => { + render( + + + + ); + const settings = registry.select( blockEditorStore ).getSettings(); + expect( settings ).not.toHaveProperty( + '__unstableInserterMediaCategories' + ); + } ); + it( 'should preserve stable settings', async () => { + render( + + + + ); + const settings = registry.select( blockEditorStore ).getSettings(); + expect( settings ).toHaveProperty( 'stableSetting' ); + } ); +} ); + +describe( 'ExperimentalBlockEditorProvider', () => { + let registry; + const setRegistry = ( reg ) => { + registry = reg; + }; + beforeEach( () => { + registry = undefined; + } ); + it( 'should preserve experimental settings', async () => { + render( + + + + ); + const settings = registry.select( blockEditorStore ).getSettings(); + expect( settings ).toHaveProperty( + '__unstableInserterMediaCategories' + ); + } ); + it( 'should preserve stable settings', async () => { + render( + + + + ); + const settings = registry.select( blockEditorStore ).getSettings(); + expect( settings ).toHaveProperty( 'stableSetting' ); + } ); +} ); diff --git a/packages/block-editor/src/components/provider/test/use-block-sync.js b/packages/block-editor/src/components/provider/test/use-block-sync.js index 9f8b091e52a10b..7901c3d98f3a92 100644 --- a/packages/block-editor/src/components/provider/test/use-block-sync.js +++ b/packages/block-editor/src/components/provider/test/use-block-sync.js @@ -14,7 +14,17 @@ import { render } from '@testing-library/react'; import useBlockSync from '../use-block-sync'; import withRegistryProvider from '../with-registry-provider'; import * as blockEditorActions from '../../../store/actions'; + import { store as blockEditorStore } from '../../../store'; +jest.mock( '../../../store/actions', () => { + const actions = jest.requireActual( '../../../store/actions' ); + return { + ...actions, + resetBlocks: jest.fn( actions.resetBlocks ), + replaceInnerBlocks: jest.fn( actions.replaceInnerBlocks ), + setHasControlledInnerBlocks: jest.fn( actions.replaceInnerBlocks ), + }; +} ); const TestWrapper = withRegistryProvider( ( props ) => { if ( props.setRegistry ) { diff --git a/packages/block-editor/src/experiments.js b/packages/block-editor/src/experiments.js index b217e14ec273be..a12b3ac68ef6ef 100644 --- a/packages/block-editor/src/experiments.js +++ b/packages/block-editor/src/experiments.js @@ -7,6 +7,7 @@ import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/exp * Internal dependencies */ import * as globalStyles from './components/global-styles'; +import { ExperimentalBlockEditorProvider } from './components/provider'; export const { lock, unlock } = __dangerousOptInToUnstableAPIsOnlyForCoreModules( @@ -20,4 +21,5 @@ export const { lock, unlock } = export const experiments = {}; lock( experiments, { ...globalStyles, + ExperimentalBlockEditorProvider, } ); diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js index e1c88c2bd99b49..426e6fae10bd91 100644 --- a/packages/block-editor/src/store/actions.js +++ b/packages/block-editor/src/store/actions.js @@ -16,6 +16,7 @@ import { speak } from '@wordpress/a11y'; import { __, _n, sprintf } from '@wordpress/i18n'; import { create, insert, remove, toHTMLString } from '@wordpress/rich-text'; import deprecated from '@wordpress/deprecated'; +import { Platform } from '@wordpress/element'; /** * Internal dependencies @@ -1442,9 +1443,45 @@ export function updateBlockListSettings( clientId, settings ) { * @return {Object} Action object */ export function updateSettings( settings ) { + return __experimentalUpdateSettings( settings, true ); +} + +/** + * A list of private/experimental block editor settings that + * should not become a part of the WordPress public API. + * BlockEditorProvider will remove these settings from the + * settings object it receives. + * + * @see https://github.com/WordPress/gutenberg/pull/46131 + */ +const privateSettings = [ '__unstableInserterMediaCategories' ]; + +/** + * Action that updates the block editor settings and + * conditionally preserves the experimental ones. + * + * @param {Object} settings Updated settings + * @param {boolean} stripExperimentalSettings Whether to strip experimental settings. + * @return {Object} Action object + */ +export function __experimentalUpdateSettings( + settings, + stripExperimentalSettings = false +) { + let cleanSettings = settings; + // There are no plugins in the mobile apps, so there is no + // need to strip the experimental settings: + if ( stripExperimentalSettings && Platform.OS === 'web' ) { + cleanSettings = {}; + for ( const key in settings ) { + if ( ! privateSettings.includes( key ) ) { + cleanSettings[ key ] = settings[ key ]; + } + } + } return { type: 'UPDATE_SETTINGS', - settings, + settings: cleanSettings, }; } diff --git a/packages/block-editor/src/store/index.js b/packages/block-editor/src/store/index.js index baa14b94ca9ad8..bad1ca2d62d610 100644 --- a/packages/block-editor/src/store/index.js +++ b/packages/block-editor/src/store/index.js @@ -8,8 +8,11 @@ import { createReduxStore, register } from '@wordpress/data'; */ import reducer from './reducer'; import * as selectors from './selectors'; -import * as actions from './actions'; +import * as allActions from './actions'; import { STORE_NAME } from './constants'; +import { unlock } from '../experiments'; + +const { __experimentalUpdateSettings, ...actions } = allActions; /** * Block editor data store configuration. @@ -33,3 +36,7 @@ export const store = createReduxStore( STORE_NAME, { } ); register( store ); + +unlock( store ).registerPrivateActions( { + __experimentalUpdateSettings, +} ); diff --git a/packages/customize-widgets/package.json b/packages/customize-widgets/package.json index e12865cee03cd3..825baeb95815d9 100644 --- a/packages/customize-widgets/package.json +++ b/packages/customize-widgets/package.json @@ -33,6 +33,7 @@ "@wordpress/data": "file:../data", "@wordpress/dom": "file:../dom", "@wordpress/element": "file:../element", + "@wordpress/experiments": "file:../experiments", "@wordpress/hooks": "file:../hooks", "@wordpress/i18n": "file:../i18n", "@wordpress/icons": "file:../icons", diff --git a/packages/customize-widgets/src/components/sidebar-block-editor/sidebar-editor-provider.js b/packages/customize-widgets/src/components/sidebar-block-editor/sidebar-editor-provider.js index 95f6259de16bd2..a7be2fbd5969e1 100644 --- a/packages/customize-widgets/src/components/sidebar-block-editor/sidebar-editor-provider.js +++ b/packages/customize-widgets/src/components/sidebar-block-editor/sidebar-editor-provider.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { BlockEditorProvider } from '@wordpress/block-editor'; +import { experiments as blockEditorExperiments } from '@wordpress/block-editor'; /** * Internal dependencies @@ -9,6 +9,10 @@ import { BlockEditorProvider } from '@wordpress/block-editor'; import useSidebarBlockEditor from './use-sidebar-block-editor'; import useBlocksFocusControl from '../focus-control/use-blocks-focus-control'; +import { unlock } from '../../experiments'; + +const { ExperimentalBlockEditorProvider } = unlock( blockEditorExperiments ); + export default function SidebarEditorProvider( { sidebar, settings, @@ -19,7 +23,7 @@ export default function SidebarEditorProvider( { useBlocksFocusControl( blocks ); return ( - { children } - + ); } diff --git a/packages/customize-widgets/src/experiments.js b/packages/customize-widgets/src/experiments.js new file mode 100644 index 00000000000000..4c5366db618560 --- /dev/null +++ b/packages/customize-widgets/src/experiments.js @@ -0,0 +1,10 @@ +/** + * 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/customize-widgets' + ); diff --git a/packages/edit-navigation/src/experiments.js b/packages/edit-navigation/src/experiments.js new file mode 100644 index 00000000000000..51758a5c3859d8 --- /dev/null +++ b/packages/edit-navigation/src/experiments.js @@ -0,0 +1,10 @@ +/** + * 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/edit-navigation' + ); diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json index 6deee3aef7bb7c..4656abd51e485c 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -39,6 +39,7 @@ "@wordpress/deprecated": "file:../deprecated", "@wordpress/editor": "file:../editor", "@wordpress/element": "file:../element", + "@wordpress/experiments": "file:../experiments", "@wordpress/hooks": "file:../hooks", "@wordpress/i18n": "file:../i18n", "@wordpress/icons": "file:../icons", diff --git a/packages/edit-post/src/editor.js b/packages/edit-post/src/editor.js index f696e6f4c25608..7fa7f33e9ef6e6 100644 --- a/packages/edit-post/src/editor.js +++ b/packages/edit-post/src/editor.js @@ -4,10 +4,10 @@ import { store as blocksStore } from '@wordpress/blocks'; import { useSelect, useDispatch } from '@wordpress/data'; import { - EditorProvider, ErrorBoundary, PostLockedModal, store as editorStore, + experiments as editorExperiments, } from '@wordpress/editor'; import { StrictMode, useMemo } from '@wordpress/element'; import { SlotFillProvider } from '@wordpress/components'; @@ -21,6 +21,9 @@ import { store as preferencesStore } from '@wordpress/preferences'; import Layout from './components/layout'; import EditorInitialization from './components/editor-initialization'; import { store as editPostStore } from './store'; +import { unlock } from './experiments'; + +const { ExperimentalEditorProvider } = unlock( editorExperiments ); function Editor( { postId, @@ -180,7 +183,7 @@ function Editor( { - - + diff --git a/packages/edit-post/src/experiments.js b/packages/edit-post/src/experiments.js new file mode 100644 index 00000000000000..afe2e553e2af9a --- /dev/null +++ b/packages/edit-post/src/experiments.js @@ -0,0 +1,10 @@ +/** + * 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/edit-post' + ); diff --git a/packages/edit-site/src/components/block-editor/index.js b/packages/edit-site/src/components/block-editor/index.js index 439d26bb7c50b5..d5a7dc7f5730a7 100644 --- a/packages/edit-site/src/components/block-editor/index.js +++ b/packages/edit-site/src/components/block-editor/index.js @@ -11,7 +11,6 @@ import { useCallback, useMemo, useRef } from '@wordpress/element'; import { useEntityBlockEditor, store as coreStore } from '@wordpress/core-data'; import { BlockList, - BlockEditorProvider, __experimentalLinkControl, BlockInspector, BlockTools, @@ -19,6 +18,7 @@ import { __unstableUseTypingObserver as useTypingObserver, BlockEditorKeyboardShortcuts, store as blockEditorStore, + experiments as blockEditorExperiments, } from '@wordpress/block-editor'; import { useMergeRefs, @@ -39,6 +39,9 @@ import BackButton from './back-button'; import ResizableEditor from './resizable-editor'; import EditorCanvas from './editor-canvas'; import StyleBook from '../style-book'; +import { unlock } from '../../experiments'; + +const { ExperimentalBlockEditorProvider } = unlock( blockEditorExperiments ); const LAYOUT = { type: 'default', @@ -152,7 +155,7 @@ export default function BlockEditor() { ( isTemplatePart && hasBlocks ) || isViewMode ? false : undefined; return ( - - + ); } diff --git a/packages/edit-widgets/package.json b/packages/edit-widgets/package.json index 7ce6947537727e..f176deb220e264 100644 --- a/packages/edit-widgets/package.json +++ b/packages/edit-widgets/package.json @@ -38,6 +38,7 @@ "@wordpress/deprecated": "file:../deprecated", "@wordpress/dom": "file:../dom", "@wordpress/element": "file:../element", + "@wordpress/experiments": "file:../experiments", "@wordpress/hooks": "file:../hooks", "@wordpress/i18n": "file:../i18n", "@wordpress/icons": "file:../icons", diff --git a/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js b/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js index 8aa616c867473d..917899d7dd99be 100644 --- a/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js +++ b/packages/edit-widgets/src/components/widget-areas-block-editor-provider/index.js @@ -11,9 +11,9 @@ import { } from '@wordpress/core-data'; import { useMemo } from '@wordpress/element'; import { - BlockEditorProvider, BlockEditorKeyboardShortcuts, CopyHandler, + experiments as blockEditorExperiments, } from '@wordpress/block-editor'; import { ReusableBlocksMenuItems } from '@wordpress/reusable-blocks'; import { ShortcutProvider } from '@wordpress/keyboard-shortcuts'; @@ -27,6 +27,9 @@ import { buildWidgetAreasPostId, KIND, POST_TYPE } from '../../store/utils'; import useLastSelectedWidgetArea from '../../hooks/use-last-selected-widget-area'; import { store as editWidgetsStore } from '../../store'; import { ALLOW_REUSABLE_BLOCKS } from '../../constants'; +import { unlock } from '../../experiments'; + +const { ExperimentalBlockEditorProvider } = unlock( blockEditorExperiments ); export default function WidgetAreasBlockEditorProvider( { blockEditorSettings, @@ -100,7 +103,7 @@ export default function WidgetAreasBlockEditorProvider( { - { children } - + ); diff --git a/packages/edit-widgets/src/experiments.js b/packages/edit-widgets/src/experiments.js new file mode 100644 index 00000000000000..7a5f0d03a3e596 --- /dev/null +++ b/packages/edit-widgets/src/experiments.js @@ -0,0 +1,10 @@ +/** + * 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/edit-widgets' + ); diff --git a/packages/editor/package.json b/packages/editor/package.json index affe1d6dea89e2..5a074daf812239 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -44,6 +44,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", diff --git a/packages/editor/src/components/provider/index.js b/packages/editor/src/components/provider/index.js index ef8d03c824b7b1..2618fc33ad1a76 100644 --- a/packages/editor/src/components/provider/index.js +++ b/packages/editor/src/components/provider/index.js @@ -8,6 +8,7 @@ import { EntityProvider, useEntityBlockEditor } from '@wordpress/core-data'; import { BlockEditorProvider, BlockContextProvider, + experiments as blockEditorExperiments, } from '@wordpress/block-editor'; import { ReusableBlocksMenuItems } from '@wordpress/reusable-blocks'; import { store as noticesStore } from '@wordpress/notices'; @@ -18,107 +19,128 @@ import { store as noticesStore } from '@wordpress/notices'; import withRegistryProvider from './with-registry-provider'; import { store as editorStore } from '../../store'; import useBlockEditorSettings from './use-block-editor-settings'; +import { unlock } from '../../lockUnlock'; -function EditorProvider( { - __unstableTemplate, - post, - settings, - recovery, - initialEdits, - children, -} ) { - const defaultBlockContext = useMemo( () => { - if ( post.type === 'wp_template' ) { - return {}; - } - return { postId: post.id, postType: post.type }; - }, [ post.id, post.type ] ); - const { selection, isReady } = useSelect( ( select ) => { - const { getEditorSelection, __unstableIsEditorReady } = - select( editorStore ); - return { - isReady: __unstableIsEditorReady(), - selection: getEditorSelection(), - }; - }, [] ); - const { id, type } = __unstableTemplate ?? post; - const [ blocks, onInput, onChange ] = useEntityBlockEditor( - 'postType', - type, - { id } - ); - const editorSettings = useBlockEditorSettings( +const { ExperimentalBlockEditorProvider } = unlock( blockEditorExperiments ); + +export const ExperimentalEditorProvider = withRegistryProvider( + ( { + __unstableTemplate, + post, settings, - !! __unstableTemplate - ); - const { - updatePostLock, - setupEditor, - updateEditorSettings, - __experimentalTearDownEditor, - } = useDispatch( editorStore ); - const { createWarningNotice } = useDispatch( noticesStore ); + recovery, + initialEdits, + children, + BlockEditorProviderComponent = ExperimentalBlockEditorProvider, + } ) => { + const defaultBlockContext = useMemo( () => { + if ( post.type === 'wp_template' ) { + return {}; + } + return { postId: post.id, postType: post.type }; + }, [ post.id, post.type ] ); + const { selection, isReady } = useSelect( ( select ) => { + const { getEditorSelection, __unstableIsEditorReady } = + select( editorStore ); + return { + isReady: __unstableIsEditorReady(), + selection: getEditorSelection(), + }; + }, [] ); + const { id, type } = __unstableTemplate ?? post; + const [ blocks, onInput, onChange ] = useEntityBlockEditor( + 'postType', + type, + { id } + ); + const editorSettings = useBlockEditorSettings( + settings, + !! __unstableTemplate + ); + const { + updatePostLock, + setupEditor, + updateEditorSettings, + __experimentalTearDownEditor, + } = useDispatch( editorStore ); + const { createWarningNotice } = useDispatch( noticesStore ); - // Initialize and tear down the editor. - // Ideally this should be synced on each change and not just something you do once. - useLayoutEffect( () => { - // Assume that we don't need to initialize in the case of an error recovery. - if ( recovery ) { - return; - } + // Initialize and tear down the editor. + // Ideally this should be synced on each change and not just something you do once. + useLayoutEffect( () => { + // Assume that we don't need to initialize in the case of an error recovery. + if ( recovery ) { + return; + } - updatePostLock( settings.postLock ); - setupEditor( post, initialEdits, settings.template ); - if ( settings.autosave ) { - createWarningNotice( - __( - 'There is an autosave of this post that is more recent than the version below.' - ), - { - id: 'autosave-exists', - actions: [ - { - label: __( 'View the autosave' ), - url: settings.autosave.editLink, - }, - ], - } - ); - } + updatePostLock( settings.postLock ); + setupEditor( post, initialEdits, settings.template ); + if ( settings.autosave ) { + createWarningNotice( + __( + 'There is an autosave of this post that is more recent than the version below.' + ), + { + id: 'autosave-exists', + actions: [ + { + label: __( 'View the autosave' ), + url: settings.autosave.editLink, + }, + ], + } + ); + } + + return () => { + __experimentalTearDownEditor(); + }; + }, [] ); - return () => { - __experimentalTearDownEditor(); - }; - }, [] ); + // Synchronize the editor settings as they change. + useEffect( () => { + updateEditorSettings( settings ); + }, [ settings ] ); - // Synchronize the editor settings as they change. - useEffect( () => { - updateEditorSettings( settings ); - }, [ settings ] ); + if ( ! isReady ) { + return null; + } - if ( ! isReady ) { - return null; + return ( + + + + + { children } + + + + + + ); } +); +export function EditorProvider( props ) { return ( - - - - - { children } - - - - - + + { props.children } + ); } -export default withRegistryProvider( EditorProvider ); +export default EditorProvider; diff --git a/packages/editor/src/components/provider/index.native.js b/packages/editor/src/components/provider/index.native.js index 7679901bf37d2c..5b4148d5efef74 100644 --- a/packages/editor/src/components/provider/index.native.js +++ b/packages/editor/src/components/provider/index.native.js @@ -345,7 +345,7 @@ class NativeEditorProvider extends Component { } } -export default compose( [ +const ComposedNativeProvider = compose( [ withSelect( ( select ) => { const { __unstableIsEditorReady: isEditorReady, @@ -414,3 +414,6 @@ export default compose( [ }; } ), ] )( NativeEditorProvider ); + +export default ComposedNativeProvider; +export { ComposedNativeProvider as ExperimentalEditorProvider }; diff --git a/packages/editor/src/experiments.js b/packages/editor/src/experiments.js new file mode 100644 index 00000000000000..c54c895d961a03 --- /dev/null +++ b/packages/editor/src/experiments.js @@ -0,0 +1,10 @@ +/** + * Internal dependencies + */ +import { ExperimentalEditorProvider } from './components/provider'; +import { lock } from './lockUnlock'; + +export const experiments = {}; +lock( experiments, { + ExperimentalEditorProvider, +} ); diff --git a/packages/editor/src/index.js b/packages/editor/src/index.js index 31fe9a672a72d1..2651b6887723b6 100644 --- a/packages/editor/src/index.js +++ b/packages/editor/src/index.js @@ -6,6 +6,7 @@ import './hooks'; export { storeConfig, store } from './store'; export * from './components'; export * from './utils'; +export * from './experiments'; /* * Backward compatibility diff --git a/packages/editor/src/index.native.js b/packages/editor/src/index.native.js index 35fcddf0b80030..3c599ddf6bec5e 100644 --- a/packages/editor/src/index.native.js +++ b/packages/editor/src/index.native.js @@ -13,3 +13,4 @@ import './hooks'; export { store } from './store'; export * from './components'; export * from './utils'; +export * from './experiments'; diff --git a/packages/editor/src/lockUnlock.js b/packages/editor/src/lockUnlock.js new file mode 100644 index 00000000000000..0ca65646f0b3d9 --- /dev/null +++ b/packages/editor/src/lockUnlock.js @@ -0,0 +1,9 @@ +/** + * 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/editor' + ); diff --git a/packages/experiments/src/implementation.js b/packages/experiments/src/implementation.js index b818a3669d8e7a..6ba5ef6dfa611f 100644 --- a/packages/experiments/src/implementation.js +++ b/packages/experiments/src/implementation.js @@ -11,9 +11,14 @@ */ const CORE_MODULES_USING_EXPERIMENTS = [ '@wordpress/data', + '@wordpress/editor', '@wordpress/blocks', '@wordpress/block-editor', + '@wordpress/customize-widgets', '@wordpress/edit-site', + '@wordpress/edit-post', + '@wordpress/edit-widgets', + '@wordpress/edit-navigation', ]; /**