From a1dd183ec09fe35f918eedb27aa171de8272fe8e Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 17 Feb 2022 11:56:01 +0800 Subject: [PATCH 01/10] Add new `@wordpress/preferences` package --- package-lock.json | 18 ++++ package.json | 1 + packages/preferences/.npmrc | 1 + packages/preferences/CHANGELOG.md | 7 ++ packages/preferences/README.md | 84 ++++++++++++++++++ packages/preferences/package.json | 44 ++++++++++ packages/preferences/src/components/index.js | 2 + .../components/more-menu-dropdown/README.md | 78 +++++++++++++++++ .../components/more-menu-dropdown/index.js | 46 ++++++++++ .../components/more-menu-dropdown/style.scss | 35 ++++++++ .../more-menu-feature-toggle/README.md | 59 +++++++++++++ .../more-menu-feature-toggle/index.js | 53 +++++++++++ packages/preferences/src/index.js | 2 + packages/preferences/src/store/actions.js | 47 ++++++++++ packages/preferences/src/store/constants.js | 6 ++ packages/preferences/src/store/index.js | 40 +++++++++ packages/preferences/src/store/reducer.js | 58 +++++++++++++ packages/preferences/src/store/selectors.js | 19 ++++ .../preferences/src/store/test/selectors.js | 87 +++++++++++++++++++ packages/preferences/src/style.scss | 1 + 20 files changed, 688 insertions(+) create mode 100644 packages/preferences/.npmrc create mode 100644 packages/preferences/CHANGELOG.md create mode 100644 packages/preferences/README.md create mode 100644 packages/preferences/package.json create mode 100644 packages/preferences/src/components/index.js create mode 100644 packages/preferences/src/components/more-menu-dropdown/README.md create mode 100644 packages/preferences/src/components/more-menu-dropdown/index.js create mode 100644 packages/preferences/src/components/more-menu-dropdown/style.scss create mode 100644 packages/preferences/src/components/more-menu-feature-toggle/README.md create mode 100644 packages/preferences/src/components/more-menu-feature-toggle/index.js create mode 100644 packages/preferences/src/index.js create mode 100644 packages/preferences/src/store/actions.js create mode 100644 packages/preferences/src/store/constants.js create mode 100644 packages/preferences/src/store/index.js create mode 100644 packages/preferences/src/store/reducer.js create mode 100644 packages/preferences/src/store/selectors.js create mode 100644 packages/preferences/src/store/test/selectors.js create mode 100644 packages/preferences/src/style.scss diff --git a/package-lock.json b/package-lock.json index ff9916158ba7c3..c4d59c64cca330 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16129,6 +16129,7 @@ "@wordpress/keyboard-shortcuts": "file:packages/keyboard-shortcuts", "@wordpress/keycodes": "file:packages/keycodes", "@wordpress/media-utils": "file:packages/media-utils", + "@wordpress/preferences": "file:packages/preferences", "@wordpress/widgets": "file:packages/widgets", "classnames": "^2.3.1", "lodash": "^4.17.21" @@ -16266,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" @@ -16295,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", @@ -16364,6 +16367,7 @@ "@wordpress/media-utils": "file:packages/media-utils", "@wordpress/notices": "file:packages/notices", "@wordpress/plugins": "file:packages/plugins", + "@wordpress/preferences": "file:packages/preferences", "@wordpress/reusable-blocks": "file:packages/reusable-blocks", "@wordpress/url": "file:packages/url", "@wordpress/widgets": "file:packages/widgets", @@ -16636,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" @@ -16782,6 +16788,18 @@ "version": "file:packages/postcss-themes", "dev": true }, + "@wordpress/preferences": { + "version": "file:packages/preferences", + "requires": { + "@babel/runtime": "^7.16.0", + "@wordpress/a11y": "file:packages/a11y", + "@wordpress/components": "file:packages/components", + "@wordpress/data": "file:packages/data", + "@wordpress/i18n": "file:packages/i18n", + "@wordpress/icons": "file:packages/icons", + "classnames": "^2.3.1" + } + }, "@wordpress/prettier-config": { "version": "file:packages/prettier-config", "dev": true diff --git a/package.json b/package.json index 1e2a6d80fd72b5..4e99cddb2e2650 100755 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "@wordpress/notices": "file:packages/notices", "@wordpress/nux": "file:packages/nux", "@wordpress/plugins": "file:packages/plugins", + "@wordpress/preferences": "file:packages/preferences", "@wordpress/primitives": "file:packages/primitives", "@wordpress/priority-queue": "file:packages/priority-queue", "@wordpress/react-i18n": "file:packages/react-i18n", diff --git a/packages/preferences/.npmrc b/packages/preferences/.npmrc new file mode 100644 index 00000000000000..43c97e719a5a82 --- /dev/null +++ b/packages/preferences/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/packages/preferences/CHANGELOG.md b/packages/preferences/CHANGELOG.md new file mode 100644 index 00000000000000..9054f06ae397a0 --- /dev/null +++ b/packages/preferences/CHANGELOG.md @@ -0,0 +1,7 @@ + + +## Unreleased + +## 1.0.0 + +- Initial version of the package. diff --git a/packages/preferences/README.md b/packages/preferences/README.md new file mode 100644 index 00000000000000..623fb746761c04 --- /dev/null +++ b/packages/preferences/README.md @@ -0,0 +1,84 @@ +# Preferences + +Utilities for storing WordPress preferences. + +## Installation + +Install the module + +```bash +npm install @wordpress/preferences --save +``` + +_This package assumes that your code will run in an **ES2015+** environment. If you're using an environment that has limited or no support for such language features and APIs, you should include [the polyfill shipped in `@wordpress/babel-preset-default`](https://github.com/WordPress/gutenberg/tree/HEAD/packages/babel-preset-default#polyfill) in your code._ + +## API Usage + +### 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 preferencesStore } from '@wordpress/preferences'; + +function initialize() { + // ... + + dispatch( preferencesStore ).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/preferences' ) + .isFeatureActive( 'namespace/editor-or-plugin-name', 'myFeatureName' ); // true +wp.data + .dispatch( 'core/preferences' ) + .toggleFeature( 'namespace/editor-or-plugin-name', 'myFeatureName' ); +wp.data + .select( 'core/preferences' ) + .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. + +To find out more about contributing to this package or Gutenberg as a whole, please read the project's main [contributor guide](https://github.com/WordPress/gutenberg/tree/HEAD/CONTRIBUTING.md). + +

Code is Poetry.

diff --git a/packages/preferences/package.json b/packages/preferences/package.json new file mode 100644 index 00000000000000..fbf4bc0e97ed43 --- /dev/null +++ b/packages/preferences/package.json @@ -0,0 +1,44 @@ +{ + "name": "@wordpress/preferences", + "version": "1.0.0-prerelease", + "private": true, + "description": "Utilities for managing WordPress preferences.", + "author": "The WordPress Contributors", + "license": "GPL-2.0-or-later", + "keywords": [ + "wordpress", + "gutenberg", + "preferences", + "settings", + "options" + ], + "homepage": "https://github.com/WordPress/gutenberg/tree/HEAD/packages/preferences/README.md", + "repository": { + "type": "git", + "url": "https://github.com/WordPress/gutenberg.git", + "directory": "packages/preferences" + }, + "bugs": { + "url": "https://github.com/WordPress/gutenberg/issues" + }, + "engines": { + "node": ">=12" + }, + "main": "build/index.js", + "module": "build-module/index.js", + "react-native": "src/index", + "types": "build-types", + "sideEffects": false, + "dependencies": { + "@babel/runtime": "^7.16.0", + "@wordpress/a11y": "file:../a11y", + "@wordpress/components": "file:../components", + "@wordpress/data": "file:../data", + "@wordpress/i18n": "file:../i18n", + "@wordpress/icons": "file:../icons", + "classnames": "^2.3.1" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/preferences/src/components/index.js b/packages/preferences/src/components/index.js new file mode 100644 index 00000000000000..694494c88b385e --- /dev/null +++ b/packages/preferences/src/components/index.js @@ -0,0 +1,2 @@ +export { default as MoreMenuDropdown } from './more-menu-dropdown'; +export { default as MoreMenuFeatureToggle } from './more-menu-feature-toggle'; diff --git a/packages/preferences/src/components/more-menu-dropdown/README.md b/packages/preferences/src/components/more-menu-dropdown/README.md new file mode 100644 index 00000000000000..712b1cb2645bb0 --- /dev/null +++ b/packages/preferences/src/components/more-menu-dropdown/README.md @@ -0,0 +1,78 @@ +# MoreMenuDropdown + +`MoreMenuDropdown` is a convenient component for rendering an editor 'more' menu. This is typically a menu that provides: + +- menu items for quick toggling editor preferences. +- a way to open dialogs for keyboard shortcuts and editor preferences. +- links to help. + +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. + +Note that just like the `DropdownMenu` component, this component accepts a render callback, which child elements should be returned from. + +## Example + +```jsx +function MyEditorMenu() { + return ( + + { () => ( + + + + ) } + + ); +} +``` + +## Props + +### as + +Provide a component that the dropdown should render as. This may be useful if you need `MoreMenuDropdown` to render a `ToolbarDropdownMenu` instead of a `DropdownMenu`. Defaults to `DropdownMenu`. + +- Type: `Component` +- Required: No. + +### className + +Provide an additional class name to the dropdown component. + +- Type: `String` +- Required: No + +### label + +Change the label of the button that opens the dropdown. + +- Default: 'Options' +- Type: `String` +- Required: No + +### popoverProps + +Override or extend the dropdown's popover props. + +See the documentation for the `DropdownMenu` and `Popover` components in the `@wordpress/components` package for more information. + +- Type: `Object` +- Required: No + +### toggleProps + +Override or extend the dropdown's toggle props. + +See the documentation for the `DropdownMenu` and `Button` components in the `@wordpress/components` package for more information. + +- Type: `Object` +- Required: No diff --git a/packages/preferences/src/components/more-menu-dropdown/index.js b/packages/preferences/src/components/more-menu-dropdown/index.js new file mode 100644 index 00000000000000..25d3bcc674f86a --- /dev/null +++ b/packages/preferences/src/components/more-menu-dropdown/index.js @@ -0,0 +1,46 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { DropdownMenu } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { moreVertical } from '@wordpress/icons'; + +export default function MoreMenuDropdown( { + as: DropdownComponent = DropdownMenu, + className, + /* translators: button label text should, if possible, be under 16 characters. */ + label = __( 'Options' ), + popoverProps, + toggleProps, + children, +} ) { + return ( + + { ( onClose ) => children( onClose ) } + + ); +} diff --git a/packages/preferences/src/components/more-menu-dropdown/style.scss b/packages/preferences/src/components/more-menu-dropdown/style.scss new file mode 100644 index 00000000000000..0c729facd98db8 --- /dev/null +++ b/packages/preferences/src/components/more-menu-dropdown/style.scss @@ -0,0 +1,35 @@ +.preferences-more-menu-dropdown { + margin-left: -4px; + + // the padding and margin of the more menu is intentionally non-standard + .components-button { + width: auto; + padding: 0 2px; + } + + @include break-small() { + margin-left: 0; + + .components-button { + padding: 0 4px; + } + } +} + +.preferences-more-menu-dropdown__content .components-popover__content { + min-width: 280px; + + // Let the menu scale to fit items. + @include break-mobile() { + width: auto; + max-width: $break-mobile; + } + + .components-dropdown-menu__menu { + padding: 0; + } +} + +.components-popover.preferences-more-menu-dropdown__content { + z-index: z-index(".components-popover.preferences-more-menu__content"); +} diff --git a/packages/preferences/src/components/more-menu-feature-toggle/README.md b/packages/preferences/src/components/more-menu-feature-toggle/README.md new file mode 100644 index 00000000000000..685ff49da2934e --- /dev/null +++ b/packages/preferences/src/components/more-menu-feature-toggle/README.md @@ -0,0 +1,59 @@ +# MoreMenuFeatureToggle + +`MoreMenuFeatureToggle` renders a menu item that can be used as a child of the `MoreMenuDropdown` component. The component +is connected to the interface package's store, and will toggle the value of a 'feature' between true and false. + +This component implements a `MenuItem` component from the `@wordpress/components` package. + +## Props + +### scope + +The 'scope' of the feature. This is usually a namespaced string that represents the name of the editor (e.g. 'core/edit-post'), and often matches the name of the store for the editor. + +- Type: `String` +- Required: Yes + +### feature + +The name of the feature to toggle (e.g. 'fixedToolbar'). + +- Type: `String` +- Required: Yes + +### label + +A human readable label for the feature. + +- Type: `String` +- Required: Yes + +### info + +A human readable description of what this toggle does. + +- Type: `Object` +- Required: No + +### messageActivated + +A message read by a screen reader when the feature is activated. (e.g. 'Fixed toolbar activated') + +- Type: `String` +- Required: No + +### messageDeactivated + +A message read by a screen reader when the feature is deactivated. (e.g. 'Fixed toolbar deactivated') + +- Type: `String` +- Required: No + +### shortcut + +A keyboard shortcut for the feature. This is just used for display purposes and the implementation of the shortcut should be handled separately. + +Consider using the `displayShortcut` helper from the `@wordpress/keycodes` package for this prop. + +- Type: `Array` +- Required: No diff --git a/packages/preferences/src/components/more-menu-feature-toggle/index.js b/packages/preferences/src/components/more-menu-feature-toggle/index.js new file mode 100644 index 00000000000000..10f3b3ead373e8 --- /dev/null +++ b/packages/preferences/src/components/more-menu-feature-toggle/index.js @@ -0,0 +1,53 @@ +/** + * WordPress dependencies + */ +import { useSelect, useDispatch } from '@wordpress/data'; +import { MenuItem } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { check } from '@wordpress/icons'; +import { speak } from '@wordpress/a11y'; + +/** + * Internal dependencies + */ +import { store as preferencesStore } from '../../store'; + +export default function MoreMenuFeatureToggle( { + scope, + label, + info, + messageActivated, + messageDeactivated, + shortcut, + feature, +} ) { + const isActive = useSelect( + ( select ) => + select( preferencesStore ).isFeatureActive( scope, feature ), + [ feature ] + ); + const { toggleFeature } = useDispatch( preferencesStore ); + 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/preferences/src/index.js b/packages/preferences/src/index.js new file mode 100644 index 00000000000000..72531a0824c178 --- /dev/null +++ b/packages/preferences/src/index.js @@ -0,0 +1,2 @@ +export * from './components'; +export { store } from './store'; diff --git a/packages/preferences/src/store/actions.js b/packages/preferences/src/store/actions.js new file mode 100644 index 00000000000000..46d8f99225bdcb --- /dev/null +++ b/packages/preferences/src/store/actions.js @@ -0,0 +1,47 @@ +/** + * Returns an action object used in signalling that a feature should be toggled. + * + * @param {string} scope The feature scope (e.g. core/edit-post). + * @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 ); + }; +} + +/** + * Returns an action object used in signalling that a feature should be set to + * a true or false value + * + * @param {string} scope The feature scope (e.g. core/edit-post). + * @param {string} featureName The feature name. + * @param {boolean} value The value to set. + * + * @return {Object} Action object. + */ +export function setFeatureValue( scope, featureName, value ) { + return { + type: 'SET_FEATURE_VALUE', + scope, + featureName, + value: !! value, + }; +} + +/** + * Returns an action object used in signalling that defaults should be set for features. + * + * @param {string} scope The feature scope (e.g. core/edit-post). + * @param {Object} defaults A key/value map of feature names to values. + * + * @return {Object} Action object. + */ +export function setFeatureDefaults( scope, defaults ) { + return { + type: 'SET_FEATURE_DEFAULTS', + scope, + defaults, + }; +} diff --git a/packages/preferences/src/store/constants.js b/packages/preferences/src/store/constants.js new file mode 100644 index 00000000000000..e09021a63a57dc --- /dev/null +++ b/packages/preferences/src/store/constants.js @@ -0,0 +1,6 @@ +/** + * The identifier for the data store. + * + * @type {string} + */ +export const STORE_NAME = 'core/preferences'; diff --git a/packages/preferences/src/store/index.js b/packages/preferences/src/store/index.js new file mode 100644 index 00000000000000..8850ded383fd7d --- /dev/null +++ b/packages/preferences/src/store/index.js @@ -0,0 +1,40 @@ +/** + * WordPress dependencies + */ +import { createReduxStore, registerStore } from '@wordpress/data'; + +/** + * Internal dependencies + */ +/** + * Internal dependencies + */ +import reducer from './reducer'; +import * as actions from './actions'; +import * as selectors from './selectors'; +import { STORE_NAME } from './constants'; + +/** + * Store definition for the interface namespace. + * + * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/data/README.md#createReduxStore + * + * @type {Object} + */ +export const store = createReduxStore( STORE_NAME, { + reducer, + actions, + selectors, + persist: [ 'features' ], + __experimentalUseThunks: true, +} ); + +// Once we build a more generic persistence plugin that works across types of stores +// we'd be able to replace this with a register call. +registerStore( STORE_NAME, { + reducer, + actions, + selectors, + persist: [ 'features' ], + __experimentalUseThunks: true, +} ); diff --git a/packages/preferences/src/store/reducer.js b/packages/preferences/src/store/reducer.js new file mode 100644 index 00000000000000..362359174e0740 --- /dev/null +++ b/packages/preferences/src/store/reducer.js @@ -0,0 +1,58 @@ +/** + * WordPress dependencies + */ +import { combineReducers } from '@wordpress/data'; + +/** + * Reducer returning the defaults for user preferences. + * + * This is kept intentionally separate from the preferences + * themselves so that defaults are not persisted. + * + * @param {Object} state Current state. + * @param {Object} action Dispatched action. + * + * @return {Object} Updated state. + */ +export function featureDefaults( state = {}, action ) { + if ( action.type === 'SET_FEATURE_DEFAULTS' ) { + const { scope, defaults } = action; + return { + ...state, + [ scope ]: { + ...state[ scope ], + ...defaults, + }, + }; + } + + return state; +} + +/** + * Reducer returning the user preferences. + * + * @param {Object} state Current state. + * @param {Object} action Dispatched action. + * + * @return {Object} Updated state. + */ +export function features( state = {}, action ) { + if ( action.type === 'SET_FEATURE_VALUE' ) { + const { scope, featureName, value } = action; + return { + ...state, + [ scope ]: { + ...state[ scope ], + [ featureName ]: value, + }, + }; + } + + return state; +} + +export default combineReducers( { + featureDefaults, + features, +} ); diff --git a/packages/preferences/src/store/selectors.js b/packages/preferences/src/store/selectors.js new file mode 100644 index 00000000000000..5ca21c88fbcafd --- /dev/null +++ b/packages/preferences/src/store/selectors.js @@ -0,0 +1,19 @@ +/** + * Returns a boolean indicating whether a feature is active for a particular + * scope. + * + * @param {Object} state The store state. + * @param {string} scope The scope of the feature (e.g. core/edit-post). + * @param {string} featureName The name of the feature. + * + * @return {boolean} Is the feature enabled? + */ +export function isFeatureActive( state, scope, featureName ) { + const featureValue = state.features[ scope ]?.[ featureName ]; + const defaultedFeatureValue = + featureValue !== undefined + ? featureValue + : state.featureDefaults[ scope ]?.[ featureName ]; + + return !! defaultedFeatureValue; +} diff --git a/packages/preferences/src/store/test/selectors.js b/packages/preferences/src/store/test/selectors.js new file mode 100644 index 00000000000000..2dd79c1298cfc9 --- /dev/null +++ b/packages/preferences/src/store/test/selectors.js @@ -0,0 +1,87 @@ +/** + * Internal dependencies + */ +import { isFeatureActive } from '../selectors'; + +describe( 'selectors', () => { + describe( 'isFeatureActive', () => { + it( 'returns false if the there is no state for the feature', () => { + const emptyState = { + featureDefaults: {}, + 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 = { + featureDefaults: { + 'test-scope': { + testFeatureName: false, + }, + }, + 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 = { + featureDefaults: { + 'test-scope': { + testFeatureName: true, + }, + }, + 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 = { + featureDefaults: { + 'test-scope': { + testFeatureName: false, + }, + }, + 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 = { + featureDefaults: { + 'test-scope': { + testFeatureName: true, + }, + }, + features: { + 'test-scope': { + testFeatureName: false, + }, + }, + }; + + expect( + isFeatureActive( emptyState, 'test-scope', 'testFeatureName' ) + ).toBe( false ); + } ); + } ); +} ); diff --git a/packages/preferences/src/style.scss b/packages/preferences/src/style.scss new file mode 100644 index 00000000000000..fcd7616195de03 --- /dev/null +++ b/packages/preferences/src/style.scss @@ -0,0 +1 @@ +@import "./components/more-menu-dropdown/style.scss"; From a7dcf28300d43cfa11c3bbe796d5aed845065ddb Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 17 Feb 2022 12:09:34 +0800 Subject: [PATCH 02/10] Add autogenerated API docs --- packages/preferences/README.md | 70 ++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/packages/preferences/README.md b/packages/preferences/README.md index 623fb746761c04..35e21228ad6049 100644 --- a/packages/preferences/README.md +++ b/packages/preferences/README.md @@ -75,6 +75,76 @@ function MyEditorMenu() { } ``` +## API Reference + +### Actions + +The following set of dispatching action creators are available on the object returned by `wp.data.dispatch( 'core/preferences' )`: + + + +#### setFeatureDefaults + +Returns an action object used in signalling that defaults should be set for features. + +_Parameters_ + +- _scope_ `string`: The feature scope (e.g. core/edit-post). +- _defaults_ `Object`: A key/value map of feature names to values. + +_Returns_ + +- `Object`: Action object. + +#### setFeatureValue + +Returns an action object used in signalling that a feature should be set to +a true or false value + +_Parameters_ + +- _scope_ `string`: The feature scope (e.g. core/edit-post). +- _featureName_ `string`: The feature name. +- _value_ `boolean`: The value to set. + +_Returns_ + +- `Object`: Action object. + +#### toggleFeature + +Returns an action object used in signalling that a feature should be toggled. + +_Parameters_ + +- _scope_ `string`: The feature scope (e.g. core/edit-post). +- _featureName_ `string`: The feature name. + + + +### Selectors + +The following selectors are available on the object returned by `wp.data.select( 'core/preferences' )`: + + + +#### isFeatureActive + +Returns a boolean indicating whether a feature is active for a particular +scope. + +_Parameters_ + +- _state_ `Object`: The store state. +- _scope_ `string`: The scope of the feature (e.g. core/edit-post). +- _featureName_ `string`: The name of the feature. + +_Returns_ + +- `boolean`: Is the feature enabled? + + + ## 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. From ce948ef6cfcd0d88532f65ede1f5a292837fadfc Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 17 Feb 2022 12:35:39 +0800 Subject: [PATCH 03/10] Update package-lock properly --- package-lock.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index c4d59c64cca330..4b6d5118f01093 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16129,7 +16129,6 @@ "@wordpress/keyboard-shortcuts": "file:packages/keyboard-shortcuts", "@wordpress/keycodes": "file:packages/keycodes", "@wordpress/media-utils": "file:packages/media-utils", - "@wordpress/preferences": "file:packages/preferences", "@wordpress/widgets": "file:packages/widgets", "classnames": "^2.3.1", "lodash": "^4.17.21" @@ -16267,7 +16266,6 @@ "@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" @@ -16297,7 +16295,6 @@ "@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", @@ -16367,7 +16364,6 @@ "@wordpress/media-utils": "file:packages/media-utils", "@wordpress/notices": "file:packages/notices", "@wordpress/plugins": "file:packages/plugins", - "@wordpress/preferences": "file:packages/preferences", "@wordpress/reusable-blocks": "file:packages/reusable-blocks", "@wordpress/url": "file:packages/url", "@wordpress/widgets": "file:packages/widgets", @@ -16640,12 +16636,10 @@ "@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" From 6fbe9f84f3a7917d4c81c83601f42fa616320141 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Thu, 17 Feb 2022 14:56:10 +0800 Subject: [PATCH 04/10] Add z-index entry for more menu --- packages/base-styles/_z-index.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss index 4cf4600b368bd8..1cfbd51d5566c9 100644 --- a/packages/base-styles/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -156,6 +156,7 @@ $z-layers: ( ".components-popover.edit-post-more-menu__content": 99998, ".components-popover.edit-site-more-menu__content": 99998, ".components-popover.edit-widgets-more-menu__content": 99998, + ".components-popover.preferences-more-menu__content": 99998, ".components-popover.block-editor-rich-text__inline-format-toolbar": 99998, ".components-popover.block-editor-warning__dropdown": 99998, ".components-popover.edit-navigation-menu-actions__switcher-dropdown": 99998, From 733246ac369c99d4874e8c7175e53814635c6d54 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 18 Feb 2022 11:39:12 +0800 Subject: [PATCH 05/10] Remove useThunks property from preferences store config --- packages/preferences/src/store/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/preferences/src/store/index.js b/packages/preferences/src/store/index.js index 8850ded383fd7d..41c134846e6d09 100644 --- a/packages/preferences/src/store/index.js +++ b/packages/preferences/src/store/index.js @@ -26,7 +26,6 @@ export const store = createReduxStore( STORE_NAME, { actions, selectors, persist: [ 'features' ], - __experimentalUseThunks: true, } ); // Once we build a more generic persistence plugin that works across types of stores @@ -36,5 +35,4 @@ registerStore( STORE_NAME, { actions, selectors, persist: [ 'features' ], - __experimentalUseThunks: true, } ); From 4bee2b785fe3a2564303d0bc1d5f4f2a7ae8b277 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 18 Feb 2022 13:01:17 +0800 Subject: [PATCH 06/10] Simplify preferences store API --- packages/preferences/README.md | 37 ++++++---- packages/preferences/src/components/index.js | 2 +- .../components/more-menu-dropdown/README.md | 6 +- .../README.md | 10 +-- .../index.js | 17 +++-- packages/preferences/src/store/actions.js | 42 +++++------ packages/preferences/src/store/index.js | 4 +- packages/preferences/src/store/reducer.js | 20 +++--- packages/preferences/src/store/selectors.js | 21 +++--- .../preferences/src/store/test/selectors.js | 71 +++++++------------ 10 files changed, 110 insertions(+), 120 deletions(-) rename packages/preferences/src/components/{more-menu-feature-toggle => more-menu-preference-toggle}/README.md (77%) rename packages/preferences/src/components/{more-menu-feature-toggle => more-menu-preference-toggle}/index.js (67%) diff --git a/packages/preferences/README.md b/packages/preferences/README.md index 35e21228ad6049..dbcd845233158a 100644 --- a/packages/preferences/README.md +++ b/packages/preferences/README.md @@ -14,11 +14,11 @@ _This package assumes that your code will run in an **ES2015+** environment. If ## API Usage -### Features +### Data store -Features are boolean values used for toggling specific editor features on or off. +Preferences are persisted values of any kind. -Set the default values for any features on editor initialization: +Set the default preferences for any features on initialization by dispatching an action: ```js import { dispatch } from '@wordpress/data'; @@ -27,10 +27,10 @@ import { store as preferencesStore } from '@wordpress/preferences'; function initialize() { // ... - dispatch( preferencesStore ).setFeatureDefaults( + dispatch( preferencesStore ).setDefaults( 'namespace/editor-or-plugin-name', { - myFeatureName: true, + myBooleanFeature: true, } ); @@ -38,21 +38,34 @@ function initialize() { } ``` -Use the `toggleFeature` action and the `isFeatureActive` selector to toggle features within your app: +Or the `get` selector to get a preference value, and the `set` action to update a preference to any value: +```js +wp.data + .select( 'core/preferences' ) + .get( 'namespace/editor-or-plugin-name', 'myPreferenceName' ); // 1 +wp.data + .dispatch( 'core/preferences' ) + .set( 'namespace/editor-or-plugin-name', 'myPreferenceName', 2 ); +wp.data + .select( 'core/preferences' ) + .get( 'namespace/editor-or-plugin-name', 'myPreferenceName' ); // 2 +``` + +Use the `toggle` action to flip a boolean preference between `true` and `false`: ```js wp.data .select( 'core/preferences' ) - .isFeatureActive( 'namespace/editor-or-plugin-name', 'myFeatureName' ); // true + .get( 'namespace/editor-or-plugin-name', 'myPreferenceName' ); // true wp.data .dispatch( 'core/preferences' ) - .toggleFeature( 'namespace/editor-or-plugin-name', 'myFeatureName' ); + .toggle( 'namespace/editor-or-plugin-name', 'myPreferenceName' ); wp.data .select( 'core/preferences' ) - .isFeatureActive( 'namespace/editor-or-plugin-name', 'myFeatureName' ); // false + .get( 'namespace/editor-or-plugin-name', 'myPreferenceName' ); // false ``` -The `MoreMenuDropdown` and `MoreMenuFeatureToggle` components help to implement an editor menu for changing preferences and feature values. +The `MoreMenuDropdown` and `MoreMenuPreferenceToggle` components help to implement an editor menu for changing preferences and feature values. ```jsx function MyEditorMenu() { @@ -60,9 +73,9 @@ function MyEditorMenu() { { () => ( - { () => ( - - select( preferencesStore ).isFeatureActive( scope, feature ), - [ feature ] + ( select ) => !! select( preferencesStore ).get( scope, name ), + [ name ] ); - const { toggleFeature } = useDispatch( preferencesStore ); + const { toggle } = useDispatch( preferencesStore ); const speakMessage = () => { if ( isActive ) { - speak( messageDeactivated || __( 'Feature deactivated' ) ); + speak( messageDeactivated || __( 'Preference deactivated' ) ); } else { - speak( messageActivated || __( 'Feature activated' ) ); + speak( messageActivated || __( 'Preference activated' ) ); } }; @@ -40,7 +39,7 @@ export default function MoreMenuFeatureToggle( { icon={ isActive && check } isSelected={ isActive } onClick={ () => { - toggleFeature( scope, feature ); + toggle( scope, name ); speakMessage(); } } role="menuitemcheckbox" diff --git a/packages/preferences/src/store/actions.js b/packages/preferences/src/store/actions.js index 46d8f99225bdcb..820a762c5d51e7 100644 --- a/packages/preferences/src/store/actions.js +++ b/packages/preferences/src/store/actions.js @@ -1,46 +1,48 @@ /** - * Returns an action object used in signalling that a feature should be toggled. + * Returns an action object used in signalling that a preference should be + * toggled. * - * @param {string} scope The feature scope (e.g. core/edit-post). - * @param {string} featureName The feature name. + * @param {string} scope The preference scope (e.g. core/edit-post). + * @param {string} name The preference name. */ -export function toggleFeature( scope, featureName ) { +export function toggle( scope, name ) { return function ( { select, dispatch } ) { - const currentValue = select.isFeatureActive( scope, featureName ); - dispatch.setFeatureValue( scope, featureName, ! currentValue ); + const currentValue = select.get( scope, name ); + dispatch.set( scope, name, ! currentValue ); }; } /** - * Returns an action object used in signalling that a feature should be set to - * a true or false value + * Returns an action object used in signalling that a preference should be set + * to a value * - * @param {string} scope The feature scope (e.g. core/edit-post). - * @param {string} featureName The feature name. - * @param {boolean} value The value to set. + * @param {string} scope The preference scope (e.g. core/edit-post). + * @param {string} name The preference name. + * @param {*} value The value to set. * * @return {Object} Action object. */ -export function setFeatureValue( scope, featureName, value ) { +export function set( scope, name, value ) { return { - type: 'SET_FEATURE_VALUE', + type: 'SET_PREFERENCE_VALUE', scope, - featureName, - value: !! value, + name, + value, }; } /** - * Returns an action object used in signalling that defaults should be set for features. + * Returns an action object used in signalling that preference defaults should + * be set. * - * @param {string} scope The feature scope (e.g. core/edit-post). - * @param {Object} defaults A key/value map of feature names to values. + * @param {string} scope The preference scope (e.g. core/edit-post). + * @param {Object} defaults A key/value map of preference names to values. * * @return {Object} Action object. */ -export function setFeatureDefaults( scope, defaults ) { +export function setDefaults( scope, defaults ) { return { - type: 'SET_FEATURE_DEFAULTS', + type: 'SET_PREFERENCE_DEFAULTS', scope, defaults, }; diff --git a/packages/preferences/src/store/index.js b/packages/preferences/src/store/index.js index 41c134846e6d09..6c7b560f0720cb 100644 --- a/packages/preferences/src/store/index.js +++ b/packages/preferences/src/store/index.js @@ -25,7 +25,7 @@ export const store = createReduxStore( STORE_NAME, { reducer, actions, selectors, - persist: [ 'features' ], + persist: [ 'preferences' ], } ); // Once we build a more generic persistence plugin that works across types of stores @@ -34,5 +34,5 @@ registerStore( STORE_NAME, { reducer, actions, selectors, - persist: [ 'features' ], + persist: [ 'preferences' ], } ); diff --git a/packages/preferences/src/store/reducer.js b/packages/preferences/src/store/reducer.js index 362359174e0740..7e4752a22ef249 100644 --- a/packages/preferences/src/store/reducer.js +++ b/packages/preferences/src/store/reducer.js @@ -14,14 +14,14 @@ import { combineReducers } from '@wordpress/data'; * * @return {Object} Updated state. */ -export function featureDefaults( state = {}, action ) { - if ( action.type === 'SET_FEATURE_DEFAULTS' ) { - const { scope, defaults } = action; +export function defaults( state = {}, action ) { + if ( action.type === 'SET_PREFERENCE_DEFAULTS' ) { + const { scope, defaults: values } = action; return { ...state, [ scope ]: { ...state[ scope ], - ...defaults, + ...values, }, }; } @@ -37,14 +37,14 @@ export function featureDefaults( state = {}, action ) { * * @return {Object} Updated state. */ -export function features( state = {}, action ) { - if ( action.type === 'SET_FEATURE_VALUE' ) { - const { scope, featureName, value } = action; +export function preferences( state = {}, action ) { + if ( action.type === 'SET_PREFERENCE_VALUE' ) { + const { scope, name, value } = action; return { ...state, [ scope ]: { ...state[ scope ], - [ featureName ]: value, + [ name ]: value, }, }; } @@ -53,6 +53,6 @@ export function features( state = {}, action ) { } export default combineReducers( { - featureDefaults, - features, + defaults, + preferences, } ); diff --git a/packages/preferences/src/store/selectors.js b/packages/preferences/src/store/selectors.js index 5ca21c88fbcafd..aed93cf6feebd8 100644 --- a/packages/preferences/src/store/selectors.js +++ b/packages/preferences/src/store/selectors.js @@ -1,19 +1,14 @@ /** - * Returns a boolean indicating whether a feature is active for a particular + * Returns a boolean indicating whether a prefer is active for a particular * scope. * - * @param {Object} state The store state. - * @param {string} scope The scope of the feature (e.g. core/edit-post). - * @param {string} featureName The name of the feature. + * @param {Object} state The store state. + * @param {string} scope The scope of the feature (e.g. core/edit-post). + * @param {string} name The name of the feature. * - * @return {boolean} Is the feature enabled? + * @return {*} Is the feature enabled? */ -export function isFeatureActive( state, scope, featureName ) { - const featureValue = state.features[ scope ]?.[ featureName ]; - const defaultedFeatureValue = - featureValue !== undefined - ? featureValue - : state.featureDefaults[ scope ]?.[ featureName ]; - - return !! defaultedFeatureValue; +export function get( state, scope, name ) { + const value = state.preferences[ scope ]?.[ name ]; + return value ?? state.defaults[ scope ]?.[ name ]; } diff --git a/packages/preferences/src/store/test/selectors.js b/packages/preferences/src/store/test/selectors.js index 2dd79c1298cfc9..8201f4dba72da4 100644 --- a/packages/preferences/src/store/test/selectors.js +++ b/packages/preferences/src/store/test/selectors.js @@ -1,87 +1,68 @@ /** * Internal dependencies */ -import { isFeatureActive } from '../selectors'; +import { get } from '../selectors'; describe( 'selectors', () => { - describe( 'isFeatureActive', () => { - it( 'returns false if the there is no state for the feature', () => { + describe( 'get', () => { + it( 'returns `undefined` if the there is no state for the preference', () => { const emptyState = { - featureDefaults: {}, - features: {}, + defaults: {}, + preferences: {}, }; expect( - isFeatureActive( emptyState, 'test-scope', 'testFeatureName' ) - ).toBe( false ); + get( emptyState, 'test-scope', 'testPreferenceName' ) + ).toBe( undefined ); } ); - it( 'returns false if the the default for a feature is false and there is no preference state', () => { + it( 'returns the default for a preference if the default is set and the preference has no value', () => { const emptyState = { - featureDefaults: { + defaults: { 'test-scope': { - testFeatureName: false, + testPreferenceName: 'test default', }, }, - features: {}, + preferences: {}, }; expect( - isFeatureActive( emptyState, 'test-scope', 'testFeatureName' ) - ).toBe( false ); + get( emptyState, 'test-scope', 'testPreferenceName' ) + ).toBe( 'test default' ); } ); - it( 'returns true if the the default for a feature is true and there is no preference state', () => { + it( 'returns the value for a preference if the preference is set and the default is not set', () => { const emptyState = { - featureDefaults: { + defaults: {}, + preferences: { 'test-scope': { - testFeatureName: true, + testPreferenceName: 'test value', }, }, - features: {}, }; expect( - isFeatureActive( emptyState, 'test-scope', 'testFeatureName' ) - ).toBe( true ); + get( emptyState, 'test-scope', 'testPreferenceName' ) + ).toBe( 'test value' ); } ); - it( 'returns true if the the default for a feature is false but the preference is true', () => { + it( 'returns the value for a preference if the preference and the default are set', () => { const emptyState = { - featureDefaults: { + defaults: { 'test-scope': { - testFeatureName: false, + testPreferenceName: 'test default', }, }, - features: { + preferences: { 'test-scope': { - testFeatureName: true, + testPreferenceName: 'test value', }, }, }; 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 = { - featureDefaults: { - 'test-scope': { - testFeatureName: true, - }, - }, - features: { - 'test-scope': { - testFeatureName: false, - }, - }, - }; - - expect( - isFeatureActive( emptyState, 'test-scope', 'testFeatureName' ) - ).toBe( false ); + get( emptyState, 'test-scope', 'testPreferenceName' ) + ).toBe( 'test value' ); } ); } ); } ); From 87f4cfc203f47b3524021ca5370a4dae6721ab85 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Fri, 18 Feb 2022 16:00:02 +0800 Subject: [PATCH 07/10] Update preferences README with new autogenerated API docs --- packages/preferences/README.md | 43 +++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/packages/preferences/README.md b/packages/preferences/README.md index dbcd845233158a..2be351ea295d6b 100644 --- a/packages/preferences/README.md +++ b/packages/preferences/README.md @@ -12,7 +12,7 @@ npm install @wordpress/preferences --save _This package assumes that your code will run in an **ES2015+** environment. If you're using an environment that has limited or no support for such language features and APIs, you should include [the polyfill shipped in `@wordpress/babel-preset-default`](https://github.com/WordPress/gutenberg/tree/HEAD/packages/babel-preset-default#polyfill) in your code._ -## API Usage +## Examples ### Data store @@ -39,6 +39,7 @@ function initialize() { ``` Or the `get` selector to get a preference value, and the `set` action to update a preference to any value: + ```js wp.data .select( 'core/preferences' ) @@ -65,6 +66,8 @@ wp.data .get( 'namespace/editor-or-plugin-name', 'myPreferenceName' ); // false ``` +### Components + The `MoreMenuDropdown` and `MoreMenuPreferenceToggle` components help to implement an editor menu for changing preferences and feature values. ```jsx @@ -96,42 +99,44 @@ The following set of dispatching action creators are available on the object ret -#### setFeatureDefaults +#### set -Returns an action object used in signalling that defaults should be set for features. +Returns an action object used in signalling that a preference should be set +to a value _Parameters_ -- _scope_ `string`: The feature scope (e.g. core/edit-post). -- _defaults_ `Object`: A key/value map of feature names to values. +- _scope_ `string`: The preference scope (e.g. core/edit-post). +- _name_ `string`: The preference name. +- _value_ `*`: The value to set. _Returns_ - `Object`: Action object. -#### setFeatureValue +#### setDefaults -Returns an action object used in signalling that a feature should be set to -a true or false value +Returns an action object used in signalling that preference defaults should +be set. _Parameters_ -- _scope_ `string`: The feature scope (e.g. core/edit-post). -- _featureName_ `string`: The feature name. -- _value_ `boolean`: The value to set. +- _scope_ `string`: The preference scope (e.g. core/edit-post). +- _defaults_ `Object`: A key/value map of preference names to values. _Returns_ - `Object`: Action object. -#### toggleFeature +#### toggle -Returns an action object used in signalling that a feature should be toggled. +Returns an action object used in signalling that a preference should be +toggled. _Parameters_ -- _scope_ `string`: The feature scope (e.g. core/edit-post). -- _featureName_ `string`: The feature name. +- _scope_ `string`: The preference scope (e.g. core/edit-post). +- _name_ `string`: The preference name. @@ -141,20 +146,20 @@ The following selectors are available on the object returned by `wp.data.select( -#### isFeatureActive +#### get -Returns a boolean indicating whether a feature is active for a particular +Returns a boolean indicating whether a prefer is active for a particular scope. _Parameters_ - _state_ `Object`: The store state. - _scope_ `string`: The scope of the feature (e.g. core/edit-post). -- _featureName_ `string`: The name of the feature. +- _name_ `string`: The name of the feature. _Returns_ -- `boolean`: Is the feature enabled? +- `*`: Is the feature enabled? From 150fbe440963b45da978e163752e49751e5cce1c Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 22 Feb 2022 16:23:48 +0800 Subject: [PATCH 08/10] Remove MoreMenuDropdown component (keep it in the interface package) --- packages/preferences/README.md | 2 +- packages/preferences/src/components/index.js | 1 - .../components/more-menu-dropdown/README.md | 78 ------------------- .../components/more-menu-dropdown/index.js | 46 ----------- .../components/more-menu-dropdown/style.scss | 35 --------- .../more-menu-preference-toggle/README.md | 3 +- packages/preferences/src/style.scss | 1 - 7 files changed, 2 insertions(+), 164 deletions(-) delete mode 100644 packages/preferences/src/components/more-menu-dropdown/README.md delete mode 100644 packages/preferences/src/components/more-menu-dropdown/index.js delete mode 100644 packages/preferences/src/components/more-menu-dropdown/style.scss delete mode 100644 packages/preferences/src/style.scss diff --git a/packages/preferences/README.md b/packages/preferences/README.md index 2be351ea295d6b..62c43a69242cbe 100644 --- a/packages/preferences/README.md +++ b/packages/preferences/README.md @@ -68,7 +68,7 @@ wp.data ### Components -The `MoreMenuDropdown` and `MoreMenuPreferenceToggle` components help to implement an editor menu for changing preferences and feature values. +The `MoreMenuPreferenceToggle` components helps to implement an editor menu for changing preferences and feature values. ```jsx function MyEditorMenu() { diff --git a/packages/preferences/src/components/index.js b/packages/preferences/src/components/index.js index 51b0fbe6bd574b..d1fdacb8595c00 100644 --- a/packages/preferences/src/components/index.js +++ b/packages/preferences/src/components/index.js @@ -1,2 +1 @@ -export { default as MoreMenuDropdown } from './more-menu-dropdown'; export { default as MoreMenuPreferenceToggle } from './more-menu-preference-toggle'; diff --git a/packages/preferences/src/components/more-menu-dropdown/README.md b/packages/preferences/src/components/more-menu-dropdown/README.md deleted file mode 100644 index 4ed63f5574c3e4..00000000000000 --- a/packages/preferences/src/components/more-menu-dropdown/README.md +++ /dev/null @@ -1,78 +0,0 @@ -# MoreMenuDropdown - -`MoreMenuDropdown` is a convenient component for rendering an editor 'more' menu. This is typically a menu that provides: - -- menu items for quick toggling editor preferences. -- a way to open dialogs for keyboard shortcuts and editor preferences. -- links to help. - -This component implements a `DropdownMenu` component from the `@wordpress/components` package. - -See also the `MoreMenuPreferenceToggle` 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. - -## Example - -```jsx -function MyEditorMenu() { - return ( - - { () => ( - - - - ) } - - ); -} -``` - -## Props - -### as - -Provide a component that the dropdown should render as. This may be useful if you need `MoreMenuDropdown` to render a `ToolbarDropdownMenu` instead of a `DropdownMenu`. Defaults to `DropdownMenu`. - -- Type: `Component` -- Required: No. - -### className - -Provide an additional class name to the dropdown component. - -- Type: `String` -- Required: No - -### label - -Change the label of the button that opens the dropdown. - -- Default: 'Options' -- Type: `String` -- Required: No - -### popoverProps - -Override or extend the dropdown's popover props. - -See the documentation for the `DropdownMenu` and `Popover` components in the `@wordpress/components` package for more information. - -- Type: `Object` -- Required: No - -### toggleProps - -Override or extend the dropdown's toggle props. - -See the documentation for the `DropdownMenu` and `Button` components in the `@wordpress/components` package for more information. - -- Type: `Object` -- Required: No diff --git a/packages/preferences/src/components/more-menu-dropdown/index.js b/packages/preferences/src/components/more-menu-dropdown/index.js deleted file mode 100644 index 25d3bcc674f86a..00000000000000 --- a/packages/preferences/src/components/more-menu-dropdown/index.js +++ /dev/null @@ -1,46 +0,0 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; - -/** - * WordPress dependencies - */ -import { DropdownMenu } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; -import { moreVertical } from '@wordpress/icons'; - -export default function MoreMenuDropdown( { - as: DropdownComponent = DropdownMenu, - className, - /* translators: button label text should, if possible, be under 16 characters. */ - label = __( 'Options' ), - popoverProps, - toggleProps, - children, -} ) { - return ( - - { ( onClose ) => children( onClose ) } - - ); -} diff --git a/packages/preferences/src/components/more-menu-dropdown/style.scss b/packages/preferences/src/components/more-menu-dropdown/style.scss deleted file mode 100644 index 0c729facd98db8..00000000000000 --- a/packages/preferences/src/components/more-menu-dropdown/style.scss +++ /dev/null @@ -1,35 +0,0 @@ -.preferences-more-menu-dropdown { - margin-left: -4px; - - // the padding and margin of the more menu is intentionally non-standard - .components-button { - width: auto; - padding: 0 2px; - } - - @include break-small() { - margin-left: 0; - - .components-button { - padding: 0 4px; - } - } -} - -.preferences-more-menu-dropdown__content .components-popover__content { - min-width: 280px; - - // Let the menu scale to fit items. - @include break-mobile() { - width: auto; - max-width: $break-mobile; - } - - .components-dropdown-menu__menu { - padding: 0; - } -} - -.components-popover.preferences-more-menu-dropdown__content { - z-index: z-index(".components-popover.preferences-more-menu__content"); -} diff --git a/packages/preferences/src/components/more-menu-preference-toggle/README.md b/packages/preferences/src/components/more-menu-preference-toggle/README.md index 84ab339278df1d..31e39c1df9899c 100644 --- a/packages/preferences/src/components/more-menu-preference-toggle/README.md +++ b/packages/preferences/src/components/more-menu-preference-toggle/README.md @@ -1,7 +1,6 @@ # MoreMenuPreferenceToggle -`MoreMenuPreferenceToggle` renders a menu item that can be used as a child of the `MoreMenuDropdown` component. The component -is connected to the preference package's store, and will toggle the value of a 'preference' between true and false. +`MoreMenuPreferenceToggle` renders a menu item that is connected to the preference package's store, and will toggle the value of a 'preference' between true and false. This component implements a `MenuItem` component from the `@wordpress/components` package. diff --git a/packages/preferences/src/style.scss b/packages/preferences/src/style.scss deleted file mode 100644 index fcd7616195de03..00000000000000 --- a/packages/preferences/src/style.scss +++ /dev/null @@ -1 +0,0 @@ -@import "./components/more-menu-dropdown/style.scss"; From 64ed5098a09796a60a6bd47a618e55a4b160aef9 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 22 Feb 2022 16:27:04 +0800 Subject: [PATCH 09/10] Rename MoreMenuPreferenceToggle to PreferenceToggleMenuItem --- packages/preferences/README.md | 6 ++++-- packages/preferences/src/components/index.js | 2 +- .../README.md | 4 ++-- .../index.js | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) rename packages/preferences/src/components/{more-menu-preference-toggle => preference-toggle-menu-item}/README.md (93%) rename packages/preferences/src/components/{more-menu-preference-toggle => preference-toggle-menu-item}/index.js (95%) diff --git a/packages/preferences/README.md b/packages/preferences/README.md index 62c43a69242cbe..72cc2fb1d9e884 100644 --- a/packages/preferences/README.md +++ b/packages/preferences/README.md @@ -68,7 +68,9 @@ wp.data ### Components -The `MoreMenuPreferenceToggle` components helps to implement an editor menu for changing preferences and feature values. +The `PreferenceToggleMenuItem` components can be used with a `DropdownMenu` to implement a menu for changing preferences. + +Also see the `MoreMenuDropdown` component from the `@wordpress/interface` package for implementing a more menu. ```jsx function MyEditorMenu() { @@ -76,7 +78,7 @@ function MyEditorMenu() { { () => ( - Date: Tue, 22 Feb 2022 17:10:03 +0800 Subject: [PATCH 10/10] Add preference label to spoken message --- .../preference-toggle-menu-item/index.js | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/preferences/src/components/preference-toggle-menu-item/index.js b/packages/preferences/src/components/preference-toggle-menu-item/index.js index 121b8a50f69fe9..5bd6da3441a9cf 100644 --- a/packages/preferences/src/components/preference-toggle-menu-item/index.js +++ b/packages/preferences/src/components/preference-toggle-menu-item/index.js @@ -3,7 +3,7 @@ */ import { useSelect, useDispatch } from '@wordpress/data'; import { MenuItem } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; +import { __, sprintf } from '@wordpress/i18n'; import { check } from '@wordpress/icons'; import { speak } from '@wordpress/a11y'; @@ -28,9 +28,23 @@ export default function PreferenceToggleMenuItem( { const { toggle } = useDispatch( preferencesStore ); const speakMessage = () => { if ( isActive ) { - speak( messageDeactivated || __( 'Preference deactivated' ) ); + const message = + messageDeactivated || + sprintf( + /* translators: %s: preference name, e.g. 'Fullscreen mode' */ + __( 'Preference deactivated - %s' ), + label + ); + speak( message ); } else { - speak( messageActivated || __( 'Preference activated' ) ); + const message = + messageActivated || + sprintf( + /* translators: %s: preference name, e.g. 'Fullscreen mode' */ + __( 'Preference activated - %s' ), + label + ); + speak( message ); } };