;
}
- /* eslint-disable jsx-a11y/label-has-associated-control */
return (
-
+
+ { /* eslint-disable jsx-a11y/label-has-associated-control */ }
+
+ { /* eslint-enable jsx-a11y/label-has-associated-control */ }
+
);
- /* eslint-enable jsx-a11y/label-has-associated-control */
}
diff --git a/packages/block-library/src/group/edit.js b/packages/block-library/src/group/edit.js
index 9c8690c4e0e8e..a763bc95e60d7 100644
--- a/packages/block-library/src/group/edit.js
+++ b/packages/block-library/src/group/edit.js
@@ -10,6 +10,7 @@ import {
store as blockEditorStore,
} from '@wordpress/block-editor';
import { SelectControl } from '@wordpress/components';
+import { useRef } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { View } from '@wordpress/primitives';
@@ -97,7 +98,8 @@ function GroupEdit( { attributes, name, setAttributes, clientId } ) {
themeSupportsLayout || type === 'flex' || type === 'grid';
// Hooks.
- const blockProps = useBlockProps();
+ const ref = useRef();
+ const blockProps = useBlockProps( { ref } );
const [ showPlaceholder, setShowPlaceholder ] = useShouldShowPlaceHolder( {
attributes,
@@ -124,6 +126,7 @@ function GroupEdit( { attributes, name, setAttributes, clientId } ) {
? blockProps
: { className: 'wp-block-group__inner-container' },
{
+ dropZoneElement: ref.current,
templateLock,
allowedBlocks,
renderAppender,
diff --git a/packages/block-library/src/paragraph/edit.js b/packages/block-library/src/paragraph/edit.js
index 3a0f4f0688ac9..ac766f69dd846 100644
--- a/packages/block-library/src/paragraph/edit.js
+++ b/packages/block-library/src/paragraph/edit.js
@@ -49,6 +49,48 @@ function hasDropCapDisabled( align ) {
return align === ( isRTL() ? 'left' : 'right' ) || align === 'center';
}
+function DropCapControl( { clientId, attributes, setAttributes } ) {
+ // Please do no add a useSelect call to the paragraph block unconditionaly.
+ // Every useSelect added to a (frequestly used) block will degrade the load
+ // and type bit. By moving it within InspectorControls, the subscription is
+ // now only added for the selected block(s).
+ const [ isDropCapFeatureEnabled ] = useSettings( 'typography.dropCap' );
+
+ if ( ! isDropCapFeatureEnabled ) {
+ return null;
+ }
+
+ const { align, dropCap } = attributes;
+
+ let helpText;
+ if ( hasDropCapDisabled( align ) ) {
+ helpText = __( 'Not available for aligned text.' );
+ } else if ( dropCap ) {
+ helpText = __( 'Showing large initial letter.' );
+ } else {
+ helpText = __( 'Toggle to show a large initial letter.' );
+ }
+
+ return (
+ !! dropCap }
+ label={ __( 'Drop cap' ) }
+ onDeselect={ () => setAttributes( { dropCap: undefined } ) }
+ resetAllFilter={ () => ( { dropCap: undefined } ) }
+ panelId={ clientId }
+ >
+ setAttributes( { dropCap: ! dropCap } ) }
+ help={ helpText }
+ disabled={ hasDropCapDisabled( align ) ? true : false }
+ />
+
+ );
+}
+
function ParagraphBlock( {
attributes,
mergeBlocks,
@@ -58,7 +100,6 @@ function ParagraphBlock( {
clientId,
} ) {
const { align, content, direction, dropCap, placeholder } = attributes;
- const [ isDropCapFeatureEnabled ] = useSettings( 'typography.dropCap' );
const blockProps = useBlockProps( {
ref: useOnEnter( { clientId, content } ),
className: classnames( {
@@ -68,15 +109,6 @@ function ParagraphBlock( {
style: { direction },
} );
- let helpText;
- if ( hasDropCapDisabled( align ) ) {
- helpText = __( 'Not available for aligned text.' );
- } else if ( dropCap ) {
- helpText = __( 'Showing large initial letter.' );
- } else {
- helpText = __( 'Toggle to show a large initial letter.' );
- }
-
return (
<>
@@ -98,32 +130,13 @@ function ParagraphBlock( {
}
/>
- { isDropCapFeatureEnabled && (
-
- !! dropCap }
- label={ __( 'Drop cap' ) }
- onDeselect={ () =>
- setAttributes( { dropCap: undefined } )
- }
- resetAllFilter={ () => ( { dropCap: undefined } ) }
- panelId={ clientId }
- >
-
- setAttributes( { dropCap: ! dropCap } )
- }
- help={ helpText }
- disabled={
- hasDropCapDisabled( align ) ? true : false
- }
- />
-
-
- ) }
+
+
+
{
event.preventDefault();
- if (
- sortedDirection === direction
- ) {
- onChangeView( {
- ...view,
- sort: undefined,
- } );
- } else {
- onChangeView( {
- ...view,
- sort: {
- field: field.id,
- direction,
- },
- } );
- }
+ onChangeView( {
+ ...view,
+ sort: {
+ field: field.id,
+ direction,
+ },
+ } );
} }
>
{ info.label }
diff --git a/packages/dataviews/src/view-table.js b/packages/dataviews/src/view-table.js
index e34d99008657b..3f5891f076791 100644
--- a/packages/dataviews/src/view-table.js
+++ b/packages/dataviews/src/view-table.js
@@ -118,24 +118,13 @@ function HeaderMenu( { field, view, onChangeView } ) {
}
onSelect={ ( event ) => {
event.preventDefault();
- if (
- isSorted &&
- view.sort.direction ===
- direction
- ) {
- onChangeView( {
- ...view,
- sort: undefined,
- } );
- } else {
- onChangeView( {
- ...view,
- sort: {
- field: field.id,
- direction,
- },
- } );
- }
+ onChangeView( {
+ ...view,
+ sort: {
+ field: field.id,
+ direction,
+ },
+ } );
} }
>
{ info.label }
diff --git a/packages/e2e-tests/specs/editor/various/change-detection.test.js b/packages/e2e-tests/specs/editor/various/change-detection.test.js
index 62057c4cbb2bc..0eb673671222f 100644
--- a/packages/e2e-tests/specs/editor/various/change-detection.test.js
+++ b/packages/e2e-tests/specs/editor/various/change-detection.test.js
@@ -370,7 +370,11 @@ describe( 'Change detection', () => {
it( 'consecutive edits to the same attribute should mark the post as dirty after a save', async () => {
// Open the sidebar block settings.
await openDocumentSettingsSidebar();
- await page.click( '.edit-post-sidebar__panel-tab[data-label="Block"]' );
+
+ const blockInspectorTab = await page.waitForXPath(
+ '//button[@role="tab"][contains(text(), "Block")]'
+ );
+ await blockInspectorTab.click();
// Insert a paragraph.
await clickBlockAppender();
diff --git a/packages/e2e-tests/specs/editor/various/editor-modes.test.js b/packages/e2e-tests/specs/editor/various/editor-modes.test.js
index 81878ebf7208e..aea6536f605bb 100644
--- a/packages/e2e-tests/specs/editor/various/editor-modes.test.js
+++ b/packages/e2e-tests/specs/editor/various/editor-modes.test.js
@@ -102,21 +102,24 @@ describe( 'Editing modes (visual/HTML)', () => {
expect( title ).toBe( 'Paragraph' );
// The Block inspector should be active.
- let blockInspectorTab = await page.$(
- '.edit-post-sidebar__panel-tab.is-active[data-label="Block"]'
+ let [ blockInspectorTab ] = await page.$x(
+ '//button[@role="tab"][@aria-selected="true"][contains(text(), "Block")]'
);
expect( blockInspectorTab ).not.toBeNull();
await switchEditorModeTo( 'Code' );
// The Block inspector should not be active anymore.
- blockInspectorTab = await page.$(
- '.edit-post-sidebar__panel-tab.is-active[data-label="Block"]'
+ [ blockInspectorTab ] = await page.$x(
+ '//button[@role="tab"][@aria-selected="true"][contains(text(), "Block")]'
);
- expect( blockInspectorTab ).toBeNull();
+ expect( blockInspectorTab ).toBeUndefined();
// No block is selected.
- await page.click( '.edit-post-sidebar__panel-tab[data-label="Block"]' );
+ const inactiveBlockInspectorTab = await page.waitForXPath(
+ '//button[@role="tab"][contains(text(), "Block")]'
+ );
+ inactiveBlockInspectorTab.click();
const noBlocksElement = await page.$(
'.block-editor-block-inspector__no-blocks'
);
diff --git a/packages/e2e-tests/specs/editor/various/preferences.test.js b/packages/e2e-tests/specs/editor/various/preferences.test.js
index 98249637c7e96..54990a4004422 100644
--- a/packages/e2e-tests/specs/editor/various/preferences.test.js
+++ b/packages/e2e-tests/specs/editor/various/preferences.test.js
@@ -17,7 +17,7 @@ describe( 'preferences', () => {
async function getActiveSidebarTabText() {
try {
return await page.$eval(
- '.edit-post-sidebar__panel-tab.is-active',
+ 'div[aria-label="Editor settings"] [role="tab"][aria-selected="true"]',
( node ) => node.textContent
);
} catch ( error ) {
@@ -29,11 +29,15 @@ describe( 'preferences', () => {
}
it( 'remembers sidebar dismissal between sessions', async () => {
+ const blockTab = await page.waitForXPath(
+ `//button[@role="tab"][contains(text(), 'Block')]`
+ );
+
// Open by default.
expect( await getActiveSidebarTabText() ).toBe( 'Post' );
// Change to "Block" tab.
- await page.click( '.edit-post-sidebar__panel-tab[aria-label="Block"]' );
+ await blockTab.click();
expect( await getActiveSidebarTabText() ).toBe( 'Block' );
// Regression test: Reload resets to document tab.
@@ -46,7 +50,7 @@ describe( 'preferences', () => {
// Dismiss.
await page.click(
- '.edit-post-sidebar__panel-tabs [aria-label="Close Settings"]'
+ 'div[aria-label="Editor settings"] div[role="tablist"] + button[aria-label="Close Settings"]'
);
expect( await getActiveSidebarTabText() ).toBe( null );
diff --git a/packages/e2e-tests/specs/editor/various/sidebar.test.js b/packages/e2e-tests/specs/editor/various/sidebar.test.js
index 2e5d46eec2f7a..0cd39093aabb8 100644
--- a/packages/e2e-tests/specs/editor/various/sidebar.test.js
+++ b/packages/e2e-tests/specs/editor/various/sidebar.test.js
@@ -13,7 +13,8 @@ import {
} from '@wordpress/e2e-test-utils';
const SIDEBAR_SELECTOR = '.edit-post-sidebar';
-const ACTIVE_SIDEBAR_TAB_SELECTOR = '.edit-post-sidebar__panel-tab.is-active';
+const ACTIVE_SIDEBAR_TAB_SELECTOR =
+ 'div[aria-label="Editor settings"] [role="tab"][aria-selected="true"]';
const ACTIVE_SIDEBAR_BUTTON_TEXT = 'Post';
describe( 'Sidebar', () => {
@@ -99,22 +100,24 @@ describe( 'Sidebar', () => {
// Tab lands at first (presumed selected) option "Post".
await page.keyboard.press( 'Tab' );
- const isActiveDocumentTab = await page.evaluate(
- () =>
- document.activeElement.textContent === 'Post' &&
- document.activeElement.classList.contains( 'is-active' )
+
+ // The Post tab should be focused and selected.
+ const [ documentInspectorTab ] = await page.$x(
+ '//button[@role="tab"][@aria-selected="true"][contains(text(), "Post")]'
);
- expect( isActiveDocumentTab ).toBe( true );
+ expect( documentInspectorTab ).toBeDefined();
+ expect( documentInspectorTab ).toHaveFocus();
- // Tab into and activate "Block".
- await page.keyboard.press( 'Tab' );
+ // Arrow key into and activate "Block".
+ await page.keyboard.press( 'ArrowRight' );
await page.keyboard.press( 'Space' );
- const isActiveBlockTab = await page.evaluate(
- () =>
- document.activeElement.textContent === 'Block' &&
- document.activeElement.classList.contains( 'is-active' )
+
+ // The Block tab should be focused and selected.
+ const [ blockInspectorTab ] = await page.$x(
+ '//button[@role="tab"][@aria-selected="true"][contains(text(), "Block")]'
);
- expect( isActiveBlockTab ).toBe( true );
+ expect( blockInspectorTab ).toBeDefined();
+ expect( blockInspectorTab ).toHaveFocus();
} );
it( 'should be possible to programmatically remove Document Settings panels', async () => {
diff --git a/packages/edit-post/src/components/sidebar/settings-header/index.js b/packages/edit-post/src/components/sidebar/settings-header/index.js
index ef32450e7209f..368bd3e9e50db 100644
--- a/packages/edit-post/src/components/sidebar/settings-header/index.js
+++ b/packages/edit-post/src/components/sidebar/settings-header/index.js
@@ -1,22 +1,20 @@
/**
* WordPress dependencies
*/
-import { Button } from '@wordpress/components';
-import { __, _x, sprintf } from '@wordpress/i18n';
-import { useDispatch, useSelect } from '@wordpress/data';
+import { privateApis as componentsPrivateApis } from '@wordpress/components';
+import { __, _x } from '@wordpress/i18n';
+import { useSelect } from '@wordpress/data';
import { store as editorStore } from '@wordpress/editor';
/**
* Internal dependencies
*/
-import { store as editPostStore } from '../../../store';
+import { unlock } from '../../../lock-unlock';
+import { sidebars } from '../settings-sidebar';
-const SettingsHeader = ( { sidebarName } ) => {
- const { openGeneralSidebar } = useDispatch( editPostStore );
- const openDocumentSettings = () =>
- openGeneralSidebar( 'edit-post/document' );
- const openBlockSettings = () => openGeneralSidebar( 'edit-post/block' );
+const { Tabs } = unlock( componentsPrivateApis );
+const SettingsHeader = () => {
const { documentLabel, isTemplateMode } = useSelect( ( select ) => {
const { getPostTypeLabel, getRenderingMode } = select( editorStore );
@@ -27,66 +25,16 @@ const SettingsHeader = ( { sidebarName } ) => {
};
}, [] );
- const [ documentAriaLabel, documentActiveClass ] =
- sidebarName === 'edit-post/document'
- ? // translators: ARIA label for the Document sidebar tab, selected. %s: Document label.
- [ sprintf( __( '%s (selected)' ), documentLabel ), 'is-active' ]
- : [ documentLabel, '' ];
-
- const [ blockAriaLabel, blockActiveClass ] =
- sidebarName === 'edit-post/block'
- ? // translators: ARIA label for the Block Settings Sidebar tab, selected.
- [ __( 'Block (selected)' ), 'is-active' ]
- : // translators: ARIA label for the Block Settings Sidebar tab, not selected.
- [ __( 'Block' ), '' ];
-
- const [ templateAriaLabel, templateActiveClass ] =
- sidebarName === 'edit-post/document'
- ? [ __( 'Template (selected)' ), 'is-active' ]
- : [ __( 'Template' ), '' ];
-
- /* Use a list so screen readers will announce how many tabs there are. */
return (
-
- { ! isTemplateMode && (
- -
-
-
- ) }
- { isTemplateMode && (
- -
-
-
- ) }
- -
-
-
-
+
+
+ { isTemplateMode ? __( 'Template' ) : documentLabel }
+
+
+ { /* translators: Text label for the Block Settings Sidebar tab. */ }
+ { __( 'Block' ) }
+
+
);
};
diff --git a/packages/edit-post/src/components/sidebar/settings-header/style.scss b/packages/edit-post/src/components/sidebar/settings-header/style.scss
deleted file mode 100644
index aaf7698cb6ddb..0000000000000
--- a/packages/edit-post/src/components/sidebar/settings-header/style.scss
+++ /dev/null
@@ -1,74 +0,0 @@
-// This tab style CSS is duplicated verbatim in
-// /packages/components/src/tab-panel/style.scss
-.components-button.edit-post-sidebar__panel-tab {
- position: relative;
- border-radius: 0;
- height: $grid-unit-60;
- background: transparent;
- border: none;
- box-shadow: none;
- cursor: pointer;
- padding: 3px $grid-unit-20; // Use padding to offset the is-active border, this benefits Windows High Contrast mode
- margin-left: 0;
- font-weight: 500;
-
- &:focus:not(:disabled) {
- position: relative;
- box-shadow: none;
- outline: none;
- }
-
- // Tab indicator
- &::after {
- content: "";
- position: absolute;
- right: 0;
- bottom: 0;
- left: 0;
- pointer-events: none;
-
- // Draw the indicator.
- background: var(--wp-admin-theme-color);
- height: calc(0 * var(--wp-admin-border-width-focus));
- border-radius: 0;
-
- // Animation
- transition: all 0.1s linear;
- @include reduce-motion("transition");
- }
-
- // Active.
- &.is-active::after {
- height: calc(1 * var(--wp-admin-border-width-focus));
-
- // Windows high contrast mode.
- outline: 2px solid transparent;
- outline-offset: -1px;
- }
-
- // Focus.
- &::before {
- content: "";
- position: absolute;
- top: $grid-unit-15;
- right: $grid-unit-15;
- bottom: $grid-unit-15;
- left: $grid-unit-15;
- pointer-events: none;
-
- // Draw the indicator.
- box-shadow: 0 0 0 0 transparent;
- border-radius: $radius-block-ui;
-
- // Animation
- transition: all 0.1s linear;
- @include reduce-motion("transition");
- }
-
- &:focus-visible::before {
- box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);
-
- // Windows high contrast mode.
- outline: 2px solid transparent;
- }
-}
diff --git a/packages/edit-post/src/components/sidebar/settings-sidebar/index.js b/packages/edit-post/src/components/sidebar/settings-sidebar/index.js
index e566ea400c12b..9fa27c6ac2ade 100644
--- a/packages/edit-post/src/components/sidebar/settings-sidebar/index.js
+++ b/packages/edit-post/src/components/sidebar/settings-sidebar/index.js
@@ -5,8 +5,8 @@ import {
BlockInspector,
store as blockEditorStore,
} from '@wordpress/block-editor';
-import { useSelect } from '@wordpress/data';
-import { Platform } from '@wordpress/element';
+import { useSelect, useDispatch } from '@wordpress/data';
+import { Platform, useCallback, useContext } from '@wordpress/element';
import { isRTL, __ } from '@wordpress/i18n';
import { drawerLeft, drawerRight } from '@wordpress/icons';
import { store as interfaceStore } from '@wordpress/interface';
@@ -29,54 +29,43 @@ import PluginDocumentSettingPanel from '../plugin-document-setting-panel';
import PluginSidebarEditPost from '../plugin-sidebar';
import TemplateSummary from '../template-summary';
import { store as editPostStore } from '../../../store';
+import { privateApis as componentsPrivateApis } from '@wordpress/components';
+import { unlock } from '../../../lock-unlock';
+
+const { Tabs } = unlock( componentsPrivateApis );
const SIDEBAR_ACTIVE_BY_DEFAULT = Platform.select( {
web: true,
native: false,
} );
+export const sidebars = {
+ document: 'edit-post/document',
+ block: 'edit-post/block',
+};
-const SettingsSidebar = () => {
- const { sidebarName, keyboardShortcut, isTemplateMode } = useSelect(
- ( select ) => {
- // The settings sidebar is used by the edit-post/document and edit-post/block sidebars.
- // sidebarName represents the sidebar that is active or that should be active when the SettingsSidebar toggle button is pressed.
- // If one of the two sidebars is active the component will contain the content of that sidebar.
- // When neither of the two sidebars is active we can not simply return null, because the PluginSidebarEditPost
- // component, besides being used to render the sidebar, also renders the toggle button. In that case sidebarName
- // should contain the sidebar that will be active when the toggle button is pressed. If a block
- // is selected, that should be edit-post/block otherwise it's edit-post/document.
- let sidebar = select( interfaceStore ).getActiveComplementaryArea(
- editPostStore.name
- );
- if (
- ! [ 'edit-post/document', 'edit-post/block' ].includes(
- sidebar
- )
- ) {
- if ( select( blockEditorStore ).getBlockSelectionStart() ) {
- sidebar = 'edit-post/block';
- }
- sidebar = 'edit-post/document';
- }
- const shortcut = select(
- keyboardShortcutsStore
- ).getShortcutRepresentation( 'core/edit-post/toggle-sidebar' );
- return {
- sidebarName: sidebar,
- keyboardShortcut: shortcut,
- isTemplateMode:
- select( editorStore ).getRenderingMode() ===
- 'template-only',
- };
- },
- []
- );
+const SidebarContent = ( {
+ sidebarName,
+ keyboardShortcut,
+ isTemplateMode,
+} ) => {
+ // Because `PluginSidebarEditPost` renders a `ComplementaryArea`, we
+ // need to forward the `Tabs` context so it can be passed through the
+ // underlying slot/fill.
+ const tabsContextValue = useContext( Tabs.Context );
return (
}
+ header={
+
+
+
+ }
closeLabel={ __( 'Close Settings' ) }
+ // This classname is added so we can apply a corrective negative
+ // margin to the panel.
+ // see https://github.com/WordPress/gutenberg/pull/55360#pullrequestreview-1737671049
+ className="edit-post-sidebar__panel"
headerClassName="edit-post-sidebar__panel-tabs"
/* translators: button label text should, if possible, be under 16 characters. */
title={ __( 'Settings' ) }
@@ -84,25 +73,96 @@ const SettingsSidebar = () => {
icon={ isRTL() ? drawerLeft : drawerRight }
isActiveByDefault={ SIDEBAR_ACTIVE_BY_DEFAULT }
>
- { ! isTemplateMode && sidebarName === 'edit-post/document' && (
- <>
-
-
-
-
-
-
-
-
-
- >
- ) }
- { isTemplateMode && sidebarName === 'edit-post/document' && (
-
- ) }
- { sidebarName === 'edit-post/block' && }
+
+
+ { ! isTemplateMode && (
+ <>
+
+
+
+
+
+
+
+
+
+ >
+ ) }
+ { isTemplateMode && }
+
+
+
+
+
);
};
+const SettingsSidebar = () => {
+ const {
+ sidebarName,
+ isSettingsSidebarActive,
+ keyboardShortcut,
+ isTemplateMode,
+ } = useSelect( ( select ) => {
+ // The settings sidebar is used by the edit-post/document and edit-post/block sidebars.
+ // sidebarName represents the sidebar that is active or that should be active when the SettingsSidebar toggle button is pressed.
+ // If one of the two sidebars is active the component will contain the content of that sidebar.
+ // When neither of the two sidebars is active we can not simply return null, because the PluginSidebarEditPost
+ // component, besides being used to render the sidebar, also renders the toggle button. In that case sidebarName
+ // should contain the sidebar that will be active when the toggle button is pressed. If a block
+ // is selected, that should be edit-post/block otherwise it's edit-post/document.
+ let sidebar = select( interfaceStore ).getActiveComplementaryArea(
+ editPostStore.name
+ );
+ let isSettingsSidebar = true;
+ if ( ! [ sidebars.document, sidebars.block ].includes( sidebar ) ) {
+ isSettingsSidebar = false;
+ if ( select( blockEditorStore ).getBlockSelectionStart() ) {
+ sidebar = sidebars.block;
+ }
+ sidebar = sidebars.document;
+ }
+ const shortcut = select(
+ keyboardShortcutsStore
+ ).getShortcutRepresentation( 'core/edit-post/toggle-sidebar' );
+ return {
+ sidebarName: sidebar,
+ isSettingsSidebarActive: isSettingsSidebar,
+ keyboardShortcut: shortcut,
+ isTemplateMode:
+ select( editorStore ).getRenderingMode() === 'template-only',
+ };
+ }, [] );
+
+ const { openGeneralSidebar } = useDispatch( editPostStore );
+
+ const onTabSelect = useCallback(
+ ( newSelectedTabId ) => {
+ if ( !! newSelectedTabId ) {
+ openGeneralSidebar( newSelectedTabId );
+ }
+ },
+ [ openGeneralSidebar ]
+ );
+
+ return (
+
+
+
+ );
+};
+
export default SettingsSidebar;
diff --git a/packages/edit-post/src/components/sidebar/style.scss b/packages/edit-post/src/components/sidebar/style.scss
index 7b10eaec0d224..1921c5cfd7b31 100644
--- a/packages/edit-post/src/components/sidebar/style.scss
+++ b/packages/edit-post/src/components/sidebar/style.scss
@@ -1,20 +1,8 @@
.components-panel__header.edit-post-sidebar__panel-tabs {
- justify-content: flex-start;
padding-left: 0;
padding-right: $grid-unit-20;
- border-top: 0;
- margin-top: 0;
-
- ul {
- display: flex;
- }
- li {
- margin: 0;
- }
.components-button.has-icon {
- display: none;
- margin: 0 0 0 auto;
padding: 0;
min-width: $icon-size;
height: $icon-size;
@@ -24,3 +12,7 @@
}
}
}
+
+.edit-post-sidebar__panel {
+ margin-top: -1px;
+}
diff --git a/packages/edit-post/src/style.scss b/packages/edit-post/src/style.scss
index 53219bc6a3736..88916bf70f76d 100644
--- a/packages/edit-post/src/style.scss
+++ b/packages/edit-post/src/style.scss
@@ -12,7 +12,6 @@
@import "./components/sidebar/post-format/style.scss";
@import "./components/sidebar/post-slug/style.scss";
@import "./components/sidebar/post-visibility/style.scss";
-@import "./components/sidebar/settings-header/style.scss";
@import "./components/sidebar/template-summary/style.scss";
@import "./components/text-editor/style.scss";
@import "./components/visual-editor/style.scss";
diff --git a/packages/edit-site/src/components/global-styles/screen-revisions/get-revision-changes.js b/packages/edit-site/src/components/global-styles/screen-revisions/get-revision-changes.js
new file mode 100644
index 0000000000000..fed075eb923ff
--- /dev/null
+++ b/packages/edit-site/src/components/global-styles/screen-revisions/get-revision-changes.js
@@ -0,0 +1,171 @@
+/**
+ * WordPress dependencies
+ */
+import { __, sprintf } from '@wordpress/i18n';
+
+const globalStylesChangesCache = new Map();
+const EMPTY_ARRAY = [];
+
+const translationMap = {
+ caption: __( 'Caption' ),
+ link: __( 'Link' ),
+ button: __( 'Button' ),
+ heading: __( 'Heading' ),
+ 'settings.color': __( 'Color settings' ),
+ 'settings.typography': __( 'Typography settings' ),
+ 'styles.color': __( 'Colors' ),
+ 'styles.spacing': __( 'Spacing' ),
+ 'styles.typography': __( 'Typography' ),
+};
+
+const isObject = ( obj ) => obj !== null && typeof obj === 'object';
+
+/**
+ * Get the translation for a given global styles key.
+ * @param {string} key A key representing a path to a global style property or setting.
+ * @param {Record} blockNames A key/value pair object of block names and their rendered titles.
+ * @return {string|undefined} A translated key or undefined if no translation exists.
+ */
+function getTranslation( key, blockNames ) {
+ if ( translationMap[ key ] ) {
+ return translationMap[ key ];
+ }
+
+ const keyArray = key.split( '.' );
+
+ if ( keyArray?.[ 0 ] === 'blocks' ) {
+ const blockName = blockNames[ keyArray[ 1 ] ];
+ return blockName
+ ? sprintf(
+ // translators: %s: block name.
+ __( '%s block' ),
+ blockName
+ )
+ : keyArray[ 1 ];
+ }
+
+ if ( keyArray?.[ 0 ] === 'elements' ) {
+ return sprintf(
+ // translators: %s: element name, e.g., heading button, link, caption.
+ __( '%s element' ),
+ translationMap[ keyArray[ 1 ] ]
+ );
+ }
+
+ return undefined;
+}
+
+/**
+ * A deep comparison of two objects, optimized for comparing global styles.
+ * @param {Object} changedObject The changed object to compare.
+ * @param {Object} originalObject The original object to compare against.
+ * @param {string} parentPath A key/value pair object of block names and their rendered titles.
+ * @return {string[]} An array of paths whose values have changed.
+ */
+function deepCompare( changedObject, originalObject, parentPath = '' ) {
+ // We have two non-object values to compare.
+ if ( ! isObject( changedObject ) && ! isObject( originalObject ) ) {
+ /*
+ * Only return a path if the value has changed.
+ * And then only the path name up to 2 levels deep.
+ */
+ return changedObject !== originalObject
+ ? parentPath.split( '.' ).slice( 0, 2 ).join( '.' )
+ : undefined;
+ }
+
+ // Enable comparison when an object doesn't have a corresponding property to compare.
+ changedObject = isObject( changedObject ) ? changedObject : {};
+ originalObject = isObject( originalObject ) ? originalObject : {};
+
+ const allKeys = new Set( [
+ ...Object.keys( changedObject ),
+ ...Object.keys( originalObject ),
+ ] );
+
+ let diffs = [];
+ for ( const key of allKeys ) {
+ const path = parentPath ? parentPath + '.' + key : key;
+ const changedPath = deepCompare(
+ changedObject[ key ],
+ originalObject[ key ],
+ path
+ );
+ if ( changedPath ) {
+ diffs = diffs.concat( changedPath );
+ }
+ }
+ return diffs;
+}
+
+/**
+ * Get an array of translated summarized global styles changes.
+ * Results are cached using a Map() key of `JSON.stringify( { revision, previousRevision } )`.
+ *
+ * @param {Object} revision The changed object to compare.
+ * @param {Object} previousRevision The original object to compare against.
+ * @param {Record} blockNames A key/value pair object of block names and their rendered titles.
+ * @return {string[]} An array of translated changes.
+ */
+export default function getRevisionChanges(
+ revision,
+ previousRevision,
+ blockNames
+) {
+ const cacheKey = JSON.stringify( { revision, previousRevision } );
+
+ if ( globalStylesChangesCache.has( cacheKey ) ) {
+ return globalStylesChangesCache.get( cacheKey );
+ }
+
+ /*
+ * Compare the two revisions with normalized keys.
+ * The order of these keys determines the order in which
+ * they'll appear in the results.
+ */
+ const changedValueTree = deepCompare(
+ {
+ styles: {
+ color: revision?.styles?.color,
+ typography: revision?.styles?.typography,
+ spacing: revision?.styles?.spacing,
+ },
+ blocks: revision?.styles?.blocks,
+ elements: revision?.styles?.elements,
+ settings: revision?.settings,
+ },
+ {
+ styles: {
+ color: previousRevision?.styles?.color,
+ typography: previousRevision?.styles?.typography,
+ spacing: previousRevision?.styles?.spacing,
+ },
+ blocks: previousRevision?.styles?.blocks,
+ elements: previousRevision?.styles?.elements,
+ settings: previousRevision?.settings,
+ }
+ );
+
+ if ( ! changedValueTree.length ) {
+ globalStylesChangesCache.set( cacheKey, EMPTY_ARRAY );
+ return EMPTY_ARRAY;
+ }
+
+ // Remove duplicate results.
+ const result = [ ...new Set( changedValueTree ) ]
+ /*
+ * Translate the keys.
+ * Remove duplicate or empty translations.
+ */
+ .reduce( ( acc, curr ) => {
+ const translation = getTranslation( curr, blockNames );
+ if ( translation && ! acc.includes( translation ) ) {
+ acc.push( translation );
+ }
+ return acc;
+ }, [] );
+
+ globalStylesChangesCache.set( cacheKey, result );
+
+ return result;
+}
diff --git a/packages/edit-site/src/components/global-styles/screen-revisions/index.js b/packages/edit-site/src/components/global-styles/screen-revisions/index.js
index 90bf68e579cb7..aa380c5a9fbd0 100644
--- a/packages/edit-site/src/components/global-styles/screen-revisions/index.js
+++ b/packages/edit-site/src/components/global-styles/screen-revisions/index.js
@@ -7,7 +7,6 @@ import {
__experimentalUseNavigator as useNavigator,
__experimentalConfirmDialog as ConfirmDialog,
Spinner,
- __experimentalSpacer as Spacer,
} from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
@@ -135,7 +134,8 @@ function ScreenRevisions() {
}
}, [ shouldSelectFirstItem, firstRevision ] );
- // Only display load button if there is a revision to load and it is different from the current editor styles.
+ // Only display load button if there is a revision to load,
+ // and it is different from the current editor styles.
const isLoadButtonEnabled =
!! currentlySelectedRevisionId && ! selectedRevisionMatchesEditorStyles;
const shouldShowRevisions = ! isLoading && revisions.length;
@@ -156,7 +156,7 @@ function ScreenRevisions() {
{ isLoading && (
) }
- { shouldShowRevisions ? (
+ { shouldShowRevisions && (
<>
{ isLoadButtonEnabled && (
@@ -215,14 +216,6 @@ function ScreenRevisions() {
) }
>
- ) : (
-
- {
- // Adding an existing translation here in case these changes are shipped to WordPress 6.3.
- // Later we could update to something better, e.g., "There are currently no style revisions.".
- __( 'No results found.' )
- }
-
) }
>
);
diff --git a/packages/edit-site/src/components/global-styles/screen-revisions/revisions-buttons.js b/packages/edit-site/src/components/global-styles/screen-revisions/revisions-buttons.js
index 2786bf6d79121..0893006942572 100644
--- a/packages/edit-site/src/components/global-styles/screen-revisions/revisions-buttons.js
+++ b/packages/edit-site/src/components/global-styles/screen-revisions/revisions-buttons.js
@@ -6,28 +6,69 @@ import classnames from 'classnames';
/**
* WordPress dependencies
*/
-import { __, sprintf } from '@wordpress/i18n';
+import { __, _n, sprintf } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
import { dateI18n, getDate, humanTimeDiff, getSettings } from '@wordpress/date';
import { store as coreStore } from '@wordpress/core-data';
import { useSelect } from '@wordpress/data';
+import { useMemo } from '@wordpress/element';
+import { getBlockTypes } from '@wordpress/blocks';
+
+/**
+ * Internal dependencies
+ */
+import getRevisionChanges from './get-revision-changes';
const DAY_IN_MILLISECONDS = 60 * 60 * 1000 * 24;
+const MAX_CHANGES = 7;
+
+function ChangesSummary( { revision, previousRevision, blockNames } ) {
+ const changes = getRevisionChanges(
+ revision,
+ previousRevision,
+ blockNames
+ );
+ const changesLength = changes.length;
+
+ if ( ! changesLength ) {
+ return null;
+ }
+
+ // Truncate to `n` results if necessary.
+ if ( changesLength > MAX_CHANGES ) {
+ const deleteCount = changesLength - MAX_CHANGES;
+ const andMoreText = sprintf(
+ // translators: %d: number of global styles changes that are not displayed in the UI.
+ _n( '…and %d more change.', '…and %d more changes.', deleteCount ),
+ deleteCount
+ );
+ changes.splice( MAX_CHANGES, deleteCount, andMoreText );
+ }
+
+ return (
+
+ { changes.join( ', ' ) }
+
+ );
+}
/**
* Returns a button label for the revision.
*
* @param {string|number} id A revision object.
- * @param {boolean} isLatest Whether the revision is the most current.
* @param {string} authorDisplayName Author name.
* @param {string} formattedModifiedDate Revision modified date formatted.
+ * @param {boolean} areStylesEqual Whether the revision matches the current editor styles.
* @return {string} Translated label.
*/
function getRevisionLabel(
id,
- isLatest,
authorDisplayName,
- formattedModifiedDate
+ formattedModifiedDate,
+ areStylesEqual
) {
if ( 'parent' === id ) {
return __( 'Reset the styles to the theme defaults' );
@@ -35,21 +76,23 @@ function getRevisionLabel(
if ( 'unsaved' === id ) {
return sprintf(
- /* translators: %s author display name */
+ /* translators: %s: author display name */
__( 'Unsaved changes by %s' ),
authorDisplayName
);
}
- return isLatest
+ return areStylesEqual
? sprintf(
- /* translators: %1$s author display name, %2$s: revision creation date */
- __( 'Changes saved by %1$s on %2$s (current)' ),
+ // translators: %1$s: author display name, %2$s: revision creation date.
+ __(
+ 'Changes saved by %1$s on %2$s. This revision matches current editor styles.'
+ ),
authorDisplayName,
formattedModifiedDate
)
: sprintf(
- /* translators: %1$s author display name, %2$s: revision creation date */
+ // translators: %1$s: author display name, %2$s: revision creation date.
__( 'Changes saved by %1$s on %2$s' ),
authorDisplayName,
formattedModifiedDate
@@ -67,7 +110,12 @@ function getRevisionLabel(
* @param {props} Component props.
* @return {JSX.Element} The modal component.
*/
-function RevisionsButtons( { userRevisions, selectedRevisionId, onChange } ) {
+function RevisionsButtons( {
+ userRevisions,
+ selectedRevisionId,
+ onChange,
+ canApplyRevision,
+} ) {
const { currentThemeName, currentUser } = useSelect( ( select ) => {
const { getCurrentTheme, getCurrentUser } = select( coreStore );
const currentTheme = getCurrentTheme();
@@ -77,8 +125,15 @@ function RevisionsButtons( { userRevisions, selectedRevisionId, onChange } ) {
currentUser: getCurrentUser(),
};
}, [] );
+ const blockNames = useMemo( () => {
+ const blockTypes = getBlockTypes();
+ return blockTypes.reduce( ( accumulator, { name, title } ) => {
+ accumulator[ name ] = title;
+ return accumulator;
+ }, {} );
+ }, [] );
const dateNowInMs = getDate().getTime();
- const { date: dateFormat, datetimeAbbreviated } = getSettings().formats;
+ const { datetimeAbbreviated } = getSettings().formats;
return (
{ userRevisions.map( ( revision, index ) => {
- const { id, isLatest, author, modified } = revision;
+ const { id, author, modified } = revision;
const isUnsaved = 'unsaved' === id;
// Unsaved changes are created by the current user.
const revisionAuthor = isUnsaved ? currentUser : author;
const authorDisplayName = revisionAuthor?.name || __( 'User' );
const authorAvatar = revisionAuthor?.avatar_urls?.[ '48' ];
+ const isFirstItem = index === 0;
const isSelected = selectedRevisionId
? selectedRevisionId === id
- : index === 0;
+ : isFirstItem;
+ const areStylesEqual = ! canApplyRevision && isSelected;
const isReset = 'parent' === id;
const modifiedDate = getDate( modified );
const displayDate =
modified &&
dateNowInMs - modifiedDate.getTime() > DAY_IN_MILLISECONDS
- ? dateI18n( dateFormat, modifiedDate )
+ ? dateI18n( datetimeAbbreviated, modifiedDate )
: humanTimeDiff( modified );
const revisionLabel = getRevisionLabel(
id,
- isLatest,
authorDisplayName,
- dateI18n( datetimeAbbreviated, modifiedDate )
+ dateI18n( datetimeAbbreviated, modifiedDate ),
+ areStylesEqual
);
return (
@@ -116,6 +173,7 @@ function RevisionsButtons( { userRevisions, selectedRevisionId, onChange } ) {
'edit-site-global-styles-screen-revisions__revision-item',
{
'is-selected': isSelected,
+ 'is-active': areStylesEqual,
'is-reset': isReset,
}
) }
@@ -127,7 +185,7 @@ function RevisionsButtons( { userRevisions, selectedRevisionId, onChange } ) {
onClick={ () => {
onChange( revision );
} }
- label={ revisionLabel }
+ aria-label={ revisionLabel }
>
{ isReset ? (
@@ -150,6 +208,17 @@ function RevisionsButtons( { userRevisions, selectedRevisionId, onChange } ) {
{ displayDate }
) }
+ { isSelected && (
+
+ ) }
{
+ const revision = {
+ id: 10,
+ styles: {
+ typography: {
+ fontSize: 'var(--wp--preset--font-size--potato)',
+ fontStyle: 'normal',
+ fontWeight: '600',
+ lineHeight: '1.85',
+ fontFamily: 'var(--wp--preset--font-family--asparagus)',
+ },
+ spacing: {
+ padding: {
+ top: '36px',
+ right: '89px',
+ bottom: '133px',
+ left: 'var(--wp--preset--spacing--20)',
+ },
+ blockGap: '114px',
+ },
+ elements: {
+ heading: {
+ typography: {
+ letterSpacing: '37px',
+ },
+ },
+ caption: {
+ color: {
+ text: 'var(--wp--preset--color--pineapple)',
+ },
+ },
+ },
+ color: {
+ text: 'var(--wp--preset--color--tomato)',
+ },
+ blocks: {
+ 'core/paragraph': {
+ color: {
+ text: '#000000',
+ },
+ },
+ },
+ },
+ settings: {
+ color: {
+ palette: {
+ theme: [
+ {
+ slug: 'one',
+ color: 'pink',
+ },
+ ],
+ },
+ },
+ },
+ };
+ const previousRevision = {
+ id: 9,
+ styles: {
+ typography: {
+ fontSize: 'var(--wp--preset--font-size--fungus)',
+ fontStyle: 'normal',
+ fontWeight: '600',
+ lineHeight: '1.85',
+ fontFamily: 'var(--wp--preset--font-family--grapes)',
+ },
+ spacing: {
+ padding: {
+ top: '36px',
+ right: '89px',
+ bottom: '133px',
+ left: 'var(--wp--preset--spacing--20)',
+ },
+ blockGap: '114px',
+ },
+ elements: {
+ heading: {
+ typography: {
+ letterSpacing: '37px',
+ },
+ },
+ caption: {
+ typography: {
+ fontSize: '1.11rem',
+ fontStyle: 'normal',
+ fontWeight: '600',
+ },
+ },
+ link: {
+ typography: {
+ lineHeight: 2,
+ textDecoration: 'line-through',
+ },
+ color: {
+ text: 'var(--wp--preset--color--egg)',
+ },
+ },
+ },
+ color: {
+ text: 'var(--wp--preset--color--tomato)',
+ background: 'var(--wp--preset--color--pumpkin)',
+ },
+ blocks: {
+ 'core/paragraph': {
+ color: {
+ text: '#fff',
+ },
+ },
+ },
+ },
+ settings: {
+ color: {
+ palette: {
+ theme: [
+ {
+ slug: 'one',
+ color: 'blue',
+ },
+ ],
+ },
+ },
+ },
+ };
+ const blockNames = {
+ 'core/paragraph': 'Paragraph',
+ };
+ it( 'returns a list of changes and caches them', () => {
+ const resultA = getRevisionChanges(
+ revision,
+ previousRevision,
+ blockNames
+ );
+ expect( resultA ).toEqual( [
+ 'Colors',
+ 'Typography',
+ 'Paragraph block',
+ 'Caption element',
+ 'Link element',
+ 'Color settings',
+ ] );
+
+ const resultB = getRevisionChanges(
+ revision,
+ previousRevision,
+ blockNames
+ );
+
+ expect( resultA ).toBe( resultB );
+ } );
+
+ it( 'skips unknown and unchanged keys', () => {
+ const result = getRevisionChanges(
+ {
+ styles: {
+ frogs: {
+ legs: 'green',
+ },
+ typography: {
+ fontSize: '1rem',
+ },
+ settings: {
+ '': {
+ '': 'foo',
+ },
+ },
+ },
+ },
+ {
+ styles: {
+ frogs: {
+ legs: 'yellow',
+ },
+ typography: {
+ fontSize: '1rem',
+ },
+ settings: {
+ '': {
+ '': 'bar',
+ },
+ },
+ },
+ }
+ );
+ expect( result ).toEqual( [] );
+ } );
+} );
diff --git a/packages/edit-site/src/components/page-templates/dataviews-templates.js b/packages/edit-site/src/components/page-templates/dataviews-templates.js
index e51f9ba970c4c..07de0cb73ff44 100644
--- a/packages/edit-site/src/components/page-templates/dataviews-templates.js
+++ b/packages/edit-site/src/components/page-templates/dataviews-templates.js
@@ -83,7 +83,7 @@ function TemplateTitle( { item } ) {
const { isCustomized } = useAddedBy( item.type, item.id );
return (
-
+
+ );
+}
+
function SidebarScreens() {
useSyncPathWithURL();
return (
<>
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
{ window?.__experimentalAdminViews && (
-
+
}
/>
-
+
) }
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
>
);
}
diff --git a/packages/edit-site/src/components/sidebar/style.scss b/packages/edit-site/src/components/sidebar/style.scss
index 9a3644cc830d5..ef24b0d4b8cf6 100644
--- a/packages/edit-site/src/components/sidebar/style.scss
+++ b/packages/edit-site/src/components/sidebar/style.scss
@@ -1,14 +1,17 @@
.edit-site-sidebar__content {
flex-grow: 1;
overflow-y: auto;
+}
+
+.edit-site-sidebar__screen-wrapper {
+ @include custom-scrollbars-on-hover(transparent, $gray-700);
+ scrollbar-gutter: stable;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
- .components-navigator-screen {
- @include custom-scrollbars-on-hover(transparent, $gray-700);
- scrollbar-gutter: stable;
- display: flex;
- flex-direction: column;
- height: 100%;
- }
+ // This matches the logo padding
+ padding: 0 $grid-unit-15;
}
.edit-site-sidebar__footer {
@@ -17,8 +20,3 @@
margin: 0 $canvas-padding;
padding: $canvas-padding 0;
}
-
-.edit-site-sidebar__content > div {
- // This matches the logo padding
- padding: 0 $grid-unit-15;
-}
diff --git a/test/e2e/specs/editor/plugins/custom-post-types.spec.js b/test/e2e/specs/editor/plugins/custom-post-types.spec.js
index 17a497f26cee0..01dde03650ef7 100644
--- a/test/e2e/specs/editor/plugins/custom-post-types.spec.js
+++ b/test/e2e/specs/editor/plugins/custom-post-types.spec.js
@@ -31,7 +31,7 @@ test.describe( 'Test Custom Post Types', () => {
await editor.openDocumentSettingsSidebar();
await page
.getByRole( 'region', { name: 'Editor settings' } )
- .getByRole( 'button', {
+ .getByRole( 'tab', {
name: 'Hierarchical No Title',
} )
.click();
diff --git a/test/e2e/specs/editor/various/block-hierarchy-navigation.spec.js b/test/e2e/specs/editor/various/block-hierarchy-navigation.spec.js
index f0bfe5bff203f..a695b0a9ead67 100644
--- a/test/e2e/specs/editor/various/block-hierarchy-navigation.spec.js
+++ b/test/e2e/specs/editor/various/block-hierarchy-navigation.spec.js
@@ -127,7 +127,7 @@ test.describe( 'Navigating the block hierarchy', () => {
await pageUtils.pressKeys( 'ctrl+`' );
// Navigate to the block settings sidebar and tweak the column count.
- await pageUtils.pressKeys( 'Tab', { times: 5 } );
+ await pageUtils.pressKeys( 'Tab', { times: 4 } );
await expect(
page.getByRole( 'slider', { name: 'Columns' } )
).toBeFocused();
diff --git a/test/e2e/specs/editor/various/footnotes.spec.js b/test/e2e/specs/editor/various/footnotes.spec.js
index 14a2fc653e387..6102f48749543 100644
--- a/test/e2e/specs/editor/various/footnotes.spec.js
+++ b/test/e2e/specs/editor/various/footnotes.spec.js
@@ -362,7 +362,7 @@ test.describe( 'Footnotes', () => {
await editor.openDocumentSettingsSidebar();
await page
.getByRole( 'region', { name: 'Editor settings' } )
- .getByRole( 'button', { name: 'Post' } )
+ .getByRole( 'tab', { name: 'Post' } )
.click();
await page.locator( 'a:text("2 Revisions")' ).click();
await page.locator( '.revisions-controls .ui-slider-handle' ).focus();
@@ -440,7 +440,7 @@ test.describe( 'Footnotes', () => {
await editor.openDocumentSettingsSidebar();
await page
.getByRole( 'region', { name: 'Editor settings' } )
- .getByRole( 'button', { name: 'Post' } )
+ .getByRole( 'tab', { name: 'Post' } )
.click();
// Visit the published post.
diff --git a/test/e2e/specs/editor/various/keyboard-navigable-blocks.spec.js b/test/e2e/specs/editor/various/keyboard-navigable-blocks.spec.js
index 080abe011206a..84536c88227ce 100644
--- a/test/e2e/specs/editor/various/keyboard-navigable-blocks.spec.js
+++ b/test/e2e/specs/editor/various/keyboard-navigable-blocks.spec.js
@@ -75,9 +75,7 @@ test.describe( 'Order of block keyboard navigation', () => {
);
await page.keyboard.press( 'Tab' );
- await KeyboardNavigableBlocks.expectLabelToHaveFocus(
- 'Post (selected)'
- );
+ await KeyboardNavigableBlocks.expectLabelToHaveFocus( 'Post' );
} );
test( 'allows tabbing in navigation mode if no block is selected (reverse)', async ( {
@@ -151,7 +149,7 @@ test.describe( 'Order of block keyboard navigation', () => {
);
await page.keyboard.press( 'Tab' );
- await KeyboardNavigableBlocks.expectLabelToHaveFocus( 'Post' );
+ await KeyboardNavigableBlocks.expectLabelToHaveFocus( 'Block' );
await pageUtils.pressKeys( 'shift+Tab' );
await KeyboardNavigableBlocks.expectLabelToHaveFocus(
@@ -233,7 +231,7 @@ class KeyboardNavigableBlocks {
await expect( activeElement ).toHaveText( paragraphText );
await this.page.keyboard.press( 'Tab' );
- await this.expectLabelToHaveFocus( 'Post' );
+ await this.expectLabelToHaveFocus( 'Block' );
// Need to shift+tab here to end back in the block. If not, we'll be in the next region and it will only require 4 region jumps instead of 5.
await this.pageUtils.pressKeys( 'shift+Tab' );
diff --git a/test/e2e/specs/site-editor/new-templates-list.spec.js b/test/e2e/specs/site-editor/new-templates-list.spec.js
index 8bb98b6ad5355..34daeb6d40f09 100644
--- a/test/e2e/specs/site-editor/new-templates-list.spec.js
+++ b/test/e2e/specs/site-editor/new-templates-list.spec.js
@@ -33,10 +33,7 @@ test.describe( 'Templates', () => {
name: 'Template',
includeHidden: true,
} )
- .getByRole( 'heading', {
- level: 3,
- includeHidden: true,
- } )
+ .getByRole( 'link', { includeHidden: true } )
.first();
await expect( firstTitle ).toHaveText( 'Tag Archives' );
// Ascending by title.
@@ -57,7 +54,7 @@ test.describe( 'Templates', () => {
await page.keyboard.type( 'tag' );
const titles = page
.getByRole( 'region', { name: 'Template' } )
- .getByRole( 'heading', { level: 3 } );
+ .getByRole( 'link' );
await expect( titles ).toHaveCount( 1 );
await expect( titles.first() ).toHaveText( 'Tag Archives' );
await page.getByRole( 'button', { name: 'Reset filters' } ).click();
diff --git a/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js b/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js
index 2d51b5ac5014b..a27bb28adbb91 100644
--- a/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js
+++ b/test/e2e/specs/site-editor/user-global-styles-revisions.spec.js
@@ -55,6 +55,11 @@ test.describe( 'Global styles revisions', () => {
name: /^Changes saved by /,
} );
+ // Shows changes made in the revision.
+ await expect(
+ page.getByTestId( 'global-styles-revision-changes' )
+ ).toHaveText( 'Colors' );
+
// There should be 2 revisions not including the reset to theme defaults button.
await expect( revisionButtons ).toHaveCount(
currentRevisions.length + 1
diff --git a/test/integration/fixtures/blocks/core__form-input.html b/test/integration/fixtures/blocks/core__form-input.html
index f30d44a2503d2..33f1fe88c2c6a 100644
--- a/test/integration/fixtures/blocks/core__form-input.html
+++ b/test/integration/fixtures/blocks/core__form-input.html
@@ -1,3 +1,3 @@
-
+
diff --git a/test/integration/fixtures/blocks/core__form-input.parsed.json b/test/integration/fixtures/blocks/core__form-input.parsed.json
index f92379b595276..5470c653c403b 100644
--- a/test/integration/fixtures/blocks/core__form-input.parsed.json
+++ b/test/integration/fixtures/blocks/core__form-input.parsed.json
@@ -3,9 +3,9 @@
"blockName": "core/form-input",
"attrs": {},
"innerBlocks": [],
- "innerHTML": "\n\n",
+ "innerHTML": "\n\n",
"innerContent": [
- "\n\n"
+ "\n\n"
]
}
]
diff --git a/test/integration/fixtures/blocks/core__form-input.serialized.html b/test/integration/fixtures/blocks/core__form-input.serialized.html
index f30d44a2503d2..33f1fe88c2c6a 100644
--- a/test/integration/fixtures/blocks/core__form-input.serialized.html
+++ b/test/integration/fixtures/blocks/core__form-input.serialized.html
@@ -1,3 +1,3 @@
-
+
diff --git a/test/integration/fixtures/blocks/core__form-input__deprecated-v1.html b/test/integration/fixtures/blocks/core__form-input__deprecated-v1.html
new file mode 100644
index 0000000000000..08ea661838620
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__form-input__deprecated-v1.html
@@ -0,0 +1,6 @@
+
+
+
diff --git a/test/integration/fixtures/blocks/core__form-input__deprecated-v1.json b/test/integration/fixtures/blocks/core__form-input__deprecated-v1.json
new file mode 100644
index 0000000000000..fee4df284f115
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__form-input__deprecated-v1.json
@@ -0,0 +1,15 @@
+[
+ {
+ "name": "core/form-input",
+ "isValid": true,
+ "attributes": {
+ "type": "text",
+ "label": "Label",
+ "inlineLabel": false,
+ "required": false,
+ "value": "",
+ "visibilityPermissions": "all"
+ },
+ "innerBlocks": []
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__form-input__deprecated-v1.parsed.json b/test/integration/fixtures/blocks/core__form-input__deprecated-v1.parsed.json
new file mode 100644
index 0000000000000..645337cbfdb4a
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__form-input__deprecated-v1.parsed.json
@@ -0,0 +1,11 @@
+[
+ {
+ "blockName": "core/form-input",
+ "attrs": {},
+ "innerBlocks": [],
+ "innerHTML": "\n\n",
+ "innerContent": [
+ "\n\n"
+ ]
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__form-input__deprecated-v1.serialized.html b/test/integration/fixtures/blocks/core__form-input__deprecated-v1.serialized.html
new file mode 100644
index 0000000000000..33f1fe88c2c6a
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__form-input__deprecated-v1.serialized.html
@@ -0,0 +1,3 @@
+
+
+
diff --git a/test/integration/fixtures/blocks/core__form.html b/test/integration/fixtures/blocks/core__form.html
index f443172601a3b..825389eb75ecd 100644
--- a/test/integration/fixtures/blocks/core__form.html
+++ b/test/integration/fixtures/blocks/core__form.html
@@ -1,19 +1,19 @@