diff --git a/packages/components/src/unit-control/index.tsx b/packages/components/src/unit-control/index.tsx
index 847056ae4da47..073801df17c15 100644
--- a/packages/components/src/unit-control/index.tsx
+++ b/packages/components/src/unit-control/index.tsx
@@ -24,6 +24,7 @@ import {
getValidParsedQuantityAndUnit,
} from './utils';
import { useControlledState } from '../utils/hooks';
+import { escapeRegExp } from '../utils/strings';
import type { UnitControlProps, UnitControlOnChangeCallback } from './types';
function UnforwardedUnitControl(
@@ -76,9 +77,9 @@ function UnforwardedUnitControl(
);
const [ { value: firstUnitValue = '' } = {}, ...rest ] = list;
const firstCharacters = rest.reduce( ( carry, { value } ) => {
- const first = value?.substring( 0, 1 ) || '';
+ const first = escapeRegExp( value?.substring( 0, 1 ) || '' );
return carry.includes( first ) ? carry : `${ carry }|${ first }`;
- }, firstUnitValue.substring( 0, 1 ) );
+ }, escapeRegExp( firstUnitValue.substring( 0, 1 ) ) );
return [ list, new RegExp( `^(?:${ firstCharacters })$`, 'i' ) ];
}, [ nonNullValueProp, unitProp, unitsProp ] );
const [ parsedQuantity, parsedUnit ] = getParsedQuantityAndUnit(
diff --git a/packages/components/src/unit-control/test/index.tsx b/packages/components/src/unit-control/test/index.tsx
index 9a2c719c46336..777004a6e8ae2 100644
--- a/packages/components/src/unit-control/test/index.tsx
+++ b/packages/components/src/unit-control/test/index.tsx
@@ -373,18 +373,21 @@ describe( 'UnitControl', () => {
const units = [
{ value: 'pt', label: 'pt', default: 0 },
{ value: 'vmax', label: 'vmax', default: 10 },
+ // Proves that units with regex control characters don't error.
+ { value: '+', label: '+', default: 10 },
];
render(
);
const options = getSelectOptions();
- expect( options.length ).toBe( 2 );
+ expect( options.length ).toBe( 3 );
- const [ pt, vmax ] = options;
+ const [ pt, vmax, plus ] = options;
expect( pt.value ).toBe( 'pt' );
expect( vmax.value ).toBe( 'vmax' );
+ expect( plus.value ).toBe( '+' );
} );
it( 'should reset value on unit change, if unit has default value', async () => {
diff --git a/packages/core-data/src/actions.js b/packages/core-data/src/actions.js
index cfab95aae9f8f..2170e3ffcb4ae 100644
--- a/packages/core-data/src/actions.js
+++ b/packages/core-data/src/actions.js
@@ -357,7 +357,7 @@ export const editEntityRecord =
`The entity being edited (${ kind }, ${ name }) does not have a loaded config.`
);
}
- const { transientEdits = {}, mergedEdits = {} } = entityConfig;
+ const { mergedEdits = {} } = entityConfig;
const record = select.getRawEntityRecord( kind, name, recordId );
const editedRecord = select.getEditedEntityRecord(
kind,
@@ -382,7 +382,6 @@ export const editEntityRecord =
: value;
return acc;
}, {} ),
- transientEdits,
};
dispatch( {
type: 'EDIT_ENTITY_RECORD',
@@ -395,6 +394,7 @@ export const editEntityRecord =
acc[ key ] = editedRecord[ key ];
return acc;
}, {} ),
+ isCached: options.isCached,
},
},
} );
diff --git a/packages/core-data/src/entity-provider.js b/packages/core-data/src/entity-provider.js
index 04bb4c21433e3..da048944f1498 100644
--- a/packages/core-data/src/entity-provider.js
+++ b/packages/core-data/src/entity-provider.js
@@ -7,7 +7,7 @@ import {
useCallback,
useEffect,
} from '@wordpress/element';
-import { useSelect, useDispatch, useRegistry } from '@wordpress/data';
+import { useSelect, useDispatch } from '@wordpress/data';
import { parse, __unstableSerializeAndClean } from '@wordpress/blocks';
import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor';
@@ -154,17 +154,16 @@ export function useEntityProp( kind, name, prop, _id ) {
* @return {[WPBlock[], Function, Function]} The block array and setters.
*/
export function useEntityBlockEditor( kind, name, { id: _id } = {} ) {
- const [ meta, updateMeta ] = useEntityProp( kind, name, 'meta', _id );
- const registry = useRegistry();
const providerId = useEntityId( kind, name );
const id = _id ?? providerId;
- const { content, blocks } = useSelect(
+ const { content, blocks, meta } = useSelect(
( select ) => {
const { getEditedEntityRecord } = select( STORE_NAME );
const editedRecord = getEditedEntityRecord( kind, name, id );
return {
blocks: editedRecord.blocks,
content: editedRecord.content,
+ meta: editedRecord.meta,
};
},
[ kind, name, id ]
@@ -194,7 +193,7 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) {
( _blocks ) => {
if ( ! meta ) return;
// If meta.footnotes is empty, it means the meta is not registered.
- if ( meta.footnotes === undefined ) return;
+ if ( meta.footnotes === undefined ) return {};
const { getRichTextValues } = unlock( blockEditorPrivateApis );
const _content = getRichTextValues( _blocks ).join( '' ) || '';
@@ -237,48 +236,57 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) {
}, {} ),
};
- updateMeta( {
- ...meta,
- footnotes: JSON.stringify( newFootnotes ),
- } );
+ return {
+ meta: {
+ ...meta,
+ footnotes: JSON.stringify( newFootnotes ),
+ },
+ };
},
- [ meta, updateMeta ]
+ [ meta ]
);
const onChange = useCallback(
( newBlocks, options ) => {
- const { selection } = options;
- const edits = { blocks: newBlocks, selection };
-
- const noChange = blocks === edits.blocks;
+ const noChange = blocks === newBlocks;
if ( noChange ) {
return __unstableCreateUndoLevel( kind, name, id );
}
+ const { selection } = options;
// We create a new function here on every persistent edit
// to make sure the edit makes the post dirty and creates
// a new undo level.
- edits.content = ( { blocks: blocksForSerialization = [] } ) =>
- __unstableSerializeAndClean( blocksForSerialization );
+ const edits = {
+ blocks: newBlocks,
+ selection,
+ content: ( { blocks: blocksForSerialization = [] } ) =>
+ __unstableSerializeAndClean( blocksForSerialization ),
+ ...updateFootnotes( newBlocks ),
+ };
- registry.batch( () => {
- updateFootnotes( edits.blocks );
- editEntityRecord( kind, name, id, edits );
- } );
+ editEntityRecord( kind, name, id, edits, { isCached: false } );
},
- [ kind, name, id, blocks, updateFootnotes ]
+ [
+ kind,
+ name,
+ id,
+ blocks,
+ updateFootnotes,
+ __unstableCreateUndoLevel,
+ editEntityRecord,
+ ]
);
const onInput = useCallback(
( newBlocks, options ) => {
const { selection } = options;
- const edits = { blocks: newBlocks, selection };
- registry.batch( () => {
- updateFootnotes( edits.blocks );
- editEntityRecord( kind, name, id, edits );
- } );
+ const footnotesChanges = updateFootnotes( newBlocks );
+ const edits = { blocks: newBlocks, selection, ...footnotesChanges };
+
+ editEntityRecord( kind, name, id, edits, { isCached: true } );
},
- [ kind, name, id, updateFootnotes ]
+ [ kind, name, id, updateFootnotes, editEntityRecord ]
);
return [ blocks ?? EMPTY_ARRAY, onInput, onChange ];
diff --git a/packages/core-data/src/reducer.js b/packages/core-data/src/reducer.js
index b7dd9d73df15a..20755dad4be8d 100644
--- a/packages/core-data/src/reducer.js
+++ b/packages/core-data/src/reducer.js
@@ -439,7 +439,7 @@ export const entities = ( state = {}, action ) => {
*
* @property {number} list The undo stack.
* @property {number} offset Where in the undo stack we are.
- * @property {Object} cache Cache of unpersisted transient edits.
+ * @property {Object} cache Cache of unpersisted edits.
*/
/** @typedef {Array
& UndoStateMeta} UndoState */
@@ -543,10 +543,6 @@ export function undo( state = UNDO_INITIAL_STATE, action ) {
return state;
}
- const isCachedChange = Object.keys( action.edits ).every(
- ( key ) => action.transientEdits[ key ]
- );
-
const edits = Object.keys( action.edits ).map( ( key ) => {
return {
kind: action.kind,
@@ -558,7 +554,7 @@ export function undo( state = UNDO_INITIAL_STATE, action ) {
};
} );
- if ( isCachedChange ) {
+ if ( action.meta.undo.isCached ) {
return {
...state,
cache: edits.reduce( appendEditToStack, state.cache ),
diff --git a/packages/core-data/src/test/reducer.js b/packages/core-data/src/test/reducer.js
index 4f7d9b9c0d2ae..7fac52c33c4b3 100644
--- a/packages/core-data/src/test/reducer.js
+++ b/packages/core-data/src/test/reducer.js
@@ -155,19 +155,21 @@ describe( 'undo', () => {
from,
to,
} );
- const createNextEditAction = ( edits, transientEdits = {} ) => {
+ const createNextEditAction = ( edits, isCached ) => {
let action = {
kind: 'someKind',
name: 'someName',
recordId: 'someRecordId',
edits,
- transientEdits,
};
action = {
type: 'EDIT_ENTITY_RECORD',
...action,
meta: {
- undo: { edits: lastValues },
+ undo: {
+ isCached,
+ edits: lastValues,
+ },
},
};
lastValues = { ...lastValues, ...edits };
@@ -303,10 +305,7 @@ describe( 'undo', () => {
it( 'handles flattened undos/redos', () => {
undoState = createNextUndoState();
undoState = createNextUndoState( { value: 1 } );
- undoState = createNextUndoState(
- { transientValue: 2 },
- { transientValue: true }
- );
+ undoState = createNextUndoState( { transientValue: 2 }, true );
undoState = createNextUndoState( { value: 3 } );
expectedUndoState.list.push(
[
@@ -335,10 +334,7 @@ describe( 'undo', () => {
// Check that transient edits are merged into the last
// edits.
- undoState = createNextUndoState(
- { transientValue: 2 },
- { transientValue: true }
- );
+ undoState = createNextUndoState( { transientValue: 2 }, true );
undoState = createNextUndoState( 'isCreate' );
expectedUndoState.list[ expectedUndoState.list.length - 1 ].push(
createExpectedDiff( 'transientValue', { from: undefined, to: 2 } )
@@ -359,10 +355,7 @@ describe( 'undo', () => {
it( 'explicitly creates an undo level when undoing while there are pending transient edits', () => {
undoState = createNextUndoState();
undoState = createNextUndoState( { value: 1 } );
- undoState = createNextUndoState(
- { transientValue: 2 },
- { transientValue: true }
- );
+ undoState = createNextUndoState( { transientValue: 2 }, true );
undoState = createNextUndoState( 'isUndo' );
expectedUndoState.list.push( [
createExpectedDiff( 'value', { from: undefined, to: 1 } ),
diff --git a/packages/data/src/registry.js b/packages/data/src/registry.js
index d193a50cbc392..02a0b19136a3e 100644
--- a/packages/data/src/registry.js
+++ b/packages/data/src/registry.js
@@ -314,6 +314,12 @@ export function createRegistry( storeConfigs = {}, parent = null ) {
}
function batch( callback ) {
+ // If we're already batching, just call the callback.
+ if ( emitter.isPaused ) {
+ callback();
+ return;
+ }
+
emitter.pause();
Object.values( stores ).forEach( ( store ) => store.emitter.pause() );
callback();
diff --git a/packages/data/src/test/registry.js b/packages/data/src/test/registry.js
index b9288eae821d8..df9cb774dfc8c 100644
--- a/packages/data/src/test/registry.js
+++ b/packages/data/src/test/registry.js
@@ -734,6 +734,27 @@ describe( 'createRegistry', () => {
unsubscribe();
expect( listener2 ).toHaveBeenCalledTimes( 1 );
} );
+
+ it( 'should support nested batches', () => {
+ const store = registry.registerStore( 'myAwesomeReducer', {
+ reducer: ( state = 0 ) => state + 1,
+ } );
+ const listener = jest.fn();
+ subscribeWithUnsubscribe( listener );
+
+ registry.batch( () => {} );
+ expect( listener ).not.toHaveBeenCalled();
+
+ registry.batch( () => {
+ store.dispatch( { type: 'dummy' } );
+ registry.batch( () => {
+ store.dispatch( { type: 'dummy' } );
+ store.dispatch( { type: 'dummy' } );
+ } );
+ store.dispatch( { type: 'dummy' } );
+ } );
+ expect( listener ).toHaveBeenCalledTimes( 1 );
+ } );
} );
describe( 'use', () => {
diff --git a/packages/e2e-test-utils/src/create-reusable-block.js b/packages/e2e-test-utils/src/create-reusable-block.js
index 7193db49a83ef..9714638154403 100644
--- a/packages/e2e-test-utils/src/create-reusable-block.js
+++ b/packages/e2e-test-utils/src/create-reusable-block.js
@@ -24,7 +24,7 @@ export const createReusableBlock = async ( content, title ) => {
await page.keyboard.type( content );
await clickBlockToolbarButton( 'Options' );
- await clickMenuItem( 'Create pattern' );
+ await clickMenuItem( 'Create pattern/reusable block' );
const nameInput = await page.waitForSelector(
reusableBlockNameInputSelector
);
@@ -38,7 +38,7 @@ export const createReusableBlock = async ( content, title ) => {
// Wait for creation to finish
await page.waitForXPath(
- '//*[contains(@class, "components-snackbar")]/*[text()="Synced Pattern created."]'
+ '//*[contains(@class, "components-snackbar")]/*[contains(text(),"Pattern created:")]'
);
// Check that we have a reusable block on the page
diff --git a/packages/e2e-tests/specs/editor/various/block-editor-keyboard-shortcuts.test.js b/packages/e2e-tests/specs/editor/various/block-editor-keyboard-shortcuts.test.js
index 24e8e3104aaaa..3be73830a4299 100644
--- a/packages/e2e-tests/specs/editor/various/block-editor-keyboard-shortcuts.test.js
+++ b/packages/e2e-tests/specs/editor/various/block-editor-keyboard-shortcuts.test.js
@@ -90,7 +90,7 @@ describe( 'block editor keyboard shortcuts', () => {
} );
it( 'should prevent deleting multiple selected blocks from inputs', async () => {
await clickBlockToolbarButton( 'Options' );
- await clickMenuItem( 'Create pattern' );
+ await clickMenuItem( 'Create pattern/reusable block' );
const reusableBlockNameInputSelector =
'.reusable-blocks-menu-items__convert-modal .components-text-control__input';
const nameInput = await page.waitForSelector(
diff --git a/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js b/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js
index 1ffd4e2414336..0a18c75528930 100644
--- a/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js
+++ b/packages/e2e-tests/specs/editor/various/reusable-blocks.test.js
@@ -197,7 +197,7 @@ describe( 'Reusable blocks', () => {
// Convert block to a reusable block.
await clickBlockToolbarButton( 'Options' );
- await clickMenuItem( 'Create pattern' );
+ await clickMenuItem( 'Create pattern/reusable block' );
// Set title.
const nameInput = await page.waitForSelector(
@@ -212,7 +212,7 @@ describe( 'Reusable blocks', () => {
// Wait for creation to finish.
await page.waitForXPath(
- '//*[contains(@class, "components-snackbar")]/*[text()="Synced Pattern created."]'
+ '//*[contains(@class, "components-snackbar")]/*[contains(text(),"Pattern created:")]'
);
await clearAllBlocks();
@@ -383,7 +383,7 @@ describe( 'Reusable blocks', () => {
// Convert to reusable.
await clickBlockToolbarButton( 'Options' );
- await clickMenuItem( 'Create pattern' );
+ await clickMenuItem( 'Create pattern/reusable block' );
const nameInput = await page.waitForSelector(
reusableBlockNameInputSelector
);
diff --git a/packages/edit-post/src/components/block-manager/style.scss b/packages/edit-post/src/components/block-manager/style.scss
index 2568856be41ab..c62e5fea93202 100644
--- a/packages/edit-post/src/components/block-manager/style.scss
+++ b/packages/edit-post/src/components/block-manager/style.scss
@@ -36,7 +36,7 @@
.edit-post-block-manager__category-title {
position: sticky;
- top: 0;
+ top: - $grid-unit-05; // Offsets the top padding on the modal content container
padding: $grid-unit-20 0;
background-color: $white;
z-index: z-index(".edit-post-block-manager__category-title");
diff --git a/packages/edit-post/src/components/header/header-toolbar/index.js b/packages/edit-post/src/components/header/header-toolbar/index.js
index 391e5473999bb..8f9e413707d50 100644
--- a/packages/edit-post/src/components/header/header-toolbar/index.js
+++ b/packages/edit-post/src/components/header/header-toolbar/index.js
@@ -19,6 +19,7 @@ import { Button, ToolbarItem } from '@wordpress/components';
import { listView, plus } from '@wordpress/icons';
import { useRef, useCallback } from '@wordpress/element';
import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts';
+import { store as preferencesStore } from '@wordpress/preferences';
/**
* Internal dependencies
@@ -36,6 +37,8 @@ function HeaderToolbar() {
const inserterButton = useRef();
const { setIsInserterOpened, setIsListViewOpened } =
useDispatch( editPostStore );
+ const { get: getPreference } = useSelect( preferencesStore );
+ const hasFixedToolbar = getPreference( 'core/edit-post', 'fixedToolbar' );
const {
isInserterEnabled,
isInserterOpened,
@@ -147,7 +150,7 @@ function HeaderToolbar() {
/>
{ ( isWideViewport || ! showIconLabels ) && (
<>
- { isLargeViewport && (
+ { isLargeViewport && ! hasFixedToolbar && (
=' );
@@ -202,6 +214,7 @@ function Layout( { styles } ) {
+
diff --git a/packages/edit-post/src/components/sidebar/post-trash/index.js b/packages/edit-post/src/components/sidebar/post-trash/index.js
index 885be537952c0..d77c7a6d82988 100644
--- a/packages/edit-post/src/components/sidebar/post-trash/index.js
+++ b/packages/edit-post/src/components/sidebar/post-trash/index.js
@@ -2,14 +2,11 @@
* WordPress dependencies
*/
import { PostTrash as PostTrashLink, PostTrashCheck } from '@wordpress/editor';
-import { FlexItem } from '@wordpress/components';
export default function PostTrash() {
return (
-
-
-
+
);
}
diff --git a/packages/edit-post/src/hooks/commands/use-common-commands.js b/packages/edit-post/src/hooks/commands/use-common-commands.js
index 0366e78179985..71233bcb6d7bd 100644
--- a/packages/edit-post/src/hooks/commands/use-common-commands.js
+++ b/packages/edit-post/src/hooks/commands/use-common-commands.js
@@ -10,6 +10,8 @@ import {
drawerRight,
blockDefault,
keyboardClose,
+ desktop,
+ listView,
} from '@wordpress/icons';
import { useCommand } from '@wordpress/commands';
import { store as preferencesStore } from '@wordpress/preferences';
@@ -23,16 +25,24 @@ import { PREFERENCES_MODAL_NAME } from '../../components/preferences-modal';
import { store as editPostStore } from '../../store';
export default function useCommonCommands() {
- const { openGeneralSidebar, closeGeneralSidebar, switchEditorMode } =
- useDispatch( editPostStore );
+ const {
+ openGeneralSidebar,
+ closeGeneralSidebar,
+ switchEditorMode,
+ setIsListViewOpened,
+ } = useDispatch( editPostStore );
const { openModal } = useDispatch( interfaceStore );
- const { editorMode, activeSidebar } = useSelect(
- ( select ) => ( {
- activeSidebar: select( interfaceStore ).getActiveComplementaryArea(
- editPostStore.name
- ),
- editorMode: select( editPostStore ).getEditorMode(),
- } ),
+ const { editorMode, activeSidebar, isListViewOpen } = useSelect(
+ ( select ) => {
+ const { getEditorMode, isListViewOpened } = select( editPostStore );
+ return {
+ activeSidebar: select(
+ interfaceStore
+ ).getActiveComplementaryArea( editPostStore.name ),
+ editorMode: getEditorMode(),
+ isListViewOpen: isListViewOpened(),
+ };
+ },
[]
);
const { toggle } = useDispatch( preferencesStore );
@@ -85,6 +95,26 @@ export default function useCommonCommands() {
},
} );
+ useCommand( {
+ name: 'core/toggle-fullscreen-mode',
+ label: __( 'Toggle fullscreen mode' ),
+ icon: desktop,
+ callback: ( { close } ) => {
+ toggle( 'core/edit-post', 'fullscreenMode' );
+ close();
+ },
+ } );
+
+ useCommand( {
+ name: 'core/toggle-list-view',
+ label: __( 'Toggle list view' ),
+ icon: listView,
+ callback: ( { close } ) => {
+ setIsListViewOpened( ! isListViewOpen );
+ close();
+ },
+ } );
+
useCommand( {
name: 'core/toggle-top-toolbar',
label: __( 'Toggle top toolbar' ),
diff --git a/packages/edit-site/src/components/block-editor/use-site-editor-settings.js b/packages/edit-site/src/components/block-editor/use-site-editor-settings.js
index af3f5ccba3498..4241c7f55cb67 100644
--- a/packages/edit-site/src/components/block-editor/use-site-editor-settings.js
+++ b/packages/edit-site/src/components/block-editor/use-site-editor-settings.js
@@ -12,11 +12,13 @@ import { unlock } from '../../lock-unlock';
import inserterMediaCategories from './inserter-media-categories';
export default function useSiteEditorSettings( templateType ) {
- const { storedSettings } = useSelect( ( select ) => {
- const { getSettings } = unlock( select( editSiteStore ) );
-
+ const { storedSettings, canvasMode } = useSelect( ( select ) => {
+ const { getSettings, getCanvasMode } = unlock(
+ select( editSiteStore )
+ );
return {
storedSettings: getSettings(),
+ canvasMode: getCanvasMode(),
};
}, [] );
@@ -70,6 +72,7 @@ export default function useSiteEditorSettings( templateType ) {
const {
__experimentalAdditionalBlockPatterns,
__experimentalAdditionalBlockPatternCategories,
+ focusMode,
...restStoredSettings
} = storedSettings;
@@ -86,6 +89,7 @@ export default function useSiteEditorSettings( templateType ) {
// active for all entities.
templateLock: false,
template: false,
+ focusMode: canvasMode === 'view' && focusMode ? false : focusMode,
};
- }, [ storedSettings, blockPatterns, blockPatternCategories ] );
+ }, [ storedSettings, blockPatterns, blockPatternCategories, canvasMode ] );
}
diff --git a/packages/edit-site/src/components/create-pattern-modal/index.js b/packages/edit-site/src/components/create-pattern-modal/index.js
index 7906cb2352c7b..46d734b86fdd1 100644
--- a/packages/edit-site/src/components/create-pattern-modal/index.js
+++ b/packages/edit-site/src/components/create-pattern-modal/index.js
@@ -56,7 +56,7 @@ export default function CreatePatternModal( {
status: 'publish',
meta:
syncType === SYNC_TYPES.unsynced
- ? { sync_status: syncType }
+ ? { wp_pattern_sync_status: syncType }
: undefined,
},
{ throwOnError: true }
diff --git a/packages/edit-site/src/components/global-styles/palette.js b/packages/edit-site/src/components/global-styles/palette.js
index 6e9757415524c..dc73f54a1701a 100644
--- a/packages/edit-site/src/components/global-styles/palette.js
+++ b/packages/edit-site/src/components/global-styles/palette.js
@@ -91,15 +91,16 @@ function Palette( { name } ) {
- { themeColors?.length > 0 && (
-
- { __( 'Randomize colors' ) }
-
- ) }
+ { window.__experimentalEnableColorRandomizer &&
+ themeColors?.length > 0 && (
+
+ { __( 'Randomize colors' ) }
+
+ ) }
);
}
diff --git a/packages/edit-site/src/components/header-edit-mode/document-actions/style.scss b/packages/edit-site/src/components/header-edit-mode/document-actions/style.scss
index 63c4d5198f8fb..6281887b13738 100644
--- a/packages/edit-site/src/components/header-edit-mode/document-actions/style.scss
+++ b/packages/edit-site/src/components/header-edit-mode/document-actions/style.scss
@@ -16,6 +16,14 @@
color: currentColor;
background: $gray-200;
}
+
+ @include break-medium() {
+ width: 50%;
+ }
+
+ @include break-large() {
+ width: min(100%, 450px);
+ }
}
.edit-site-document-actions__command {
diff --git a/packages/edit-site/src/components/header-edit-mode/index.js b/packages/edit-site/src/components/header-edit-mode/index.js
index 415dfb1d67cb0..b8d3b6e4cc255 100644
--- a/packages/edit-site/src/components/header-edit-mode/index.js
+++ b/packages/edit-site/src/components/header-edit-mode/index.js
@@ -108,6 +108,9 @@ export default function HeaderEditMode() {
};
}, [] );
+ const { get: getPreference } = useSelect( preferencesStore );
+ const hasFixedToolbar = getPreference( editSiteStore.name, 'fixedToolbar' );
+
const {
__experimentalSetPreviewDeviceType: setPreviewDeviceType,
setIsInserterOpened,
@@ -213,14 +216,18 @@ export default function HeaderEditMode() {
) }
{ isLargeViewport && (
<>
-
+ { ! hasFixedToolbar && (
+
+ ) }
) }
{ isZoomedOutViewExperimentEnabled &&
- ! isDistractionFree && (
+ ! isDistractionFree &&
+ ! hasFixedToolbar && (
( props ) => {
- const isContent = PAGE_CONTENT_BLOCK_TYPES.includes( props.name );
- const mode = isContent ? 'contentOnly' : undefined;
+ const isDescendentOfQueryLoop = !! props.context.queryId;
+ const isPageContent =
+ PAGE_CONTENT_BLOCK_TYPES.includes( props.name ) &&
+ ! isDescendentOfQueryLoop;
+ const mode = isPageContent ? 'contentOnly' : undefined;
useBlockEditingMode( mode );
return ;
},
diff --git a/packages/edit-site/src/components/page-patterns/grid-item.js b/packages/edit-site/src/components/page-patterns/grid-item.js
index 377a04aca1c16..7db14e1d37788 100644
--- a/packages/edit-site/src/components/page-patterns/grid-item.js
+++ b/packages/edit-site/src/components/page-patterns/grid-item.js
@@ -122,7 +122,12 @@ export default function GridItem( { categoryId, composite, icon, item } ) {
aria-label={ item.title }
aria-describedby={
ariaDescriptions.length
- ? ariaDescriptions.join( ' ' )
+ ? ariaDescriptions
+ .map(
+ ( _, index ) =>
+ `${ descriptionId }-${ index }`
+ )
+ .join( ' ' )
: undefined
}
>
diff --git a/packages/edit-site/src/components/page-patterns/patterns-list.js b/packages/edit-site/src/components/page-patterns/patterns-list.js
index 2dfe197c37963..545ffdb044275 100644
--- a/packages/edit-site/src/components/page-patterns/patterns-list.js
+++ b/packages/edit-site/src/components/page-patterns/patterns-list.js
@@ -79,14 +79,14 @@ export default function PatternsList( { categoryId, type } ) {
{ __( 'Synced' ) }
{ __(
- 'Patterns that are kept in sync across your site'
+ 'Patterns that are kept in sync across the site'
) }
>
@@ -97,7 +97,7 @@ export default function PatternsList( { categoryId, type } ) {
{ __( 'Standard' ) }
{ __(
- 'Patterns that can be changed freely without affecting your site'
+ 'Patterns that can be changed freely without affecting the site'
) }
diff --git a/packages/edit-site/src/components/page-patterns/use-patterns.js b/packages/edit-site/src/components/page-patterns/use-patterns.js
index a8d76b58cb45d..a394aabf572c4 100644
--- a/packages/edit-site/src/components/page-patterns/use-patterns.js
+++ b/packages/edit-site/src/components/page-patterns/use-patterns.js
@@ -38,14 +38,8 @@ const templatePartToPattern = ( templatePart ) => ( {
templatePart,
} );
-const templatePartCategories = [ 'header', 'footer', 'sidebar' ];
-const templatePartHasCategory = ( item, category ) => {
- if ( category === 'uncategorized' ) {
- return ! templatePartCategories.includes( item.templatePart.area );
- }
-
- return item.templatePart.area === category;
-};
+const templatePartHasCategory = ( item, category ) =>
+ item.templatePart.area === category;
const useTemplatePartsAsPatterns = (
categoryId,
@@ -154,7 +148,7 @@ const reusableBlockToPattern = ( reusableBlock ) => ( {
categories: reusableBlock.wp_pattern,
id: reusableBlock.id,
name: reusableBlock.slug,
- syncStatus: reusableBlock.meta?.sync_status || SYNC_TYPES.full,
+ syncStatus: reusableBlock.wp_pattern_sync_status || SYNC_TYPES.full,
title: reusableBlock.title.raw,
type: reusableBlock.type,
reusableBlock,
diff --git a/packages/edit-site/src/components/page-template-parts/index.js b/packages/edit-site/src/components/page-template-parts/index.js
index 0a50f83927979..7e9c8cb6dd6e1 100644
--- a/packages/edit-site/src/components/page-template-parts/index.js
+++ b/packages/edit-site/src/components/page-template-parts/index.js
@@ -45,7 +45,7 @@ export default function PageTemplateParts() {
header: __( 'Template Part' ),
cell: ( templatePart ) => (
-
+
(
-
+
diff --git a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-content.js b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-content.js
index d6e7dd23a709f..dd40bcaef9f70 100644
--- a/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-content.js
+++ b/packages/edit-site/src/components/sidebar-edit-mode/page-panels/page-content.js
@@ -6,22 +6,24 @@ import {
store as blockEditorStore,
privateApis as blockEditorPrivateApis,
} from '@wordpress/block-editor';
+import { useMemo } from '@wordpress/element';
/**
* Internal dependencies
*/
-import { PAGE_CONTENT_BLOCK_TYPES } from '../../page-content-focus-manager/constants';
import { unlock } from '../../../lock-unlock';
const { BlockQuickNavigation } = unlock( blockEditorPrivateApis );
export default function PageContent() {
- const clientIds = useSelect(
+ const clientIdsTree = useSelect(
( select ) =>
- select( blockEditorStore ).__experimentalGetGlobalBlocksByName(
- PAGE_CONTENT_BLOCK_TYPES
- ),
+ unlock( select( blockEditorStore ) ).getEnabledClientIdsTree(),
[]
);
+ const clientIds = useMemo(
+ () => clientIdsTree.map( ( { clientId } ) => clientId ),
+ [ clientIdsTree ]
+ );
return ;
}
diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js
index 1e2e7aac159ef..1e9200bf0af01 100644
--- a/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js
+++ b/packages/edit-site/src/components/sidebar-navigation-screen-global-styles/index.js
@@ -37,17 +37,27 @@ export function SidebarNavigationItemGlobalStyles( props ) {
const { setCanvasMode } = unlock( useDispatch( editSiteStore ) );
const { createNotice } = useDispatch( noticesStore );
const { set: setPreference } = useDispatch( preferencesStore );
- const { hasGlobalStyleVariations, isDistractionFree } = useSelect(
- ( select ) => ( {
- hasGlobalStyleVariations:
- !! select(
- coreStore
- ).__experimentalGetCurrentThemeGlobalStylesVariations()?.length,
- isDistractionFree: select( preferencesStore ).get(
- editSiteStore.name,
- 'distractionFree'
- ),
- } ),
+ const { get: getPrefference } = useSelect( preferencesStore );
+
+ const turnOffDistractionFreeMode = useCallback( () => {
+ const isDistractionFree = getPrefference(
+ editSiteStore.name,
+ 'distractionFree'
+ );
+ if ( ! isDistractionFree ) {
+ return;
+ }
+ setPreference( editSiteStore.name, 'distractionFree', false );
+ createNotice( 'info', __( 'Distraction free mode turned off' ), {
+ isDismissible: true,
+ type: 'snackbar',
+ } );
+ }, [ createNotice, setPreference, getPrefference ] );
+ const hasGlobalStyleVariations = useSelect(
+ ( select ) =>
+ !! select(
+ coreStore
+ ).__experimentalGetCurrentThemeGlobalStylesVariations()?.length,
[]
);
if ( hasGlobalStyleVariations ) {
@@ -63,19 +73,7 @@ export function SidebarNavigationItemGlobalStyles( props ) {
{
- // Disable distraction free mode.
- if ( isDistractionFree ) {
- setPreference(
- editSiteStore.name,
- 'distractionFree',
- false
- );
- createNotice(
- 'info',
- __( 'Distraction free mode turned off.' ),
- { type: 'snackbar' }
- );
- }
+ turnOffDistractionFreeMode();
// Switch to edit mode.
setCanvasMode( 'edit' );
// Open global styles sidebar.
@@ -170,22 +168,41 @@ export default function SidebarNavigationScreenGlobalStyles() {
const { setCanvasMode, setEditorCanvasContainerView } = unlock(
useDispatch( editSiteStore )
);
+ const { createNotice } = useDispatch( noticesStore );
+ const { set: setPreference } = useDispatch( preferencesStore );
+ const { get: getPrefference } = useSelect( preferencesStore );
+ const { isViewMode, isStyleBookOpened } = useSelect( ( select ) => {
+ const { getCanvasMode, getEditorCanvasContainerView } = unlock(
+ select( editSiteStore )
+ );
+ return {
+ isViewMode: 'view' === getCanvasMode(),
+ isStyleBookOpened: 'style-book' === getEditorCanvasContainerView(),
+ };
+ }, [] );
- const isStyleBookOpened = useSelect(
- ( select ) =>
- 'style-book' ===
- unlock( select( editSiteStore ) ).getEditorCanvasContainerView(),
- []
- );
+ const turnOffDistractionFreeMode = useCallback( () => {
+ const isDistractionFree = getPrefference(
+ editSiteStore.name,
+ 'distractionFree'
+ );
+ if ( ! isDistractionFree ) {
+ return;
+ }
+ setPreference( editSiteStore.name, 'distractionFree', false );
+ createNotice( 'info', __( 'Distraction free mode turned off' ), {
+ isDismissible: true,
+ type: 'snackbar',
+ } );
+ }, [ createNotice, setPreference, getPrefference ] );
- const openGlobalStyles = useCallback(
- async () =>
- Promise.all( [
- setCanvasMode( 'edit' ),
- openGeneralSidebar( 'edit-site/global-styles' ),
- ] ),
- [ setCanvasMode, openGeneralSidebar ]
- );
+ const openGlobalStyles = useCallback( async () => {
+ turnOffDistractionFreeMode();
+ return Promise.all( [
+ setCanvasMode( 'edit' ),
+ openGeneralSidebar( 'edit-site/global-styles' ),
+ ] );
+ }, [ setCanvasMode, openGeneralSidebar, turnOffDistractionFreeMode ] );
const openStyleBook = useCallback( async () => {
await openGlobalStyles();
@@ -246,7 +263,7 @@ export default function SidebarNavigationScreenGlobalStyles() {
>
}
/>
- { isStyleBookOpened && ! isMobileViewport && (
+ { isStyleBookOpened && ! isMobileViewport && isViewMode && (
false }
diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-main/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-main/index.js
index 58b93d61c45a6..152139870fa59 100644
--- a/packages/edit-site/src/components/sidebar-navigation-screen-main/index.js
+++ b/packages/edit-site/src/components/sidebar-navigation-screen-main/index.js
@@ -20,6 +20,7 @@ import SidebarNavigationItem from '../sidebar-navigation-item';
import { SidebarNavigationItemGlobalStyles } from '../sidebar-navigation-screen-global-styles';
import { unlock } from '../../lock-unlock';
import { store as editSiteStore } from '../../store';
+import TemplatePartHint from './template-part-hint';
export default function SidebarNavigationScreenMain() {
const { location } = useNavigator();
@@ -42,46 +43,49 @@ export default function SidebarNavigationScreenMain() {
'Customize the appearance of your website using the block editor.'
) }
content={
-
-
- { __( 'Navigation' ) }
-
-
- { __( 'Styles' ) }
-
-
- { __( 'Pages' ) }
-
-
- { __( 'Templates' ) }
-
-
- { __( 'Patterns' ) }
-
-
+ <>
+
+
+ { __( 'Navigation' ) }
+
+
+ { __( 'Styles' ) }
+
+
+ { __( 'Pages' ) }
+
+
+ { __( 'Templates' ) }
+
+
+ { __( 'Patterns' ) }
+
+
+
+ >
}
/>
);
diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-main/template-part-hint.js b/packages/edit-site/src/components/sidebar-navigation-screen-main/template-part-hint.js
new file mode 100644
index 0000000000000..8fbe74f81bb4d
--- /dev/null
+++ b/packages/edit-site/src/components/sidebar-navigation-screen-main/template-part-hint.js
@@ -0,0 +1,36 @@
+/**
+ * WordPress dependencies
+ */
+import { Notice } from '@wordpress/components';
+import { useDispatch, useSelect } from '@wordpress/data';
+import { __ } from '@wordpress/i18n';
+import { store as preferencesStore } from '@wordpress/preferences';
+
+const PREFERENCE_NAME = 'isTemplatePartMoveHintVisible';
+
+export default function TemplatePartHint() {
+ const showTemplatePartHint = useSelect(
+ ( select ) =>
+ select( preferencesStore ).get( 'core', PREFERENCE_NAME ) ?? true,
+ []
+ );
+
+ const { set: setPreference } = useDispatch( preferencesStore );
+ if ( ! showTemplatePartHint ) {
+ return null;
+ }
+
+ return (
+ {
+ setPreference( 'core', PREFERENCE_NAME, false );
+ } }
+ >
+ { __(
+ 'Looking for template parts? You can now find them in the new "Patterns" page.'
+ ) }
+
+ );
+}
diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/edit-button.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/edit-button.js
index 7d084b6db4e26..391017796b5e6 100644
--- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/edit-button.js
+++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/edit-button.js
@@ -2,23 +2,20 @@
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
-import { useDispatch } from '@wordpress/data';
import { pencil } from '@wordpress/icons';
/**
* Internal dependencies
*/
-import { store as editSiteStore } from '../../store';
import SidebarButton from '../sidebar-button';
-import { unlock } from '../../lock-unlock';
-
-export default function EditButton() {
- const { setCanvasMode } = unlock( useDispatch( editSiteStore ) );
+import { useLink } from '../routes/link';
+export default function EditButton( { postId } ) {
+ const linkInfo = useLink( {
+ postId,
+ postType: 'wp_navigation',
+ canvas: 'edit',
+ } );
return (
- setCanvasMode( 'edit' ) }
- label={ __( 'Edit' ) }
- icon={ pencil }
- />
+
);
}
diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/single-navigation-menu.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/single-navigation-menu.js
index 6351c83323f98..6b6fc8587228f 100644
--- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/single-navigation-menu.js
+++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menu/single-navigation-menu.js
@@ -9,6 +9,7 @@ import { decodeEntities } from '@wordpress/html-entities';
import { SidebarNavigationScreenWrapper } from '../sidebar-navigation-screen-navigation-menus';
import ScreenNavigationMoreMenu from './more-menu';
import NavigationMenuEditor from './navigation-menu-editor';
+import EditButton from './edit-button';
export default function SingleNavigationMenu( {
navigationMenu,
@@ -21,12 +22,15 @@ export default function SingleNavigationMenu( {
return (
+ <>
+
+
+ >
}
title={ decodeEntities( menuTitle ) }
description={ __(
diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-page/status-label.js b/packages/edit-site/src/components/sidebar-navigation-screen-page/status-label.js
index 13aba13aacbec..bcfc540b1f841 100644
--- a/packages/edit-site/src/components/sidebar-navigation-screen-page/status-label.js
+++ b/packages/edit-site/src/components/sidebar-navigation-screen-page/status-label.js
@@ -43,7 +43,7 @@ const pendingIcon = (
export default function StatusLabel( { status, date, short } ) {
const relateToNow = humanTimeDiff( date );
- let statusLabel = '';
+ let statusLabel = status;
let statusIcon = pendingIcon;
switch ( status ) {
case 'publish':
diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-pattern-details.js b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-pattern-details.js
index dfc367ea0b97d..ed36bb907301b 100644
--- a/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-pattern-details.js
+++ b/packages/edit-site/src/components/sidebar-navigation-screen-pattern/use-pattern-details.js
@@ -38,7 +38,7 @@ export default function usePatternDetails( postType, postId ) {
if ( ! descriptionText && addedBy.text ) {
descriptionText = sprintf(
// translators: %s: pattern title e.g: "Header".
- __( 'This is your %s pattern.' ),
+ __( 'This is the %s pattern.' ),
getTitle()
);
}
@@ -46,7 +46,7 @@ export default function usePatternDetails( postType, postId ) {
if ( ! descriptionText && postType === 'wp_block' && record?.title ) {
descriptionText = sprintf(
// translators: %s: user created pattern title e.g. "Footer".
- __( 'This is your %s pattern.' ),
+ __( 'This is the %s pattern.' ),
record.title
);
}
@@ -95,7 +95,7 @@ export default function usePatternDetails( postType, postId ) {
details.push( {
label: __( 'Syncing' ),
value:
- record.meta?.sync_status === 'unsynced'
+ record.wp_pattern_sync_status === 'unsynced'
? __( 'Not synced' )
: __( 'Fully synced' ),
} );
diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/index.js b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/index.js
index 04f893cdbf2c8..f200382f96311 100644
--- a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/index.js
+++ b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/index.js
@@ -7,6 +7,7 @@ import {
Flex,
Icon,
Tooltip,
+ __experimentalHeading as Heading,
} from '@wordpress/components';
import { useViewportMatch } from '@wordpress/compose';
import { useSelect } from '@wordpress/data';
@@ -29,12 +30,79 @@ import usePatternCategories from './use-pattern-categories';
import useMyPatterns from './use-my-patterns';
import useTemplatePartAreas from './use-template-part-areas';
-const templatePartAreaLabels = {
- header: __( 'Headers' ),
- footer: __( 'Footers' ),
- sidebar: __( 'Sidebar' ),
- uncategorized: __( 'Uncategorized' ),
-};
+function TemplatePartGroup( { areas, currentArea, currentType } ) {
+ return (
+ <>
+
+
{ __( 'Template parts' ) }
+
{ __( 'Synced patterns for use in template building.' ) }
+
+
+ { Object.entries( areas ).map(
+ ( [ area, { label, templateParts } ] ) => (
+
+ )
+ ) }
+
+ >
+ );
+}
+
+function ThemePatternsGroup( { categories, currentCategory, currentType } ) {
+ return (
+ <>
+
+
{ __( 'Theme patterns' ) }
+
+ { __(
+ 'For insertion into documents where they can then be customized.'
+ ) }
+
+
+
+ { categories.map( ( category ) => (
+
+ { category.label }
+
+
+
+
+
+
+ }
+ icon={ file }
+ id={ category.name }
+ type="pattern"
+ isActive={
+ currentCategory === `${ category.name }` &&
+ currentType === 'pattern'
+ }
+ />
+ ) ) }
+
+ >
+ );
+}
export default function SidebarNavigationScreenPatterns() {
const isMobileViewport = useViewportMatch( 'medium', '<' );
@@ -73,7 +141,7 @@ export default function SidebarNavigationScreenPatterns() {
isRoot={ isTemplatePartsMode }
title={ __( 'Patterns' ) }
description={ __(
- 'Manage what patterns are available when editing your site.'
+ 'Manage what patterns are available when editing the site.'
) }
actions={ }
footer={ footer }
@@ -109,76 +177,18 @@ export default function SidebarNavigationScreenPatterns() {
) }
{ hasTemplateParts && (
-
- { Object.entries( templatePartAreas ).map(
- ( [ area, parts ] ) => (
-
- )
- ) }
-
+
) }
{ hasPatterns && (
-
- { patternCategories.map( ( category ) => (
-
- { category.label }
-
-
-
-
-
-
- }
- icon={ file }
- id={ category.name }
- type="pattern"
- isActive={
- currentCategory ===
- `${ category.name }` &&
- currentType === 'pattern'
- }
- />
- ) ) }
-
+
) }
>
) }
diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/style.scss
index f0edb96164abc..65790b5e86216 100644
--- a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/style.scss
+++ b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/style.scss
@@ -1,3 +1,28 @@
.edit-site-sidebar-navigation-screen-patterns__group {
- margin-bottom: $grid-unit-30;
+ margin-bottom: $grid-unit-40;
+ padding-bottom: $grid-unit-30;
+ border-bottom: 1px solid $gray-800;
+
+ &:last-of-type,
+ &:first-of-type {
+ border-bottom: 0;
+ padding-bottom: 0;
+ margin-bottom: 0;
+ }
+
+ &:first-of-type {
+ margin-bottom: $grid-unit-40;
+ }
+}
+
+.edit-site-sidebar-navigation-screen-patterns__group-header {
+ p {
+ color: $gray-600;
+ }
+
+ h2 {
+ font-size: 11px;
+ font-weight: 500;
+ text-transform: uppercase;
+ }
}
diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/use-template-part-areas.js b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/use-template-part-areas.js
index aa258344d132d..bc538c5e7a85f 100644
--- a/packages/edit-site/src/components/sidebar-navigation-screen-patterns/use-template-part-areas.js
+++ b/packages/edit-site/src/components/sidebar-navigation-screen-patterns/use-template-part-areas.js
@@ -2,19 +2,41 @@
* WordPress dependencies
*/
import { useEntityRecords } from '@wordpress/core-data';
+import { useSelect } from '@wordpress/data';
+import { store as editorStore } from '@wordpress/editor';
-const getTemplatePartAreas = ( items ) => {
+const useTemplatePartsGroupedByArea = ( items ) => {
const allItems = items || [];
- const groupedByArea = allItems.reduce(
- ( accumulator, item ) => {
- const key = accumulator[ item.area ] ? item.area : 'uncategorized';
- accumulator[ key ].push( item );
- return accumulator;
- },
- { header: [], footer: [], sidebar: [], uncategorized: [] }
+ const templatePartAreas = useSelect(
+ ( select ) =>
+ select( editorStore ).__experimentalGetDefaultTemplatePartAreas(),
+ []
);
+ // Create map of template areas ensuring that default areas are displayed before
+ // any custom registered template part areas.
+ const knownAreas = {
+ header: {},
+ footer: {},
+ sidebar: {},
+ uncategorized: {},
+ };
+
+ templatePartAreas.forEach(
+ ( templatePartArea ) =>
+ ( knownAreas[ templatePartArea.area ] = {
+ ...templatePartArea,
+ templateParts: [],
+ } )
+ );
+
+ const groupedByArea = allItems.reduce( ( accumulator, item ) => {
+ const key = accumulator[ item.area ] ? item.area : 'uncategorized';
+ accumulator[ key ].templateParts.push( item );
+ return accumulator;
+ }, knownAreas );
+
return groupedByArea;
};
@@ -28,6 +50,6 @@ export default function useTemplatePartAreas() {
return {
hasTemplateParts: templateParts ? !! templateParts.length : false,
isLoading,
- templatePartAreas: getTemplatePartAreas( templateParts ),
+ templatePartAreas: useTemplatePartsGroupedByArea( templateParts ),
};
}
diff --git a/packages/edit-site/src/components/sidebar-navigation-screen/style.scss b/packages/edit-site/src/components/sidebar-navigation-screen/style.scss
index dadee024b0ead..26a30da286fc8 100644
--- a/packages/edit-site/src/components/sidebar-navigation-screen/style.scss
+++ b/packages/edit-site/src/components/sidebar-navigation-screen/style.scss
@@ -98,6 +98,20 @@
border-top: 1px solid $gray-800;
}
+.edit-site-sidebar__notice {
+ background: $gray-800;
+ color: $gray-300;
+ margin: $grid-unit-30 0;
+ &.is-dismissible {
+ padding-right: $grid-unit-10;
+ }
+ .components-notice__dismiss:not(:disabled):not([aria-disabled="true"]):focus,
+ .components-notice__dismiss:not(:disabled):not([aria-disabled="true"]):not(.is-secondary):active,
+ .components-notice__dismiss:not(:disabled):not([aria-disabled="true"]):not(.is-secondary):hover {
+ color: $gray-100;
+ }
+}
+
/* In general style overrides are discouraged.
* This is a temporary solution to override the InputControl component's styles.
* The `Theme` component will potentially be the more appropriate approach
@@ -107,7 +121,11 @@
.edit-site-sidebar-navigation-screen__input-control {
width: 100%;
.components-input-control__container {
- background: transparent;
+ background: $gray-800;
+
+ .components-button {
+ color: $gray-200 !important;
+ }
}
.components-input-control__input {
color: $gray-200 !important;
diff --git a/packages/edit-site/src/components/site-hub/index.js b/packages/edit-site/src/components/site-hub/index.js
index 5162091f91d42..d4f68943c46ae 100644
--- a/packages/edit-site/src/components/site-hub/index.js
+++ b/packages/edit-site/src/components/site-hub/index.js
@@ -174,7 +174,7 @@ const SiteHub = forwardRef( ( props, ref ) => {
className="edit-site-site-hub_toggle-command-center"
icon={ search }
onClick={ () => openCommandCenter() }
- label={ __( 'Open command center' ) }
+ label={ __( 'Open command palette' ) }
/>
) }
diff --git a/packages/edit-site/src/components/template-actions/index.js b/packages/edit-site/src/components/template-actions/index.js
index 0b54d6ef3ea71..b4618dcae966d 100644
--- a/packages/edit-site/src/components/template-actions/index.js
+++ b/packages/edit-site/src/components/template-actions/index.js
@@ -3,8 +3,14 @@
*/
import { useDispatch, useSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
+import { useState } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
-import { DropdownMenu, MenuGroup, MenuItem } from '@wordpress/components';
+import {
+ DropdownMenu,
+ MenuGroup,
+ MenuItem,
+ __experimentalConfirmDialog as ConfirmDialog,
+} from '@wordpress/components';
import { moreVertical } from '@wordpress/icons';
import { store as noticesStore } from '@wordpress/notices';
@@ -84,17 +90,14 @@ export default function TemplateActions( {
template={ template }
onClose={ onClose }
/>
- {
+ {
removeTemplate( template );
onRemove?.();
onClose();
} }
- >
- { __( 'Delete' ) }
-
+ isTemplate={ template.type === 'wp_template' }
+ />
>
) }
{ isRevertable && (
@@ -115,3 +118,30 @@ export default function TemplateActions( {
);
}
+
+function DeleteMenuItem( { onRemove, isTemplate } ) {
+ const [ isModalOpen, setIsModalOpen ] = useState( false );
+ return (
+ <>
+ setIsModalOpen( true ) }
+ >
+ { __( 'Delete' ) }
+
+ setIsModalOpen( false ) }
+ confirmButtonText={ __( 'Delete' ) }
+ >
+ { isTemplate
+ ? __( 'Are you sure you want to delete this template?' )
+ : __(
+ 'Are you sure you want to delete this template part?'
+ ) }
+
+ >
+ );
+}
diff --git a/packages/edit-site/src/components/welcome-guide/page.js b/packages/edit-site/src/components/welcome-guide/page.js
index 2f162c0b2e84e..adb64a8033e99 100644
--- a/packages/edit-site/src/components/welcome-guide/page.js
+++ b/packages/edit-site/src/components/welcome-guide/page.js
@@ -31,7 +31,7 @@ export default function WelcomeGuidePage() {
return null;
}
- const heading = __( 'Editing your page' );
+ const heading = __( 'Editing a page' );
return (
{ __(
- 'We’ve recently introduced the ability to edit pages within the site editor. You can switch to editing your template using the settings sidebar.'
+ 'It’s now possible to edit page content in the site editor. To customise other parts of the page like the header and footer switch to editing the template using the settings sidebar.'
) }
>
diff --git a/packages/edit-site/src/components/welcome-guide/template.js b/packages/edit-site/src/components/welcome-guide/template.js
index cf0e723ab4609..f0c02c09d1124 100644
--- a/packages/edit-site/src/components/welcome-guide/template.js
+++ b/packages/edit-site/src/components/welcome-guide/template.js
@@ -36,7 +36,7 @@ export default function WelcomeGuideTemplate() {
return null;
}
- const heading = __( 'Editing your template' );
+ const heading = __( 'Editing a template' );
return (
{ __(
- 'You’re now editing your page’s template. To switch back to editing your page you can click the back button in the toolbar.'
+ 'Note that the same template can be used by multiple pages, so any changes made here may affect other pages on the site. To switch back to editing the page content click the ‘Back’ button in the toolbar.'
) }
>
diff --git a/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js b/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js
index e9e00b9723a12..3bd1b561a3ab1 100644
--- a/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js
+++ b/packages/edit-site/src/hooks/commands/use-edit-mode-commands.js
@@ -250,7 +250,6 @@ export function useEditModeCommands() {
useCommandLoader( {
name: 'core/edit-site/manipulate-document',
hook: useManipulateDocumentCommands,
- context: 'site-editor-edit',
} );
useCommandLoader( {
diff --git a/packages/edit-site/src/store/private-actions.js b/packages/edit-site/src/store/private-actions.js
index 952c1852ae305..1b97959277760 100644
--- a/packages/edit-site/src/store/private-actions.js
+++ b/packages/edit-site/src/store/private-actions.js
@@ -11,7 +11,7 @@ import { store as preferencesStore } from '@wordpress/preferences';
*/
export const setCanvasMode =
( mode ) =>
- ( { registry, dispatch } ) => {
+ ( { registry, dispatch, select } ) => {
registry.dispatch( blockEditorStore ).__unstableSetEditorMode( 'edit' );
dispatch( {
type: 'SET_CANVAS_MODE',
@@ -26,6 +26,10 @@ export const setCanvasMode =
) {
dispatch.setIsListViewOpened( true );
}
+ // Switch focus away from editing the template when switching to view mode.
+ if ( mode === 'view' && select.isPage() ) {
+ dispatch.setHasPageContentFocus( true );
+ }
};
/**
diff --git a/packages/editor/src/components/post-switch-to-draft-button/index.js b/packages/editor/src/components/post-switch-to-draft-button/index.js
index 6521200fde590..1fb04931bfce1 100644
--- a/packages/editor/src/components/post-switch-to-draft-button/index.js
+++ b/packages/editor/src/components/post-switch-to-draft-button/index.js
@@ -3,7 +3,6 @@
*/
import {
Button,
- FlexItem,
__experimentalConfirmDialog as ConfirmDialog,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
@@ -41,7 +40,7 @@ function PostSwitchToDraftButton( {
};
return (
-
+ <>
{
@@ -49,7 +48,7 @@ function PostSwitchToDraftButton( {
} }
disabled={ isSaving }
variant="secondary"
- style={ { width: '100%', display: 'block' } }
+ style={ { flexGrow: '1', justifyContent: 'center' } }
>
{ __( 'Switch to draft' ) }
@@ -60,7 +59,7 @@ function PostSwitchToDraftButton( {
>
{ alertMessage }
-
+ >
);
}
diff --git a/packages/editor/src/components/post-sync-status/index.js b/packages/editor/src/components/post-sync-status/index.js
index c384fd234c7a3..0600ece953173 100644
--- a/packages/editor/src/components/post-sync-status/index.js
+++ b/packages/editor/src/components/post-sync-status/index.js
@@ -11,17 +11,16 @@ import { PanelRow } from '@wordpress/components';
import { store as editorStore } from '../../store';
export default function PostSyncStatus() {
- const { meta, postType } = useSelect( ( select ) => {
+ const { syncStatus, postType } = useSelect( ( select ) => {
const { getEditedPostAttribute } = select( editorStore );
return {
- meta: getEditedPostAttribute( 'meta' ),
+ syncStatus: getEditedPostAttribute( 'wp_pattern_sync_status' ),
postType: getEditedPostAttribute( 'type' ),
};
}, [] );
if ( postType !== 'wp_block' ) {
return null;
}
- const syncStatus = meta?.sync_status;
const isFullySynced = ! syncStatus;
return (
diff --git a/packages/editor/src/components/post-trash/style.scss b/packages/editor/src/components/post-trash/style.scss
index f24a6eb2743dd..e47981314d5f2 100644
--- a/packages/editor/src/components/post-trash/style.scss
+++ b/packages/editor/src/components/post-trash/style.scss
@@ -1,4 +1,4 @@
.editor-post-trash.components-button {
- width: 100%;
- display: block;
+ flex-grow: 1;
+ justify-content: center;
}
diff --git a/packages/list-reusable-blocks/src/components/import-form/index.js b/packages/list-reusable-blocks/src/components/import-form/index.js
index 3b3daa49571f4..9ba1589e52f39 100644
--- a/packages/list-reusable-blocks/src/components/import-form/index.js
+++ b/packages/list-reusable-blocks/src/components/import-form/index.js
@@ -49,8 +49,8 @@ function ImportForm( { instanceId, onUpload } ) {
case 'Invalid JSON file':
uiMessage = __( 'Invalid JSON file' );
break;
- case 'Invalid Reusable block JSON file':
- uiMessage = __( 'Invalid Reusable block JSON file' );
+ case 'Invalid Pattern JSON file':
+ uiMessage = __( 'Invalid Pattern JSON file' );
break;
default:
uiMessage = __( 'Unknown error' );
diff --git a/packages/list-reusable-blocks/src/index.js b/packages/list-reusable-blocks/src/index.js
index 3c9945139856f..4440ba1c49f05 100644
--- a/packages/list-reusable-blocks/src/index.js
+++ b/packages/list-reusable-blocks/src/index.js
@@ -31,9 +31,7 @@ document.addEventListener( 'DOMContentLoaded', () => {
const showNotice = () => {
const notice = document.createElement( 'div' );
notice.className = 'notice notice-success is-dismissible';
- notice.innerHTML = `${ __(
- 'Reusable block imported successfully!'
- ) }
`;
+ notice.innerHTML = `${ __( 'Pattern imported successfully!' ) }
`;
const headerEnd = document.querySelector( '.wp-header-end' );
if ( ! headerEnd ) {
diff --git a/packages/list-reusable-blocks/src/utils/export.js b/packages/list-reusable-blocks/src/utils/export.js
index 0f70931c50080..4075c7576f134 100644
--- a/packages/list-reusable-blocks/src/utils/export.js
+++ b/packages/list-reusable-blocks/src/utils/export.js
@@ -25,11 +25,13 @@ async function exportReusableBlock( id ) {
} );
const title = post.title.raw;
const content = post.content.raw;
+ const syncStatus = post.wp_pattern_sync_status;
const fileContent = JSON.stringify(
{
__file: 'wp_block',
title,
content,
+ syncStatus,
},
null,
2
diff --git a/packages/list-reusable-blocks/src/utils/import.js b/packages/list-reusable-blocks/src/utils/import.js
index 84c28b5fcfc80..465fb080ce8df 100644
--- a/packages/list-reusable-blocks/src/utils/import.js
+++ b/packages/list-reusable-blocks/src/utils/import.js
@@ -27,9 +27,11 @@ async function importReusableBlock( file ) {
! parsedContent.title ||
! parsedContent.content ||
typeof parsedContent.title !== 'string' ||
- typeof parsedContent.content !== 'string'
+ typeof parsedContent.content !== 'string' ||
+ ( parsedContent.syncStatus &&
+ typeof parsedContent.syncStatus !== 'string' )
) {
- throw new Error( 'Invalid Reusable block JSON file' );
+ throw new Error( 'Invalid Pattern JSON file' );
}
const postType = await apiFetch( { path: `/wp/v2/types/wp_block` } );
const reusableBlock = await apiFetch( {
@@ -38,6 +40,10 @@ async function importReusableBlock( file ) {
title: parsedContent.title,
content: parsedContent.content,
status: 'publish',
+ meta:
+ parsedContent.syncStatus === 'unsynced'
+ ? { wp_pattern_sync_status: parsedContent.syncStatus }
+ : undefined,
},
method: 'POST',
} );
diff --git a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-convert-button.js b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-convert-button.js
index d051e36641281..981776880a137 100644
--- a/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-convert-button.js
+++ b/packages/reusable-blocks/src/components/reusable-blocks-menu-items/reusable-block-convert-button.js
@@ -5,6 +5,7 @@ import { hasBlockSupport, isReusableBlock } from '@wordpress/blocks';
import {
BlockSettingsMenuControls,
store as blockEditorStore,
+ ReusableBlocksRenameHint,
} from '@wordpress/block-editor';
import { useCallback, useState } from '@wordpress/element';
import {
@@ -18,7 +19,7 @@ import {
} from '@wordpress/components';
import { symbol } from '@wordpress/icons';
import { useDispatch, useSelect } from '@wordpress/data';
-import { __ } from '@wordpress/i18n';
+import { __, sprintf } from '@wordpress/i18n';
import { store as noticesStore } from '@wordpress/notices';
import { store as coreStore } from '@wordpress/core-data';
@@ -97,15 +98,25 @@ export default function ReusableBlockConvertButton( {
);
createSuccessNotice(
syncType === 'fully'
- ? __( 'Synced Pattern created.' )
- : __( 'Unsynced Pattern created.' ),
+ ? sprintf(
+ // translators: %s: the name the user has given to the pattern.
+ __( 'Synced Pattern created: %s' ),
+ reusableBlockTitle
+ )
+ : sprintf(
+ // translators: %s: the name the user has given to the pattern.
+ __( 'Unsynced Pattern created: %s' ),
+ reusableBlockTitle
+ ),
{
type: 'snackbar',
+ id: 'convert-to-reusable-block-success',
}
);
} catch ( error ) {
createErrorNotice( error.message, {
type: 'snackbar',
+ id: 'convert-to-reusable-block-error',
} );
}
},
@@ -130,7 +141,7 @@ export default function ReusableBlockConvertButton( {
icon={ symbol }
onClick={ () => setIsModalOpen( true ) }
>
- { __( 'Create pattern' ) }
+ { __( 'Create pattern/reusable block' ) }
{ isModalOpen && (
+
{
diff --git a/packages/reusable-blocks/src/store/actions.js b/packages/reusable-blocks/src/store/actions.js
index aae706adfab36..17a2e83d5e776 100644
--- a/packages/reusable-blocks/src/store/actions.js
+++ b/packages/reusable-blocks/src/store/actions.js
@@ -52,7 +52,7 @@ export const __experimentalConvertBlocksToReusable =
const meta =
syncType === 'unsynced'
? {
- sync_status: syncType,
+ wp_pattern_sync_status: syncType,
}
: undefined;
diff --git a/phpunit/class-gutenberg-navigation-fallback-gutenberg-test.php b/phpunit/class-gutenberg-navigation-fallback-gutenberg-test.php
index 7b0719950df8b..edc48f1c71761 100644
--- a/phpunit/class-gutenberg-navigation-fallback-gutenberg-test.php
+++ b/phpunit/class-gutenberg-navigation-fallback-gutenberg-test.php
@@ -32,7 +32,6 @@ public function test_it_exists() {
$this->assertTrue( class_exists( 'Gutenberg_Navigation_Fallback' ), 'Gutenberg_Navigation_Fallback class should exist.' );
}
-
/**
* @covers WP_REST_Navigation_Fallback_Controller::get_fallback
*/
@@ -54,6 +53,24 @@ public function test_should_return_a_default_fallback_navigation_menu_in_absence
$this->assertCount( 1, $navs_in_db, 'The fallback Navigation post should be the only one in the database.' );
}
+ /**
+ * @covers WP_REST_Navigation_Fallback_Controller::get_fallback
+ */
+ public function test_should_not_automatically_create_fallback_if_filter_is_falsey() {
+
+ add_filter( 'gutenberg_navigation_should_create_fallback', '__return_false' );
+
+ $data = Gutenberg_Navigation_Fallback::get_fallback();
+
+ $this->assertEmpty( $data );
+
+ $navs_in_db = $this->get_navigations_in_database();
+
+ $this->assertCount( 0, $navs_in_db, 'The fallback Navigation post should not have been created.' );
+
+ remove_filter( 'gutenberg_navigation_should_create_fallback', '__return_false' );
+ }
+
/**
* @covers WP_REST_Navigation_Fallback_Controller::get_fallback
*/
diff --git a/test/e2e/specs/editor/various/manage-reusable-blocks.spec.js b/test/e2e/specs/editor/various/manage-reusable-blocks.spec.js
index 64d09dc39af72..bb390d2b39a8e 100644
--- a/test/e2e/specs/editor/various/manage-reusable-blocks.spec.js
+++ b/test/e2e/specs/editor/various/manage-reusable-blocks.spec.js
@@ -35,7 +35,7 @@ test.describe( 'Managing reusable blocks', () => {
// Wait for the success notice.
await expect(
- page.locator( 'text=Reusable block imported successfully!' )
+ page.locator( 'text=Pattern imported successfully!' )
).toBeVisible();
// Refresh the page.
diff --git a/test/e2e/specs/editor/various/shortcut-focus-toolbar.spec.js b/test/e2e/specs/editor/various/shortcut-focus-toolbar.spec.js
index 0510512354628..9361da403c6d9 100644
--- a/test/e2e/specs/editor/various/shortcut-focus-toolbar.spec.js
+++ b/test/e2e/specs/editor/various/shortcut-focus-toolbar.spec.js
@@ -100,11 +100,8 @@ test.describe( 'Focus toolbar shortcut (alt + F10)', () => {
await editor.insertBlock( { name: 'core/paragraph' } );
await toolbarUtils.moveToToolbarShortcut();
await expect(
- toolbarUtils.blockToolbarShowDocumentButton
+ toolbarUtils.blockToolbarParagraphButton
).toBeFocused();
- await expect(
- toolbarUtils.documentToolbarTooltip
- ).not.toBeVisible();
// Test: Focus the block toolbar from paragraph block with content
await editor.insertBlock( { name: 'core/paragraph' } );
@@ -113,11 +110,8 @@ test.describe( 'Focus toolbar shortcut (alt + F10)', () => {
);
await toolbarUtils.moveToToolbarShortcut();
await expect(
- toolbarUtils.blockToolbarShowDocumentButton
+ toolbarUtils.blockToolbarParagraphButton
).toBeFocused();
- await expect(
- toolbarUtils.documentToolbarTooltip
- ).not.toBeVisible();
} );
test( 'Focuses the correct toolbar in select mode', async ( {
@@ -135,11 +129,8 @@ test.describe( 'Focus toolbar shortcut (alt + F10)', () => {
await toolbarUtils.useSelectMode();
await toolbarUtils.moveToToolbarShortcut();
await expect(
- toolbarUtils.blockToolbarShowDocumentButton
+ toolbarUtils.blockToolbarParagraphButton
).toBeFocused();
- await expect(
- toolbarUtils.documentToolbarTooltip
- ).not.toBeVisible();
// Test: Focus the block toolbar from paragraph in select mode
await editor.insertBlock( { name: 'core/paragraph' } );
@@ -149,11 +140,8 @@ test.describe( 'Focus toolbar shortcut (alt + F10)', () => {
await toolbarUtils.useSelectMode();
await toolbarUtils.moveToToolbarShortcut();
await expect(
- toolbarUtils.blockToolbarShowDocumentButton
+ toolbarUtils.blockToolbarParagraphButton
).toBeFocused();
- await expect(
- toolbarUtils.documentToolbarTooltip
- ).not.toBeVisible();
} );
} );
@@ -254,7 +242,7 @@ class ToolbarUtils {
exact: true,
} );
this.blockToolbarShowDocumentButton = this.page.getByRole( 'button', {
- name: 'Show document tools',
+ name: 'Hide block tools',
exact: true,
} );
}
diff --git a/test/e2e/specs/site-editor/command-center.spec.js b/test/e2e/specs/site-editor/command-center.spec.js
index 0ee6d77d3b301..9d22248bc2362 100644
--- a/test/e2e/specs/site-editor/command-center.spec.js
+++ b/test/e2e/specs/site-editor/command-center.spec.js
@@ -3,7 +3,7 @@
*/
const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' );
-test.describe( 'Site editor command center', () => {
+test.describe( 'Site editor command palette', () => {
test.beforeAll( async ( { requestUtils } ) => {
await requestUtils.activateTheme( 'emptytheme' );
} );
@@ -17,11 +17,11 @@ test.describe( 'Site editor command center', () => {
await admin.visitSiteEditor();
} );
- test( 'Open the command center and navigate to the page create page', async ( {
+ test( 'Open the command palette and navigate to the page create page', async ( {
page,
} ) => {
await page
- .getByRole( 'button', { name: 'Open command center' } )
+ .getByRole( 'button', { name: 'Open command palette' } )
.focus();
await page.keyboard.press( 'Meta+k' );
await page.keyboard.type( 'new page' );
@@ -36,11 +36,11 @@ test.describe( 'Site editor command center', () => {
).toBeVisible();
} );
- test( 'Open the command center and navigate to a template', async ( {
+ test( 'Open the command palette and navigate to a template', async ( {
page,
} ) => {
await page
- .getByRole( 'button', { name: 'Open command center' } )
+ .getByRole( 'button', { name: 'Open command palette' } )
.click();
await page.keyboard.type( 'index' );
await page.getByRole( 'option', { name: 'index' } ).click();