diff --git a/docs/reference-guides/data/data-core-edit-post.md b/docs/reference-guides/data/data-core-edit-post.md index 782c34192308cd..e39a5b483b8949 100644 --- a/docs/reference-guides/data/data-core-edit-post.md +++ b/docs/reference-guides/data/data-core-edit-post.md @@ -490,7 +490,11 @@ _Returns_ ### switchEditorMode -Undocumented declaration. +Triggers an action used to switch editor mode. + +_Parameters_ + +- _mode_ `string`: The editor mode. ### toggleEditorPanelEnabled diff --git a/package-lock.json b/package-lock.json index 9873ed9bb793b2..ad3f87027f6234 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16267,6 +16267,7 @@ "@wordpress/keycodes": "file:packages/keycodes", "@wordpress/notices": "file:packages/notices", "@wordpress/plugins": "file:packages/plugins", + "@wordpress/preferences": "file:packages/preferences", "@wordpress/url": "file:packages/url", "classnames": "^2.3.1", "lodash": "^4.17.21" @@ -16296,6 +16297,7 @@ "@wordpress/media-utils": "file:packages/media-utils", "@wordpress/notices": "file:packages/notices", "@wordpress/plugins": "file:packages/plugins", + "@wordpress/preferences": "file:packages/preferences", "@wordpress/url": "file:packages/url", "@wordpress/viewport": "file:packages/viewport", "@wordpress/warning": "file:packages/warning", @@ -16638,10 +16640,12 @@ "@wordpress/components": "file:packages/components", "@wordpress/compose": "file:packages/compose", "@wordpress/data": "file:packages/data", + "@wordpress/deprecated": "file:packages/deprecated", "@wordpress/element": "file:packages/element", "@wordpress/i18n": "file:packages/i18n", "@wordpress/icons": "file:packages/icons", "@wordpress/plugins": "file:packages/plugins", + "@wordpress/preferences": "file:packages/preferences", "@wordpress/viewport": "file:packages/viewport", "classnames": "^2.3.1", "lodash": "^4.17.21" diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss index 68b706fb8fc390..aa054b8ef6286f 100644 --- a/packages/base-styles/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -152,7 +152,6 @@ $z-layers: ( // ...Except for popovers immediately beneath wp-admin menu on large breakpoints ".components-popover.block-editor-inserter__popover": 99999, ".components-popover.table-of-contents__popover": 99998, - ".components-popover.edit-post-more-menu__content": 99998, ".components-popover.edit-site-more-menu__content": 99998, ".components-popover.interface-more-menu__content": 99998, ".components-popover.block-editor-rich-text__inline-format-toolbar": 99998, diff --git a/packages/data/src/plugins/persistence/index.js b/packages/data/src/plugins/persistence/index.js index b2702a36c325fe..2f444d6ca591aa 100644 --- a/packages/data/src/plugins/persistence/index.js +++ b/packages/data/src/plugins/persistence/index.js @@ -296,53 +296,6 @@ export function migrateFeaturePreferencesToPreferencesStore( } } -/** - * Move the 'features' object in local storage from the sourceStoreName to the - * interface store. - * - * @param {Object} persistence The persistence interface. - * @param {string} sourceStoreName The name of the store that has persisted - * preferences to migrate to the interface - * package. - */ -export function migrateFeaturePreferencesToInterfaceStore( - persistence, - sourceStoreName -) { - const interfaceStoreName = 'core/interface'; - const state = persistence.get(); - const sourcePreferences = state[ sourceStoreName ]?.preferences; - const sourceFeatures = sourcePreferences?.features; - - if ( sourceFeatures ) { - const targetFeatures = - state[ interfaceStoreName ]?.preferences?.features; - - // Avoid migrating features again if they've previously been migrated. - if ( ! targetFeatures?.[ sourceStoreName ] ) { - // Set the feature values in the interface store, the features - // object is keyed by 'scope', which matches the store name for - // the source. - persistence.set( interfaceStoreName, { - preferences: { - features: { - ...targetFeatures, - [ sourceStoreName ]: sourceFeatures, - }, - }, - } ); - - // Remove feature preferences from the source. - persistence.set( sourceStoreName, { - preferences: { - ...sourcePreferences, - features: undefined, - }, - } ); - } - } -} - /** * Deprecated: Remove this function and the code in WordPress Core that calls * it once WordPress 6.0 is released. @@ -359,7 +312,10 @@ persistencePlugin.__unstableMigrate = ( pluginOptions ) => { persistence, 'core/customize-widgets' ); - migrateFeaturePreferencesToInterfaceStore( persistence, 'core/edit-post' ); + migrateFeaturePreferencesToPreferencesStore( + persistence, + 'core/edit-post' + ); }; export default persistencePlugin; diff --git a/packages/data/src/plugins/persistence/test/index.js b/packages/data/src/plugins/persistence/test/index.js index ee34b97fb09bf5..613b1313840725 100644 --- a/packages/data/src/plugins/persistence/test/index.js +++ b/packages/data/src/plugins/persistence/test/index.js @@ -9,7 +9,6 @@ import deepFreeze from 'deep-freeze'; import plugin, { createPersistenceInterface, withLazySameState, - migrateFeaturePreferencesToInterfaceStore, migrateFeaturePreferencesToPreferencesStore, } from '../'; import objectStorage from '../storage/object'; @@ -383,13 +382,13 @@ describe( 'persistence', () => { } ); } ); -describe( 'migrateFeaturePreferencesToInterfaceStore', () => { - it( 'migrates preferences from the source to the interface store', () => { +describe( 'migrateFeaturePreferencesToPreferencesStore', () => { + it( 'migrates multiple preferences from persisted source stores to preferences', () => { const persistenceInterface = createPersistenceInterface( { storageKey: 'test-username', } ); - const initialState = { + const sourceStateA = { preferences: { features: { featureA: true, @@ -399,26 +398,50 @@ describe( 'migrateFeaturePreferencesToInterfaceStore', () => { }, }; - persistenceInterface.set( 'core/test', initialState ); + const sourceStateB = { + preferences: { + features: { + featureD: true, + featureE: false, + featureF: true, + }, + }, + }; + + persistenceInterface.set( 'core/test-a', sourceStateA ); + persistenceInterface.set( 'core/test-b', sourceStateB ); - migrateFeaturePreferencesToInterfaceStore( + migrateFeaturePreferencesToPreferencesStore( persistenceInterface, - 'core/test' + 'core/test-a' + ); + + migrateFeaturePreferencesToPreferencesStore( + persistenceInterface, + 'core/test-b' ); expect( persistenceInterface.get() ).toEqual( { - 'core/interface': { + 'core/preferences': { preferences: { - features: { - 'core/test': { - featureA: true, - featureB: false, - featureC: true, - }, + 'core/test-a': { + featureA: true, + featureB: false, + featureC: true, + }, + 'core/test-b': { + featureD: true, + featureE: false, + featureF: true, }, }, }, - 'core/test': { + 'core/test-a': { + preferences: { + features: undefined, + }, + }, + 'core/test-b': { preferences: { features: undefined, }, @@ -426,12 +449,12 @@ describe( 'migrateFeaturePreferencesToInterfaceStore', () => { } ); } ); - it( 'handles multiple preferences from different stores to be migrated', () => { + it( 'migrates multiple preferences from the persisted interface store to preferences, with interface state taking precedence over source stores', () => { const persistenceInterface = createPersistenceInterface( { storageKey: 'test-username', } ); - const initialStateA = { + const sourceStateA = { preferences: { features: { featureA: true, @@ -441,7 +464,7 @@ describe( 'migrateFeaturePreferencesToInterfaceStore', () => { }, }; - const initialStateB = { + const sourceStateB = { preferences: { features: { featureD: true, @@ -451,33 +474,57 @@ describe( 'migrateFeaturePreferencesToInterfaceStore', () => { }, }; - persistenceInterface.set( 'core/test-a', initialStateA ); - persistenceInterface.set( 'core/test-b', initialStateB ); + const interfaceState = { + preferences: { + features: { + 'core/test-a': { + featureG: true, + featureH: false, + featureI: true, + }, + 'core/test-b': { + featureJ: true, + featureK: false, + featureL: true, + }, + }, + }, + }; + + persistenceInterface.set( 'core/test-a', sourceStateA ); + persistenceInterface.set( 'core/test-b', sourceStateB ); + persistenceInterface.set( 'core/interface', interfaceState ); - migrateFeaturePreferencesToInterfaceStore( + migrateFeaturePreferencesToPreferencesStore( persistenceInterface, 'core/test-a' ); - migrateFeaturePreferencesToInterfaceStore( + migrateFeaturePreferencesToPreferencesStore( persistenceInterface, 'core/test-b' ); expect( persistenceInterface.get() ).toEqual( { + 'core/preferences': { + preferences: { + 'core/test-a': { + featureG: true, + featureH: false, + featureI: true, + }, + 'core/test-b': { + featureJ: true, + featureK: false, + featureL: true, + }, + }, + }, 'core/interface': { preferences: { features: { - 'core/test-a': { - featureA: true, - featureB: false, - featureC: true, - }, - 'core/test-b': { - featureD: true, - featureE: false, - featureF: true, - }, + 'core/test-a': undefined, + 'core/test-b': undefined, }, }, }, @@ -494,89 +541,55 @@ describe( 'migrateFeaturePreferencesToInterfaceStore', () => { } ); } ); - describe( 'migrateFeaturePreferencesToPreferencesStore', () => { - it( 'migrates multiple preferences from persisted source stores to preferences', () => { - const persistenceInterface = createPersistenceInterface( { - storageKey: 'test-username', - } ); + it( 'only migrates persisted data for the source name from source stores', () => { + const persistenceInterface = createPersistenceInterface( { + storageKey: 'test-username', + } ); - const sourceStateA = { - preferences: { - features: { - featureA: true, - featureB: false, - featureC: true, - }, + const sourceStateA = { + preferences: { + features: { + featureA: true, + featureB: false, + featureC: true, }, - }; + }, + }; - const sourceStateB = { - preferences: { - features: { - featureD: true, - featureE: false, - featureF: true, - }, - }, - }; - - persistenceInterface.set( 'core/test-a', sourceStateA ); - persistenceInterface.set( 'core/test-b', sourceStateB ); - - migrateFeaturePreferencesToPreferencesStore( - persistenceInterface, - 'core/test-a' - ); - - migrateFeaturePreferencesToPreferencesStore( - persistenceInterface, - 'core/test-b' - ); - - expect( persistenceInterface.get() ).toEqual( { - 'core/preferences': { - preferences: { - 'core/test-a': { - featureA: true, - featureB: false, - featureC: true, - }, - 'core/test-b': { - featureD: true, - featureE: false, - featureF: true, - }, - }, - }, - 'core/test-a': { - preferences: { - features: undefined, - }, - }, - 'core/test-b': { - preferences: { - features: undefined, - }, + const sourceStateB = { + preferences: { + features: { + featureD: true, + featureE: false, + featureF: true, }, - } ); - } ); + }, + }; - it( 'migrates multiple preferences from the persisted interface store to preferences, with interface state taking precedence over source stores', () => { - const persistenceInterface = createPersistenceInterface( { - storageKey: 'test-username', - } ); + persistenceInterface.set( 'core/test-a', sourceStateA ); + persistenceInterface.set( 'core/test-b', sourceStateB ); + + migrateFeaturePreferencesToPreferencesStore( + persistenceInterface, + 'core/test-a' + ); - const sourceStateA = { + expect( persistenceInterface.get() ).toEqual( { + 'core/preferences': { preferences: { - features: { + 'core/test-a': { featureA: true, featureB: false, featureC: true, }, }, - }; - - const sourceStateB = { + }, + 'core/test-a': { + preferences: { + features: undefined, + }, + }, + 'core/test-b': { preferences: { features: { featureD: true, @@ -584,148 +597,53 @@ describe( 'migrateFeaturePreferencesToInterfaceStore', () => { featureF: true, }, }, - }; + }, + } ); + } ); - const interfaceState = { - preferences: { - features: { - 'core/test-a': { - featureG: true, - featureH: false, - featureI: true, - }, - 'core/test-b': { - featureJ: true, - featureK: false, - featureL: true, - }, - }, - }, - }; - - persistenceInterface.set( 'core/test-a', sourceStateA ); - persistenceInterface.set( 'core/test-b', sourceStateB ); - persistenceInterface.set( 'core/interface', interfaceState ); - - migrateFeaturePreferencesToPreferencesStore( - persistenceInterface, - 'core/test-a' - ); - - migrateFeaturePreferencesToPreferencesStore( - persistenceInterface, - 'core/test-b' - ); - - expect( persistenceInterface.get() ).toEqual( { - 'core/preferences': { - preferences: { - 'core/test-a': { - featureG: true, - featureH: false, - featureI: true, - }, - 'core/test-b': { - featureJ: true, - featureK: false, - featureL: true, - }, - }, - }, - 'core/interface': { - preferences: { - features: { - 'core/test-a': undefined, - 'core/test-b': undefined, - }, - }, - }, - 'core/test-a': { - preferences: { - features: undefined, + it( 'only migrates persisted data for the source name from interface', () => { + const persistenceInterface = createPersistenceInterface( { + storageKey: 'test-username', + } ); + + const interfaceState = { + preferences: { + features: { + 'core/test-a': { + featureG: true, + featureH: false, + featureI: true, }, - }, - 'core/test-b': { - preferences: { - features: undefined, + 'core/test-b': { + featureJ: true, + featureK: false, + featureL: true, }, }, - } ); - } ); + }, + }; - it( 'only migrates persisted data for the source name from source stores', () => { - const persistenceInterface = createPersistenceInterface( { - storageKey: 'test-username', - } ); + persistenceInterface.set( 'core/interface', interfaceState ); - const sourceStateA = { - preferences: { - features: { - featureA: true, - featureB: false, - featureC: true, - }, - }, - }; + migrateFeaturePreferencesToPreferencesStore( + persistenceInterface, + 'core/test-a' + ); - const sourceStateB = { + expect( persistenceInterface.get() ).toEqual( { + 'core/preferences': { preferences: { - features: { - featureD: true, - featureE: false, - featureF: true, - }, - }, - }; - - persistenceInterface.set( 'core/test-a', sourceStateA ); - persistenceInterface.set( 'core/test-b', sourceStateB ); - - migrateFeaturePreferencesToPreferencesStore( - persistenceInterface, - 'core/test-a' - ); - - expect( persistenceInterface.get() ).toEqual( { - 'core/preferences': { - preferences: { - 'core/test-a': { - featureA: true, - featureB: false, - featureC: true, - }, + 'core/test-a': { + featureG: true, + featureH: false, + featureI: true, }, }, - 'core/test-a': { - preferences: { - features: undefined, - }, - }, - 'core/test-b': { - preferences: { - features: { - featureD: true, - featureE: false, - featureF: true, - }, - }, - }, - } ); - } ); - - it( 'only migrates persisted data for the source name from interface', () => { - const persistenceInterface = createPersistenceInterface( { - storageKey: 'test-username', - } ); - - const interfaceState = { + }, + 'core/interface': { preferences: { features: { - 'core/test-a': { - featureG: true, - featureH: false, - featureI: true, - }, + 'core/test-a': undefined, 'core/test-b': { featureJ: true, featureK: false, @@ -733,38 +651,7 @@ describe( 'migrateFeaturePreferencesToInterfaceStore', () => { }, }, }, - }; - - persistenceInterface.set( 'core/interface', interfaceState ); - - migrateFeaturePreferencesToPreferencesStore( - persistenceInterface, - 'core/test-a' - ); - - expect( persistenceInterface.get() ).toEqual( { - 'core/preferences': { - preferences: { - 'core/test-a': { - featureG: true, - featureH: false, - featureI: true, - }, - }, - }, - 'core/interface': { - preferences: { - features: { - 'core/test-a': undefined, - 'core/test-b': { - featureJ: true, - featureK: false, - featureL: true, - }, - }, - }, - }, - } ); + }, } ); } ); } ); diff --git a/packages/edit-navigation/package.json b/packages/edit-navigation/package.json index 39db7025f82019..ea504ea09bd069 100644 --- a/packages/edit-navigation/package.json +++ b/packages/edit-navigation/package.json @@ -47,6 +47,7 @@ "@wordpress/keycodes": "file:../keycodes", "@wordpress/notices": "file:../notices", "@wordpress/plugins": "file:../plugins", + "@wordpress/preferences": "file:../preferences", "@wordpress/url": "file:../url", "classnames": "^2.3.1", "lodash": "^4.17.21" diff --git a/packages/edit-navigation/src/components/header/more-menu.js b/packages/edit-navigation/src/components/header/more-menu.js index a75e60c1766e6f..31dbc4de63be19 100644 --- a/packages/edit-navigation/src/components/header/more-menu.js +++ b/packages/edit-navigation/src/components/header/more-menu.js @@ -2,9 +2,9 @@ * WordPress dependencies */ import { MenuGroup, MenuItem, VisuallyHidden } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; import { external } from '@wordpress/icons'; import { MoreMenuDropdown } from '@wordpress/interface'; -import { __ } from '@wordpress/i18n'; /** * Internal dependencies diff --git a/packages/edit-post/package.json b/packages/edit-post/package.json index 8773329118e5e8..3be4e70d9cf908 100644 --- a/packages/edit-post/package.json +++ b/packages/edit-post/package.json @@ -47,6 +47,7 @@ "@wordpress/media-utils": "file:../media-utils", "@wordpress/notices": "file:../notices", "@wordpress/plugins": "file:../plugins", + "@wordpress/preferences": "file:../preferences", "@wordpress/url": "file:../url", "@wordpress/viewport": "file:../viewport", "@wordpress/warning": "file:../warning", diff --git a/packages/edit-post/src/components/header/more-menu/index.js b/packages/edit-post/src/components/header/more-menu/index.js index ed06ba47259996..1d0b307509d1ba 100644 --- a/packages/edit-post/src/components/header/more-menu/index.js +++ b/packages/edit-post/src/components/header/more-menu/index.js @@ -4,8 +4,8 @@ import { __ } from '@wordpress/i18n'; import { MenuGroup } from '@wordpress/components'; import { - ActionItem, MoreMenuDropdown, + ActionItem, PinnedItems, } from '@wordpress/interface'; import { useViewportMatch } from '@wordpress/compose'; diff --git a/packages/edit-post/src/components/header/more-menu/style.scss b/packages/edit-post/src/components/header/more-menu/style.scss index 5dbe555304a407..c74f8beef7eb2a 100644 --- a/packages/edit-post/src/components/header/more-menu/style.scss +++ b/packages/edit-post/src/components/header/more-menu/style.scss @@ -29,7 +29,3 @@ padding: 0; } } - -.components-popover.edit-post-more-menu__content { - z-index: z-index(".components-popover.edit-post-more-menu__content"); -} diff --git a/packages/edit-post/src/components/header/writing-menu/index.js b/packages/edit-post/src/components/header/writing-menu/index.js index bcd4edad123ce2..a6e6052fd29df3 100644 --- a/packages/edit-post/src/components/header/writing-menu/index.js +++ b/packages/edit-post/src/components/header/writing-menu/index.js @@ -5,7 +5,7 @@ import { MenuGroup } from '@wordpress/components'; import { __, _x } from '@wordpress/i18n'; import { useViewportMatch } from '@wordpress/compose'; import { displayShortcut } from '@wordpress/keycodes'; -import { MoreMenuFeatureToggle } from '@wordpress/interface'; +import { PreferenceToggleMenuItem } from '@wordpress/preferences'; function WritingMenu() { const isLargeViewport = useViewportMatch( 'medium' ); @@ -15,9 +15,9 @@ function WritingMenu() { return ( - - - ); diff --git a/packages/edit-post/src/store/actions.js b/packages/edit-post/src/store/actions.js index 392e05ab38ec6a..71fdbad8dc8576 100644 --- a/packages/edit-post/src/store/actions.js +++ b/packages/edit-post/src/store/actions.js @@ -8,8 +8,9 @@ import { castArray, reduce } from 'lodash'; */ import { __ } from '@wordpress/i18n'; import apiFetch from '@wordpress/api-fetch'; -import { speak } from '@wordpress/a11y'; import { store as interfaceStore } from '@wordpress/interface'; +import { store as preferencesStore } from '@wordpress/preferences'; +import { speak } from '@wordpress/a11y'; import { store as noticesStore } from '@wordpress/notices'; import { store as coreStore } from '@wordpress/core-data'; import { store as blockEditorStore } from '@wordpress/block-editor'; @@ -147,10 +148,13 @@ export function removeEditorPanel( panelName ) { * @param {string} feature Feature name. */ export const toggleFeature = ( feature ) => ( { registry } ) => - registry - .dispatch( interfaceStore ) - .toggleFeature( 'core/edit-post', feature ); + registry.dispatch( preferencesStore ).toggle( 'core/edit-post', feature ); +/** + * Triggers an action used to switch editor mode. + * + * @param {string} mode The editor mode. + */ export const switchEditorMode = ( mode ) => ( { dispatch, registry } ) => { dispatch( { type: 'SWITCH_MODE', diff --git a/packages/edit-post/src/store/selectors.js b/packages/edit-post/src/store/selectors.js index 83fab6977ccb51..a9f6aacb97fcc7 100644 --- a/packages/edit-post/src/store/selectors.js +++ b/packages/edit-post/src/store/selectors.js @@ -9,6 +9,7 @@ import { get, includes, some, flatten, values } from 'lodash'; */ import { createRegistrySelector } from '@wordpress/data'; import { store as interfaceStore } from '@wordpress/interface'; +import { store as preferencesStore } from '@wordpress/preferences'; import { store as coreStore } from '@wordpress/core-data'; import { store as editorStore } from '@wordpress/editor'; /** @@ -191,10 +192,7 @@ export function isModalActive( state, modalName ) { */ export const isFeatureActive = createRegistrySelector( ( select ) => ( state, feature ) => { - return select( interfaceStore ).isFeatureActive( - 'core/edit-post', - feature - ); + return !! select( preferencesStore ).get( 'core/edit-post', feature ); } ); diff --git a/packages/edit-post/src/store/test/actions.js b/packages/edit-post/src/store/test/actions.js index 43ac2844a6dc96..82e750d76b8c8d 100644 --- a/packages/edit-post/src/store/test/actions.js +++ b/packages/edit-post/src/store/test/actions.js @@ -3,6 +3,7 @@ */ import { createRegistry } from '@wordpress/data'; import { store as interfaceStore } from '@wordpress/interface'; +import { store as preferencesStore } from '@wordpress/preferences'; import { store as noticesStore } from '@wordpress/notices'; import { store as coreStore } from '@wordpress/core-data'; import { store as blockEditorStore } from '@wordpress/block-editor'; @@ -22,6 +23,7 @@ function createRegistryWithStores() { blockEditorStore, coreStore, interfaceStore, + preferencesStore, editorStore, ].forEach( registry.register ); return registry; @@ -53,15 +55,15 @@ describe( 'actions', () => { registry.dispatch( editPostStore ).toggleFeature( 'welcomeGuide' ); expect( registry - .select( interfaceStore ) - .isFeatureActive( editPostStore.name, 'welcomeGuide' ) + .select( preferencesStore ) + .get( editPostStore.name, 'welcomeGuide' ) ).toBe( true ); registry.dispatch( editPostStore ).toggleFeature( 'welcomeGuide' ); expect( registry - .select( interfaceStore ) - .isFeatureActive( editPostStore.name, 'welcomeGuide' ) + .select( preferencesStore ) + .get( editPostStore.name, 'welcomeGuide' ) ).toBe( false ); } ); describe( 'switchEditorMode', () => { diff --git a/packages/interface/README.md b/packages/interface/README.md index 915be856af178d..1777cf212fa81d 100644 --- a/packages/interface/README.md +++ b/packages/interface/README.md @@ -69,71 +69,6 @@ wp.data.dispatch( 'core/interface' ).unpinItem( 'core/edit-post', 'edit-post-blo wp.data.select( 'core/interface' ).isItemPinned( 'core/edit-post', 'edit-post-block-patterns/block-patterns-sidebar' ); -> false ``` -### Preferences - -The interface package provides some helpers for implementing editor preferences. - -#### Features - -Features are boolean values used for toggling specific editor features on or off. - -Set the default values for any features on editor initialization: - -```js -import { dispatch } from '@wordpress/data'; -import { store as interfaceStore } from '@wordpress/interface'; - -function initialize() { - // ... - - dispatch( interfaceStore ).setFeatureDefaults( - 'namespace/editor-or-plugin-name', - { - myFeatureName: true, - } - ); - - // ... -} -``` - -Use the `toggleFeature` action and the `isFeatureActive` selector to toggle features within your app: - -```js -wp.data - .select( 'core/interface' ) - .isFeatureActive( 'namespace/editor-or-plugin-name', 'myFeatureName' ); // true -wp.data - .dispatch( 'core/interface' ) - .toggleFeature( 'namespace/editor-or-plugin-name', 'myFeatureName' ); -wp.data - .select( 'core/interface' ) - .isFeatureActive( 'namespace/editor-or-plugin-name', 'myFeatureName' ); // false -``` - -The `MoreMenuDropdown` and `MoreMenuFeatureToggle` components help to implement an editor menu for changing preferences and feature values. - -```jsx -function MyEditorMenu() { - return ( - - { () => ( - - - - ) } - - ); -} -``` - ## Contributing to this package This is an individual package that's part of the Gutenberg project. The project is organized as a monorepo. It's made up of multiple self-contained software packages, each with a specific purpose. The packages in this monorepo are published to [npm](https://www.npmjs.com/) and used by [WordPress](https://make.wordpress.org/core/) as well as other software projects. diff --git a/packages/interface/package.json b/packages/interface/package.json index a5d4fac82e1e4c..008d5c29268b6c 100644 --- a/packages/interface/package.json +++ b/packages/interface/package.json @@ -36,10 +36,12 @@ "@wordpress/components": "file:../components", "@wordpress/compose": "file:../compose", "@wordpress/data": "file:../data", + "@wordpress/deprecated": "file:../deprecated", "@wordpress/element": "file:../element", "@wordpress/i18n": "file:../i18n", "@wordpress/icons": "file:../icons", "@wordpress/plugins": "file:../plugins", + "@wordpress/preferences": "file:../preferences", "@wordpress/viewport": "file:../viewport", "classnames": "^2.3.1", "lodash": "^4.17.21" diff --git a/packages/interface/src/components/index.js b/packages/interface/src/components/index.js index 27b41f4ecb999e..f6ec119cc44b05 100644 --- a/packages/interface/src/components/index.js +++ b/packages/interface/src/components/index.js @@ -3,6 +3,5 @@ export { default as ComplementaryAreaMoreMenuItem } from './complementary-area-m export { default as FullscreenMode } from './fullscreen-mode'; export { default as InterfaceSkeleton } from './interface-skeleton'; export { default as PinnedItems } from './pinned-items'; -export { default as MoreMenuDropdown } from './more-menu-dropdown'; -export { default as MoreMenuFeatureToggle } from './more-menu-feature-toggle'; export { default as ActionItem } from './action-item'; +export { default as MoreMenuDropdown } from './more-menu-dropdown'; diff --git a/packages/interface/src/components/more-menu-dropdown/README.md b/packages/interface/src/components/more-menu-dropdown/README.md index 712b1cb2645bb0..30f618f50bc72e 100644 --- a/packages/interface/src/components/more-menu-dropdown/README.md +++ b/packages/interface/src/components/more-menu-dropdown/README.md @@ -8,7 +8,7 @@ This component implements a `DropdownMenu` component from the `@wordpress/components` package. -See also the `MoreMenuFeatureToggle` component in the `@wordpress/interface` package, which provides an easy way to implement a feature toggle as a child of this component. Use with the `MenuGroup`, `MenuItem`, `MenuItemsChoice` components from the `@wordpress/components` package to implement more advanced behaviors. +See also the `PreferenceToggleMenuItem` component in the `@wordpress/preference` package, which provides an easy way to implement a feature toggle as a child of this component. Use with the `MenuGroup`, `MenuItem`, `MenuItemsChoice` components from the `@wordpress/components` package to implement more advanced behaviors. Note that just like the `DropdownMenu` component, this component accepts a render callback, which child elements should be returned from. @@ -20,9 +20,9 @@ function MyEditorMenu() { { () => ( - - select( interfaceStore ).isFeatureActive( scope, feature ), - [ feature ] - ); - const { toggleFeature } = useDispatch( interfaceStore ); - const speakMessage = () => { - if ( isActive ) { - speak( messageDeactivated || __( 'Feature deactivated' ) ); - } else { - speak( messageActivated || __( 'Feature activated' ) ); - } - }; - - return ( - { - toggleFeature( scope, feature ); - speakMessage(); - } } - role="menuitemcheckbox" - info={ info } - shortcut={ shortcut } - > - { label } - - ); -} diff --git a/packages/interface/src/store/actions.js b/packages/interface/src/store/actions.js index 12155d6a186e17..cfbace27faf4fc 100644 --- a/packages/interface/src/store/actions.js +++ b/packages/interface/src/store/actions.js @@ -1,3 +1,9 @@ +/** + * WordPress dependencies + */ +import deprecated from '@wordpress/deprecated'; +import { store as preferencesStore } from '@wordpress/preferences'; + /** * Returns an action object used in signalling that an active area should be changed. * @@ -90,9 +96,13 @@ export function unpinItem( scope, itemId ) { * @param {string} featureName The feature name. */ export function toggleFeature( scope, featureName ) { - return function ( { select, dispatch } ) { - const currentValue = select.isFeatureActive( scope, featureName ); - dispatch.setFeatureValue( scope, featureName, ! currentValue ); + deprecated( `dispatch( 'core/interface' ).toggleFeature`, { + since: '6.0', + alternative: `dispatch( 'core/preferences' ).toggle`, + } ); + + return function ( { registry } ) { + registry.dispatch( preferencesStore ).toggle( scope, featureName ); }; } @@ -107,11 +117,15 @@ export function toggleFeature( scope, featureName ) { * @return {Object} Action object. */ export function setFeatureValue( scope, featureName, value ) { - return { - type: 'SET_FEATURE_VALUE', - scope, - featureName, - value: !! value, + deprecated( `dispatch( 'core/interface' ).setFeatureValue`, { + since: '6.0', + alternative: `dispatch( 'core/preferences' ).set`, + } ); + + return function ( { registry } ) { + registry + .dispatch( preferencesStore ) + .set( scope, featureName, !! value ); }; } @@ -123,10 +137,13 @@ export function setFeatureValue( scope, featureName, value ) { * * @return {Object} Action object. */ -export function setFeatureDefaults( scope, defaults ) { - return { - type: 'SET_FEATURE_DEFAULTS', - scope, - defaults, +export function* setFeatureDefaults( scope, defaults ) { + deprecated( `dispatch( 'core/interface' ).setFeatureDefaults`, { + since: '6.0', + alternative: `dispatch( 'core/preferences' ).setDefaults`, + } ); + + return function ( { registry } ) { + registry.dispatch( preferencesStore ).setDefaults( scope, defaults ); }; } diff --git a/packages/interface/src/store/selectors.js b/packages/interface/src/store/selectors.js index becb2dd1684b1e..985f63693d6349 100644 --- a/packages/interface/src/store/selectors.js +++ b/packages/interface/src/store/selectors.js @@ -3,6 +3,13 @@ */ import { get } from 'lodash'; +/** + * WordPress dependencies + */ +import { createRegistrySelector } from '@wordpress/data'; +import deprecated from '@wordpress/deprecated'; +import { store as preferencesStore } from '@wordpress/preferences'; + /** * Returns the item that is enabled in a given scope. * @@ -72,12 +79,13 @@ export function isItemPinned( state, scope, item ) { * * @return {boolean} Is the feature enabled? */ -export function isFeatureActive( state, scope, featureName ) { - const featureValue = state.preferences.features[ scope ]?.[ featureName ]; - const defaultedFeatureValue = - featureValue !== undefined - ? featureValue - : state.preferenceDefaults.features[ scope ]?.[ featureName ]; +export const isFeatureActive = createRegistrySelector( + ( select ) => ( state, scope, featureName ) => { + deprecated( `select( 'core/interface' ).isFeatureActive`, { + since: '6.0', + alternative: `select( 'core/preferences' ).get`, + } ); - return !! defaultedFeatureValue; -} + return !! select( preferencesStore ).get( scope, featureName ); + } +); diff --git a/packages/interface/src/store/test/selectors.js b/packages/interface/src/store/test/selectors.js deleted file mode 100644 index d5d9c60bf6f5ec..00000000000000 --- a/packages/interface/src/store/test/selectors.js +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Internal dependencies - */ -import { isFeatureActive } from '../selectors'; - -describe( 'selectors', () => { - describe( 'isFeatureActive', () => { - it( 'returns false if the there is no state for the feature', () => { - const emptyState = { - preferenceDefaults: { - features: {}, - }, - preferences: { - features: {}, - }, - }; - - expect( - isFeatureActive( emptyState, 'test-scope', 'testFeatureName' ) - ).toBe( false ); - } ); - - it( 'returns false if the the default for a feature is false and there is no preference state', () => { - const emptyState = { - preferenceDefaults: { - features: { - 'test-scope': { - testFeatureName: false, - }, - }, - }, - preferences: { - features: {}, - }, - }; - - expect( - isFeatureActive( emptyState, 'test-scope', 'testFeatureName' ) - ).toBe( false ); - } ); - - it( 'returns true if the the default for a feature is true and there is no preference state', () => { - const emptyState = { - preferenceDefaults: { - features: { - 'test-scope': { - testFeatureName: true, - }, - }, - }, - preferences: { - features: {}, - }, - }; - - expect( - isFeatureActive( emptyState, 'test-scope', 'testFeatureName' ) - ).toBe( true ); - } ); - - it( 'returns true if the the default for a feature is false but the preference is true', () => { - const emptyState = { - preferenceDefaults: { - features: { - 'test-scope': { - testFeatureName: false, - }, - }, - }, - preferences: { - features: { - 'test-scope': { - testFeatureName: true, - }, - }, - }, - }; - - expect( - isFeatureActive( emptyState, 'test-scope', 'testFeatureName' ) - ).toBe( true ); - } ); - - it( 'returns false if the the default for a feature is true but the preference is false', () => { - const emptyState = { - preferenceDefaults: { - features: { - 'test-scope': { - testFeatureName: true, - }, - }, - }, - preferences: { - features: { - 'test-scope': { - testFeatureName: false, - }, - }, - }, - }; - - expect( - isFeatureActive( emptyState, 'test-scope', 'testFeatureName' ) - ).toBe( false ); - } ); - } ); -} ); diff --git a/packages/interface/src/style.scss b/packages/interface/src/style.scss index e6950de411156a..d2b259a2ff7ff4 100644 --- a/packages/interface/src/style.scss +++ b/packages/interface/src/style.scss @@ -2,5 +2,5 @@ @import "./components/complementary-area/style.scss"; @import "./components/fullscreen-mode/style.scss"; @import "./components/interface-skeleton/style.scss"; -@import "./components/more-menu-dropdown/style.scss"; @import "./components/pinned-items/style.scss"; +@import "./components/more-menu-dropdown/style.scss";