From 0558ec8ada12ae733fec1a38e2a50f11b2df3439 Mon Sep 17 00:00:00 2001 From: Kerry Liu Date: Wed, 1 Sep 2021 15:39:48 -0700 Subject: [PATCH] List View: make sure sidebar component updates after blocks are added removed or inserted --- package-lock.json | 3 +- packages/block-editor/package.json | 3 +- .../src/components/list-view/index.js | 5 +- .../list-view/use-list-view-client-ids.js | 24 +++++-- packages/block-editor/src/store/reducer.js | 22 +++++++ packages/block-editor/src/store/selectors.js | 11 ++++ .../block-editor/src/store/test/reducer.js | 64 +++++++++++++++++++ .../edit-post/src/components/layout/index.js | 14 +++- .../secondary-sidebar/list-view-sidebar.js | 6 +- .../edit-site/src/components/editor/index.js | 15 ++++- .../secondary-sidebar/list-view-sidebar.js | 6 +- 11 files changed, 157 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index d4899cfca2fa11..0d8338dfa9adfe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18033,7 +18033,8 @@ "redux-multi": "^0.1.12", "rememo": "^3.0.0", "tinycolor2": "^1.4.2", - "traverse": "^0.6.6" + "traverse": "^0.6.6", + "uuid": "^8.3.0" } }, "@wordpress/block-library": { diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index 63a1cd006a4597..d55eeff94542ca 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -69,7 +69,8 @@ "redux-multi": "^0.1.12", "rememo": "^3.0.0", "tinycolor2": "^1.4.2", - "traverse": "^0.6.6" + "traverse": "^0.6.6", + "uuid": "^8.3.0" }, "publishConfig": { "access": "public" diff --git a/packages/block-editor/src/components/list-view/index.js b/packages/block-editor/src/components/list-view/index.js index 82909cd957368c..f2e43b13a45c19 100644 --- a/packages/block-editor/src/components/list-view/index.js +++ b/packages/block-editor/src/components/list-view/index.js @@ -47,6 +47,7 @@ const expanded = ( state, action ) => { * @param {Function} props.onSelect Block selection callback. * @param {boolean} props.showNestedBlocks Flag to enable displaying nested blocks. * @param {boolean} props.showOnlyCurrentHierarchy Flag to limit the list to the current hierarchy of blocks. + * @param {string} props.blocksChangedUUID if blocks is not specified, used to help cache break clientIdsTree * @param {boolean} props.__experimentalFeatures Flag to enable experimental features. * @param {boolean} props.__experimentalPersistentListViewFeatures Flag to enable features for the Persistent List View experiment. * @param {Object} ref Forwarded ref @@ -58,6 +59,7 @@ function ListView( onSelect = noop, __experimentalFeatures, __experimentalPersistentListViewFeatures, + blocksChangedUUID = '', ...props }, ref @@ -65,7 +67,8 @@ function ListView( const { clientIdsTree, selectedClientIds } = useListViewClientIds( blocks, showOnlyCurrentHierarchy, - __experimentalPersistentListViewFeatures + __experimentalPersistentListViewFeatures, + blocksChangedUUID ); const { selectBlock } = useDispatch( blockEditorStore ); const selectEditorBlock = useCallback( diff --git a/packages/block-editor/src/components/list-view/use-list-view-client-ids.js b/packages/block-editor/src/components/list-view/use-list-view-client-ids.js index 4e71ffe323c901..a3c07843822915 100644 --- a/packages/block-editor/src/components/list-view/use-list-view-client-ids.js +++ b/packages/block-editor/src/components/list-view/use-list-view-client-ids.js @@ -11,7 +11,8 @@ import { isClientIdSelected } from './utils'; import { store as blockEditorStore } from '../../store'; const useListViewSelectedClientIds = ( - __experimentalPersistentListViewFeatures + __experimentalPersistentListViewFeatures, + blocksChangedUUID ) => useSelect( ( select ) => { @@ -26,13 +27,14 @@ const useListViewSelectedClientIds = ( return getSelectedBlockClientId(); }, - [ __experimentalPersistentListViewFeatures ] + [ __experimentalPersistentListViewFeatures, blocksChangedUUID ] ); const useListViewClientIdsTree = ( blocks, selectedClientIds, - showOnlyCurrentHierarchy + showOnlyCurrentHierarchy, + blocksChangedUUID ) => useSelect( ( select ) => { @@ -68,21 +70,29 @@ const useListViewClientIdsTree = ( return __unstableGetClientIdsTree(); }, - [ blocks, selectedClientIds, showOnlyCurrentHierarchy ] + [ + blocks, + selectedClientIds, + showOnlyCurrentHierarchy, + blocksChangedUUID, + ] ); export default function useListViewClientIds( blocks, showOnlyCurrentHierarchy, - __experimentalPersistentListViewFeatures + __experimentalPersistentListViewFeatures, + blocksChangedUUID ) { const selectedClientIds = useListViewSelectedClientIds( - __experimentalPersistentListViewFeatures + __experimentalPersistentListViewFeatures, + blocksChangedUUID ); const clientIdsTree = useListViewClientIdsTree( blocks, selectedClientIds, - showOnlyCurrentHierarchy + showOnlyCurrentHierarchy, + blocksChangedUUID ); return { clientIdsTree, selectedClientIds }; } diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 4fd2e38efa0d4d..05b0d3162f7acc 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -16,6 +16,7 @@ import { omitBy, pickBy, } from 'lodash'; +import { v4 as uuid } from 'uuid'; /** * WordPress dependencies @@ -1194,6 +1195,26 @@ export function isTyping( state = false, action ) { return state; } +/** + * Reducer that returns a new uuid every time a block is added, removed or inserted. + * + * @param {string} state uuid string + * @param {Object} action Dispated action + * + * @return {string} Updated state + */ +export function __unstableBlocksChangedUUID( state = uuid(), action ) { + switch ( action.type ) { + case 'REPLACE_INNER_BLOCKS': + case 'INSERT_BLOCKS': + case 'REMOVE_BLOCKS': + case 'REPLACE_BLOCKS': + case 'RESET_BLOCKS': + return uuid(); + } + return state; +} + /** * Reducer returning dragged block client id. * @@ -1799,4 +1820,5 @@ export default combineReducers( { automaticChangeStatus, highlightedBlock, lastBlockInserted, + __unstableBlocksChangedUUID, } ); diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index d10a80d83e2451..113e9ad723b8e5 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -2163,3 +2163,14 @@ export function wasBlockJustInserted( state, clientId, source ) { lastBlockInserted.source === source ); } + +/** + * Returns a uuid which changes every time a block is added, removed or inserted + * + * @param {Object} state Block editor state. + * + * @return {string} uuid string + */ +export function __unstableGetBlocksChangedUUID( state ) { + return state.__unstableBlocksChangedUUID; +} diff --git a/packages/block-editor/src/store/test/reducer.js b/packages/block-editor/src/store/test/reducer.js index e3c7cdbc05bf21..fba6fb9ffd721c 100644 --- a/packages/block-editor/src/store/test/reducer.js +++ b/packages/block-editor/src/store/test/reducer.js @@ -33,8 +33,25 @@ import { blockListSettings, lastBlockAttributesChange, lastBlockInserted, + __unstableBlocksChangedUUID, } from '../reducer'; +jest.mock( 'uuid', () => { + const v4 = jest.requireActual( 'uuid' ).v4; + let lastCall = null; + return { + v4: jest.fn( () => { + lastCall = v4(); + return lastCall; + } ), + getLastCall: () => { + return lastCall; + }, + }; +} ); + +const uuid = require( 'uuid' ); + describe( 'state', () => { describe( 'hasSameKeys()', () => { it( 'returns false if two objects do not have the same keys', () => { @@ -3039,4 +3056,51 @@ describe( 'state', () => { expect( state ).toEqual( expectedState ); } ); } ); + describe( '__unstableBlocksChangedUUID', () => { + it( 'should return a new uuid when blocks are reset', () => { + const initialState = 'initial-state-uuid'; + const action = { + type: 'RESET_BLOCKS', + }; + const state = __unstableBlocksChangedUUID( initialState, action ); + expect( state ).toEqual( uuid.getLastCall() ); + expect( state ).not.toEqual( initialState ); + } ); + it( 'should return a new uuid when inner blocks are replaced', () => { + const initialState = 'initial-state-uuid'; + const action = { + type: 'REPLACE_INNER_BLOCKS', + }; + const state = __unstableBlocksChangedUUID( initialState, action ); + expect( state ).toEqual( uuid.getLastCall() ); + expect( state ).not.toEqual( initialState ); + } ); + it( 'should return a new uuid when blocks are removed', () => { + const initialState = 'initial-state-uuid'; + const action = { + type: 'REMOVE_BLOCKS', + }; + const state = __unstableBlocksChangedUUID( initialState, action ); + expect( state ).toEqual( uuid.getLastCall() ); + expect( state ).not.toEqual( initialState ); + } ); + it( 'should return a new uuid when blocks are replaced', () => { + const initialState = 'initial-state-uuid'; + const action = { + type: 'REPLACE_BLOCKS', + }; + const state = __unstableBlocksChangedUUID( initialState, action ); + expect( state ).toEqual( uuid.getLastCall() ); + expect( state ).not.toEqual( initialState ); + } ); + it( 'should return a new uuid when blocks are inserted', () => { + const initialState = 'initial-state-uuid'; + const action = { + type: 'INSERT_BLOCKS', + }; + const state = __unstableBlocksChangedUUID( initialState, action ); + expect( state ).toEqual( uuid.getLastCall() ); + expect( state ).not.toEqual( initialState ); + } ); + } ); } ); diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js index 51311d59ea7b6e..53d1bc2d4588d7 100644 --- a/packages/edit-post/src/components/layout/index.js +++ b/packages/edit-post/src/components/layout/index.js @@ -16,7 +16,10 @@ import { store as editorStore, } from '@wordpress/editor'; import { AsyncModeProvider, useSelect, useDispatch } from '@wordpress/data'; -import { BlockBreadcrumb } from '@wordpress/block-editor'; +import { + BlockBreadcrumb, + store as blockEditorStore, +} from '@wordpress/block-editor'; import { Button, ScrollLock, Popover } from '@wordpress/components'; import { useViewportMatch } from '@wordpress/compose'; import { PluginArea } from '@wordpress/plugins'; @@ -70,6 +73,13 @@ function Layout( { styles } ) { closeGeneralSidebar, setIsInserterOpened, } = useDispatch( editPostStore ); + // Used to help determine if ListViewSidebar should update + const { blocksChangedUUID } = useSelect( ( select ) => { + const { __unstableGetBlocksChangedUUID } = select( blockEditorStore ); + return { + blocksChangedUUID: __unstableGetBlocksChangedUUID(), + }; + } ); const { mode, isFullscreenActive, @@ -177,7 +187,7 @@ function Layout( { styles } ) { if ( mode === 'visual' && isListViewOpened ) { return ( - + ); } diff --git a/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js b/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js index 617a22f9b76326..5da01323d6c5a9 100644 --- a/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js +++ b/packages/edit-post/src/components/secondary-sidebar/list-view-sidebar.js @@ -13,6 +13,7 @@ import { useMergeRefs, } from '@wordpress/compose'; import { useDispatch } from '@wordpress/data'; +import { memo } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { closeSmall } from '@wordpress/icons'; import { ESCAPE } from '@wordpress/keycodes'; @@ -22,7 +23,7 @@ import { ESCAPE } from '@wordpress/keycodes'; */ import { store as editPostStore } from '../../store'; -export default function ListViewSidebar() { +function ListViewSidebar( blocksChangedUUID ) { const { setIsListViewOpened } = useDispatch( editPostStore ); const { clearSelectedBlock, selectBlock } = useDispatch( blockEditorStore ); @@ -64,6 +65,7 @@ export default function ListViewSidebar() { > @@ -71,3 +73,5 @@ export default function ListViewSidebar() { ); } + +export default memo( ListViewSidebar ); diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index c9225340907ba3..ed7914a79597a6 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -10,7 +10,11 @@ import { Notice, } from '@wordpress/components'; import { EntityProvider, store as coreStore } from '@wordpress/core-data'; -import { BlockContextProvider, BlockBreadcrumb } from '@wordpress/block-editor'; +import { + BlockContextProvider, + BlockBreadcrumb, + store as blockEditorStore, +} from '@wordpress/block-editor'; import { FullscreenMode, InterfaceSkeleton, @@ -48,6 +52,13 @@ const interfaceLabels = { }; function Editor( { initialSettings, onError } ) { + // Used to help determine if ListViewSidebar should update + const { blocksChangedUUID } = useSelect( ( select ) => { + const { __unstableGetBlocksChangedUUID } = select( blockEditorStore ); + return { + blocksChangedUUID: __unstableGetBlocksChangedUUID(), + }; + } ); const { isInserterOpen, isListViewOpen, @@ -170,7 +181,7 @@ function Editor( { initialSettings, onError } ) { if ( isListViewOpen ) { return ( - + ); } diff --git a/packages/edit-site/src/components/secondary-sidebar/list-view-sidebar.js b/packages/edit-site/src/components/secondary-sidebar/list-view-sidebar.js index 469dabaff56198..b12dbc7944780b 100644 --- a/packages/edit-site/src/components/secondary-sidebar/list-view-sidebar.js +++ b/packages/edit-site/src/components/secondary-sidebar/list-view-sidebar.js @@ -13,6 +13,7 @@ import { useMergeRefs, } from '@wordpress/compose'; import { useDispatch } from '@wordpress/data'; +import { memo } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { closeSmall } from '@wordpress/icons'; import { ESCAPE } from '@wordpress/keycodes'; @@ -22,7 +23,7 @@ import { ESCAPE } from '@wordpress/keycodes'; */ import { store as editSiteStore } from '../../store'; -export default function ListViewSidebar() { +function ListViewSidebar( blocksChangedUUID ) { const { setIsListViewOpened } = useDispatch( editSiteStore ); const { clearSelectedBlock, selectBlock } = useDispatch( blockEditorStore ); @@ -64,9 +65,12 @@ export default function ListViewSidebar() { ); } + +export default memo( ListViewSidebar );