From a325579de694b4e65aa9d3ad6a31b64eda515c8a Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Tue, 7 Jul 2020 13:42:43 -0700 Subject: [PATCH 01/24] Editor still loads if page or template type is not set --- .../edit-site/src/components/editor/index.js | 38 +++++++++++-------- .../src/components/page-switcher/index.js | 10 ++--- .../src/components/save-button/index.js | 1 - .../src/components/template-switcher/index.js | 25 ++++-------- 4 files changed, 35 insertions(+), 39 deletions(-) diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index f61e30ecf34f3..94a909bed1eaa 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -82,11 +82,15 @@ function Editor() { templatePartId: _templatePartId, templateType: _templateType, page: getPage(), - template: _select( 'core' ).getEntityRecord( - 'postType', - _templateType, - _templateType === 'wp_template' ? _templateId : _templatePartId - ), + template: _templateType + ? _select( 'core' ).getEntityRecord( + 'postType', + _templateType, + _templateType === 'wp_template' + ? _templateId + : _templatePartId + ) + : null, select: _select, }; }, [] ); @@ -125,25 +129,31 @@ function Editor() { // and Query Pagination blocks. const blockContext = useMemo( () => ( { - ...page.context, - query: page.context.query || { categoryIds: [] }, + ...page?.context, + query: page?.context.query || { categoryIds: [] }, queryContext: [ - page.context.queryContext || { page: 1 }, + page?.context.queryContext || { page: 1 }, ( newQueryContext ) => setPage( { ...page, context: { - ...page.context, + ...page?.context, queryContext: { - ...page.context.queryContext, + ...page?.context.queryContext, ...newQueryContext, }, }, } ), ], } ), - [ page.context ] + [ page?.context ] ); + + let entityId; + if ( templateType ) { + entityId = templateType === 'wp_template' ? templateId : templatePartId; + } + return ( <> @@ -154,11 +164,7 @@ function Editor() { diff --git a/packages/edit-site/src/components/page-switcher/index.js b/packages/edit-site/src/components/page-switcher/index.js index 53629bd88ea7a..5200ee9d95eee 100644 --- a/packages/edit-site/src/components/page-switcher/index.js +++ b/packages/edit-site/src/components/page-switcher/index.js @@ -111,8 +111,8 @@ export default function PageSwitcher( { toggleProps={ { children: [ ...pages, ...categories, ...posts ].find( - ( choice ) => choice.value === activePage.path - )?.label || activePage.path, + ( choice ) => choice.value === activePage?.path + )?.label || activePage?.path, } } menuProps={ { className: 'edit-site-page-switcher__menu' } } > @@ -121,21 +121,21 @@ export default function PageSwitcher( { isSavingEntityRecord( record.kind, record.name, record.key ) ), - templateType: select( 'core/edit-site' ).getTemplateType(), }; } ); diff --git a/packages/edit-site/src/components/template-switcher/index.js b/packages/edit-site/src/components/template-switcher/index.js index e713cbaf8c1ab..a1537253dbe0b 100644 --- a/packages/edit-site/src/components/template-switcher/index.js +++ b/packages/edit-site/src/components/template-switcher/index.js @@ -2,8 +2,8 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { useRegistry, useSelect } from '@wordpress/data'; -import { useEffect, useState } from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; +import { useState } from '@wordpress/element'; import { Tooltip, DropdownMenu, @@ -16,7 +16,6 @@ import { Icon, home, plus, undo } from '@wordpress/icons'; /** * Internal dependencies */ -import { findTemplate } from '../../utils'; import TemplatePreview from './template-preview'; import ThemePreview from './theme-preview'; @@ -69,20 +68,7 @@ export default function TemplateSwitcher( { setThemePreviewVisible( () => false ); }; - const registry = useRegistry(); - const [ homeId, setHomeId ] = useState(); - - useEffect( () => { - findTemplate( - '/', - registry.__experimentalResolveSelect( 'core' ).getEntityRecords - ).then( - ( newHomeId ) => setHomeId( newHomeId ), - () => setHomeId( null ) - ); - }, [ registry ] ); - - const { currentTheme, template, templateParts } = useSelect( + const { currentTheme, template, templateParts, homeId } = useSelect( ( select ) => { const { getCurrentTheme, @@ -96,6 +82,8 @@ export default function TemplateSwitcher( { activeId ); + const { getHomeTemplateId } = select( 'core/edit-site' ); + return { currentTheme: getCurrentTheme(), template: _template, @@ -105,6 +93,7 @@ export default function TemplateSwitcher( { template: _template.slug, } ) : null, + homeId: getHomeTemplateId(), }; }, [ activeId ] @@ -128,9 +117,11 @@ export default function TemplateSwitcher( { } ) ); const overwriteSlug = + page && TEMPLATE_OVERRIDES[ page.type ] && page.slug && TEMPLATE_OVERRIDES[ page.type ]( page.slug ); + const overwriteTemplate = () => onAddTemplate( { slug: overwriteSlug, From a379413d738a01d9b57d39d56dd072a5383b7cbe Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Tue, 7 Jul 2020 13:46:06 -0700 Subject: [PATCH 02/24] Create homepage state reducer --- packages/edit-site/src/store/reducer.js | 34 ++++++++++++----------- packages/edit-site/src/store/selectors.js | 14 +++++----- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/packages/edit-site/src/store/reducer.js b/packages/edit-site/src/store/reducer.js index bd7cfd8740eb1..1423397bf44c3 100644 --- a/packages/edit-site/src/store/reducer.js +++ b/packages/edit-site/src/store/reducer.js @@ -85,17 +85,6 @@ export function settings( state = {}, action ) { return state; } -/** - * Reducer returning the home template ID. - * - * @param {Object} state Current state. - * - * @return {Object} Updated state. - */ -export function homeTemplateId( state ) { - return state; -} - /** * Reducer returning the template ID. * @@ -159,7 +148,7 @@ export function templateType( state, action ) { * * @return {Object} Updated state. */ -export function page( state = {}, action ) { +export function page( state, action ) { switch ( action.type ) { case 'SET_PAGE': return action.page; @@ -169,13 +158,27 @@ export function page( state = {}, action ) { } /** - * Reducer returning the site's `show_on_front` setting. + * Reducer for information about the site's homepage. * * @param {Object} state Current state. + * @param {Object} action Dispatched action. * * @return {Object} Updated state. */ -export function showOnFront( state ) { +export function home( state = {}, action ) { + switch ( action.type ) { + case 'SET_HOME_TEMPLATE': + return { + ...state, + templateId: action.templateId, + }; + case 'SET_SHOW_ON_FRONT': + return { + ...state, + showOnFront: action.showOnFront, + }; + } + return state; } @@ -183,10 +186,9 @@ export default combineReducers( { preferences, deviceType, settings, - homeTemplateId, templateId, templatePartId, templateType, page, - showOnFront, + home, } ); diff --git a/packages/edit-site/src/store/selectors.js b/packages/edit-site/src/store/selectors.js index 9e98f3ba36191..49fce90bd9747 100644 --- a/packages/edit-site/src/store/selectors.js +++ b/packages/edit-site/src/store/selectors.js @@ -88,10 +88,10 @@ export const getSettings = createSelector( * * @param {Object} state Global application state. * - * @return {number} Home template ID. + * @return {number?} Home template ID. */ export function getHomeTemplateId( state ) { - return state.homeTemplateId; + return state.home.templateId; } /** @@ -99,7 +99,7 @@ export function getHomeTemplateId( state ) { * * @param {Object} state Global application state. * - * @return {number} Template ID. + * @return {number?} Template ID. */ export function getTemplateId( state ) { return state.templateId; @@ -110,7 +110,7 @@ export function getTemplateId( state ) { * * @param {Object} state Global application state. * - * @return {number} Template part ID. + * @return {number?} Template part ID. */ export function getTemplatePartId( state ) { return state.templatePartId; @@ -121,7 +121,7 @@ export function getTemplatePartId( state ) { * * @param {Object} state Global application state. * - * @return {string} Template type. + * @return {string?} Template type. */ export function getTemplateType( state ) { return state.templateType; @@ -143,8 +143,8 @@ export function getPage( state ) { * * @param {Object} state Global application state. * - * @return {Object} The setting. + * @return {string?} The setting. */ export function getShowOnFront( state ) { - return state.showOnFront; + return state.home.showOnFront; } From 62b37d7066da1fbe7251890436a0085901396eb8 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Tue, 7 Jul 2020 13:46:32 -0700 Subject: [PATCH 03/24] Remove initial state from PHP --- lib/edit-site-page.php | 12 ------------ packages/edit-site/src/index.js | 5 +---- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/lib/edit-site-page.php b/lib/edit-site-page.php index a811770b3eef3..1558e52f6b5d1 100644 --- a/lib/edit-site-page.php +++ b/lib/edit-site-page.php @@ -145,18 +145,6 @@ function gutenberg_edit_site_init( $hook ) { } $settings['styles'] = gutenberg_get_editor_styles(); - $settings['editSiteInitialState'] = array(); - - $settings['editSiteInitialState']['templateType'] = 'wp_template'; - $settings['editSiteInitialState']['showOnFront'] = get_option( 'show_on_front' ); - $settings['editSiteInitialState']['page'] = array( - 'path' => '/', - 'context' => 'page' === $settings['editSiteInitialState']['showOnFront'] ? array( - 'postType' => 'page', - 'postId' => get_option( 'page_on_front' ), - ) : array(), - ); - // This is so other parts of the code can hook their own settings. // Example: Global Styles. global $post; diff --git a/packages/edit-site/src/index.js b/packages/edit-site/src/index.js index 3607eae790820..08a0e3993f0e9 100644 --- a/packages/edit-site/src/index.js +++ b/packages/edit-site/src/index.js @@ -54,10 +54,7 @@ const fetchLinkSuggestions = ( search, { perPage = 20 } = {} ) => export function initialize( id, settings ) { settings.__experimentalFetchLinkSuggestions = fetchLinkSuggestions; - const initialState = settings.editSiteInitialState; - delete settings.editSiteInitialState; - initialState.settings = settings; - registerEditSiteStore( initialState ); + registerEditSiteStore( { settings } ); registerCoreBlocks(); if ( process.env.GUTENBERG_PHASE === 2 ) { From 4af2c1c107bbb3bfcbcb63157f3f1326db3a3129 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Tue, 7 Jul 2020 16:00:09 -0700 Subject: [PATCH 04/24] Move home page resolution to client --- lib/edit-site-page.php | 3 ++ packages/edit-site/src/index.js | 10 ++++++- packages/edit-site/src/store/actions.js | 36 ++++++++++++++++++----- packages/edit-site/src/store/index.js | 14 ++++++++- packages/edit-site/src/store/reducer.js | 5 ---- packages/edit-site/src/store/selectors.js | 11 +++++++ 6 files changed, 64 insertions(+), 15 deletions(-) diff --git a/lib/edit-site-page.php b/lib/edit-site-page.php index 1558e52f6b5d1..8e3434fd47d7c 100644 --- a/lib/edit-site-page.php +++ b/lib/edit-site-page.php @@ -145,6 +145,9 @@ function gutenberg_edit_site_init( $hook ) { } $settings['styles'] = gutenberg_get_editor_styles(); + $settings['showOnFront'] = get_option( 'show_on_front' ); + $settings['pageOnFront'] = get_option( 'page_on_front' ); + // This is so other parts of the code can hook their own settings. // Example: Global Styles. global $post; diff --git a/packages/edit-site/src/index.js b/packages/edit-site/src/index.js index 08a0e3993f0e9..217bd604fd0f2 100644 --- a/packages/edit-site/src/index.js +++ b/packages/edit-site/src/index.js @@ -54,7 +54,15 @@ const fetchLinkSuggestions = ( search, { perPage = 20 } = {} ) => export function initialize( id, settings ) { settings.__experimentalFetchLinkSuggestions = fetchLinkSuggestions; - registerEditSiteStore( { settings } ); + const initialState = { + settings, + home: { + showOnFront: settings.showOnFront, + pageOnFront: settings.pageOnFront, + }, + }; + + registerEditSiteStore( initialState ); registerCoreBlocks(); if ( process.env.GUTENBERG_PHASE === 2 ) { diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index c68baf50a5509..ed358c1b25f49 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -107,19 +107,39 @@ export function setTemplatePart( templatePartId ) { /** * Resolves the template for a page and sets them. * - * @param {Object} page The page object. - * @param {string} page.type The page type. - * @param {string} page.slug The page slug. - * @param {string} page.path The page path. - * @param {Object} page.context The page context. + * @param {Object} page The page object. + * @param {string} page.type The page type. + * @param {string} page.slug The page slug. + * @param {string} page.path The page path. + * @param {Object} page.context The page context. + * @param {number?} templateId An optional template ID for the page to avoid a second fetch if we already know it. * * @return {Object} Action object. */ -export function* setPage( page ) { - const templateId = yield findTemplate( page.path ); +export function* setPage( page, templateId ) { + const resolvedTemplate = templateId ?? ( yield findTemplate( page.path ) ); return { type: 'SET_PAGE', page, - templateId, + templateId: resolvedTemplate, }; } + +/** + * Sets up the initial template and page for edit site, and fetches other + * information it needs. + * + * @param {Object} initialPage The initial page to load in the site editor. + */ +export function* setupState( initialPage ) { + const homeTemplateId = yield findTemplate( '/' ); + dispatch( { + type: 'SET_HOME_TEMPLATE', + homeTemplateId, + } ); + + yield setPage( + initialPage, + initialPage.path === '/' ? homeTemplateId : null + ); +} diff --git a/packages/edit-site/src/store/index.js b/packages/edit-site/src/store/index.js index 06bcf529febe1..d3d29f4d1ae26 100644 --- a/packages/edit-site/src/store/index.js +++ b/packages/edit-site/src/store/index.js @@ -23,8 +23,20 @@ export default function registerEditSiteStore( initialState ) { initialState, } ); + const { showOnFront, pageOnFront } = initialState.home; + const initialPage = { + path: '/', + context: + showOnFront === 'page' + ? { + postType: 'page', + postId: pageOnFront, + } + : {}, + }; + // We set the initial page here to include the template fetch which will // resolve the correct homepage template. - store.dispatch( actions.setPage( initialState.page ) ); + store.dispatch( actions.setupState( initialPage ) ); return store; } diff --git a/packages/edit-site/src/store/reducer.js b/packages/edit-site/src/store/reducer.js index 1423397bf44c3..37e291a7b0aec 100644 --- a/packages/edit-site/src/store/reducer.js +++ b/packages/edit-site/src/store/reducer.js @@ -172,11 +172,6 @@ export function home( state = {}, action ) { ...state, templateId: action.templateId, }; - case 'SET_SHOW_ON_FRONT': - return { - ...state, - showOnFront: action.showOnFront, - }; } return state; diff --git a/packages/edit-site/src/store/selectors.js b/packages/edit-site/src/store/selectors.js index 49fce90bd9747..7eb993003ed63 100644 --- a/packages/edit-site/src/store/selectors.js +++ b/packages/edit-site/src/store/selectors.js @@ -148,3 +148,14 @@ export function getPage( state ) { export function getShowOnFront( state ) { return state.home.showOnFront; } + +/** + * Returns the site's current `page_on_front` setting. + * + * @param {Object} state Global application state. + * + * @return {number?} The setting. + */ +export function getPageOnFront( state ) { + return state.home.pageOnFront; +} From 95124bf52770f537be420d795b9429b2ba13eb67 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Thu, 9 Jul 2020 11:46:49 -0700 Subject: [PATCH 05/24] Consolidate entityId logic --- .../edit-site/src/components/editor/index.js | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index 94a909bed1eaa..e67aaa4b73823 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -52,8 +52,7 @@ function Editor() { deviceType, sidebarIsOpened, settings, - templateId, - templatePartId, + entityId, templateType, page, template, @@ -71,6 +70,14 @@ function Editor() { const _templateId = getTemplateId(); const _templatePartId = getTemplatePartId(); const _templateType = getTemplateType(); + + // The currently selected entity to display. Typically template or template part. + let _entityId; + if ( _templateType ) { + _entityId = + _templateType === 'wp_template' ? _templateId : _templatePartId; + } + return { isFullscreenActive: isFeatureActive( 'fullscreenMode' ), deviceType: __experimentalGetPreviewDeviceType(), @@ -78,20 +85,17 @@ function Editor() { 'core/interface' ).getActiveComplementaryArea( 'core/edit-site' ), settings: getSettings(), - templateId: _templateId, - templatePartId: _templatePartId, templateType: _templateType, page: getPage(), template: _templateType ? _select( 'core' ).getEntityRecord( 'postType', _templateType, - _templateType === 'wp_template' - ? _templateId - : _templatePartId + _entityId ) : null, select: _select, + entityId: _entityId, }; }, [] ); const { editEntityRecord } = useDispatch( 'core' ); @@ -149,11 +153,6 @@ function Editor() { [ page?.context ] ); - let entityId; - if ( templateType ) { - entityId = templateType === 'wp_template' ? templateId : templatePartId; - } - return ( <> From 9fe3fea9a36ddc837b111837f6891bae10bae9a7 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Thu, 9 Jul 2020 11:49:22 -0700 Subject: [PATCH 06/24] Consolidate activePage?.path --- .../edit-site/src/components/page-switcher/index.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/edit-site/src/components/page-switcher/index.js b/packages/edit-site/src/components/page-switcher/index.js index 5200ee9d95eee..ffef44bc45dc2 100644 --- a/packages/edit-site/src/components/page-switcher/index.js +++ b/packages/edit-site/src/components/page-switcher/index.js @@ -97,6 +97,7 @@ export default function PageSwitcher( { ].find( ( choice ) => choice.value === newPath ); onActivePageChange( { ...rest, path } ); }; + const onPostSelect = ( post ) => onActivePageChange( { type: 'post', @@ -104,6 +105,8 @@ export default function PageSwitcher( { path: getPathAndQueryString( post.url ), context: { postType: post.type, postId: post.id }, } ); + + const activePath = activePage?.path; return ( choice.value === activePage?.path - )?.label || activePage?.path, + ( choice ) => choice.value === activePath + )?.label || activePath, } } menuProps={ { className: 'edit-site-page-switcher__menu' } } > @@ -121,21 +124,21 @@ export default function PageSwitcher( { Date: Thu, 9 Jul 2020 12:00:24 -0700 Subject: [PATCH 07/24] Rework setupstate --- packages/edit-site/src/store/actions.js | 40 +++++++++++-------------- packages/edit-site/src/store/index.js | 7 +++-- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index ed358c1b25f49..c7bd24fca45f3 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -104,6 +104,20 @@ export function setTemplatePart( templatePartId ) { }; } +/** + * Updates the homeTemplateId state with the templateId of the page resolved + * from the given path. + * + * @param {string} path The path to the page which should be set as the homepage. + */ +export function* setHomeTemplatePath( path ) { + const homeTemplateId = yield findTemplate( path ); + dispatch( { + type: 'SET_HOME_TEMPLATE', + homeTemplateId, + } ); +} + /** * Resolves the template for a page and sets them. * @@ -112,34 +126,14 @@ export function setTemplatePart( templatePartId ) { * @param {string} page.slug The page slug. * @param {string} page.path The page path. * @param {Object} page.context The page context. - * @param {number?} templateId An optional template ID for the page to avoid a second fetch if we already know it. * * @return {Object} Action object. */ -export function* setPage( page, templateId ) { - const resolvedTemplate = templateId ?? ( yield findTemplate( page.path ) ); +export function* setPage( page ) { + const templateId = yield findTemplate( page.path ); return { type: 'SET_PAGE', page, - templateId: resolvedTemplate, + templateId, }; } - -/** - * Sets up the initial template and page for edit site, and fetches other - * information it needs. - * - * @param {Object} initialPage The initial page to load in the site editor. - */ -export function* setupState( initialPage ) { - const homeTemplateId = yield findTemplate( '/' ); - dispatch( { - type: 'SET_HOME_TEMPLATE', - homeTemplateId, - } ); - - yield setPage( - initialPage, - initialPage.path === '/' ? homeTemplateId : null - ); -} diff --git a/packages/edit-site/src/store/index.js b/packages/edit-site/src/store/index.js index d3d29f4d1ae26..a9f3175d57c9f 100644 --- a/packages/edit-site/src/store/index.js +++ b/packages/edit-site/src/store/index.js @@ -35,8 +35,9 @@ export default function registerEditSiteStore( initialState ) { : {}, }; - // We set the initial page here to include the template fetch which will - // resolve the correct homepage template. - store.dispatch( actions.setupState( initialPage ) ); + // Setup async data for the store. + store.dispatch( actions.setHomeTemplatePath( '/' ) ); + store.dispatch( actions.setPage( initialPage ) ); + return store; } From f9b2d5216731a75e4accf734be0677023c23d726 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Thu, 9 Jul 2020 12:53:10 -0700 Subject: [PATCH 08/24] Load home data over API --- lib/edit-site-page.php | 33 ++++++++++++-- .../edit-site/src/components/header/index.js | 10 ++++- packages/edit-site/src/index.js | 10 +---- packages/edit-site/src/store/actions.js | 45 +++++++++++++++---- packages/edit-site/src/store/index.js | 16 +------ packages/edit-site/src/store/reducer.js | 9 ++-- packages/edit-site/src/store/selectors.js | 24 +--------- 7 files changed, 80 insertions(+), 67 deletions(-) diff --git a/lib/edit-site-page.php b/lib/edit-site-page.php index 8e3434fd47d7c..dee3ef7cdc6ff 100644 --- a/lib/edit-site-page.php +++ b/lib/edit-site-page.php @@ -145,9 +145,6 @@ function gutenberg_edit_site_init( $hook ) { } $settings['styles'] = gutenberg_get_editor_styles(); - $settings['showOnFront'] = get_option( 'show_on_front' ); - $settings['pageOnFront'] = get_option( 'page_on_front' ); - // This is so other parts of the code can hook their own settings. // Example: Global Styles. global $post; @@ -216,3 +213,33 @@ function gutenberg_edit_site_init( $hook ) { wp_enqueue_style( 'wp-format-library' ); } add_action( 'admin_enqueue_scripts', 'gutenberg_edit_site_init' ); + +/** + * Register a core site setting for front page information. + */ +function register_site_editor_homepage_settings() { + register_setting( + 'general', + 'show_on_front', + array( + 'show_in_rest' => array( + 'name' => 'show_on_front', + ), + 'type' => 'string', + 'description' => __( 'Whether to show posts or a static page for the front page.' ), + ) + ); + + register_setting( + 'general', + 'page_on_front', + array( + 'show_in_rest' => array( + 'name' => 'page_on_front', + ), + 'type' => 'number', + 'description' => __( 'The page ID to show on the front page.' ), + ) + ); +} +add_action( 'rest_api_init', 'register_site_editor_homepage_settings', 10 ); diff --git a/packages/edit-site/src/components/header/index.js b/packages/edit-site/src/components/header/index.js index fe9eb71ada311..2ab4b1d84d304 100644 --- a/packages/edit-site/src/components/header/index.js +++ b/packages/edit-site/src/components/header/index.js @@ -49,8 +49,14 @@ export default function Header( { getTemplatePartId, getTemplateType, getPage, - getShowOnFront, } = select( 'core/edit-site' ); + + // eslint-disable-next-line camelcase + const { show_on_front } = select( 'core' ).getEditedEntityRecord( + 'root', + 'site' + ); + return { deviceType: __experimentalGetPreviewDeviceType(), hasFixedToolbar: isFeatureActive( 'fixedToolbar' ), @@ -58,7 +64,7 @@ export default function Header( { templatePartId: getTemplatePartId(), templateType: getTemplateType(), page: getPage(), - showOnFront: getShowOnFront(), + showOnFront: show_on_front, }; }, [] ); diff --git a/packages/edit-site/src/index.js b/packages/edit-site/src/index.js index 217bd604fd0f2..08a0e3993f0e9 100644 --- a/packages/edit-site/src/index.js +++ b/packages/edit-site/src/index.js @@ -54,15 +54,7 @@ const fetchLinkSuggestions = ( search, { perPage = 20 } = {} ) => export function initialize( id, settings ) { settings.__experimentalFetchLinkSuggestions = fetchLinkSuggestions; - const initialState = { - settings, - home: { - showOnFront: settings.showOnFront, - pageOnFront: settings.pageOnFront, - }, - }; - - registerEditSiteStore( initialState ); + registerEditSiteStore( { settings } ); registerCoreBlocks(); if ( process.env.GUTENBERG_PHASE === 2 ) { diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index c7bd24fca45f3..236fbb4535d09 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -108,32 +108,59 @@ export function setTemplatePart( templatePartId ) { * Updates the homeTemplateId state with the templateId of the page resolved * from the given path. * - * @param {string} path The path to the page which should be set as the homepage. + * @param {number} homeTemplateId The template ID for the homepage. */ -export function* setHomeTemplatePath( path ) { - const homeTemplateId = yield findTemplate( path ); - dispatch( { +export function setHomeTemplateId( homeTemplateId ) { + return { type: 'SET_HOME_TEMPLATE', homeTemplateId, - } ); + }; } /** - * Resolves the template for a page and sets them. + * Resolves the template for a page if no templateId is passed and sets them. * * @param {Object} page The page object. * @param {string} page.type The page type. * @param {string} page.slug The page slug. * @param {string} page.path The page path. * @param {Object} page.context The page context. + * @param {number?} templateId The template ID for the page if we already know it. * * @return {Object} Action object. */ -export function* setPage( page ) { - const templateId = yield findTemplate( page.path ); +export function* setPage( page, templateId ) { + const id = templateId ?? ( yield findTemplate( page.path ) ); return { type: 'SET_PAGE', page, - templateId, + templateId: id, }; } + +/** + * Displays the site homepage for editing in the editor. + */ +export function* showHomepage() { + const templateId = yield findTemplate( '/' ); + + yield setHomeTemplateId( templateId ); + + const { + show_on_front: showOnFront, + page_on_front: frontpageId, + } = yield select( 'core', 'getEntityRecord', 'root', 'site' ); + + const homePage = { + path: '/', + context: + showOnFront === 'page' + ? { + postType: 'page', + postId: frontpageId, + } + : {}, + }; + + yield setPage( homePage, templateId ); +} diff --git a/packages/edit-site/src/store/index.js b/packages/edit-site/src/store/index.js index a9f3175d57c9f..fa012273201cd 100644 --- a/packages/edit-site/src/store/index.js +++ b/packages/edit-site/src/store/index.js @@ -23,21 +23,7 @@ export default function registerEditSiteStore( initialState ) { initialState, } ); - const { showOnFront, pageOnFront } = initialState.home; - const initialPage = { - path: '/', - context: - showOnFront === 'page' - ? { - postType: 'page', - postId: pageOnFront, - } - : {}, - }; - - // Setup async data for the store. - store.dispatch( actions.setHomeTemplatePath( '/' ) ); - store.dispatch( actions.setPage( initialPage ) ); + store.dispatch( actions.showHomepage() ); return store; } diff --git a/packages/edit-site/src/store/reducer.js b/packages/edit-site/src/store/reducer.js index 37e291a7b0aec..14a33632fda85 100644 --- a/packages/edit-site/src/store/reducer.js +++ b/packages/edit-site/src/store/reducer.js @@ -165,13 +165,10 @@ export function page( state, action ) { * * @return {Object} Updated state. */ -export function home( state = {}, action ) { +export function homeTemplateId( state = {}, action ) { switch ( action.type ) { case 'SET_HOME_TEMPLATE': - return { - ...state, - templateId: action.templateId, - }; + return action.homeTemplateId; } return state; @@ -185,5 +182,5 @@ export default combineReducers( { templatePartId, templateType, page, - home, + homeTemplateId, } ); diff --git a/packages/edit-site/src/store/selectors.js b/packages/edit-site/src/store/selectors.js index 7eb993003ed63..48e67fca292ee 100644 --- a/packages/edit-site/src/store/selectors.js +++ b/packages/edit-site/src/store/selectors.js @@ -91,7 +91,7 @@ export const getSettings = createSelector( * @return {number?} Home template ID. */ export function getHomeTemplateId( state ) { - return state.home.templateId; + return state.homeTemplateId; } /** @@ -137,25 +137,3 @@ export function getTemplateType( state ) { export function getPage( state ) { return state.page; } - -/** - * Returns the site's current `show_on_front` setting. - * - * @param {Object} state Global application state. - * - * @return {string?} The setting. - */ -export function getShowOnFront( state ) { - return state.home.showOnFront; -} - -/** - * Returns the site's current `page_on_front` setting. - * - * @param {Object} state Global application state. - * - * @return {number?} The setting. - */ -export function getPageOnFront( state ) { - return state.home.pageOnFront; -} From 6265ec3402b729778f32824dc216f0abb6f46375 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Mon, 13 Jul 2020 14:28:30 -0700 Subject: [PATCH 09/24] Rename camelcase --- packages/edit-site/src/components/header/index.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/edit-site/src/components/header/index.js b/packages/edit-site/src/components/header/index.js index 2ab4b1d84d304..2ba155b9d6264 100644 --- a/packages/edit-site/src/components/header/index.js +++ b/packages/edit-site/src/components/header/index.js @@ -51,11 +51,7 @@ export default function Header( { getPage, } = select( 'core/edit-site' ); - // eslint-disable-next-line camelcase - const { show_on_front } = select( 'core' ).getEditedEntityRecord( - 'root', - 'site' - ); + const { getEntityRecord } = select( 'core' ); return { deviceType: __experimentalGetPreviewDeviceType(), @@ -64,7 +60,7 @@ export default function Header( { templatePartId: getTemplatePartId(), templateType: getTemplateType(), page: getPage(), - showOnFront: show_on_front, + showOnFront: getEntityRecord( 'root', 'site' ).show_on_front, }; }, [] ); From 631e81503d2c71e98ca7e6ca747a54500b512055 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Mon, 13 Jul 2020 14:37:30 -0700 Subject: [PATCH 10/24] Remove templateId from setPage --- packages/edit-site/src/store/actions.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index 236fbb4535d09..48b06dbedc0bb 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -118,23 +118,22 @@ export function setHomeTemplateId( homeTemplateId ) { } /** - * Resolves the template for a page if no templateId is passed and sets them. + * Resolves the template for a page displays both. * * @param {Object} page The page object. * @param {string} page.type The page type. * @param {string} page.slug The page slug. * @param {string} page.path The page path. * @param {Object} page.context The page context. - * @param {number?} templateId The template ID for the page if we already know it. * * @return {Object} Action object. */ -export function* setPage( page, templateId ) { - const id = templateId ?? ( yield findTemplate( page.path ) ); +export function* setPage( page ) { + const templateId = yield findTemplate( page.path ); return { type: 'SET_PAGE', page, - templateId: id, + templateId, }; } @@ -151,7 +150,7 @@ export function* showHomepage() { page_on_front: frontpageId, } = yield select( 'core', 'getEntityRecord', 'root', 'site' ); - const homePage = { + const page = { path: '/', context: showOnFront === 'page' @@ -162,5 +161,9 @@ export function* showHomepage() { : {}, }; - yield setPage( homePage, templateId ); + return { + type: 'SET_PAGE', + templateId, + page, + }; } From 0d30ccaa1eb3e8f9a5f2baed2af66207a28977be Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Mon, 13 Jul 2020 17:30:36 -0700 Subject: [PATCH 11/24] Naive cache results of findTemplate --- packages/edit-site/src/components/header/index.js | 6 ++++-- packages/edit-site/src/store/actions.js | 6 +----- packages/edit-site/src/utils/find-template.js | 9 ++++++++- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/edit-site/src/components/header/index.js b/packages/edit-site/src/components/header/index.js index 2ba155b9d6264..79b66dd93b04c 100644 --- a/packages/edit-site/src/components/header/index.js +++ b/packages/edit-site/src/components/header/index.js @@ -51,7 +51,9 @@ export default function Header( { getPage, } = select( 'core/edit-site' ); - const { getEntityRecord } = select( 'core' ); + const { show_on_front: _showOnFront } = select( + 'core' + ).getEditedEntityRecord( 'root', 'site' ); return { deviceType: __experimentalGetPreviewDeviceType(), @@ -60,7 +62,7 @@ export default function Header( { templatePartId: getTemplatePartId(), templateType: getTemplateType(), page: getPage(), - showOnFront: getEntityRecord( 'root', 'site' ).show_on_front, + showOnFront: _showOnFront, }; }, [] ); diff --git a/packages/edit-site/src/store/actions.js b/packages/edit-site/src/store/actions.js index 48b06dbedc0bb..dd82f32102ba3 100644 --- a/packages/edit-site/src/store/actions.js +++ b/packages/edit-site/src/store/actions.js @@ -161,9 +161,5 @@ export function* showHomepage() { : {}, }; - return { - type: 'SET_PAGE', - templateId, - page, - }; + yield setPage( page ); } diff --git a/packages/edit-site/src/utils/find-template.js b/packages/edit-site/src/utils/find-template.js index 83e2d984ceb6a..b15101cd8df6c 100644 --- a/packages/edit-site/src/utils/find-template.js +++ b/packages/edit-site/src/utils/find-template.js @@ -8,6 +8,10 @@ import { addQueryArgs } from '@wordpress/url'; */ const { fetch } = window; +// Naive cache to avoid running expensive template resolution for the same path +// multiple times. +const TEMPLATE_PATH_CACHE = {}; + /** * Find the template for a given page path. * @@ -17,6 +21,9 @@ const { fetch } = window; * @return {number} The found template ID. */ export default async function findTemplate( path, getEntityRecords ) { + if ( TEMPLATE_PATH_CACHE[ path ] ) { + return TEMPLATE_PATH_CACHE[ path ]; + } const { data } = await fetch( addQueryArgs( path, { '_wp-find-template': true } ) ).then( ( res ) => res.json() ); @@ -30,6 +37,6 @@ export default async function findTemplate( path, getEntityRecords ) { } ) )[ 0 ].id; } - + TEMPLATE_PATH_CACHE[ path ] = newTemplateId; return newTemplateId; } From 3a1da28da7b27c42dfe822c47c2ef996e8b10c95 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Mon, 13 Jul 2020 17:37:38 -0700 Subject: [PATCH 12/24] Rename cache variable --- packages/edit-site/src/utils/find-template.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/edit-site/src/utils/find-template.js b/packages/edit-site/src/utils/find-template.js index b15101cd8df6c..9455100486c42 100644 --- a/packages/edit-site/src/utils/find-template.js +++ b/packages/edit-site/src/utils/find-template.js @@ -10,7 +10,7 @@ const { fetch } = window; // Naive cache to avoid running expensive template resolution for the same path // multiple times. -const TEMPLATE_PATH_CACHE = {}; +const TEMPLATE_ID_CACHE = {}; /** * Find the template for a given page path. @@ -21,8 +21,8 @@ const TEMPLATE_PATH_CACHE = {}; * @return {number} The found template ID. */ export default async function findTemplate( path, getEntityRecords ) { - if ( TEMPLATE_PATH_CACHE[ path ] ) { - return TEMPLATE_PATH_CACHE[ path ]; + if ( TEMPLATE_ID_CACHE[ path ] ) { + return TEMPLATE_ID_CACHE[ path ]; } const { data } = await fetch( addQueryArgs( path, { '_wp-find-template': true } ) @@ -37,6 +37,6 @@ export default async function findTemplate( path, getEntityRecords ) { } ) )[ 0 ].id; } - TEMPLATE_PATH_CACHE[ path ] = newTemplateId; + TEMPLATE_ID_CACHE[ path ] = newTemplateId; return newTemplateId; } From 7e6e1e3864aad2dcac710fdba78794dc1490bd63 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Mon, 13 Jul 2020 17:56:55 -0700 Subject: [PATCH 13/24] Add text domain to setting translation --- lib/edit-site-page.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/edit-site-page.php b/lib/edit-site-page.php index dee3ef7cdc6ff..99dc2fc944dc7 100644 --- a/lib/edit-site-page.php +++ b/lib/edit-site-page.php @@ -226,7 +226,7 @@ function register_site_editor_homepage_settings() { 'name' => 'show_on_front', ), 'type' => 'string', - 'description' => __( 'Whether to show posts or a static page for the front page.' ), + 'description' => __( 'What to show on the front page', 'gutenberg' ), ) ); @@ -238,7 +238,7 @@ function register_site_editor_homepage_settings() { 'name' => 'page_on_front', ), 'type' => 'number', - 'description' => __( 'The page ID to show on the front page.' ), + 'description' => __( 'The ID of the page that should be displayed on the front page', 'gutenberg' ), ) ); } From f3d2b40283bc9463ce5e964bc9cc1887634dd794 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Mon, 13 Jul 2020 18:03:52 -0700 Subject: [PATCH 14/24] Fix existing JS unit tests --- packages/edit-site/src/store/reducer.js | 2 +- packages/edit-site/src/store/test/reducer.js | 14 +------------- packages/edit-site/src/store/test/selectors.js | 8 -------- 3 files changed, 2 insertions(+), 22 deletions(-) diff --git a/packages/edit-site/src/store/reducer.js b/packages/edit-site/src/store/reducer.js index 14a33632fda85..c13d792518ddb 100644 --- a/packages/edit-site/src/store/reducer.js +++ b/packages/edit-site/src/store/reducer.js @@ -165,7 +165,7 @@ export function page( state, action ) { * * @return {Object} Updated state. */ -export function homeTemplateId( state = {}, action ) { +export function homeTemplateId( state, action ) { switch ( action.type ) { case 'SET_HOME_TEMPLATE': return action.homeTemplateId; diff --git a/packages/edit-site/src/store/test/reducer.js b/packages/edit-site/src/store/test/reducer.js index c220305c6dd70..3156db15d57db 100644 --- a/packages/edit-site/src/store/test/reducer.js +++ b/packages/edit-site/src/store/test/reducer.js @@ -14,7 +14,6 @@ import { templatePartId, templateType, page, - showOnFront, } from '../reducer'; import { PREFERENCES_DEFAULTS } from '../defaults'; @@ -165,7 +164,7 @@ describe( 'state', () => { describe( 'page()', () => { it( 'should apply default state', () => { - expect( page( undefined, {} ) ).toEqual( {} ); + expect( page( undefined, {} ) ).toEqual( undefined ); } ); it( 'should default to returning the same state', () => { @@ -183,15 +182,4 @@ describe( 'state', () => { ).toBe( newPage ); } ); } ); - - describe( 'showOnFront()', () => { - it( 'should apply default state', () => { - expect( showOnFront( undefined, {} ) ).toEqual( undefined ); - } ); - - it( 'should default to returning the same state', () => { - const state = {}; - expect( showOnFront( state, {} ) ).toBe( state ); - } ); - } ); } ); diff --git a/packages/edit-site/src/store/test/selectors.js b/packages/edit-site/src/store/test/selectors.js index f71e885b6b147..688c6b053fac5 100644 --- a/packages/edit-site/src/store/test/selectors.js +++ b/packages/edit-site/src/store/test/selectors.js @@ -10,7 +10,6 @@ import { getTemplatePartId, getTemplateType, getPage, - getShowOnFront, } from '../selectors'; describe( 'selectors', () => { @@ -142,11 +141,4 @@ describe( 'selectors', () => { expect( getPage( state ) ).toBe( state.page ); } ); } ); - - describe( 'getShowOnFront', () => { - it( 'returns the `show_on_front` setting', () => { - const state = { showOnFront: {} }; - expect( getShowOnFront( state ) ).toBe( state.showOnFront ); - } ); - } ); } ); From 821ced628cae4654f93c9415e0cd346f6261efce Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Tue, 14 Jul 2020 20:54:25 -0700 Subject: [PATCH 15/24] Fix e2e test --- .../e2e-tests/specs/experiments/template-part.test.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/e2e-tests/specs/experiments/template-part.test.js b/packages/e2e-tests/specs/experiments/template-part.test.js index 386b8941c69a0..51873eb0d4df7 100644 --- a/packages/e2e-tests/specs/experiments/template-part.test.js +++ b/packages/e2e-tests/specs/experiments/template-part.test.js @@ -42,9 +42,11 @@ describe( 'Template Part', () => { it( 'Should load customizations when in a template even if only the slug and theme attributes are set.', async () => { // Switch to editing the header template part. - await page.click( - '.components-dropdown-menu__toggle[aria-label="Switch Template"]' - ); + const openDropdownSelector = + 'button.components-dropdown-menu__toggle[aria-label="Switch Template"]'; + await page.waitForSelector( openDropdownSelector ); + await page.click( openDropdownSelector ); + const switchToHeaderTemplatePartButton = await page.waitForXPath( '//button[contains(text(), "header")]' ); From 8a80e6a410609f8c16453f83f99bd1b2cb113e4f Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Fri, 14 Aug 2020 09:23:55 -0600 Subject: [PATCH 16/24] update after merge from origin/master --- .../e2e-tests/specs/experiments/template-part.test.js | 8 +++----- packages/edit-site/src/utils/find-template.js | 9 +-------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/packages/e2e-tests/specs/experiments/template-part.test.js b/packages/e2e-tests/specs/experiments/template-part.test.js index 5385e56adc324..cb788345bea1e 100644 --- a/packages/e2e-tests/specs/experiments/template-part.test.js +++ b/packages/e2e-tests/specs/experiments/template-part.test.js @@ -43,11 +43,9 @@ describe( 'Template Part', () => { it( 'Should load customizations when in a template even if only the slug and theme attributes are set.', async () => { // Switch to editing the header template part. - const openDropdownSelector = - 'button.components-dropdown-menu__toggle[aria-label="Switch Template"]'; - await page.waitForSelector( openDropdownSelector ); - await page.click( openDropdownSelector ); - + await page.click( + '.components-dropdown-menu__toggle[aria-label="Switch Template"]' + ); const switchToHeaderTemplatePartButton = await page.waitForXPath( '//button[contains(text(), "header")]' ); diff --git a/packages/edit-site/src/utils/find-template.js b/packages/edit-site/src/utils/find-template.js index 9455100486c42..83e2d984ceb6a 100644 --- a/packages/edit-site/src/utils/find-template.js +++ b/packages/edit-site/src/utils/find-template.js @@ -8,10 +8,6 @@ import { addQueryArgs } from '@wordpress/url'; */ const { fetch } = window; -// Naive cache to avoid running expensive template resolution for the same path -// multiple times. -const TEMPLATE_ID_CACHE = {}; - /** * Find the template for a given page path. * @@ -21,9 +17,6 @@ const TEMPLATE_ID_CACHE = {}; * @return {number} The found template ID. */ export default async function findTemplate( path, getEntityRecords ) { - if ( TEMPLATE_ID_CACHE[ path ] ) { - return TEMPLATE_ID_CACHE[ path ]; - } const { data } = await fetch( addQueryArgs( path, { '_wp-find-template': true } ) ).then( ( res ) => res.json() ); @@ -37,6 +30,6 @@ export default async function findTemplate( path, getEntityRecords ) { } ) )[ 0 ].id; } - TEMPLATE_ID_CACHE[ path ] = newTemplateId; + return newTemplateId; } From 375bdc9dca9e907dd28f30a7a106daa382ec03b6 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Fri, 11 Sep 2020 16:58:17 -0600 Subject: [PATCH 17/24] Page selector combobox, first pass. --- packages/components/src/index.js | 1 + .../src/components/page-attributes/parent.js | 194 ++++++++++++------ 2 files changed, 130 insertions(+), 65 deletions(-) diff --git a/packages/components/src/index.js b/packages/components/src/index.js index 177bfc406480a..bdb599254ff36 100644 --- a/packages/components/src/index.js +++ b/packages/components/src/index.js @@ -30,6 +30,7 @@ export { default as ClipboardButton } from './clipboard-button'; export { default as ColorIndicator } from './color-indicator'; export { default as ColorPalette } from './color-palette'; export { default as ColorPicker } from './color-picker'; +export { default as ComboboxControl } from './combobox-control'; export { default as CustomSelectControl } from './custom-select-control'; export { default as Dashicon } from './dashicon'; export { DateTimePicker, DatePicker, TimePicker } from './date-time'; diff --git a/packages/editor/src/components/page-attributes/parent.js b/packages/editor/src/components/page-attributes/parent.js index cb098fd08e131..f6a72adde6ca8 100644 --- a/packages/editor/src/components/page-attributes/parent.js +++ b/packages/editor/src/components/page-attributes/parent.js @@ -1,92 +1,156 @@ /** * External dependencies */ -import { get } from 'lodash'; +import { + get, + unescape as unescapeString, + debounce, + flatMap, + repeat, +} from 'lodash'; /** * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { TreeSelect } from '@wordpress/components'; -import { compose } from '@wordpress/compose'; -import { withSelect, withDispatch } from '@wordpress/data'; +import { ComboboxControl } from '@wordpress/components'; +import { useState, useMemo } from '@wordpress/element'; +import { useSelect, useDispatch } from '@wordpress/data'; /** * Internal dependencies */ import { buildTermsTree } from '../../utils/terms'; -export function PageAttributesParent( { - parent, - postType, - items, - onUpdateParent, -} ) { +export function PageAttributesParent() { + const { editPost } = useDispatch( 'core/editor' ); + const [ fieldValue, setFieldValue ] = useState(); + const { isLoading, parent, items, postType, parents } = useSelect( + ( select ) => { + const { getPostType, getEntityRecords, isResolving } = select( + 'core' + ); + const { getCurrentPostId, getEditedPostAttribute } = select( + 'core/editor' + ); + const postTypeSlug = getEditedPostAttribute( 'type' ); + const pType = getPostType( postTypeSlug ); + const postId = getCurrentPostId(); + const isHierarchical = get( pType, [ 'hierarchical' ], false ); + const query = { + per_page: 100, + exclude: postId, + parent_exclude: postId, + orderby: 'menu_order', + order: 'asc', + _fields: 'id,title,parent', + }; + if ( + ! fieldValue || + '' === fieldValue || + fieldValue === parent?.name + ) { + query.search = fieldValue; + } + const par = getEditedPostAttribute( 'parent' ); + const pars = getEntityRecords( 'postType', postTypeSlug, { + include: [ par ], + } ); + + return { + parent: par, + items: isHierarchical + ? getEntityRecords( 'postType', postTypeSlug, query ) + : [], + postType: pType, + isLoading: isResolving( 'postType', postTypeSlug, query ), + parents: pars, + }; + }, + [ fieldValue ] + ); const isHierarchical = get( postType, [ 'hierarchical' ], false ); const parentPageLabel = get( postType, [ 'labels', 'parent_item_colon' ] ); const pageItems = items || []; - if ( ! isHierarchical || ! parentPageLabel || ! pageItems.length ) { + + const getOptionsFromTree = ( tree, level = 0 ) => { + return flatMap( tree, ( treeNode ) => [ + { + key: treeNode.id, + name: + repeat( '\u00A0', level * 3 ) + + unescapeString( treeNode.name ), + }, + ...getOptionsFromTree( treeNode.children || [], level + 1 ), + ] ); + }; + + const parentOptions = useMemo( () => { + const tree = buildTermsTree( + pageItems.map( ( item ) => ( { + id: item.id, + parent: item.parent, + name: + item.title && item.title.raw + ? item.title.raw + : `#${ item.id } (${ __( 'no title' ) })`, + } ) ) + ); + const opts = getOptionsFromTree( tree ); + + // Ensure the current page is included in the dropdown list. + const foundParent = opts.findIndex( ( { key } ) => parent === key ); + if ( foundParent < 0 && parentPost ) { + return [ { key: parentPost.id, name: parentPost.title }, ...opts ]; + } + return opts; + }, [ parent, parentPost, pageItems ] ); + //console.log( parentOptions ); + + if ( ! isHierarchical || ! parentPageLabel ) { return null; } + /** + * Handle user input. + * + * @param {string} inputValue The current value of the input field. + */ + const handleKeydown = ( { inputValue } ) => { + setFieldValue( inputValue ); + }; - const pagesTree = buildTermsTree( - pageItems.map( ( item ) => ( { - id: item.id, - parent: item.parent, - name: - item.title && item.title.raw - ? item.title.raw - : `#${ item.id } (${ __( 'no title' ) })`, - } ) ) - ); + /** + * Handle author selection. + * + * @param {Object} value The selected Author. + * @param {Object} value.selectedItem The selected Author. + */ + const handleChange = ( { selectedItem } ) => { + if ( ! selectedItem ) { + return; + } + setFieldValue( selectedItem.name ); + editPost( { parent: selectedItem.key } ); + }; + + const parentPost = parents ? parents[ 0 ] : false; + const inputValue = parentPost?.title?.raw; + const initialSelectedItem = { + key: parentPost?.id, + name: inputValue, + }; return ( - ); } -const applyWithSelect = withSelect( ( select ) => { - const { getPostType, getEntityRecords } = select( 'core' ); - const { getCurrentPostId, getEditedPostAttribute } = select( - 'core/editor' - ); - const postTypeSlug = getEditedPostAttribute( 'type' ); - const postType = getPostType( postTypeSlug ); - const postId = getCurrentPostId(); - const isHierarchical = get( postType, [ 'hierarchical' ], false ); - const query = { - per_page: -1, - exclude: postId, - parent_exclude: postId, - orderby: 'menu_order', - order: 'asc', - _fields: 'id,title,parent', - }; - - return { - parent: getEditedPostAttribute( 'parent' ), - items: isHierarchical - ? getEntityRecords( 'postType', postTypeSlug, query ) - : [], - postType, - }; -} ); - -const applyWithDispatch = withDispatch( ( dispatch ) => { - const { editPost } = dispatch( 'core/editor' ); - return { - onUpdateParent( parent ) { - editPost( { parent: parent || 0 } ); - }, - }; -} ); - -export default compose( [ applyWithSelect, applyWithDispatch ] )( - PageAttributesParent -); +export default PageAttributesParent; From 353c1d3e0d19135db15f0567dd629f5d01244929 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 14 Sep 2020 12:39:15 -0600 Subject: [PATCH 18/24] Additional work on page selector. --- .../components/src/combobox-control/index.js | 29 ++++-- .../src/combobox-control/style.scss | 58 ++++++------ .../src/components/page-attributes/parent.js | 92 +++++++++++++------ 3 files changed, 112 insertions(+), 67 deletions(-) diff --git a/packages/components/src/combobox-control/index.js b/packages/components/src/combobox-control/index.js index a8befe7416e5a..c279785a9cdf8 100644 --- a/packages/components/src/combobox-control/index.js +++ b/packages/components/src/combobox-control/index.js @@ -3,21 +3,28 @@ */ import { useCombobox } from 'downshift'; import classnames from 'classnames'; +import { isEqual } from 'lodash'; + +/** + * WordPress dependencies + */ +import { check, chevronDown } from '@wordpress/icons'; /** * Internal dependencies */ -import { Button, Dashicon } from '../'; +import { Button, Icon, Spinner } from '../'; const itemToString = ( item ) => item && item.name; export default function ComboboxControl( { className, hideLabelFromVision, label, + isLoading, options: items, onInputValueChange: onInputValueChange, onChange: onSelectedItemChange, - value: _selectedItem, + initialSelectedItem, } ) { const { getLabelProps, @@ -30,15 +37,16 @@ export default function ComboboxControl( { highlightedIndex, selectedItem, } = useCombobox( { - initialSelectedItem: items[ 0 ], + initialSelectedItem, items, itemToString, onInputValueChange, onSelectedItemChange, - selectedItem: _selectedItem, } ); const menuProps = getMenuProps( { - className: 'components-combobox-control__menu', + className: classnames( 'components-combobox-control__menu', { + 'is-loading': isLoading, + } ), } ); // We need this here, because the null active descendant is not // fully ARIA compliant. @@ -68,6 +76,7 @@ export default function ComboboxControl( { > { label } + { isLoading && }
-
@@ -111,10 +120,10 @@ export default function ComboboxControl( { style: item.style, } ) } > - { item === selectedItem && ( - ) } { item.name } diff --git a/packages/components/src/combobox-control/style.scss b/packages/components/src/combobox-control/style.scss index 4ed994bc2af73..a109efd8d7092 100644 --- a/packages/components/src/combobox-control/style.scss +++ b/packages/components/src/combobox-control/style.scss @@ -1,48 +1,46 @@ .components-combobox-control { - color: $dark-gray-500; position: relative; + margin-bottom: $grid-unit-15; } .components-combobox-control__label { display: block; - margin-bottom: 5px; + margin-bottom: $grid-unit-05; } .components-combobox-control__button { - border: 1px solid $dark-gray-200; - border-radius: 4px; - color: $dark-gray-500; display: inline-block; - min-height: 30px; - min-width: 130px; + min-height: $button-size-small; + min-width: 140px; position: relative; text-align: left; - &:focus { - border-color: var(--wp-admin-theme-color); - } - - &-input { - border: none; - height: calc(100% - 2px); - left: 1px; - padding: 0 4px; + .components-combobox-control__button-input { + height: 100%; + width: 100%; + left: 0; + top: 0; position: absolute; - top: 1px; - width: calc(100% - 2px); - } + margin: 0; - &-button:hover { - box-shadow: none !important; + @include input-control; } - &-icon { + .components-combobox-control__button-icon { height: 100%; - padding: 0 4px; + padding: 0; position: absolute; right: 0; top: 0; } + + .components-combobox-control__button-button { + height: $button-size-small + $border-width + $border-width; + + &:focus:not(:disabled) { + box-shadow: none; + } + } } .components-combobox-control__menu { @@ -51,20 +49,26 @@ padding: 0; position: absolute; z-index: z-index(".components-popover"); + + &:not(:empty) { + margin: $grid-unit-15 0 0 0; + border-radius: $radius-block-ui; + border: $border-width solid $gray-900; + } } .components-combobox-control__item { align-items: center; display: flex; list-style-type: none; - padding: 10px 5px 10px 25px; + padding: $grid-unit-10 $grid-unit-15 $grid-unit-10 ($button-size-small + $grid-unit-15); &.is-highlighted { background: $gray-200; } - &-icon { - margin-left: -20px; - margin-right: 0; + .components-combobox-control__item-icon { + margin-left: -$button-size-small - $grid-unit-05; + margin-right: $grid-unit-05; } } diff --git a/packages/editor/src/components/page-attributes/parent.js b/packages/editor/src/components/page-attributes/parent.js index f6a72adde6ca8..592680e608c5b 100644 --- a/packages/editor/src/components/page-attributes/parent.js +++ b/packages/editor/src/components/page-attributes/parent.js @@ -24,12 +24,26 @@ import { buildTermsTree } from '../../utils/terms'; export function PageAttributesParent() { const { editPost } = useDispatch( 'core/editor' ); - const [ fieldValue, setFieldValue ] = useState(); - const { isLoading, parent, items, postType, parents } = useSelect( + const { parentPost } = useSelect( ( select ) => { + const { getEntityRecords } = select( 'core' ); + const { getEditedPostAttribute } = select( 'core/editor' ); + const postTypeSlug = getEditedPostAttribute( 'type' ); + + const theParentID = getEditedPostAttribute( 'parent' ); + + return { + parentPost: getEntityRecords( 'postType', postTypeSlug, { + include: [ theParentID ], + } ), + }; + }, [] ); + const [ fieldValue, setFieldValue ] = useState( + parentPost ? parentPost[ 0 ].title.raw : false + ); + const { isLoading, parent, items, postType } = useSelect( ( select ) => { - const { getPostType, getEntityRecords, isResolving } = select( - 'core' - ); + const { getPostType, getEntityRecords } = select( 'core' ); + const { isResolving } = select( 'core/data' ); const { getCurrentPostId, getEditedPostAttribute } = select( 'core/editor' ); @@ -46,25 +60,28 @@ export function PageAttributesParent() { _fields: 'id,title,parent', }; if ( - ! fieldValue || - '' === fieldValue || - fieldValue === parent?.name + parentPost && + fieldValue && + ( '' !== fieldValue || + fieldValue !== parentPost[ 0 ].title.raw ) ) { query.search = fieldValue; } - const par = getEditedPostAttribute( 'parent' ); - const pars = getEntityRecords( 'postType', postTypeSlug, { - include: [ par ], - } ); - + const theParentID = getEditedPostAttribute( 'parent' ); return { - parent: par, + parent: theParentID, items: isHierarchical ? getEntityRecords( 'postType', postTypeSlug, query ) : [], postType: pType, - isLoading: isResolving( 'postType', postTypeSlug, query ), - parents: pars, + isLoading: isResolving( 'core', 'getEntityRecords', [ + 'postType', + postTypeSlug, + query, + ] ), + parentPost: getEntityRecords( 'postType', postTypeSlug, { + include: [ theParentID ], + } ), }; }, [ fieldValue ] @@ -72,7 +89,6 @@ export function PageAttributesParent() { const isHierarchical = get( postType, [ 'hierarchical' ], false ); const parentPageLabel = get( postType, [ 'labels', 'parent_item_colon' ] ); const pageItems = items || []; - const getOptionsFromTree = ( tree, level = 0 ) => { return flatMap( tree, ( treeNode ) => [ { @@ -84,28 +100,38 @@ export function PageAttributesParent() { ...getOptionsFromTree( treeNode.children || [], level + 1 ), ] ); }; - const parentOptions = useMemo( () => { const tree = buildTermsTree( pageItems.map( ( item ) => ( { id: item.id, parent: item.parent, name: - item.title && item.title.raw - ? item.title.raw + item.title && item.title.rendered + ? item.title.rendered : `#${ item.id } (${ __( 'no title' ) })`, } ) ) ); const opts = getOptionsFromTree( tree ); - // Ensure the current page is included in the dropdown list. + // Ensure the current page is included in the dropdown list (except when searching). const foundParent = opts.findIndex( ( { key } ) => parent === key ); - if ( foundParent < 0 && parentPost ) { - return [ { key: parentPost.id, name: parentPost.title }, ...opts ]; + if ( + opts.length > 0 && + foundParent < 0 && + parentPost && + parent && + fieldValue === parentPost[ 0 ].title.rendered + ) { + return [ + { + key: parentPost[ 0 ].id, + name: parentPost[ 0 ].title.rendered, + }, + ...opts, + ]; } return opts; }, [ parent, parentPost, pageItems ] ); - //console.log( parentOptions ); if ( ! isHierarchical || ! parentPageLabel ) { return null; @@ -133,12 +159,18 @@ export function PageAttributesParent() { editPost( { parent: selectedItem.key } ); }; - const parentPost = parents ? parents[ 0 ] : false; - const inputValue = parentPost?.title?.raw; - const initialSelectedItem = { - key: parentPost?.id, - name: inputValue, - }; + const inputValue = parentPost ? parentPost[ 0 ].title.raw : false; + const selected = parentPost + ? parentOptions.findIndex( ( { key } ) => parentPost[ 0 ].id === key ) + : false; + const initialSelectedItem = + false !== selected && parentPost && parentOptions[ selected ] + ? parentOptions[ selected ] + : parentOptions[ 0 ]; + + if ( ! parentPost && ! isLoading ) { + return null; + } return ( Date: Tue, 15 Sep 2020 07:55:33 -0600 Subject: [PATCH 19/24] Clean up refresh on search, load. --- .../src/components/page-attributes/parent.js | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/packages/editor/src/components/page-attributes/parent.js b/packages/editor/src/components/page-attributes/parent.js index 592680e608c5b..c3fa0decc3069 100644 --- a/packages/editor/src/components/page-attributes/parent.js +++ b/packages/editor/src/components/page-attributes/parent.js @@ -24,19 +24,21 @@ import { buildTermsTree } from '../../utils/terms'; export function PageAttributesParent() { const { editPost } = useDispatch( 'core/editor' ); - const { parentPost } = useSelect( ( select ) => { - const { getEntityRecords } = select( 'core' ); - const { getEditedPostAttribute } = select( 'core/editor' ); - const postTypeSlug = getEditedPostAttribute( 'type' ); - - const theParentID = getEditedPostAttribute( 'parent' ); + const { parentPost } = useSelect( + ( select ) => { + const { getEntityRecords } = select( 'core' ); + const { getEditedPostAttribute } = select( 'core/editor' ); + const postTypeSlug = getEditedPostAttribute( 'type' ); + const theParentID = getEditedPostAttribute( 'parent' ); - return { - parentPost: getEntityRecords( 'postType', postTypeSlug, { - include: [ theParentID ], - } ), - }; - }, [] ); + return { + parentPost: getEntityRecords( 'postType', postTypeSlug, { + include: [ theParentID ], + } ), + }; + }, + [ parent ] + ); const [ fieldValue, setFieldValue ] = useState( parentPost ? parentPost[ 0 ].title.raw : false ); @@ -44,9 +46,7 @@ export function PageAttributesParent() { ( select ) => { const { getPostType, getEntityRecords } = select( 'core' ); const { isResolving } = select( 'core/data' ); - const { getCurrentPostId, getEditedPostAttribute } = select( - 'core/editor' - ); + const { getCurrentPostId, getEditedPostAttribute } = select( 'core/editor' ); const postTypeSlug = getEditedPostAttribute( 'type' ); const pType = getPostType( postTypeSlug ); const postId = getCurrentPostId(); @@ -79,13 +79,11 @@ export function PageAttributesParent() { postTypeSlug, query, ] ), - parentPost: getEntityRecords( 'postType', postTypeSlug, { - include: [ theParentID ], - } ), }; }, [ fieldValue ] ); + const isHierarchical = get( postType, [ 'hierarchical' ], false ); const parentPageLabel = get( postType, [ 'labels', 'parent_item_colon' ] ); const pageItems = items || []; @@ -93,9 +91,7 @@ export function PageAttributesParent() { return flatMap( tree, ( treeNode ) => [ { key: treeNode.id, - name: - repeat( '\u00A0', level * 3 ) + - unescapeString( treeNode.name ), + name: repeat( '— ', level ) + unescapeString( treeNode.name ), }, ...getOptionsFromTree( treeNode.children || [], level + 1 ), ] ); @@ -159,7 +155,7 @@ export function PageAttributesParent() { editPost( { parent: selectedItem.key } ); }; - const inputValue = parentPost ? parentPost[ 0 ].title.raw : false; + const inputValue = parentPost ? parentPost[ 0 ].title.raw : fieldValue; const selected = parentPost ? parentOptions.findIndex( ( { key } ) => parentPost[ 0 ].id === key ) : false; @@ -168,7 +164,7 @@ export function PageAttributesParent() { ? parentOptions[ selected ] : parentOptions[ 0 ]; - if ( ! parentPost && ! isLoading ) { + if ( ! fieldValue && isLoading ) { return null; } return ( From 51938693e8878ffe27750ae4ca412166dae510af Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 12 Oct 2020 15:22:31 +0100 Subject: [PATCH 20/24] Fix end2end test --- .../components/src/combobox-control/index.js | 8 +++- .../editor/plugins/custom-post-types.test.js | 31 ++++++++------ .../src/components/page-attributes/parent.js | 42 +++++++------------ 3 files changed, 40 insertions(+), 41 deletions(-) diff --git a/packages/components/src/combobox-control/index.js b/packages/components/src/combobox-control/index.js index ff9377d57d59a..9cf667f6a0938 100644 --- a/packages/components/src/combobox-control/index.js +++ b/packages/components/src/combobox-control/index.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + /** * WordPress dependencies */ @@ -26,6 +31,7 @@ function ComboboxControl( { hideLabelFromVision, help, allowReset = true, + className, messages = { selected: __( 'Item selected.' ), }, @@ -155,7 +161,7 @@ function ComboboxControl( { /* eslint-disable jsx-a11y/no-static-element-interactions */ return ( { await openButton.click(); } }; -const SELECT_OPTION_SELECTOR = - '.editor-page-attributes__parent option:nth-child(2)'; describe( 'Test Custom Post Types', () => { beforeAll( async () => { @@ -35,6 +33,11 @@ describe( 'Test Custom Post Types', () => { } ); it( 'It should be able to create an hierarchical post without title support', async () => { + const PARENT_PAGE_INPUT = + '.editor-page-attributes__parent input:not([disabled])'; + const SUGGESTION = + '.editor-page-attributes__parent .components-form-token-field__suggestion:first-child'; + // Create a parent post. await createNewPost( { postType: 'hierar-no-title' } ); await page.click( '.block-editor-writing-flow' ); @@ -43,23 +46,27 @@ describe( 'Test Custom Post Types', () => { // Create a post that is a child of the previously created post. await createNewPost( { postType: 'hierar-no-title' } ); await openPageAttributesPanel(); - await page.waitForSelector( SELECT_OPTION_SELECTOR ); - const optionToSelect = await page.$( SELECT_OPTION_SELECTOR ); - const valueToSelect = await ( - await optionToSelect.getProperty( 'value' ) - ).jsonValue(); - await page.select( - '.editor-page-attributes__parent select', - valueToSelect + await page.waitForSelector( PARENT_PAGE_INPUT ); + await page.click( PARENT_PAGE_INPUT ); + await page.waitForSelector( SUGGESTION ); + const optionToSelect = await page.$( SUGGESTION ); + const valueToSelect = await page.$eval( + SUGGESTION, + ( element ) => element.textContent ); + await optionToSelect.click(); await page.click( '.block-editor-writing-flow' ); await page.keyboard.type( 'Child Post' ); await publishPost(); // Reload the child post and verify it is still correctly selected as a child post. await page.reload(); - await page.waitForSelector( SELECT_OPTION_SELECTOR ); + await page.waitForSelector( PARENT_PAGE_INPUT ); + // Wait for the list of suggestions to fetch + // There should be a better way to do that. + // eslint-disable-next-line no-restricted-syntax + await page.waitFor( 1000 ); const selectedValue = await page.$eval( - '.editor-page-attributes__parent select', + PARENT_PAGE_INPUT, ( el ) => el.value ); expect( selectedValue ).toEqual( valueToSelect ); diff --git a/packages/editor/src/components/page-attributes/parent.js b/packages/editor/src/components/page-attributes/parent.js index 43f804cb1909c..ad4d9dc12171c 100644 --- a/packages/editor/src/components/page-attributes/parent.js +++ b/packages/editor/src/components/page-attributes/parent.js @@ -22,24 +22,29 @@ import { useSelect, useDispatch } from '@wordpress/data'; */ import { buildTermsTree } from '../../utils/terms'; +function getTitle( post ) { + return post.title && post.title.rendered + ? post.title.rendered + : `#${ post.id } (${ __( 'no title' ) })`; +} + export function PageAttributesParent() { const { editPost } = useDispatch( 'core/editor' ); const { parentPost, parentPostId } = useSelect( ( select ) => { - const { getEntityRecords } = select( 'core' ); + const { getEntityRecord } = select( 'core' ); const { getEditedPostAttribute } = select( 'core/editor' ); const postTypeSlug = getEditedPostAttribute( 'type' ); const pageId = getEditedPostAttribute( 'parent' ); return { parentPostId: pageId, - parentPost: getEntityRecords( 'postType', postTypeSlug, { - include: [ pageId ], - } ), + parentPost: pageId + ? getEntityRecord( 'postType', postTypeSlug, pageId ) + : null, }; }, [] ); - const [ fieldValue, setFieldValue ] = useState( - parentPost ? parentPost[ 0 ].title.raw : false - ); + const [ fieldValue, setFieldValue ] = useState( false ); + const { isLoading, parent, items, postType } = useSelect( ( select ) => { const { getPostType, getEntityRecords } = select( 'core' ); @@ -63,6 +68,7 @@ export function PageAttributesParent() { query.search = fieldValue; } const theParentID = getEditedPostAttribute( 'parent' ); + return { parent: theParentID, items: isHierarchical @@ -97,31 +103,11 @@ export function PageAttributesParent() { pageItems.map( ( item ) => ( { id: item.id, parent: item.parent, - name: - item.title && item.title.rendered - ? item.title.rendered - : `#${ item.id } (${ __( 'no title' ) })`, + name: getTitle( item ), } ) ) ); const opts = getOptionsFromTree( tree ); - // Ensure the current page is included in the dropdown list (except when searching). - const foundParent = opts.findIndex( ( { key } ) => parent === key ); - if ( - opts.length > 0 && - foundParent < 0 && - parentPost && - parent && - fieldValue === parentPost[ 0 ].title.rendered - ) { - return [ - { - value: parentPost[ 0 ].id, - label: parentPost[ 0 ].title.rendered, - }, - ...opts, - ]; - } return opts; }, [ parent, parentPost, pageItems ] ); From aa701ad82ede38371c0c8f00e044b7343ae03cf6 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 13 Oct 2020 12:25:44 +0100 Subject: [PATCH 21/24] deoptimize fields filtering in the data module --- .../src/queried-data/get-query-parts.js | 13 +++++++++---- .../core-data/src/queried-data/selectors.js | 10 +--------- .../src/queried-data/test/get-query-parts.js | 12 ------------ .../src/queried-data/test/selectors.js | 4 ++-- packages/core-data/src/selectors.js | 18 +++++++++++++++--- .../editor/plugins/custom-post-types.test.js | 10 +++++----- 6 files changed, 32 insertions(+), 35 deletions(-) diff --git a/packages/core-data/src/queried-data/get-query-parts.js b/packages/core-data/src/queried-data/get-query-parts.js index 665da38baf358..ad6ed09792c00 100644 --- a/packages/core-data/src/queried-data/get-query-parts.js +++ b/packages/core-data/src/queried-data/get-query-parts.js @@ -65,11 +65,16 @@ export function getQueryParts( query ) { ); break; - case '_fields': - parts.fields = getNormalizedCommaSeparable( value ); - break; - default: + // While in theory, we could exclude "_fields" from the stableKey + // because two request with different fields have the same results + // We're not able to ensure that because the server can decide to omit + // fields from the response even if we explicitely asked for it. + // Example: Asking for titles in posts without title support. + if ( key === '_fields' ) { + parts.fields = getNormalizedCommaSeparable( value ); + } + // While it could be any deterministic string, for simplicity's // sake mimic querystring encoding for stable key. // diff --git a/packages/core-data/src/queried-data/selectors.js b/packages/core-data/src/queried-data/selectors.js index 364557115de84..63468bc65491b 100644 --- a/packages/core-data/src/queried-data/selectors.js +++ b/packages/core-data/src/queried-data/selectors.js @@ -3,7 +3,7 @@ */ import createSelector from 'rememo'; import EquivalentKeyMap from 'equivalent-key-map'; -import { get, has, set } from 'lodash'; +import { get, set } from 'lodash'; /** * Internal dependencies @@ -31,7 +31,6 @@ function getQueriedItemsUncached( state, query ) { const { stableKey, page, perPage, include, fields } = getQueryParts( query ); - let itemIds; if ( Array.isArray( include ) && ! stableKey ) { // If the parsed query yields a set of IDs, but otherwise no filtering, @@ -73,14 +72,7 @@ function getQueriedItemsUncached( state, query ) { filteredItem = {}; for ( let f = 0; f < fields.length; f++ ) { - // Abort the entire request if a field is missing from the item. - // This accounts for the fact that queried items are stored by - // stable key without an associated fields query. Other requests - // may have included fewer fields properties. const field = fields[ f ].split( '.' ); - if ( ! has( item, field ) ) { - return null; - } const value = get( item, field ); set( filteredItem, field, value ); } diff --git a/packages/core-data/src/queried-data/test/get-query-parts.js b/packages/core-data/src/queried-data/test/get-query-parts.js index c97328dfa057e..9390c1b36ac16 100644 --- a/packages/core-data/src/queried-data/test/get-query-parts.js +++ b/packages/core-data/src/queried-data/test/get-query-parts.js @@ -28,18 +28,6 @@ describe( 'getQueryParts', () => { } ); } ); - it( 'parses out `_fields` property filtering', () => { - const parts = getQueryParts( { _fields: 'content', a: 1 } ); - - expect( parts ).toEqual( { - page: 1, - perPage: 10, - stableKey: 'a=1', - fields: [ 'content' ], - include: null, - } ); - } ); - it( 'encodes stable string key', () => { const first = getQueryParts( { '?': '&', b: 2 } ); const second = getQueryParts( { b: 2, '?': '&' } ); diff --git a/packages/core-data/src/queried-data/test/selectors.js b/packages/core-data/src/queried-data/test/selectors.js index 5226ae831898c..b490ca8f714c6 100644 --- a/packages/core-data/src/queried-data/test/selectors.js +++ b/packages/core-data/src/queried-data/test/selectors.js @@ -94,7 +94,7 @@ describe( 'getQueriedItems', () => { 2: true, }, queries: { - '': [ 1, 2 ], + '_fields%5B0%5D=content': [ 1, 2 ], }, }; @@ -133,7 +133,7 @@ describe( 'getQueriedItems', () => { 2: true, }, queries: { - '': [ 1, 2 ], + '_fields%5B0%5D=content&_fields%5B0%5D=meta.template': [ 1, 2 ], }, }; diff --git a/packages/core-data/src/selectors.js b/packages/core-data/src/selectors.js index cd56f12bfc052..429fbab424fec 100644 --- a/packages/core-data/src/selectors.js +++ b/packages/core-data/src/selectors.js @@ -2,7 +2,7 @@ * External dependencies */ import createSelector from 'rememo'; -import { first, map, find, get, filter, compact, defaultTo } from 'lodash'; +import { set, map, find, get, filter, compact, defaultTo } from 'lodash'; /** * WordPress dependencies @@ -16,6 +16,7 @@ import deprecated from '@wordpress/deprecated'; import { REDUCER_KEY } from './name'; import { getQueriedItems } from './queried-data'; import { DEFAULT_ENTITY_KEY } from './entities'; +import { getNormalizedCommaSeparable } from './utils'; /** * Returns true if a request is in progress for embed preview data, or false @@ -135,8 +136,19 @@ export function getEntityRecord( state, kind, name, key, query ) { return queriedState.items[ key ]; } - query = { ...query, include: [ key ] }; - return first( getQueriedItems( queriedState, query ) ); + const item = queriedState.items[ key ]; + if ( item && query._fields ) { + const filteredItem = {}; + const fields = getNormalizedCommaSeparable( query._fields ); + for ( let f = 0; f < fields.length; f++ ) { + const field = fields[ f ].split( '.' ); + const value = get( item, field ); + set( filteredItem, field, value ); + } + return filteredItem; + } + + return item; } /** diff --git a/packages/e2e-tests/specs/editor/plugins/custom-post-types.test.js b/packages/e2e-tests/specs/editor/plugins/custom-post-types.test.js index b3cb0dfdfc25f..b93b8a7aa6a2c 100644 --- a/packages/e2e-tests/specs/editor/plugins/custom-post-types.test.js +++ b/packages/e2e-tests/specs/editor/plugins/custom-post-types.test.js @@ -64,11 +64,11 @@ describe( 'Test Custom Post Types', () => { // Wait for the list of suggestions to fetch // There should be a better way to do that. // eslint-disable-next-line no-restricted-syntax - await page.waitFor( 1000 ); - const selectedValue = await page.$eval( - PARENT_PAGE_INPUT, - ( el ) => el.value + await page.waitFor( + ( [ value, inputSelector ] ) => + document.querySelector( inputSelector ).value === value, + {}, + [ valueToSelect, PARENT_PAGE_INPUT ] ); - expect( selectedValue ).toEqual( valueToSelect ); } ); } ); From 1398f6f489d06a3a7988271b303e362b006380c5 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 13 Oct 2020 12:30:27 +0100 Subject: [PATCH 22/24] Avoid useless double useSelect --- .../src/components/page-attributes/parent.js | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/editor/src/components/page-attributes/parent.js b/packages/editor/src/components/page-attributes/parent.js index ad4d9dc12171c..a85770431cd10 100644 --- a/packages/editor/src/components/page-attributes/parent.js +++ b/packages/editor/src/components/page-attributes/parent.js @@ -23,36 +23,32 @@ import { useSelect, useDispatch } from '@wordpress/data'; import { buildTermsTree } from '../../utils/terms'; function getTitle( post ) { - return post.title && post.title.rendered + return post?.title?.rendered ? post.title.rendered : `#${ post.id } (${ __( 'no title' ) })`; } export function PageAttributesParent() { const { editPost } = useDispatch( 'core/editor' ); - const { parentPost, parentPostId } = useSelect( ( select ) => { - const { getEntityRecord } = select( 'core' ); - const { getEditedPostAttribute } = select( 'core/editor' ); - const postTypeSlug = getEditedPostAttribute( 'type' ); - const pageId = getEditedPostAttribute( 'parent' ); - - return { - parentPostId: pageId, - parentPost: pageId - ? getEntityRecord( 'postType', postTypeSlug, pageId ) - : null, - }; - }, [] ); const [ fieldValue, setFieldValue ] = useState( false ); - - const { isLoading, parent, items, postType } = useSelect( + const { + parentPost, + parentPostId, + isLoading, + parent, + items, + postType, + } = useSelect( ( select ) => { - const { getPostType, getEntityRecords } = select( 'core' ); + const { getPostType, getEntityRecords, getEntityRecord } = select( + 'core' + ); const { isResolving } = select( 'core/data' ); const { getCurrentPostId, getEditedPostAttribute } = select( 'core/editor' ); const postTypeSlug = getEditedPostAttribute( 'type' ); + const pageId = getEditedPostAttribute( 'parent' ); const pType = getPostType( postTypeSlug ); const postId = getCurrentPostId(); const isHierarchical = get( pType, [ 'hierarchical' ], false ); @@ -70,6 +66,10 @@ export function PageAttributesParent() { const theParentID = getEditedPostAttribute( 'parent' ); return { + parentPostId: pageId, + parentPost: pageId + ? getEntityRecord( 'postType', postTypeSlug, pageId ) + : null, parent: theParentID, items: isHierarchical ? getEntityRecords( 'postType', postTypeSlug, query ) From ac897bd4f4db8790da1d7d1218091cf6397400c4 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 13 Oct 2020 12:37:47 +0100 Subject: [PATCH 23/24] Remove duplications --- .../src/components/page-attributes/parent.js | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/packages/editor/src/components/page-attributes/parent.js b/packages/editor/src/components/page-attributes/parent.js index a85770431cd10..147b7d5a51fae 100644 --- a/packages/editor/src/components/page-attributes/parent.js +++ b/packages/editor/src/components/page-attributes/parent.js @@ -31,19 +31,11 @@ function getTitle( post ) { export function PageAttributesParent() { const { editPost } = useDispatch( 'core/editor' ); const [ fieldValue, setFieldValue ] = useState( false ); - const { - parentPost, - parentPostId, - isLoading, - parent, - items, - postType, - } = useSelect( + const { parentPost, parentPostId, items, postType } = useSelect( ( select ) => { const { getPostType, getEntityRecords, getEntityRecord } = select( 'core' ); - const { isResolving } = select( 'core/data' ); const { getCurrentPostId, getEditedPostAttribute } = select( 'core/editor' ); @@ -63,23 +55,16 @@ export function PageAttributesParent() { if ( parentPost && fieldValue && '' !== fieldValue ) { query.search = fieldValue; } - const theParentID = getEditedPostAttribute( 'parent' ); return { parentPostId: pageId, parentPost: pageId ? getEntityRecord( 'postType', postTypeSlug, pageId ) : null, - parent: theParentID, items: isHierarchical ? getEntityRecords( 'postType', postTypeSlug, query ) : [], postType: pType, - isLoading: isResolving( 'core', 'getEntityRecords', [ - 'postType', - postTypeSlug, - query, - ] ), }; }, [ fieldValue ] @@ -109,7 +94,7 @@ export function PageAttributesParent() { const opts = getOptionsFromTree( tree ); return opts; - }, [ parent, parentPost, pageItems ] ); + }, [ pageItems ] ); if ( ! isHierarchical || ! parentPageLabel ) { return null; @@ -140,7 +125,6 @@ export function PageAttributesParent() { options={ parentOptions } onFilterValueChange={ debounce( handleKeydown, 300 ) } onChange={ handleChange } - isLoading={ isLoading } /> ); } From edd314bca8c5c611639f6b5230ed7f2af0680ee7 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 13 Oct 2020 13:49:10 +0100 Subject: [PATCH 24/24] Fix unit test --- packages/core-data/src/queried-data/test/selectors.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-data/src/queried-data/test/selectors.js b/packages/core-data/src/queried-data/test/selectors.js index b490ca8f714c6..f9aa52e1176ca 100644 --- a/packages/core-data/src/queried-data/test/selectors.js +++ b/packages/core-data/src/queried-data/test/selectors.js @@ -133,7 +133,7 @@ describe( 'getQueriedItems', () => { 2: true, }, queries: { - '_fields%5B0%5D=content&_fields%5B0%5D=meta.template': [ 1, 2 ], + '_fields%5B0%5D=content&_fields%5B1%5D=meta.template': [ 1, 2 ], }, };