From 21b434e6dda1ab4db3ac8493b0f41358fead19fd Mon Sep 17 00:00:00 2001 From: Jorge Date: Fri, 19 Apr 2019 12:53:34 +0100 Subject: [PATCH] Connect gutenberg widget screen to widget-area endpoints. --- packages/core-data/src/entities.js | 1 + .../edit-widgets-initializer/index.js | 37 ++++++ .../src/components/header/index.js | 16 ++- .../src/components/layout/index.js | 23 ++-- .../src/components/widget-area/index.js | 27 ++++- packages/edit-widgets/src/index.js | 5 +- packages/edit-widgets/src/store/actions.js | 62 ++++++++++ packages/edit-widgets/src/store/constants.js | 6 + packages/edit-widgets/src/store/controls.js | 113 ++++++++++++++++++ packages/edit-widgets/src/store/index.js | 23 ++++ packages/edit-widgets/src/store/reducer.js | 58 +++++++++ packages/edit-widgets/src/store/selectors.js | 13 ++ 12 files changed, 364 insertions(+), 20 deletions(-) create mode 100644 packages/edit-widgets/src/components/edit-widgets-initializer/index.js create mode 100644 packages/edit-widgets/src/store/actions.js create mode 100644 packages/edit-widgets/src/store/constants.js create mode 100644 packages/edit-widgets/src/store/controls.js create mode 100644 packages/edit-widgets/src/store/index.js create mode 100644 packages/edit-widgets/src/store/reducer.js create mode 100644 packages/edit-widgets/src/store/selectors.js diff --git a/packages/core-data/src/entities.js b/packages/core-data/src/entities.js index 84c4dc3b160b18..280e54a5297f3b 100644 --- a/packages/core-data/src/entities.js +++ b/packages/core-data/src/entities.js @@ -15,6 +15,7 @@ export const defaultEntities = [ { name: 'postType', kind: 'root', key: 'slug', baseURL: '/wp/v2/types' }, { name: 'media', kind: 'root', baseURL: '/wp/v2/media', plural: 'mediaItems' }, { name: 'taxonomy', kind: 'root', key: 'slug', baseURL: '/wp/v2/taxonomies', plural: 'taxonomies' }, + { name: 'widgetArea', kind: 'root', baseURL: '/__experimental/widget-areas', plural: 'widgetAreas' }, ]; export const kinds = [ diff --git a/packages/edit-widgets/src/components/edit-widgets-initializer/index.js b/packages/edit-widgets/src/components/edit-widgets-initializer/index.js new file mode 100644 index 00000000000000..f33b4ce96c5f3d --- /dev/null +++ b/packages/edit-widgets/src/components/edit-widgets-initializer/index.js @@ -0,0 +1,37 @@ +/** + * WordPress dependencies + */ +import { compose } from '@wordpress/compose'; +import { useEffect } from '@wordpress/element'; +import { withDispatch, withSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import Layout from '../layout'; + +function EditWidgetsInitializer( { widgetAreas, setupWidgetAreas } ) { + useEffect( () => { + if ( ! widgetAreas ) { + return; + } + console.log( widgetAreas ); + setupWidgetAreas( widgetAreas ); + }, [ widgetAreas, setupWidgetAreas ] ); + return ; +} + +export default compose( [ + withSelect( ( select ) => { + const widgetAreas = select( 'core' ).getEntityRecords( 'root', 'widgetArea' ); + return { + widgetAreas, + }; + } ), + withDispatch( ( dispatch ) => { + const { setupWidgetAreas } = dispatch( 'core/edit-widgets' ); + return { + setupWidgetAreas, + }; + } ), +] )( EditWidgetsInitializer ); diff --git a/packages/edit-widgets/src/components/header/index.js b/packages/edit-widgets/src/components/header/index.js index c8ec2d3bffee9c..3dc3f650e9d8ea 100644 --- a/packages/edit-widgets/src/components/header/index.js +++ b/packages/edit-widgets/src/components/header/index.js @@ -1,10 +1,12 @@ /** * WordPress dependencies */ +import { compose } from '@wordpress/compose'; import { Button } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; +import { withDispatch } from '@wordpress/data'; -function Header() { +function Header( { saveWidgetAreas } ) { return (
-
@@ -25,4 +27,12 @@ function Header() { ); } -export default Header; +export default compose( [ + withDispatch( ( dispatch ) => { + const { saveWidgetAreas } = dispatch( 'core/edit-widgets' ); + return { + saveWidgetAreas, + }; + } ), +] )( Header ); + diff --git a/packages/edit-widgets/src/components/layout/index.js b/packages/edit-widgets/src/components/layout/index.js index 1e8a2787509a25..8d011d0f993aa4 100644 --- a/packages/edit-widgets/src/components/layout/index.js +++ b/packages/edit-widgets/src/components/layout/index.js @@ -1,9 +1,11 @@ /** * WordPress dependencies */ +import { compose } from '@wordpress/compose'; import { Fragment } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { navigateRegions } from '@wordpress/components'; +import { withSelect } from '@wordpress/data'; /** * Internal dependencies @@ -12,13 +14,7 @@ import Header from '../header'; import Sidebar from '../sidebar'; import WidgetArea from '../widget-area'; -function Layout() { - const areas = [ - __( 'Sidebar' ), - __( 'Footer' ), - __( 'Header' ), - ]; - +function Layout( { areas } ) { return (
@@ -31,7 +27,7 @@ function Layout() { > { areas.map( ( area, index ) => (
- +
) ) }
@@ -39,4 +35,13 @@ function Layout() { ); } -export default navigateRegions( Layout ); +export default compose( [ + withSelect( ( select ) => { + const { getWidgetAreas } = select( 'core/edit-widgets' ); + const areas = getWidgetAreas(); + return { + areas, + }; + } ), + navigateRegions, +] )( Layout ); diff --git a/packages/edit-widgets/src/components/widget-area/index.js b/packages/edit-widgets/src/components/widget-area/index.js index 5826f470756fd2..f073d0199d049c 100644 --- a/packages/edit-widgets/src/components/widget-area/index.js +++ b/packages/edit-widgets/src/components/widget-area/index.js @@ -1,20 +1,19 @@ /** * WordPress dependencies */ +import { compose } from '@wordpress/compose'; import { Panel, PanelBody } from '@wordpress/components'; import { BlockEditorProvider, BlockList, } from '@wordpress/block-editor'; -import { useState } from '@wordpress/element'; - -function WidgetArea( { title, initialOpen } ) { - const [ blocks, updateBlocks ] = useState( [] ); +import { withDispatch, withSelect } from '@wordpress/data'; +function WidgetArea( { area, initialOpen, blocks, updateBlocks } ) { return ( { + const { getWidgetAreaBlocks } = select( 'core/edit-widgets' ); + const blocks = getWidgetAreaBlocks( area.id ); + return { + blocks, + }; + } ), + withDispatch( ( dispatch, { area } ) => { + return { + updateBlocks( blocks ) { + const { updateBlocksInWidgetArea } = dispatch( 'core/edit-widgets' ); + updateBlocksInWidgetArea( area.id, blocks ); + }, + }; + } ), +] )( WidgetArea ); diff --git a/packages/edit-widgets/src/index.js b/packages/edit-widgets/src/index.js index 2181853fa461de..ede34168d9cb0c 100644 --- a/packages/edit-widgets/src/index.js +++ b/packages/edit-widgets/src/index.js @@ -7,7 +7,8 @@ import { registerCoreBlocks } from '@wordpress/block-library'; /** * Internal dependencies */ -import Layout from './components/layout'; +import './store'; +import EditWidgetsInitializer from './components/edit-widgets-initializer'; /** * Initilizes the widgets screen @@ -17,7 +18,7 @@ import Layout from './components/layout'; export function initialize( id ) { registerCoreBlocks(); render( - , + , document.getElementById( id ) ); } diff --git a/packages/edit-widgets/src/store/actions.js b/packages/edit-widgets/src/store/actions.js new file mode 100644 index 00000000000000..76dfc33f308a08 --- /dev/null +++ b/packages/edit-widgets/src/store/actions.js @@ -0,0 +1,62 @@ +/** + * External dependencies + */ +import { map } from 'lodash'; + +/** + * WordPress dependencies + */ +import { + parse, + serialize, +} from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import { STORE_KEY } from './constants'; +import { + select, + apiFetch, +} from './controls'; + +export function setupWidgetAreas( widgetAreas ) { + return { + type: 'SETUP_WIDGET_AREAS', + widgetAreas: map( widgetAreas, ( area ) => { + return { + ...area, + blocks: parse( ( area.content && area.content.raw ) || '' ), + }; + } ), + }; +} + +export function updateBlocksInWidgetArea( widgetAreaId, blocks ) { + return { + type: 'UPDATE_BLOCKS_IN_WIDGET_AREA', + widgetAreaId, + blocks, + }; +} + +export function* saveWidgetAreas() { + const widgetAreas = yield select( + STORE_KEY, + 'getWidgetAreas' + ); + for ( const widgetArea of widgetAreas ) { + const widgetAreaBlocks = yield select( + STORE_KEY, + 'getWidgetAreaBlocks', + widgetArea.id + ); + const content = serialize( widgetAreaBlocks ); + const path = `/__experimental/widget-areas/${ widgetArea.id }`; + yield apiFetch( { + path, + method: 'POST', + data: { content }, + } ); + } +} diff --git a/packages/edit-widgets/src/store/constants.js b/packages/edit-widgets/src/store/constants.js new file mode 100644 index 00000000000000..4968386ea38ea1 --- /dev/null +++ b/packages/edit-widgets/src/store/constants.js @@ -0,0 +1,6 @@ +/** + * Constant for the store module (or reducer) key. + * @type {string} + */ +export const STORE_KEY = 'core/edit-widgets'; + diff --git a/packages/edit-widgets/src/store/controls.js b/packages/edit-widgets/src/store/controls.js new file mode 100644 index 00000000000000..597a5f726145b5 --- /dev/null +++ b/packages/edit-widgets/src/store/controls.js @@ -0,0 +1,113 @@ +/** + * WordPress dependencies + */ +import triggerFetch from '@wordpress/api-fetch'; +import { createRegistryControl } from '@wordpress/data'; + +/** + * Dispatches a control action for triggering an api fetch call. + * + * @param {Object} request Arguments for the fetch request. + * + * @return {Object} control descriptor. + */ +export function apiFetch( request ) { + return { + type: 'API_FETCH', + request, + }; +} + +/** + * Dispatches a control action for triggering a registry select. + * + * @param {string} storeKey + * @param {string} selectorName + * @param {Array} args Arguments for the select. + * + * @return {Object} control descriptor. + */ +export function select( storeKey, selectorName, ...args ) { + return { + type: 'SELECT', + storeKey, + selectorName, + args, + }; +} + +/** + * Dispatches a control action for triggering a registry select that has a + * resolver. + * + * @param {string} storeKey + * @param {string} selectorName + * @param {Array} args Arguments for the select. + * + * @return {Object} control descriptor. + */ +export function resolveSelect( storeKey, selectorName, ...args ) { + return { + type: 'RESOLVE_SELECT', + storeKey, + selectorName, + args, + }; +} + +/** + * Dispatches a control action for triggering a registry dispatch. + * + * @param {string} storeKey + * @param {string} actionName + * @param {Array} args Arguments for the dispatch action. + * + * @return {Object} control descriptor. + */ +export function dispatch( storeKey, actionName, ...args ) { + return { + type: 'DISPATCH', + storeKey, + actionName, + args, + }; +} + +export default { + API_FETCH( { request } ) { + return triggerFetch( request ); + }, + SELECT: createRegistryControl( + ( registry ) => ( { storeKey, selectorName, args } ) => { + return registry.select( storeKey )[ selectorName ]( ...args ); + } + ), + DISPATCH: createRegistryControl( + ( registry ) => ( { storeKey, actionName, args } ) => { + return registry.dispatch( storeKey )[ actionName ]( ...args ); + } + ), + RESOLVE_SELECT: createRegistryControl( + ( registry ) => ( { storeKey, selectorName, args } ) => { + return new Promise( ( resolve ) => { + const hasFinished = () => registry.select( 'core/data' ) + .hasFinishedResolution( storeKey, selectorName, args ); + const getResult = () => registry.select( storeKey )[ selectorName ] + .apply( null, args ); + + // trigger the selector (to trigger the resolver) + const result = getResult(); + if ( hasFinished() ) { + return resolve( result ); + } + + const unsubscribe = registry.subscribe( () => { + if ( hasFinished() ) { + unsubscribe(); + resolve( getResult() ); + } + } ); + } ); + } + ), +}; diff --git a/packages/edit-widgets/src/store/index.js b/packages/edit-widgets/src/store/index.js new file mode 100644 index 00000000000000..7fa24db4cee960 --- /dev/null +++ b/packages/edit-widgets/src/store/index.js @@ -0,0 +1,23 @@ +/** + * WordPress dependencies + */ +import { registerStore } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import reducer from './reducer'; +import * as actions from './actions'; +import * as selectors from './selectors'; +import controls from './controls'; + +const store = registerStore( 'core/edit-widgets', { + reducer, + actions, + selectors, + controls, +} ); + +store.dispatch( { type: 'INIT' } ); + +export default store; diff --git a/packages/edit-widgets/src/store/reducer.js b/packages/edit-widgets/src/store/reducer.js new file mode 100644 index 00000000000000..33e9972d8d62fb --- /dev/null +++ b/packages/edit-widgets/src/store/reducer.js @@ -0,0 +1,58 @@ +/** + * External dependencies + */ +import { keyBy, mapValues, pick } from 'lodash'; + +/** + * WordPress dependencies + */ +import { combineReducers } from '@wordpress/data'; + +export function widgetAreas( state = {}, action ) { + switch ( action.type ) { + case 'SETUP_WIDGET_AREAS': + return mapValues( + keyBy( action.widgetAreas, 'id' ), + ( value ) => pick( value, [ + 'name', + 'id', + 'description', + ] ) + ); + } + + return state; +} + +export function widgetAreaEditors( state = {}, action ) { + switch ( action.type ) { + case 'SETUP_WIDGET_AREAS': + return mapValues( + keyBy( action.widgetAreas, 'id' ), + ( value ) => pick( value, [ + 'blocks', + ] ) + ); + case 'UPDATE_BLOCKS_IN_WIDGET_AREA': { + const area = state[ action.widgetAreaId ] || {}; + // check if change is required + if ( area.blocks === action.blocks ) { + return state; + } + return { + ...state, + [ action.widgetAreaId ]: { + ...area, + blocks: action.blocks, + }, + }; + } + } + + return state; +} + +export default combineReducers( { + widgetAreas, + widgetAreaEditors, +} ); diff --git a/packages/edit-widgets/src/store/selectors.js b/packages/edit-widgets/src/store/selectors.js new file mode 100644 index 00000000000000..e19bffe70051b3 --- /dev/null +++ b/packages/edit-widgets/src/store/selectors.js @@ -0,0 +1,13 @@ +/** + * External dependencies + */ +import { toArray } from 'lodash'; + +export function getWidgetAreas( state ) { + return toArray( state.widgetAreas ); +} + +export function getWidgetAreaBlocks( state, widgetAreaId ) { + return state.widgetAreaEditors[ widgetAreaId ] && + state.widgetAreaEditors[ widgetAreaId ].blocks; +}