diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index ed8e1ac11dce85..b0cb0b13ff9e9b 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -407,7 +407,7 @@ Introduce new sections and organize content to help visitors (and search engines - **Name:** core/heading - **Category:** text -- **Supports:** __unstablePasteTextInline, align (full, wide), anchor, className, color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), splitting, typography (fitText, fontSize, lineHeight, textAlign) +- **Supports:** __unstablePasteTextInline, align (full, wide), anchor, background (backgroundClip, backgroundImage, gradient), className, color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), splitting, typography (fitText, fontSize, lineHeight, textAlign) - **Attributes:** content, level, levelOptions, placeholder ## Home Link @@ -606,7 +606,7 @@ Start with the basic building block of all narrative. ([Source](https://github.c - **Name:** core/paragraph - **Category:** text -- **Supports:** __unstablePasteTextInline, align (full, wide), anchor, color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), splitting, typography (fitText, fontSize, lineHeight, textAlign, textColumns, textIndent), ~~className~~ +- **Supports:** __unstablePasteTextInline, align (full, wide), anchor, background (backgroundClip, gradient), color (background, gradients, link, text), interactivity (clientNavigation), spacing (margin, padding), splitting, typography (fitText, fontSize, lineHeight, textAlign, textColumns, textIndent), ~~className~~ - **Attributes:** content, direction, dropCap, placeholder ## Pattern Placeholder diff --git a/packages/block-editor/src/components/background-clip-control/index.js b/packages/block-editor/src/components/background-clip-control/index.js new file mode 100644 index 00000000000000..9fcec69112807e --- /dev/null +++ b/packages/block-editor/src/components/background-clip-control/index.js @@ -0,0 +1,183 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { + Dropdown, + Button, + __experimentalDropdownContentWrapper as DropdownContentWrapper, + __experimentalHStack as HStack, + __experimentalVStack as VStack, + FlexBlock, +} from '@wordpress/components'; +import { useState } from '@wordpress/element'; + +/** + * External dependencies + */ +import clsx from 'clsx'; + +export const ALL_BACKGROUND_CLIP_VALUES = [ + 'border-box', + 'padding-box', + 'content-box', + 'text', +]; + +const BACKGROUND_CLIP_OPTIONS = [ + { + label: __( 'Border box' ), + value: 'border-box', + }, + { + label: __( 'Padding box' ), + value: 'padding-box', + }, + { + label: __( 'Content box' ), + value: 'content-box', + }, + { + label: __( 'Text' ), + value: 'text', + }, +]; + +const BACKGROUND_POPOVER_PROPS = { + placement: 'left-start', + offset: 36, + shift: true, + className: 'block-editor-background-clip-control__popover', +}; + +function BackgroundClipPreview( { value } ) { + const isText = value === 'text'; + + return ( + + ); +} + +function BackgroundClipOption( { label, value, isActive, onSelect } ) { + return ( + + ); +} + +function BackgroundClipToggle( { value, toggleProps } ) { + const activeOption = BACKGROUND_CLIP_OPTIONS.find( + ( option ) => option.value === value + ); + const label = activeOption ? activeOption.label : __( 'Border box' ); + + return ( + + ); +} + +export default function BackgroundClipControl( { + value, + onChange, + allowedValues, +} ) { + const [ isOpen, setIsOpen ] = useState( false ); + + const options = allowedValues + ? BACKGROUND_CLIP_OPTIONS.filter( ( opt ) => + allowedValues.includes( opt.value ) + ) + : BACKGROUND_CLIP_OPTIONS; + + return ( +
+ { + return ( + { + onToggle(); + setIsOpen( ! dropdownIsOpen ); + }, + className: + 'block-editor-background-clip-control__dropdown-toggle', + 'aria-expanded': dropdownIsOpen, + 'aria-label': __( 'Background clip options' ), + } } + /> + ); + } } + onClose={ () => setIsOpen( false ) } + renderContent={ () => ( + + + + { __( 'Background clip' ) } + +
+ { options.map( ( option ) => ( + + onChange( + value === option.value + ? undefined + : option.value + ) + } + /> + ) ) } +
+
+
+ ) } + /> +
+ ); +} diff --git a/packages/block-editor/src/components/background-clip-control/style.scss b/packages/block-editor/src/components/background-clip-control/style.scss new file mode 100644 index 00000000000000..02e7c311012334 --- /dev/null +++ b/packages/block-editor/src/components/background-clip-control/style.scss @@ -0,0 +1,158 @@ +@use "@wordpress/base-styles/variables" as *; +@use "@wordpress/base-styles/colors" as *; + +.block-editor-background-clip-control__container { + position: relative; + + &.is-open { + background-color: $gray-100; + } + + .components-dropdown { + display: block; + } +} + +.block-editor-background-clip-control__dropdown-toggle { + cursor: pointer; + background: transparent; + border: none; + height: $button-size-next-default-40px; + width: 100%; + padding-left: $grid-unit-15; + padding-right: $grid-unit-15; + color: $gray-900; + + &:hover { + color: var(--wp-admin-theme-color); + } + + &:focus { + box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); + } +} + +// Popover content +.block-editor-background-clip-control__dropdown-content-wrapper { + min-width: 260px; +} + +.block-editor-background-clip-control__popover-title { + font-size: 11px; + font-weight: 500; + text-transform: uppercase; + color: $gray-700; +} + +.block-editor-background-clip-control__options { + display: grid; + grid-template-columns: 1fr 1fr; + gap: $grid-unit-10; +} + +.block-editor-background-clip-control__option { + appearance: none; + background: $gray-100; + color: $gray-900; + border: $border-width solid $gray-200; + border-radius: $radius-small; + cursor: pointer; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: $grid-unit-10; + padding: $grid-unit-15; + box-sizing: border-box; + + @media not (prefers-reduced-motion) { + transition: + border-color 0.1s ease, + background-color 0.1s ease; + } + + &:hover { + background-color: $gray-200; + border-color: $gray-400; + } + + &:focus-visible { + outline: none; + box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); + } + + &.is-active { + background-color: $white; + border-color: var(--wp-admin-theme-color); + box-shadow: inset 0 0 0 $border-width var(--wp-admin-theme-color); + } +} + +.block-editor-background-clip-control__option-label { + font-size: 12px; + line-height: 1; +} + +// Preview swatch shared styles +.block-editor-background-clip-control__preview { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + border-radius: 2px; + box-sizing: border-box; + + // Background gradient as the "fill" that gets clipped. + background: linear-gradient(135deg, var(--wp-admin-theme-color) 0%, var(--wp-admin-theme-color-darker-20, #{$gray-700}) 100%); + + // Default: border-box — fill extends to border edge. + // Show a dashed border to indicate the border area is included. + border: 3px dashed rgba(0, 0, 0, 0.15); + background-clip: border-box; + + &.is-padding-box { + // Fill clips to inside the border — border area is empty. + border: 3px dashed $gray-400; + background-clip: padding-box; + } + + &.is-content-box { + // Fill clips to content only — padding and border are empty. + border: 3px dashed $gray-400; + padding: 4px; + background-clip: content-box; + } + + &.is-text { + font-size: 18px; + font-weight: 800; + line-height: 1; + color: transparent; + border: none; + padding: 0; + -webkit-background-clip: text; + background-clip: text; + } +} + +// Smaller preview in the toggle button row. +.block-editor-background-clip-control__toggle-inner { + height: 100%; + + .block-editor-background-clip-control__preview { + width: 20px; + height: 20px; + border-width: 2px; + flex-shrink: 0; + + &.is-content-box { + padding: 2px; + border-width: 2px; + } + + &.is-text { + font-size: 11px; + } + } +} diff --git a/packages/block-editor/src/components/block-inspector/index.js b/packages/block-editor/src/components/block-inspector/index.js index 23a8ec5a6eb981..aa6315ec73dfdf 100644 --- a/packages/block-editor/src/components/block-inspector/index.js +++ b/packages/block-editor/src/components/block-inspector/index.js @@ -53,7 +53,7 @@ function StyleInspectorSlots( { /> 0 || areCustomGradientsEnabled; + // Determine whether backgroundClip is currently set to text (text gradient). + const isTextGradient = value?.background?.backgroundClip === 'text'; + const hasBackgroundGradientControl = useHasBackgroundControl( settings, 'gradient' @@ -150,11 +160,48 @@ export default function BackgroundImagePanel( { 'backgroundImage' ); + const clipSetting = settings?.background?.backgroundClip; + let allowedClipValues = []; + if ( clipSetting === true ) { + allowedClipValues = ALL_BACKGROUND_CLIP_VALUES; + } else if ( Array.isArray( clipSetting ) ) { + allowedClipValues = clipSetting; + } + // When text gradient support is active (the color panel's text section + // handles setting backgroundClip to 'text'), exclude 'text' from the + // background panel's clip control. This avoids shared-state confusion + // between the two panels. + const hasTextGradientSupport = + allowedClipValues.includes( 'text' ) && hasBackgroundGradientControl; + if ( hasTextGradientSupport ) { + allowedClipValues = allowedClipValues.filter( ( v ) => v !== 'text' ); + } + const showBackgroundClipControl = allowedClipValues.length > 0; + + const resetBackgroundClip = () => + onChange( + setImmutably( value, [ 'background', 'backgroundClip' ], undefined ) + ); + const resetAllFilter = useCallback( ( previousValue ) => { + const prevClip = previousValue?.background?.backgroundClip; + const isTextGrad = prevClip === 'text'; + return { ...previousValue, - background: {}, + background: { + // When a text gradient is active, the color panel owns + // gradient and backgroundClip. Preserve them here so the + // background panel's "Reset all" only clears background- + // panel values (image, size, position, etc.). + ...( isTextGrad + ? { + gradient: previousValue?.background?.gradient, + backgroundClip: prevClip, + } + : {} ), + }, color: hasBackgroundGradientControl ? { ...previousValue?.color, @@ -166,7 +213,11 @@ export default function BackgroundImagePanel( { [ hasBackgroundGradientControl ] ); - if ( ! showBackgroundGradientControl && ! showBackgroundImageControl ) { + if ( + ! showBackgroundGradientControl && + ! showBackgroundImageControl && + ! showBackgroundClipControl + ) { return null; } @@ -184,7 +235,7 @@ export default function BackgroundImagePanel( { : gradientValue; }; - const resetBackground = () => + const resetBackgroundImage = () => onChange( setImmutably( value, @@ -200,6 +251,15 @@ export default function BackgroundImagePanel( { undefined ); newValue = setImmutably( newValue, [ 'color', 'gradient' ], undefined ); + // If the gradient was used as a text gradient, also clear backgroundClip + // to avoid leaving text invisible with no gradient applied. + if ( value?.background?.backgroundClip === 'text' ) { + newValue = setImmutably( + newValue, + [ 'background', 'backgroundClip' ], + undefined + ); + } onChange( newValue ); }; @@ -240,7 +300,7 @@ export default function BackgroundImagePanel( { className="block-editor-background-panel__item" hasValue={ () => hasBackgroundImageValue( value ) } label={ __( 'Image' ) } - onDeselect={ resetBackground } + onDeselect={ resetBackgroundImage } isShownByDefault={ defaultControls.backgroundImage } panelId={ panelId } > @@ -258,7 +318,9 @@ export default function BackgroundImagePanel( { hasBackgroundGradientValue( value ) } + hasValue={ () => + hasBackgroundGradientValue( value ) && ! isTextGradient + } resetValue={ resetGradient } isShownByDefault={ defaultControls.gradient } indicators={ [ currentGradient ] } @@ -280,6 +342,33 @@ export default function BackgroundImagePanel( { panelId={ panelId } /> ) } + { showBackgroundClipControl && ( + + !! value?.background?.backgroundClip && + value?.background?.backgroundClip !== 'text' + } + label={ __( 'Clip' ) } + onDeselect={ resetBackgroundClip } + isShownByDefault={ defaultControls.backgroundClip } + panelId={ panelId } + > + { + onChange( + setImmutably( + value, + [ 'background', 'backgroundClip' ], + newClip + ) + ); + } } + allowedValues={ allowedClipValues } + /> + + ) } ); } diff --git a/packages/block-editor/src/components/global-styles/color-panel.js b/packages/block-editor/src/components/global-styles/color-panel.js index 04fd9817880423..2512d18e7155d1 100644 --- a/packages/block-editor/src/components/global-styles/color-panel.js +++ b/packages/block-editor/src/components/global-styles/color-panel.js @@ -21,7 +21,7 @@ import { Button, privateApis as componentsPrivateApis, } from '@wordpress/components'; -import { useCallback, useRef } from '@wordpress/element'; +import { useCallback, useMemo, useRef } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { getValueFromVariable } from '@wordpress/global-styles-engine'; import { reset as resetIcon } from '@wordpress/icons'; @@ -30,6 +30,7 @@ import { reset as resetIcon } from '@wordpress/icons'; * Internal dependencies */ import ColorGradientControl from '../colors-gradients/control'; +import { ALL_BACKGROUND_CLIP_VALUES } from '../background-clip-control'; import { useColorsPerOrigin, useGradientsPerOrigin } from './hooks'; import { useToolsPanelDropdownMenuProps } from './utils'; import { setImmutably } from '../../utils/object'; @@ -372,11 +373,17 @@ export default function ColorPanel( { const showBackgroundPanel = useHasBackgroundColorPanel( settings ); const backgroundColor = decodeValue( inheritedValue?.color?.background ); const userBackgroundColor = decodeValue( value?.color?.background ); - const gradient = decodeValue( inheritedValue?.color?.gradient ); - const userGradient = decodeValue( value?.color?.gradient ); - const hasBackground = () => - !! userBackgroundColor || - ( ! hasBackgroundGradientSupport && !! userGradient ); + // Exclude gradient from background panel when it's being used as a text gradient. + const inheritedIsTextGradient = + inheritedValue?.background?.backgroundClip === 'text'; + const isTextGradient = value?.background?.backgroundClip === 'text'; + const gradient = inheritedIsTextGradient + ? undefined + : decodeValue( inheritedValue?.color?.gradient ); + const userGradient = isTextGradient + ? undefined + : decodeValue( value?.color?.gradient ); + const hasBackground = () => !! userBackgroundColor || !! userGradient; const setBackgroundColor = ( newColor ) => { const newValue = setImmutably( value, @@ -475,62 +482,134 @@ export default function ColorPanel( { onChange( changedObject ); }; - const resetTextColor = () => setTextColor( undefined ); + // Text Gradient (background-clip: text) + const clipSetting = settings?.background?.backgroundClip; + let allowedClipValues = []; + if ( clipSetting === true ) { + allowedClipValues = ALL_BACKGROUND_CLIP_VALUES; + } else if ( Array.isArray( clipSetting ) ) { + allowedClipValues = clipSetting; + } + const showTextGradient = + allowedClipValues.includes( 'text' ) && hasBackgroundGradientSupport; + // Text gradient is stored at background.gradient, discriminated from a + // regular background gradient by backgroundClip === 'text'. + const textGradient = inheritedIsTextGradient + ? decodeValue( inheritedValue?.background?.gradient ) + : undefined; + const userTextGradient = isTextGradient + ? decodeValue( value?.background?.gradient ) + : undefined; + const hasTextGradientValue = () => + !! userTextGradient && value?.background?.backgroundClip === 'text'; + const setTextGradient = ( newGradient ) => { + let newValue = setImmutably( + value, + [ 'background', 'gradient' ], + encodeGradientValue( newGradient ) + ); + newValue = setImmutably( + newValue, + [ 'background', 'backgroundClip' ], + newGradient ? 'text' : undefined + ); + onChange( newValue ); + }; + const resetTextAndGradient = () => { + let newValue = setImmutably( value, [ 'color', 'text' ], undefined ); + if ( textColor === linkColor ) { + newValue = setImmutably( + newValue, + [ 'elements', 'link', 'color', 'text' ], + undefined + ); + } + if ( hasTextGradientValue() ) { + newValue = setImmutably( + newValue, + [ 'background', 'gradient' ], + undefined + ); + newValue = setImmutably( + newValue, + [ 'background', 'backgroundClip' ], + undefined + ); + } + onChange( newValue ); + }; // Elements - const elements = [ - { - name: 'caption', - label: __( 'Captions' ), - showPanel: useHasCaptionPanel( settings ), - }, - { - name: 'button', - label: __( 'Button' ), - showPanel: useHasButtonPanel( settings ), - }, - { - name: 'heading', - label: __( 'Heading' ), - showPanel: useHasHeadingPanel( settings ), - }, - { - name: 'h1', - label: __( 'H1' ), - showPanel: useHasHeadingPanel( settings ), - }, - { - name: 'h2', - label: __( 'H2' ), - showPanel: useHasHeadingPanel( settings ), - }, - { - name: 'h3', - label: __( 'H3' ), - showPanel: useHasHeadingPanel( settings ), - }, - { - name: 'h4', - label: __( 'H4' ), - showPanel: useHasHeadingPanel( settings ), - }, - { - name: 'h5', - label: __( 'H5' ), - showPanel: useHasHeadingPanel( settings ), - }, - { - name: 'h6', - label: __( 'H6' ), - showPanel: useHasHeadingPanel( settings ), - }, - ]; + const showCaptionPanel = useHasCaptionPanel( settings ); + const showButtonPanel = useHasButtonPanel( settings ); + const showHeadingPanel = useHasHeadingPanel( settings ); + const elements = useMemo( + () => [ + { + name: 'caption', + label: __( 'Captions' ), + showPanel: showCaptionPanel, + }, + { + name: 'button', + label: __( 'Button' ), + showPanel: showButtonPanel, + }, + { + name: 'heading', + label: __( 'Heading' ), + showPanel: showHeadingPanel, + }, + { + name: 'h1', + label: __( 'H1' ), + showPanel: showHeadingPanel, + }, + { + name: 'h2', + label: __( 'H2' ), + showPanel: showHeadingPanel, + }, + { + name: 'h3', + label: __( 'H3' ), + showPanel: showHeadingPanel, + }, + { + name: 'h4', + label: __( 'H4' ), + showPanel: showHeadingPanel, + }, + { + name: 'h5', + label: __( 'H5' ), + showPanel: showHeadingPanel, + }, + { + name: 'h6', + label: __( 'H6' ), + showPanel: showHeadingPanel, + }, + ], + [ showCaptionPanel, showButtonPanel, showHeadingPanel ] + ); const resetAllFilter = useCallback( ( previousValue ) => { + // If a text gradient is active (background.gradient + backgroundClip: + // text), it is owned by this panel and must be cleared on reset all. + const isTextGradientSet = + previousValue?.background?.backgroundClip === 'text'; return { ...previousValue, color: undefined, + ...( isTextGradientSet && { + background: { + ...previousValue?.background, + gradient: undefined, + backgroundClip: undefined, + }, + } ), elements: { ...previousValue?.elements, link: { @@ -559,10 +638,14 @@ export default function ColorPanel( { showTextPanel && { key: 'text', label: __( 'Text' ), - hasValue: hasTextColor, - resetValue: resetTextColor, + hasValue: () => hasTextColor() || hasTextGradientValue(), + resetValue: resetTextAndGradient, isShownByDefault: defaultControls.text, - indicators: [ textColor ], + indicators: [ + hasTextGradientValue() + ? userTextGradient ?? textGradient + : textColor, + ], tabs: [ { key: 'text', @@ -571,7 +654,16 @@ export default function ColorPanel( { setValue: setTextColor, userValue: userTextColor, }, - ], + showTextGradient && + hasGradientColors && { + key: 'text-gradient', + label: __( 'Gradient' ), + inheritedValue: textGradient, + setValue: setTextGradient, + userValue: userTextGradient, + isGradient: true, + }, + ].filter( Boolean ), }, showBackgroundPanel && { key: 'background', diff --git a/packages/block-editor/src/components/inspector-controls-tabs/styles-tab.js b/packages/block-editor/src/components/inspector-controls-tabs/styles-tab.js index 664bc454e8f01f..7a97cd55f64534 100644 --- a/packages/block-editor/src/components/inspector-controls-tabs/styles-tab.js +++ b/packages/block-editor/src/components/inspector-controls-tabs/styles-tab.js @@ -87,7 +87,7 @@ const StylesTab = ( { /> diff --git a/packages/block-editor/src/hooks/background.js b/packages/block-editor/src/hooks/background.js index 64e093206af48e..09931e8bc7fbde 100644 --- a/packages/block-editor/src/hooks/background.js +++ b/packages/block-editor/src/hooks/background.js @@ -127,6 +127,8 @@ function BackgroundInspectorControl( { } ) { const resetAllFilter = useCallback( ( attributes ) => { + const prevClip = attributes.style?.background?.backgroundClip; + const isTextGradient = prevClip === 'text'; const updatedClassName = attributes.className?.includes( 'has-background' ) @@ -140,7 +142,13 @@ function BackgroundInspectorControl( { className: updatedClassName, style: cleanEmptyObject( { ...attributes.style, - background: undefined, + background: isTextGradient + ? { + gradient: + attributes.style?.background?.gradient, + backgroundClip: prevClip, + } + : undefined, color: backgroundGradientSupported ? { ...attributes.style?.color, @@ -276,6 +284,9 @@ export function BackgroundImagePanel( { backgroundSize: settings?.background?.backgroundSize && hasBackgroundSupport( name, 'backgroundSize' ), + backgroundClip: + settings?.background?.backgroundClip && + hasBackgroundSupport( name, 'backgroundClip' ), }, }; diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss index 257f6a190065ea..d494884efe5efe 100644 --- a/packages/block-editor/src/style.scss +++ b/packages/block-editor/src/style.scss @@ -1,6 +1,7 @@ @use "@wordpress/base-styles/default-custom-properties"; @use "@wordpress/base-styles/mixins" as *; @use "./autocompleters/style.scss" as *; +@use "./components/background-clip-control/style.scss" as *; @use "./components/background-image-control/style.scss" as *; @use "./components/block-alignment-control/style.scss" as *; @use "./components/block-canvas/style.scss" as *; diff --git a/packages/block-library/src/heading/block.json b/packages/block-library/src/heading/block.json index 7bc491e1658b1b..57871fe27be51d 100644 --- a/packages/block-library/src/heading/block.json +++ b/packages/block-library/src/heading/block.json @@ -36,6 +36,11 @@ "style": true, "width": true }, + "background": { + "backgroundClip": true, + "backgroundImage": true, + "gradient": true + }, "color": { "gradients": true, "link": true, diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json index 556c2870557f7c..2c1891f2d06602 100644 --- a/packages/block-library/src/paragraph/block.json +++ b/packages/block-library/src/paragraph/block.json @@ -37,6 +37,10 @@ "style": true, "width": true }, + "background": { + "backgroundClip": true, + "gradient": true + }, "color": { "gradients": true, "link": true, diff --git a/packages/global-styles-ui/src/screen-block.tsx b/packages/global-styles-ui/src/screen-block.tsx index 3918403cdbb39e..9f3e61e4964be9 100644 --- a/packages/global-styles-ui/src/screen-block.tsx +++ b/packages/global-styles-ui/src/screen-block.tsx @@ -187,6 +187,12 @@ function ScreenBlock( { name, variation }: ScreenBlockProps ) { disableAspectRatio = true; } + // In global styles, enable backgroundClip if the block declares support + // for it, even when the theme hasn't opted in via settings. + const enableBackgroundClip = + ! settingsForBlockElement?.background?.backgroundClip && + blockType?.supports?.background?.backgroundClip; + const settings = useMemo( () => { const updatedSettings = structuredClone( settingsForBlockElement ); if ( disableBlockGap ) { @@ -195,8 +201,19 @@ function ScreenBlock( { name, variation }: ScreenBlockProps ) { if ( disableAspectRatio ) { updatedSettings.dimensions.aspectRatio = false; } + if ( enableBackgroundClip ) { + updatedSettings.background = { + ...updatedSettings.background, + backgroundClip: true, + }; + } return updatedSettings; - }, [ settingsForBlockElement, disableBlockGap, disableAspectRatio ] ); + }, [ + settingsForBlockElement, + disableBlockGap, + disableAspectRatio, + enableBackgroundClip, + ] ); const blockVariations = useBlockVariations( name ); const hasBackgroundPanel = useHasBackgroundPanel( settings ); @@ -383,6 +400,9 @@ function ScreenBlock( { name, variation }: ScreenBlockProps ) { value={ style } onChange={ setStyle } settings={ settings } + defaultControls={ { + backgroundImage: true, + } } defaultValues={ BACKGROUND_BLOCK_DEFAULT_VALUES } /> ) } diff --git a/test/e2e/specs/editor/various/contrast-checker.spec.js b/test/e2e/specs/editor/various/contrast-checker.spec.js index 3a3f49b4e99923..e4acd005a4d81e 100644 --- a/test/e2e/specs/editor/various/contrast-checker.spec.js +++ b/test/e2e/specs/editor/various/contrast-checker.spec.js @@ -35,6 +35,7 @@ test.describe( 'Contrast Checker', () => { } ); const backgroundButton = editorSettings.getByRole( 'button', { name: 'Background', + exact: true, } ); await expect( textButton ).toBeVisible(); @@ -102,6 +103,7 @@ test.describe( 'Contrast Checker', () => { } ); const backgroundButton = editorSettings.getByRole( 'button', { name: 'Background', + exact: true, } ); await expect( textButton ).toBeVisible(); await textButton.click(); @@ -143,6 +145,7 @@ test.describe( 'Contrast Checker', () => { } ); const backgroundButton = editorSettings.getByRole( 'button', { name: 'Background', + exact: true, } ); await expect( textButton ).toBeVisible(); await textButton.click(); @@ -191,6 +194,7 @@ test.describe( 'Contrast Checker', () => { // Set background to black first const backgroundButton = editorSettings.getByRole( 'button', { name: 'Background', + exact: true, } ); await backgroundButton.click(); await page.getByRole( 'option', { name: 'Black' } ).click(); @@ -246,6 +250,7 @@ test.describe( 'Contrast Checker', () => { // Set background to black (poor contrast with black text) const backgroundButton = editorSettings.getByRole( 'button', { name: 'Background', + exact: true, } ); await backgroundButton.click(); await page.getByRole( 'option', { name: 'Black' } ).click(); @@ -286,6 +291,7 @@ test.describe( 'Contrast Checker', () => { // Set background to white (good contrast with black text) const backgroundButton = editorSettings.getByRole( 'button', { name: 'Background', + exact: true, } ); await backgroundButton.click(); await page.getByRole( 'option', { name: 'White' } ).click();