From 13593c0d7d9db44351493eeb6a9179a789bf260c Mon Sep 17 00:00:00 2001 From: Grzegorz Ziolkowski Date: Wed, 4 Mar 2020 13:52:27 +0100 Subject: [PATCH] Block Editor: Introduce experimental features API --- packages/block-editor/src/store/actions.js | 15 ++ packages/block-editor/src/store/defaults.js | 180 ++++++++++++++++++ packages/block-editor/src/store/reducer.js | 27 ++- packages/block-editor/src/store/selectors.js | 12 ++ .../block-editor/src/store/test/actions.js | 15 ++ .../block-editor/src/store/test/reducer.js | 49 +++++ .../block-editor/src/store/test/selectors.js | 27 +++ 7 files changed, 324 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js index c59bd31bdd9c6..9293ed52003e1 100644 --- a/packages/block-editor/src/store/actions.js +++ b/packages/block-editor/src/store/actions.js @@ -802,6 +802,21 @@ export function updateSettings( settings ) { }; } +/* + * Returns an action object used in signalling that the block editor features + * have been updated. + * + * @param {Object} features Updated features. + * + * @return {Object} Action object. + */ +export function __experimentalUpdateFeatures( features ) { + return { + type: 'UPDATE_FEATURES', + features, + }; +} + /** * Returns an action object used in signalling that a temporary reusable blocks have been saved * in order to switch its temporary id with the real id. diff --git a/packages/block-editor/src/store/defaults.js b/packages/block-editor/src/store/defaults.js index 3066066e3ea48..6b709f0aeb3ae 100644 --- a/packages/block-editor/src/store/defaults.js +++ b/packages/block-editor/src/store/defaults.js @@ -226,3 +226,183 @@ export const SETTINGS_DEFAULTS = { }, ], }; + +/** + * The default block editor features. + * + * @typedef {Object} FEATURES_DEFAULT + * + * @property {Array} colors Settings related to colors. + * @property {Array} typography Settings related to typography. + */ +export const FEATURES_DEFAULTS = { + colors: { + enabled: true, + colorPalette: [ + { + name: __( 'Pale pink' ), + slug: 'pale-pink', + color: '#f78da7', + }, + { name: __( 'Vivid red' ), slug: 'vivid-red', color: '#cf2e2e' }, + { + name: __( 'Luminous vivid orange' ), + slug: 'luminous-vivid-orange', + color: '#ff6900', + }, + { + name: __( 'Luminous vivid amber' ), + slug: 'luminous-vivid-amber', + color: '#fcb900', + }, + { + name: __( 'Light green cyan' ), + slug: 'light-green-cyan', + color: '#7bdcb5', + }, + { + name: __( 'Vivid green cyan' ), + slug: 'vivid-green-cyan', + color: '#00d084', + }, + { + name: __( 'Pale cyan blue' ), + slug: 'pale-cyan-blue', + color: '#8ed1fc', + }, + { + name: __( 'Vivid cyan blue' ), + slug: 'vivid-cyan-blue', + color: '#0693e3', + }, + { + name: __( 'Vivid purple' ), + slug: 'vivid-purple', + color: '#9b51e0', + }, + { + name: __( 'Very light gray' ), + slug: 'very-light-gray', + color: '#eeeeee', + }, + { + name: __( 'Cyan bluish gray' ), + slug: 'cyan-bluish-gray', + color: '#abb8c3', + }, + { + name: __( 'Very dark gray' ), + slug: 'very-dark-gray', + color: '#313131', + }, + ], + allowCustomColors: true, + gradientPalette: [ + { + name: __( 'Vivid cyan blue to vivid purple' ), + gradient: + 'linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%)', + slug: 'vivid-cyan-blue-to-vivid-purple', + }, + { + name: __( 'Light green cyan to vivid green cyan' ), + gradient: + 'linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%)', + slug: 'light-green-cyan-to-vivid-green-cyan', + }, + { + name: __( 'Luminous vivid amber to luminous vivid orange' ), + gradient: + 'linear-gradient(135deg,rgba(252,185,0,1) 0%,rgba(255,105,0,1) 100%)', + slug: 'luminous-vivid-amber-to-luminous-vivid-orange', + }, + { + name: __( 'Luminous vivid orange to vivid red' ), + gradient: + 'linear-gradient(135deg,rgba(255,105,0,1) 0%,rgb(207,46,46) 100%)', + slug: 'luminous-vivid-orange-to-vivid-red', + }, + { + name: __( 'Very light gray to cyan bluish gray' ), + gradient: + 'linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%)', + slug: 'very-light-gray-to-cyan-bluish-gray', + }, + { + name: __( 'Cool to warm spectrum' ), + gradient: + 'linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%)', + slug: 'cool-to-warm-spectrum', + }, + { + name: __( 'Blush light purple' ), + gradient: + 'linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%)', + slug: 'blush-light-purple', + }, + { + name: __( 'Blush bordeaux' ), + gradient: + 'linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%)', + slug: 'blush-bordeaux', + }, + { + name: __( 'Luminous dusk' ), + gradient: + 'linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%)', + slug: 'luminous-dusk', + }, + { + name: __( 'Pale ocean' ), + gradient: + 'linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%)', + slug: 'pale-ocean', + }, + { + name: __( 'Electric grass' ), + gradient: + 'linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%)', + slug: 'electric-grass', + }, + { + name: __( 'Midnight' ), + gradient: + 'linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%)', + slug: 'midnight', + }, + ], + allowCustomGradients: true, + }, + typography: { + enabled: true, + fontSizes: [ + { + name: _x( 'Small', 'font size name' ), + size: 13, + slug: 'small', + }, + { + name: _x( 'Normal', 'font size name' ), + size: 16, + slug: 'normal', + }, + { + name: _x( 'Medium', 'font size name' ), + size: 20, + slug: 'medium', + }, + { + name: _x( 'Large', 'font size name' ), + size: 36, + slug: 'large', + }, + { + name: _x( 'Huge', 'font size name' ), + size: 48, + slug: 'huge', + }, + ], + allowCustomFontSizes: true, + allowDropCap: true, + }, +}; diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 78e405a528f5a..1b3e7a95df59a 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -26,7 +26,11 @@ import { isReusableBlock } from '@wordpress/blocks'; /** * Internal dependencies */ -import { PREFERENCES_DEFAULTS, SETTINGS_DEFAULTS } from './defaults'; +import { + FEATURES_DEFAULTS, + PREFERENCES_DEFAULTS, + SETTINGS_DEFAULTS, +} from './defaults'; import { insertAt, moveTo } from './array'; /** @@ -1286,6 +1290,26 @@ export function settings( state = SETTINGS_DEFAULTS, action ) { return state; } +/** + * Reducer returning the editor features. + * + * @param {Object} state Current state. + * @param {Object} action Dispatched action. + * + * @return {Object} Updated state. + */ +export function features( state = FEATURES_DEFAULTS, action ) { + switch ( action.type ) { + case 'UPDATE_FEATURES': + return { + ...state, + ...action.features, + }; + } + + return state; +} + /** * Reducer returning the user preferences. * @@ -1485,6 +1509,7 @@ export default combineReducers( { insertionPoint, template, settings, + features, preferences, lastBlockAttributesChange, isNavigationMode, diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index fc9dd38979294..f52d77e56159b 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -1501,6 +1501,18 @@ export function getSettings( state ) { return state.settings; } +/** + * Returns the block editor feature by name. + * + * @param {Object} state Editor state. + * @param {string} featureName Feature name. + * + * @return {Object} The block editor feature object. + */ +export function __experimentalGetFeature( state, featureName ) { + return state.features?.[ featureName ]; +} + /** * Returns true if the most recent block change is be considered persistent, or * false otherwise. A persistent change is one committed by BlockEditorProvider diff --git a/packages/block-editor/src/store/test/actions.js b/packages/block-editor/src/store/test/actions.js index 3cad986cd639b..f5d37466ca364 100644 --- a/packages/block-editor/src/store/test/actions.js +++ b/packages/block-editor/src/store/test/actions.js @@ -29,6 +29,7 @@ import { updateBlock, updateBlockAttributes, updateBlockListSettings, + __experimentalUpdateFeatures, } from '../actions'; import { select } from '../controls'; @@ -907,4 +908,18 @@ describe( 'actions', () => { } ); } ); } ); + + describe( 'updateFeatures', () => { + it( 'should return the UPDATE_FEATURES action', () => { + const features = { + foo: { + enabled: true, + }, + }; + expect( __experimentalUpdateFeatures( features ) ).toEqual( { + type: 'UPDATE_FEATURES', + features, + } ); + } ); + } ); } ); diff --git a/packages/block-editor/src/store/test/reducer.js b/packages/block-editor/src/store/test/reducer.js index 8610a1b8a3f31..fc0e40a2ac5f6 100644 --- a/packages/block-editor/src/store/test/reducer.js +++ b/packages/block-editor/src/store/test/reducer.js @@ -26,6 +26,7 @@ import { selectionEnd, initialPosition, isMultiSelecting, + features, preferences, blocksMode, insertionPoint, @@ -33,6 +34,7 @@ import { blockListSettings, lastBlockAttributesChange, } from '../reducer'; +import { FEATURES_DEFAULTS } from '../defaults'; describe( 'state', () => { describe( 'hasSameKeys()', () => { @@ -2308,6 +2310,53 @@ describe( 'state', () => { } ); } ); + describe( 'features', () => { + it( 'should apply all defaults', () => { + const state = features( undefined, {} ); + + expect( state ).toEqual( FEATURES_DEFAULTS ); + } ); + + it( 'should add new feature when not found', () => { + const intialState = deepFreeze( {} ); + const newFeature = { + enabled: true, + }; + const state = features( intialState, { + type: 'UPDATE_FEATURES', + features: { + newFeature, + }, + } ); + + expect( state ).toEqual( { + newFeature, + } ); + } ); + + it( 'should replace an existing feature when found', () => { + const initialState = deepFreeze( { + existingFeature: { + enabled: false, + foo: 'bar', + }, + } ); + const updatedExistingFeature = { + enabled: true, + }; + const state = features( initialState, { + type: 'UPDATE_FEATURES', + features: { + existingFeature: updatedExistingFeature, + }, + } ); + + expect( state ).toEqual( { + existingFeature: updatedExistingFeature, + } ); + } ); + } ); + describe( 'preferences()', () => { it( 'should apply all defaults', () => { const state = preferences( undefined, {} ); diff --git a/packages/block-editor/src/store/test/selectors.js b/packages/block-editor/src/store/test/selectors.js index 3cff15b7c922b..39c9c941ffb53 100644 --- a/packages/block-editor/src/store/test/selectors.js +++ b/packages/block-editor/src/store/test/selectors.js @@ -1,6 +1,7 @@ /** * External dependencies */ +import deepFreeze from 'deep-freeze'; import { filter } from 'lodash'; /** @@ -66,6 +67,7 @@ const { INSERTER_UTILITY_MEDIUM, INSERTER_UTILITY_LOW, getLowestCommonAncestorWithSelectedBlock, + __experimentalGetFeature, } = selectors; describe( 'selectors', () => { @@ -2797,4 +2799,29 @@ describe( 'selectors', () => { ).toBe( 'a' ); } ); } ); + + describe( '__experimentalGetFeature', () => { + it( 'returns undefined when feature not found', () => { + const state = deepFreeze( {} ); + + const result = __experimentalGetFeature( state, 'unknown' ); + + expect( result ).toBeUndefined(); + } ); + + it( 'returns the feature object when found', () => { + const testFeature = { + enabled: true, + }; + const state = deepFreeze( { + features: { + testFeature, + }, + } ); + + const result = __experimentalGetFeature( state, 'testFeature' ); + + expect( result ).toEqual( testFeature ); + } ); + } ); } );