diff --git a/lib/block-supports/typography.php b/lib/block-supports/typography.php index 469b429f9b3ddf..d7438ad1fb12e6 100644 --- a/lib/block-supports/typography.php +++ b/lib/block-supports/typography.php @@ -451,7 +451,7 @@ function gutenberg_get_typography_font_size_value( $preset, $should_use_fluid_ty $fluid_settings = isset( $typography_settings['fluid'] ) && is_array( $typography_settings['fluid'] ) ? $typography_settings['fluid'] : array(); // Defaults. - $default_maximum_viewport_width = isset( $layout_settings['wideSize'] ) ? $layout_settings['wideSize'] : '1600px'; + $default_maximum_viewport_width = isset( $layout_settings['wideSize'] ) && ! empty( gutenberg_get_typography_value_and_unit( $layout_settings['wideSize'] ) ) ? $layout_settings['wideSize'] : '1600px'; $default_minimum_viewport_width = '320px'; $default_minimum_font_size_factor_max = 0.75; $default_minimum_font_size_factor_min = 0.25; diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 7313afeba91bca..70816ff6d5917b 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -2906,6 +2906,20 @@ public static function remove_insecure_properties( $theme_json ) { if ( ! empty( $output ) ) { _wp_array_set( $sanitized, $metadata['path'], $output ); } + + if ( isset( $metadata['variations'] ) ) { + foreach ( $metadata['variations'] as $variation ) { + $variation_input = _wp_array_get( $theme_json, $variation['path'], array() ); + if ( empty( $variation_input ) ) { + continue; + } + + $variation_output = static::remove_insecure_styles( $variation_input ); + if ( ! empty( $variation_output ) ) { + _wp_array_set( $sanitized, $variation['path'], $variation_output ); + } + } + } } $setting_nodes = static::get_setting_nodes( $theme_json ); diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss index 7987e1b4b5b4dc..11a31a0dd268af 100644 --- a/packages/base-styles/_z-index.scss +++ b/packages/base-styles/_z-index.scss @@ -189,6 +189,7 @@ $z-layers: ( ".edit-site-layout__hub": 3, ".edit-site-layout__header": 2, ".edit-site-page-header": 2, + ".edit-site-page-content": 1, ".edit-site-layout__canvas-container": 2, ".edit-site-layout__sidebar": 1, ".edit-site-layout__canvas-container.is-resizing::after": 100, diff --git a/packages/block-editor/src/components/block-controls/use-has-block-controls.js b/packages/block-editor/src/components/block-controls/use-has-block-controls.js new file mode 100644 index 00000000000000..f7884cc1882ed5 --- /dev/null +++ b/packages/block-editor/src/components/block-controls/use-has-block-controls.js @@ -0,0 +1,35 @@ +/** + * WordPress dependencies + */ +import { __experimentalUseSlotFills as useSlotFills } from '@wordpress/components'; +import warning from '@wordpress/warning'; + +/** + * Internal dependencies + */ +import groups from './groups'; + +export function useHasAnyBlockControls() { + let hasAnyBlockControls = false; + for ( const group in groups ) { + // It is safe to violate the rules of hooks here as the `groups` object + // is static and will not change length between renders. Do not return + // early as that will cause the hook to be called a different number of + // times between renders. + // eslint-disable-next-line react-hooks/rules-of-hooks + if ( useHasBlockControls( group ) ) { + hasAnyBlockControls = true; + } + } + return hasAnyBlockControls; +} + +export function useHasBlockControls( group = 'default' ) { + const Slot = groups[ group ]?.Slot; + const fills = useSlotFills( Slot?.__unstableName ); + if ( ! Slot ) { + warning( `Unknown BlockControls group "${ group }" provided.` ); + return null; + } + return !! fills?.length; +} diff --git a/packages/block-editor/src/components/block-list/content.scss b/packages/block-editor/src/components/block-list/content.scss index a163ddaa789551..52f26217730773 100644 --- a/packages/block-editor/src/components/block-list/content.scss +++ b/packages/block-editor/src/components/block-list/content.scss @@ -85,7 +85,6 @@ .block-editor-block-list__block.is-highlighted, .block-editor-block-list__block.is-highlighted ~ .is-multi-selected, &.is-navigate-mode .block-editor-block-list__block.is-selected, - & .is-block-moving-mode.block-editor-block-list__block.has-child-selected, .block-editor-block-list__block:not([contenteditable]):focus { outline: none; @@ -113,8 +112,6 @@ // Moving blocks using keyboard (Ellipsis > Move). & .is-block-moving-mode.block-editor-block-list__block.is-selected { - box-shadow: none; - outline: none; &::after { content: ""; @@ -130,6 +127,8 @@ top: -$default-block-margin * 0.5; border-radius: $radius-block-ui; border-top: 4px solid $gray-400; + bottom: auto; + box-shadow: none; } } diff --git a/packages/block-editor/src/components/block-toolbar/style.scss b/packages/block-editor/src/components/block-toolbar/style.scss index 0b49c9299212f9..eef1678e2dfab3 100644 --- a/packages/block-editor/src/components/block-toolbar/style.scss +++ b/packages/block-editor/src/components/block-toolbar/style.scss @@ -252,6 +252,16 @@ flex-shrink: 1; } + @include break-medium() { + .block-editor-block-contextual-toolbar.is-fixed { + .components-toolbar, + .components-toolbar-group { + flex-shrink: 0; + } + } + } + + .block-editor-rich-text__inline-format-toolbar-group { .components-button + .components-button { margin-left: 6px; diff --git a/packages/block-editor/src/components/block-tools/block-contextual-toolbar.js b/packages/block-editor/src/components/block-tools/block-contextual-toolbar.js index 7dcd88d1cfd10f..455ea6e569277d 100644 --- a/packages/block-editor/src/components/block-tools/block-contextual-toolbar.js +++ b/packages/block-editor/src/components/block-tools/block-contextual-toolbar.js @@ -7,7 +7,12 @@ import classnames from 'classnames'; * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { useEffect, useRef, useState } from '@wordpress/element'; +import { + useLayoutEffect, + useEffect, + useRef, + useState, +} from '@wordpress/element'; import { hasBlockSupport, store as blocksStore } from '@wordpress/blocks'; import { useSelect } from '@wordpress/data'; import { @@ -25,6 +30,7 @@ import NavigableToolbar from '../navigable-toolbar'; import BlockToolbar from '../block-toolbar'; import { store as blockEditorStore } from '../../store'; import { unlock } from '../../lock-unlock'; +import { useHasAnyBlockControls } from '../block-controls/use-has-block-controls'; function BlockContextualToolbar( { focusOnMount, isFixed, ...props } ) { // When the toolbar is fixed it can be collapsed @@ -34,10 +40,10 @@ function BlockContextualToolbar( { focusOnMount, isFixed, ...props } ) { const isLargeViewport = useViewportMatch( 'medium' ); const { blockType, + blockEditingMode, hasParents, showParentSelector, selectedBlockClientId, - isContentOnly, } = useSelect( ( select ) => { const { getBlockName, @@ -58,9 +64,8 @@ function BlockContextualToolbar( { focusOnMount, isFixed, ...props } ) { blockType: _selectedBlockClientId && getBlockType( getBlockName( _selectedBlockClientId ) ), + blockEditingMode: getBlockEditingMode( _selectedBlockClientId ), hasParents: parents.length, - isContentOnly: - getBlockEditingMode( _selectedBlockClientId ) === 'contentOnly', showParentSelector: parentBlockType && getBlockEditingMode( firstParentClientId ) === 'default' && @@ -78,10 +83,84 @@ function BlockContextualToolbar( { focusOnMount, isFixed, ...props } ) { setIsCollapsed( false ); }, [ selectedBlockClientId ] ); + const isLargerThanTabletViewport = useViewportMatch( 'large', '>=' ); + const isFullscreen = + document.body.classList.contains( 'is-fullscreen-mode' ); + + useLayoutEffect( () => { + // don't do anything if not fixed toolbar + if ( ! isFixed || ! blockType ) { + return; + } + + const blockToolbar = document.querySelector( + '.block-editor-block-contextual-toolbar' + ); + + if ( ! blockToolbar ) { + return; + } + + if ( ! isLargerThanTabletViewport ) { + // set the width of the toolbar to auto + blockToolbar.style = {}; + return; + } + + if ( isCollapsed ) { + // set the width of the toolbar to auto + blockToolbar.style.width = 'auto'; + return; + } + + // get the width of the pinned items in the post editor + const pinnedItems = document.querySelector( + '.edit-post-header__settings' + ); + + // get the width of the left header in the site editor + const leftHeader = document.querySelector( + '.edit-site-header-edit-mode__end' + ); + + const computedToolbarStyle = window.getComputedStyle( blockToolbar ); + const computedPinnedItemsStyle = pinnedItems + ? window.getComputedStyle( pinnedItems ) + : false; + const computedLeftHeaderStyle = leftHeader + ? window.getComputedStyle( leftHeader ) + : false; + + const marginLeft = parseFloat( computedToolbarStyle.marginLeft ); + const pinnedItemsWidth = computedPinnedItemsStyle + ? parseFloat( computedPinnedItemsStyle.width ) + 10 // 10 is the pinned items padding + : 0; + const leftHeaderWidth = computedLeftHeaderStyle + ? parseFloat( computedLeftHeaderStyle.width ) + : 0; + + // set the new witdth of the toolbar + blockToolbar.style.width = `calc(100% - ${ + leftHeaderWidth + + pinnedItemsWidth + + marginLeft + + ( isFullscreen ? 0 : 160 ) // the width of the admin sidebar expanded + }px)`; + }, [ + isFixed, + isLargerThanTabletViewport, + isCollapsed, + isFullscreen, + blockType, + ] ); + + const isToolbarEnabled = + ! blockType || + hasBlockSupport( blockType, '__experimentalToolbar', true ); + const hasAnyBlockControls = useHasAnyBlockControls(); if ( - isContentOnly || - ( blockType && - ! hasBlockSupport( blockType, '__experimentalToolbar', true ) ) + ! isToolbarEnabled || + ( blockEditingMode !== 'default' && ! hasAnyBlockControls ) ) { return null; } diff --git a/packages/block-editor/src/components/block-tools/style.scss b/packages/block-editor/src/components/block-tools/style.scss index 1f08727bbfb9b1..6a987929dffb70 100644 --- a/packages/block-editor/src/components/block-tools/style.scss +++ b/packages/block-editor/src/components/block-tools/style.scss @@ -110,6 +110,12 @@ z-index: z-index(".block-editor-block-popover"); display: block; width: 100%; + overflow: hidden; + + .block-editor-block-toolbar { + overflow: auto; + overflow-y: hidden; + } border: none; border-bottom: $border-width solid $gray-200; @@ -121,10 +127,6 @@ } } - &:has(.block-editor-block-toolbar:empty) { - display: none; - } - // Add a scrim to the right of the collapsed button. &.is-collapsed::after { content: ""; @@ -137,14 +139,14 @@ // on desktop and tablet viewports the toolbar is fixed // on top of interface header + $toolbar-margin: $grid-unit-80 * 3 - 2 * $grid-unit + $grid-unit-05; @include break-medium() { &.is-fixed { - // leave room for block inserter, undo and redo, list view - margin-left: $grid-unit-80 * 3 - 2 * $grid-unit + $grid-unit-05; + margin-left: $toolbar-margin; // position on top of interface header position: fixed; - top: $admin-bar-height + $grid-unit - $border-width; + top: $admin-bar-height; // Don't fill up when empty min-height: initial; // remove the border @@ -152,18 +154,32 @@ // has to be flex for collapse button to fit display: flex; + // Mimic the height of the parent, vertically align center, and provide a max-height. + height: $header-height; + align-items: center; + &.is-collapsed { width: initial; } + &:empty { + width: initial; + } + .is-fullscreen-mode & { // leave room for block inserter, undo and redo, list view // and some margin left margin-left: $grid-unit-80 * 4 - 2 * $grid-unit; - top: $grid-unit - $border-width; + + top: 0; + &.is-collapsed { width: initial; } + + &:empty { + width: initial; + } } & > .block-editor-block-toolbar.is-showing-movers { @@ -249,7 +265,7 @@ .show-icon-labels & { - margin-left: $grid-unit-80 + 2 * $grid-unit; // inserter and margin ; + margin-left: $grid-unit-80 + 2 * $grid-unit; // inserter and margin .is-fullscreen-mode & { margin-left: $grid-unit * 18; // site hub, inserter and margin @@ -322,7 +338,12 @@ // except for the inserter on the left @include break-medium() { &.is-fixed { - width: 100%; + width: calc(100% - #{$toolbar-margin}); + + .show-icon-labels & { + width: calc(100% + 40px - #{$toolbar-margin}); //there are no undo, redo and list view buttons + } + } } @@ -332,6 +353,9 @@ @include break-large() { &.is-fixed { width: auto; + .show-icon-labels & { + width: auto; //there are no undo, redo and list view buttons + } } .is-fullscreen-mode &.is-fixed { // in full screen mode we need to account for diff --git a/packages/block-editor/src/components/global-styles/test/typography-utils.js b/packages/block-editor/src/components/global-styles/test/typography-utils.js index cfbf0ea68edd94..8416e8d5313686 100644 --- a/packages/block-editor/src/components/global-styles/test/typography-utils.js +++ b/packages/block-editor/src/components/global-styles/test/typography-utils.js @@ -561,6 +561,16 @@ describe( 'typography utils', () => { }, expected: { fluid: { maxViewPortWidth: '10px' } }, }, + { + message: 'should not merge `layout.wideSize` if it is fluid', + settings: { + typography: { fluid: { minFontSize: '16px' } }, + layout: { wideSize: 'clamp(1000px, 85vw, 2000px)' }, + }, + expected: { + fluid: { minFontSize: '16px' }, + }, + }, ].forEach( ( { message, settings, expected } ) => { it( `${ message }`, () => { expect( diff --git a/packages/block-editor/src/components/global-styles/typography-utils.js b/packages/block-editor/src/components/global-styles/typography-utils.js index 8eaefefbdb83eb..f38e8ffff60fdb 100644 --- a/packages/block-editor/src/components/global-styles/typography-utils.js +++ b/packages/block-editor/src/components/global-styles/typography-utils.js @@ -7,7 +7,10 @@ /** * Internal dependencies */ -import { getComputedFluidTypographyValue } from '../font-sizes/fluid-utils'; +import { + getComputedFluidTypographyValue, + getTypographyValueAndUnit, +} from '../font-sizes/fluid-utils'; /** * @typedef {Object} FluidPreset @@ -98,11 +101,16 @@ function isFluidTypographyEnabled( typographySettings ) { export function getFluidTypographyOptionsFromSettings( settings ) { const typographySettings = settings?.typography; const layoutSettings = settings?.layout; - return isFluidTypographyEnabled( typographySettings ) && + const defaultMaxViewportWidth = getTypographyValueAndUnit( layoutSettings?.wideSize + ) + ? layoutSettings?.wideSize + : null; + return isFluidTypographyEnabled( typographySettings ) && + defaultMaxViewportWidth ? { fluid: { - maxViewPortWidth: layoutSettings.wideSize, + maxViewPortWidth: defaultMaxViewportWidth, ...typographySettings.fluid, }, } diff --git a/packages/block-editor/src/components/link-control/README.md b/packages/block-editor/src/components/link-control/README.md index c3fc7262adb956..fef68318867e8a 100644 --- a/packages/block-editor/src/components/link-control/README.md +++ b/packages/block-editor/src/components/link-control/README.md @@ -15,6 +15,16 @@ The distinction between the two components is perhaps best summarized by the fol - `` - an input for presenting and managing selection behaviors associated with choosing a URL, optionally from a pool of available candidates. - `` - includes the features of ``, plus additional UI and behaviors to control how this URL applies to the concept of a "link". This includes link "settings" (eg: "opens in new tab", etc) and dynamic, "on the fly" link creation capabilities. +## Persistent "Advanced" (settings) toggle state + +By default the link "settings" are hidden and can be toggled open/closed by way of a button labelled `Advanced` in the UI. + +In some circumstances if may be desirable to persist the toggle state of this portion of the UI so that it remains in the last state triggered by user interaction. + +For example, once the user has toggled the UI to "open", then it may remain open across all links on the site until such time as the user toggles the UI back again. + +Consumers who which to take advantage of this functionality should ensure that their block editor environment utilizes the [`@wordpress/preferences`](packages/preferences/README.md) package. By default the `` component will attempt to persist the state of UI to a setting named `linkControlSettingsDrawer` with a scope of `core/block-editor`. If the preferences package is not available then local state is used and the setting will not be persisted. + ## Search Suggestions When creating links the `LinkControl` component will handle two kinds of input from users: @@ -69,9 +79,7 @@ An array of settings objects associated with a link (for example: a setting to d To disable settings, pass in an empty array. for example: ```jsx - + ``` ### onChange @@ -192,6 +200,7 @@ A `suggestion` should have the following shape: )} /> ``` + ### renderControlBottom - Type: `Function` diff --git a/packages/block-editor/src/components/link-control/index.js b/packages/block-editor/src/components/link-control/index.js index 41455c4ecf9704..a2025d1338338b 100644 --- a/packages/block-editor/src/components/link-control/index.js +++ b/packages/block-editor/src/components/link-control/index.js @@ -12,6 +12,8 @@ import { useRef, useState, useEffect } from '@wordpress/element'; import { focus } from '@wordpress/dom'; import { ENTER } from '@wordpress/keycodes'; import { isShallowEqualObjects } from '@wordpress/is-shallow-equal'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { store as preferencesStore } from '@wordpress/preferences'; /** * Internal dependencies @@ -101,6 +103,9 @@ import { DEFAULT_LINK_SETTINGS } from './constants'; const noop = () => {}; +const PREFERENCE_SCOPE = 'core/block-editor'; +const PREFERENCE_KEY = 'linkControlSettingsDrawer'; + /** * Renders a link control. A link control is a controlled input which maintains * a value associated with a link (HTML anchor element) and relevant settings @@ -133,6 +138,41 @@ function LinkControl( { withCreateSuggestion = true; } + const [ settingsOpen, setSettingsOpen ] = useState( false ); + + const { advancedSettingsPreference } = useSelect( ( select ) => { + const prefsStore = select( preferencesStore ); + + return { + advancedSettingsPreference: + prefsStore.get( PREFERENCE_SCOPE, PREFERENCE_KEY ) ?? false, + }; + }, [] ); + + const { set: setPreference } = useDispatch( preferencesStore ); + + /** + * Sets the open/closed state of the Advanced Settings Drawer, + * optionlly persisting the state to the user's preferences. + * + * Note that Block Editor components can be consumed by non-WordPress + * environments which may not have preferences setup. + * Therefore a local state is also used as a fallback. + * + * @param {boolean} prefVal the open/closed state of the Advanced Settings Drawer. + */ + const setSettingsOpenWithPreference = ( prefVal ) => { + if ( setPreference ) { + setPreference( PREFERENCE_SCOPE, PREFERENCE_KEY, prefVal ); + } + setSettingsOpen( prefVal ); + }; + + // Block Editor components can be consumed by non-WordPress environments + // which may not have these preferences setup. + // Therefore a local state is used as a fallback. + const isSettingsOpen = advancedSettingsPreference || settingsOpen; + const isMounting = useRef( true ); const wrapperNode = useRef(); const textInputRef = useRef(); @@ -140,8 +180,6 @@ function LinkControl( { const settingsKeys = settings.map( ( { id } ) => id ); - const [ settingsOpen, setSettingsOpen ] = useState( false ); - const [ internalControlValue, setInternalControlValue, @@ -207,7 +245,6 @@ function LinkControl( { wrapperNode.current.ownerDocument.activeElement ); - setSettingsOpen( false ); setIsEditingLink( false ); }; @@ -292,7 +329,6 @@ function LinkControl( { const shownUnlinkControl = onRemove && value && ! isEditingLink && ! isCreatingPage; - const showSettings = !! settings?.length && isEditingLink && hasLinkValue; const showActions = isEditingLink && hasLinkValue; // Only show text control once a URL value has been committed @@ -302,6 +338,7 @@ function LinkControl( { const isEditing = ( isEditingLink || ! value ) && ! isCreatingPage; const isDisabled = ! valueHasChanges || currentInputIsEmpty; + const showSettings = !! settings?.length && isEditingLink && hasLinkValue; return (
{ ! currentInputIsEmpty && ( { } ); describe( 'Addition Settings UI', () => { - it( 'should not show a means to toggle the link settings when not editing a link', async () => { + it( 'should hide advanced link settings when not editing a link', async () => { const selectedLink = fauxEntitySuggestions[ 0 ]; const LinkControlConsumer = () => { @@ -1723,6 +1723,7 @@ describe( 'Addition Settings UI', () => { expect( settingsToggle ).not.toBeInTheDocument(); } ); + it( 'should provides a means to toggle the link settings', async () => { const selectedLink = fauxEntitySuggestions[ 0 ]; diff --git a/packages/block-editor/src/components/rich-text/use-format-types.js b/packages/block-editor/src/components/rich-text/use-format-types.js index f798e41ede4dfb..9d26d619432496 100644 --- a/packages/block-editor/src/components/rich-text/use-format-types.js +++ b/packages/block-editor/src/components/rich-text/use-format-types.js @@ -66,21 +66,21 @@ export function useFormatTypes( { } ) { const allFormatTypes = useSelect( formatTypesSelector, [] ); const formatTypes = useMemo( () => { - return allFormatTypes.filter( ( { name, tagName } ) => { + return allFormatTypes.filter( ( { name, interactive, tagName } ) => { if ( allowedFormats && ! allowedFormats.includes( name ) ) { return false; } if ( withoutInteractiveFormatting && - interactiveContentTags.has( tagName ) + ( interactive || interactiveContentTags.has( tagName ) ) ) { return false; } return true; } ); - }, [ allFormatTypes, allowedFormats, interactiveContentTags ] ); + }, [ allFormatTypes, allowedFormats, withoutInteractiveFormatting ] ); const keyedSelected = useSelect( ( select ) => formatTypes.reduce( ( accumulator, type ) => { diff --git a/packages/block-library/src/footnotes/format.js b/packages/block-library/src/footnotes/format.js index c7730c53316b77..2145845652b66e 100644 --- a/packages/block-library/src/footnotes/format.js +++ b/packages/block-library/src/footnotes/format.js @@ -37,6 +37,7 @@ export const format = { attributes: { 'data-fn': 'data-fn', }, + interactive: true, contentEditable: false, [ usesContextKey ]: [ 'postType' ], edit: function Edit( { diff --git a/packages/block-library/src/image/block.json b/packages/block-library/src/image/block.json index 7c8b2c2715c99a..fad94a62e5a755 100644 --- a/packages/block-library/src/image/block.json +++ b/packages/block-library/src/image/block.json @@ -64,10 +64,10 @@ "__experimentalRole": "content" }, "width": { - "type": "number" + "type": "string" }, "height": { - "type": "number" + "type": "string" }, "aspectRatio": { "type": "string" diff --git a/packages/block-library/src/image/deprecated.js b/packages/block-library/src/image/deprecated.js index edbabd7fb2d835..d159009b173739 100644 --- a/packages/block-library/src/image/deprecated.js +++ b/packages/block-library/src/image/deprecated.js @@ -740,4 +740,211 @@ const v6 = { }, }; -export default [ v6, v5, v4, v3, v2, v1 ]; +/** + * Deprecation for converting to string width and height block attributes and + * removing the width and height img element attributes which are not needed + * as they get added by the TODO hook. + * + * @see https://github.com/WordPress/gutenberg/pull/53274 + */ +const v7 = { + attributes: { + align: { + type: 'string', + }, + url: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'src', + __experimentalRole: 'content', + }, + alt: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'alt', + default: '', + __experimentalRole: 'content', + }, + caption: { + type: 'string', + source: 'html', + selector: 'figcaption', + __experimentalRole: 'content', + }, + title: { + type: 'string', + source: 'attribute', + selector: 'img', + attribute: 'title', + __experimentalRole: 'content', + }, + href: { + type: 'string', + source: 'attribute', + selector: 'figure > a', + attribute: 'href', + __experimentalRole: 'content', + }, + rel: { + type: 'string', + source: 'attribute', + selector: 'figure > a', + attribute: 'rel', + }, + linkClass: { + type: 'string', + source: 'attribute', + selector: 'figure > a', + attribute: 'class', + }, + id: { + type: 'number', + __experimentalRole: 'content', + }, + width: { + type: 'number', + }, + height: { + type: 'number', + }, + aspectRatio: { + type: 'string', + }, + scale: { + type: 'string', + }, + sizeSlug: { + type: 'string', + }, + linkDestination: { + type: 'string', + }, + linkTarget: { + type: 'string', + source: 'attribute', + selector: 'figure > a', + attribute: 'target', + }, + }, + supports: { + anchor: true, + behaviors: { + lightbox: true, + }, + color: { + text: false, + background: false, + }, + filter: { + duotone: true, + }, + __experimentalBorder: { + color: true, + radius: true, + width: true, + __experimentalSkipSerialization: true, + __experimentalDefaultControls: { + color: true, + radius: true, + width: true, + }, + }, + }, + migrate( { width, height, ...attributes } ) { + return { + ...attributes, + width: `${ width }px`, + height: `${ height }px`, + }; + }, + save( { attributes } ) { + const { + url, + alt, + caption, + align, + href, + rel, + linkClass, + width, + height, + aspectRatio, + scale, + id, + linkTarget, + sizeSlug, + title, + } = attributes; + + const newRel = ! rel ? undefined : rel; + const borderProps = getBorderClassesAndStyles( attributes ); + + const classes = classnames( { + [ `align${ align }` ]: align, + [ `size-${ sizeSlug }` ]: sizeSlug, + 'is-resized': width || height, + 'has-custom-border': + !! borderProps.className || + ( borderProps.style && + Object.keys( borderProps.style ).length > 0 ), + } ); + + const imageClasses = classnames( borderProps.className, { + [ `wp-image-${ id }` ]: !! id, + } ); + + const image = ( + { + ); + + const figure = ( + <> + { href ? ( + + { image } + + ) : ( + image + ) } + { ! RichText.isEmpty( caption ) && ( + + ) } + + ); + + return ( +
+ { figure } +
+ ); + }, +}; + +export default [ v7, v6, v5, v4, v3, v2, v1 ]; diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index 5fe5c93f4199b4..001369a4b3513e 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -114,6 +114,11 @@ export default function Image( { linkTarget, sizeSlug, } = attributes; + + // The only supported unit is px, so we can parseInt to strip the px here. + const numericWidth = width ? parseInt( width, 10 ) : undefined; + const numericHeight = height ? parseInt( height, 10 ) : undefined; + const imageRef = useRef(); const prevCaption = usePrevious( caption ); const [ showCaption, setShowCaption ] = useState( !! caption ); @@ -321,7 +326,12 @@ export default function Image( { function updateAlignment( nextAlign ) { const extraUpdatedAttributes = [ 'wide', 'full' ].includes( nextAlign ) - ? { width: undefined, height: undefined } + ? { + width: undefined, + height: undefined, + aspectRatio: undefined, + scale: undefined, + } : {}; setAttributes( { ...extraUpdatedAttributes, @@ -472,33 +482,26 @@ export default function Image( { /> ) } - { - // Rebuilding the object forces setting `undefined` - // for values that are removed since setAttributes - // doesn't do anything with keys that aren't set. - setAttributes( { - width: - newValue.width && - parseInt( newValue.width, 10 ), - height: - newValue.height && - parseInt( newValue.height, 10 ), - scale: newValue.scale, - aspectRatio: newValue.aspectRatio, - } ); - } } - defaultScale="cover" - defaultAspectRatio="auto" - scaleOptions={ scaleOptions } - unitsOptions={ dimensionsUnitsOptions } - /> + { isResizable && ( + { + // Rebuilding the object forces setting `undefined` + // for values that are removed since setAttributes + // doesn't do anything with keys that aren't set. + setAttributes( { + width: newValue.width, + height: newValue.height, + scale: newValue.scale, + aspectRatio: newValue.aspectRatio, + } ); + } } + defaultScale="cover" + defaultAspectRatio="auto" + scaleOptions={ scaleOptions } + unitsOptions={ dimensionsUnitsOptions } + /> + ) } { img }
; } else { + const numericRatio = aspectRatio && evalAspectRatio( aspectRatio ); + const customRatio = numericWidth / numericHeight; const ratio = - ( aspectRatio && evalAspectRatio( aspectRatio ) ) || - ( width && height && width / height ) || - naturalWidth / naturalHeight || - 1; - - const currentWidth = ! width && height ? height * ratio : width; - const currentHeight = ! height && width ? width / ratio : height; + numericRatio || customRatio || naturalWidth / naturalHeight || 1; + const currentWidth = + ! numericWidth && numericHeight + ? numericHeight * ratio + : numericWidth; + const currentHeight = + ! numericHeight && numericWidth + ? numericWidth / ratio + : numericHeight; const minWidth = naturalWidth < naturalHeight ? MIN_SIZE : MIN_SIZE * ratio; @@ -687,10 +694,14 @@ export default function Image( { onResizeStart={ onResizeStart } onResizeStop={ ( event, direction, elt ) => { onResizeStop(); + // Since the aspect ratio is locked when resizing, we can + // use the width of the resized element to calculate the + // height in CSS to prevent stretching when the max-width + // is reached. setAttributes( { - width: elt.offsetWidth, - height: elt.offsetHeight, - aspectRatio: undefined, + width: `${ elt.offsetWidth }px`, + height: 'auto', + aspectRatio: `${ ratio }`, } ); } } resizeRatio={ align === 'center' ? 2 : 1 } diff --git a/packages/block-library/src/image/save.js b/packages/block-library/src/image/save.js index 6fa8c6b2342f32..81565af09ababf 100644 --- a/packages/block-library/src/image/save.js +++ b/packages/block-library/src/image/save.js @@ -61,8 +61,6 @@ export default function save( { attributes } ) { width, height, } } - width={ width } - height={ height } title={ title } /> ); diff --git a/packages/core-data/src/entity-provider.js b/packages/core-data/src/entity-provider.js index 3039105d7c55c6..6cc1e021841b4a 100644 --- a/packages/core-data/src/entity-provider.js +++ b/packages/core-data/src/entity-provider.js @@ -229,15 +229,6 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) { ); function updateAttributes( attributes ) { - // Only attempt to update attributes, if attributes is an object. - if ( - ! attributes || - Array.isArray( attributes ) || - typeof attributes !== 'object' - ) { - return attributes; - } - attributes = { ...attributes }; for ( const key in attributes ) { diff --git a/packages/edit-post/src/components/visual-editor/index.js b/packages/edit-post/src/components/visual-editor/index.js index 29e6e15da4579f..c48282e12651c2 100644 --- a/packages/edit-post/src/components/visual-editor/index.js +++ b/packages/edit-post/src/components/visual-editor/index.js @@ -338,11 +338,19 @@ export default function VisualEditor( { styles } ) { .is-root-container.alignfull { max-width: none; margin-left: auto; margin-right: auto;} .is-root-container.alignfull:where(.is-layout-flow) > :not(.alignleft):not(.alignright) { max-width: none;}`; + const isToBeIframed = + ( ( hasV3BlocksOnly || ( isGutenbergPlugin && isBlockBasedTheme ) ) && + ! hasMetaBoxes ) || + isTemplateMode || + deviceType === 'Tablet' || + deviceType === 'Mobile'; + return ( @@ -359,14 +367,7 @@ export default function VisualEditor( { styles } ) { className={ previewMode } > diff --git a/packages/edit-post/src/components/visual-editor/style.scss b/packages/edit-post/src/components/visual-editor/style.scss index 00678f9867ec3c..fa61cc9889cf9c 100644 --- a/packages/edit-post/src/components/visual-editor/style.scss +++ b/packages/edit-post/src/components/visual-editor/style.scss @@ -2,7 +2,11 @@ position: relative; display: flex; flex-flow: column; - overflow: hidden; + // In the iframed canvas this keeps extra scrollbars from appearing (when block toolbars overflow). In the + // legacy (non-iframed) canvas, overflow must not be hidden in order to maintain support for sticky positioning. + &:not(.has-inline-canvas) { + overflow: hidden; + } // Gray preview overlay (desktop/tablet/mobile) is intentionally not set on an element with scrolling content like // interface-interface-skeleton__content. This causes graphical glitches (flashes of the background color) diff --git a/packages/edit-site/src/components/header-edit-mode/document-actions/index.js b/packages/edit-site/src/components/header-edit-mode/document-actions/index.js index b644b0fe2afd5c..143135bda3ce41 100644 --- a/packages/edit-site/src/components/header-edit-mode/document-actions/index.js +++ b/packages/edit-site/src/components/header-edit-mode/document-actions/index.js @@ -6,7 +6,7 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { sprintf, __, isRTL } from '@wordpress/i18n'; +import { __, isRTL } from '@wordpress/i18n'; import { useSelect, useDispatch } from '@wordpress/data'; import { Button, @@ -33,8 +33,18 @@ import { store as coreStore } from '@wordpress/core-data'; import useEditedEntityRecord from '../../use-edited-entity-record'; import { store as editSiteStore } from '../../../store'; +const typeLabels = { + wp_block: __( 'Editing pattern:' ), + wp_navigation: __( 'Editing navigation menu:' ), + wp_template: __( 'Editing template:' ), + wp_template_part: __( 'Editing template part:' ), +}; + export default function DocumentActions() { - const isPage = useSelect( ( select ) => select( editSiteStore ).isPage() ); + const isPage = useSelect( + ( select ) => select( editSiteStore ).isPage(), + [] + ); return isPage ? : ; } @@ -118,8 +128,6 @@ function TemplateDocumentActions( { className, onBack } ) { ); } - const entityLabel = getEntityLabel( record.type ); - let typeIcon = icon; if ( record.type === 'wp_navigation' ) { typeIcon = navigationIcon; @@ -134,11 +142,7 @@ function TemplateDocumentActions( { className, onBack } ) { onBack={ onBack } > - { sprintf( - /* translators: %s: the entity being edited, like "template"*/ - __( 'Editing %s: ' ), - entityLabel - ) } + { typeLabels[ record.type ] ?? typeLabels.wp_template } { getTitle() } @@ -184,20 +188,3 @@ function BaseDocumentActions( { className, icon, children, onBack } ) { ); } - -function getEntityLabel( entityType ) { - let label = ''; - switch ( entityType ) { - case 'wp_navigation': - label = 'navigation menu'; - break; - case 'wp_template_part': - label = 'template part'; - break; - default: - label = 'template'; - break; - } - - return label; -} 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 d3116b4576490b..2fdcf8d2816253 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 @@ -1,7 +1,6 @@ .edit-site-document-actions { display: flex; align-items: center; - gap: $grid-unit; height: $button-size; justify-content: space-between; // Flex items will, by default, refuse to shrink below a minimum @@ -10,7 +9,7 @@ // See https://dev.w3.org/csswg/css-flexbox/#min-size-auto min-width: 0; background: $gray-100; - border-radius: 4px; + border-radius: $grid-unit-05; width: min(100%, 450px); // Make the document title shorter in top-toolbar mode, as it has to be covered. @@ -18,17 +17,19 @@ width: min(100%, 380px); } + &:hover { + background-color: $gray-200; + } + .components-button { + border-radius: $grid-unit-05; + &:hover { color: var(--wp-block-synced-color); background: $gray-200; } } - @include break-medium() { - width: 50%; - } - @include break-large() { width: min(100%, 450px); } @@ -45,6 +46,9 @@ color: var(--wp-block-synced-color); overflow: hidden; + // Offset the layout based on the width of the ⌘K label. This ensures the title is centrally aligned. + padding-left: $grid-unit-40; + &:hover { color: var(--wp-block-synced-color); } @@ -59,6 +63,7 @@ overflow: hidden; text-overflow: ellipsis; color: var(--wp-block-synced-color); + max-width: 50%; } .edit-site-document-actions.is-page & { @@ -82,6 +87,7 @@ .edit-site-document-actions__shortcut { color: $gray-800; + min-width: $grid-unit-40; } .edit-site-document-actions__back.components-button.has-icon.has-text { @@ -90,9 +96,11 @@ color: $gray-700; gap: 0; z-index: 1; + position: absolute; &:hover { color: currentColor; + background-color: transparent; } .edit-site-document-actions.is-animated & { diff --git a/packages/edit-site/src/components/page/index.js b/packages/edit-site/src/components/page/index.js index a713989a3ce0a2..02d0bd2e746eec 100644 --- a/packages/edit-site/src/components/page/index.js +++ b/packages/edit-site/src/components/page/index.js @@ -26,17 +26,17 @@ export default function Page( { return ( - { ! hideTitleFromUI && title && ( -
- ) }
+ { ! hideTitleFromUI && title && ( +
+ ) } { children } -
+ ); } diff --git a/packages/edit-site/src/components/page/style.scss b/packages/edit-site/src/components/page/style.scss index e29849e408c0d5..8da7df8e0385b8 100644 --- a/packages/edit-site/src/components/page/style.scss +++ b/packages/edit-site/src/components/page/style.scss @@ -2,7 +2,7 @@ color: $gray-800; background: $white; flex-grow: 1; - overflow: auto; + overflow: hidden; margin: 0; margin-top: $header-height; @include break-medium() { @@ -13,8 +13,7 @@ .edit-site-page-header { padding: 0 $grid-unit-40; - height: $header-height; - padding-left: $grid-unit-40; + min-height: $header-height; border-bottom: 1px solid $gray-100; background: $white; position: sticky; @@ -33,6 +32,10 @@ } .edit-site-page-content { - padding: $grid-unit-40 $grid-unit-40; - overflow-x: auto; + height: 100%; + display: flex; + overflow: auto; + flex-flow: column; + position: relative; + z-index: z-index(".edit-site-page-content"); } diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js index 14d78405b3abe5..6b093ad27e25b9 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js @@ -126,16 +126,17 @@ export default function LeafMoreMenu( props ) { > { __( 'Move down' ) } - { block.attributes?.id && ( - { - onGoToPage( block ); - onClose(); - } } - > - { goToLabel } - - ) } + { block.attributes?.type === 'page' && + block.attributes?.id && ( + { + onGoToPage( block ); + onClose(); + } } + > + { goToLabel } + + ) } diff --git a/packages/edit-site/src/components/table/style.scss b/packages/edit-site/src/components/table/style.scss index a53b2c836f54cc..85c741575d5a44 100644 --- a/packages/edit-site/src/components/table/style.scss +++ b/packages/edit-site/src/components/table/style.scss @@ -1,5 +1,6 @@ .edit-site-table-wrapper { width: 100%; + padding: $grid-unit-40; } .edit-site-table { diff --git a/packages/editor/src/components/post-sync-status/style.scss b/packages/editor/src/components/post-sync-status/style.scss index 7f81a327443ee1..90a75c86bf466d 100644 --- a/packages/editor/src/components/post-sync-status/style.scss +++ b/packages/editor/src/components/post-sync-status/style.scss @@ -2,15 +2,18 @@ width: 100%; position: relative; justify-content: flex-start; + align-items: flex-start; > span { display: block; width: 45%; flex-shrink: 0; + padding: $grid-unit-15 * 0.5 0; + word-break: break-word; } > div { // Match padding on tertiary buttons for alignment. - padding-left: $grid-unit-15; + padding: $grid-unit-15 * 0.5 0 $grid-unit-15 * 0.5 $grid-unit-15; } } diff --git a/packages/interface/src/components/interface-skeleton/style.scss b/packages/interface/src/components/interface-skeleton/style.scss index a45239b529ddd0..d7ce996226ba59 100644 --- a/packages/interface/src/components/interface-skeleton/style.scss +++ b/packages/interface/src/components/interface-skeleton/style.scss @@ -88,6 +88,12 @@ html.interface-interface-skeleton__html-container { // to "bleed" through the header. // See https://github.com/WordPress/gutenberg/issues/32631 z-index: z-index(".interface-interface-skeleton__content"); + + // On Safari the z-index is not respected when the element is fixed. + // Setting it to auto fixes the problem + @include break-medium() { + z-index: auto; + } } .interface-interface-skeleton__secondary-sidebar, diff --git a/packages/rich-text/src/register-format-type.js b/packages/rich-text/src/register-format-type.js index 8ea19a97f595ff..a3ca5b64903f9f 100644 --- a/packages/rich-text/src/register-format-type.js +++ b/packages/rich-text/src/register-format-type.js @@ -13,6 +13,7 @@ import { store as richTextStore } from './store'; * unique across all registered formats. * @property {string} tagName The HTML tag this format will wrap the * selection with. + * @property {boolean} interactive Whether format makes content interactive or not. * @property {string} [className] A class to match the format. * @property {string} title Name of the format. * @property {Function} edit Should return a component for the user to diff --git a/phpunit/block-supports/typography-test.php b/phpunit/block-supports/typography-test.php index e3e136eabc6921..d2bc68e04e12ce 100644 --- a/phpunit/block-supports/typography-test.php +++ b/phpunit/block-supports/typography-test.php @@ -686,6 +686,11 @@ public function data_generate_block_supports_font_size_fixtures() { 'theme_slug' => 'block-theme-child-with-fluid-typography-config', 'expected_output' => 'font-size:15px;', ), + 'returns clamp value using default config if layout is fluid' => array( + 'font_size_value' => '15px', + 'theme_slug' => 'block-theme-child-with-fluid-layout', + 'expected_output' => 'font-size:clamp(14px, 0.875rem + ((1vw - 3.2px) * 0.078), 15px);', + ), ); } diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index bd1d578a1297b6..2acab0fd33356d 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -1702,6 +1702,85 @@ public function data_get_styles_for_block_with_style_variations() { ); } + public function test_block_style_variations() { + wp_set_current_user( static::$administrator_id ); + + $expected = array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'styles' => array( + 'blocks' => array( + 'core/button' => array( + 'color' => array( + 'background' => 'blue', + ), + 'variations' => array( + 'outline' => array( + 'color' => array( + 'background' => 'purple', + ), + ), + ), + ), + ), + ), + ); + + $actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties( $expected ); + + $this->assertSameSetsWithIndex( $expected, $actual ); + } + + public function test_block_style_variations_with_invalid_properties() { + wp_set_current_user( static::$administrator_id ); + + $partially_invalid_variation = array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'styles' => array( + 'blocks' => array( + 'core/button' => array( + 'color' => array( + 'background' => 'blue', + ), + 'variations' => array( + 'outline' => array( + 'color' => array( + 'background' => 'purple', + ), + 'invalid' => array( + 'value' => 'should be stripped', + ), + ), + ), + ), + ), + ), + ); + + $expected = array( + 'version' => WP_Theme_JSON_Gutenberg::LATEST_SCHEMA, + 'styles' => array( + 'blocks' => array( + 'core/button' => array( + 'color' => array( + 'background' => 'blue', + ), + 'variations' => array( + 'outline' => array( + 'color' => array( + 'background' => 'purple', + ), + ), + ), + ), + ), + ), + ); + + $actual = WP_Theme_JSON_Gutenberg::remove_insecure_properties( $partially_invalid_variation ); + + $this->assertSameSetsWithIndex( $expected, $actual ); + } + public function test_update_separator_declarations() { // If only background is defined, test that includes border-color to the style so it is applied on the front end. $theme_json = new WP_Theme_JSON_Gutenberg( diff --git a/phpunit/data/themedir1/block-theme-child-with-fluid-layout/style.css b/phpunit/data/themedir1/block-theme-child-with-fluid-layout/style.css new file mode 100644 index 00000000000000..193b8f098af64b --- /dev/null +++ b/phpunit/data/themedir1/block-theme-child-with-fluid-layout/style.css @@ -0,0 +1,8 @@ +/* +Theme Name: Block Theme Child Theme With Fluid Layout +Theme URI: https://wordpress.org/ +Description: For testing purposes only. +Template: block-theme +Version: 1.0.0 +Text Domain: block-theme-child-with-fluid-layout +*/ diff --git a/phpunit/data/themedir1/block-theme-child-with-fluid-layout/theme.json b/phpunit/data/themedir1/block-theme-child-with-fluid-layout/theme.json new file mode 100644 index 00000000000000..6985da16c60636 --- /dev/null +++ b/phpunit/data/themedir1/block-theme-child-with-fluid-layout/theme.json @@ -0,0 +1,12 @@ +{ + "version": 2, + "settings": { + "appearanceTools": true, + "layout": { + "wideSize": "clamp(1000px, 85vw, 2000px)" + }, + "typography": { + "fluid": true + } + } +} diff --git a/test/e2e/specs/editor/blocks/links.spec.js b/test/e2e/specs/editor/blocks/links.spec.js index 8443a04d0417e6..b84f954566fd55 100644 --- a/test/e2e/specs/editor/blocks/links.spec.js +++ b/test/e2e/specs/editor/blocks/links.spec.js @@ -145,4 +145,92 @@ test.describe( 'Links', () => { }, ] ); } ); + + test( 'toggle state of advanced link settings is preserved across editing links', async ( { + page, + editor, + pageUtils, + } ) => { + // Create a block with some text. + await editor.insertBlock( { + name: 'core/paragraph', + } ); + await page.keyboard.type( 'This is Gutenberg WordPress' ); + + // Select "WordPress". + await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); + + // Create a link. + await pageUtils.pressKeys( 'primary+k' ); + await page.keyboard.type( 'w.org' ); + await page.keyboard.press( 'Enter' ); + + // Move to edge of text "Gutenberg". + await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); // If you just use Alt here it won't work on windows. + await pageUtils.pressKeys( 'ArrowLeft' ); + await pageUtils.pressKeys( 'ArrowLeft' ); + + // Select "Gutenberg". + await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); + + // Create a link. + await pageUtils.pressKeys( 'primary+k' ); + await page.keyboard.type( 'https://wordpress.org/plugins/gutenberg/' ); + await page.keyboard.press( 'Enter' ); + + // Move back into the link. + await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); + await pageUtils.pressKeys( 'primary+k' ); + + // Toggle the Advanced settings to be open. + // This should set the editor preference to persist this + // UI state. + await page + .getByRole( 'region', { + name: 'Editor content', + } ) + .getByRole( 'button', { + name: 'Advanced', + } ) + .click(); + + // Move focus out of Link UI and into Paragraph block. + await pageUtils.pressKeys( 'Escape' ); + + // Move caret back into the "WordPress" link to trigger + // the Link UI for that link. + await pageUtils.pressKeys( 'Alt+ArrowRight' ); + await pageUtils.pressKeys( 'ArrowRight' ); + await pageUtils.pressKeys( 'ArrowRight' ); + + // Switch Link UI to "edit" mode. + await page.getByRole( 'button', { name: 'Edit' } ).click(); + + // Check that the Advanced settings are still expanded/open + // and I can see the open in new tab checkbox. This verifies + // that the editor preference was persisted. + await expect( page.getByLabel( 'Open in new tab' ) ).toBeVisible(); + + // Toggle the Advanced settings back to being closed. + await page + .getByRole( 'region', { + name: 'Editor content', + } ) + .getByRole( 'button', { + name: 'Advanced', + } ) + .click(); + + // Move focus out of Link UI and into Paragraph block. + await pageUtils.pressKeys( 'Escape' ); + + // Move caret back into the "Gutenberg" link and open + // the Link UI for that link. + await pageUtils.pressKeys( 'shiftAlt+ArrowLeft' ); + await pageUtils.pressKeys( 'primary+k' ); + + // Check that the Advanced settings are still closed. + // This verifies that the editor preference was persisted. + await expect( page.getByLabel( 'Open in new tab' ) ).not.toBeVisible(); + } ); } ); diff --git a/test/e2e/specs/site-editor/title.spec.js b/test/e2e/specs/site-editor/title.spec.js index 21cfc544829705..aa2942670c5a85 100644 --- a/test/e2e/specs/site-editor/title.spec.js +++ b/test/e2e/specs/site-editor/title.spec.js @@ -26,7 +26,7 @@ test.describe( 'Site editor title', () => { 'role=region[name="Editor top bar"i] >> role=heading[level=1]' ); - await expect( title ).toHaveText( 'Editing template: Index' ); + await expect( title ).toHaveText( 'Editing template:Index' ); } ); test( 'displays the selected template name in the title for the header template', async ( { @@ -43,6 +43,6 @@ test.describe( 'Site editor title', () => { 'role=region[name="Editor top bar"i] >> role=heading[level=1]' ); - await expect( title ).toHaveText( 'Editing template part: header' ); + await expect( title ).toHaveText( 'Editing template part:header' ); } ); } ); diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.serialized.html index 7ce56e11fa75e9..9a66da6c018989 100644 --- a/test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.serialized.html +++ b/test/integration/fixtures/blocks/core__image__deprecated-v2-add-is-resized-class.serialized.html @@ -1,3 +1,3 @@ -
+
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v3-add-align-wrapper.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-v3-add-align-wrapper.serialized.html index 7ce56e11fa75e9..9a66da6c018989 100644 --- a/test/integration/fixtures/blocks/core__image__deprecated-v3-add-align-wrapper.serialized.html +++ b/test/integration/fixtures/blocks/core__image__deprecated-v3-add-align-wrapper.serialized.html @@ -1,3 +1,3 @@ -
+
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v4-remove-align-wrapper.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-v4-remove-align-wrapper.serialized.html index cfdc52e3cbb6ea..99da2155bce88f 100644 --- a/test/integration/fixtures/blocks/core__image__deprecated-v4-remove-align-wrapper.serialized.html +++ b/test/integration/fixtures/blocks/core__image__deprecated-v4-remove-align-wrapper.serialized.html @@ -1,3 +1,3 @@ -
+
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v6-add-style-width-height.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-v6-add-style-width-height.serialized.html index ad70fe65f35827..807ba3abc9f9ce 100644 --- a/test/integration/fixtures/blocks/core__image__deprecated-v6-add-style-width-height.serialized.html +++ b/test/integration/fixtures/blocks/core__image__deprecated-v6-add-style-width-height.serialized.html @@ -1,3 +1,3 @@ -
+
diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.html b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.html new file mode 100644 index 00000000000000..7210aee3564b5f --- /dev/null +++ b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.html @@ -0,0 +1,3 @@ + +
+ diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.json b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.json new file mode 100644 index 00000000000000..b49c515f8e5ab6 --- /dev/null +++ b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.json @@ -0,0 +1,15 @@ +[ + { + "name": "core/image", + "isValid": true, + "attributes": { + "url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==", + "alt": "", + "caption": "", + "sizeSlug": "large", + "width": "164px", + "height": "164px" + }, + "innerBlocks": [] + } +] diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.parsed.json b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.parsed.json new file mode 100644 index 00000000000000..fbd8627807b3da --- /dev/null +++ b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.parsed.json @@ -0,0 +1,15 @@ +[ + { + "blockName": "core/image", + "attrs": { + "width": 164, + "height": 164, + "sizeSlug": "large" + }, + "innerBlocks": [], + "innerHTML": "\n
\"\"
\n", + "innerContent": [ + "\n
\"\"
\n" + ] + } +] diff --git a/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.serialized.html b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.serialized.html new file mode 100644 index 00000000000000..5392d261af89d6 --- /dev/null +++ b/test/integration/fixtures/blocks/core__image__deprecated-v7-string-width-height-attributes.serialized.html @@ -0,0 +1,3 @@ + +
+