diff --git a/packages/block-editor/src/components/colors-gradients/panel-color-gradient-settings.native.js b/packages/block-editor/src/components/colors-gradients/panel-color-gradient-settings.native.js index 548137215fafc3..b00b643dfb99a4 100644 --- a/packages/block-editor/src/components/colors-gradients/panel-color-gradient-settings.native.js +++ b/packages/block-editor/src/components/colors-gradients/panel-color-gradient-settings.native.js @@ -21,6 +21,7 @@ export default function PanelColorGradientSettings( { settings, title } ) { return settings.map( ( { onColorChange, + onColorCleared, colorValue, onGradientChange, gradientValue, @@ -33,6 +34,7 @@ export default function PanelColorGradientSettings( { settings, title } ) { colorValue: gradientValue || colorValue, gradientValue, onGradientChange, + onColorCleared, label, } ); } } diff --git a/packages/block-library/src/cover/edit.native.js b/packages/block-library/src/cover/edit.native.js index 56437afde080da..bffb94c7655e35 100644 --- a/packages/block-library/src/cover/edit.native.js +++ b/packages/block-library/src/cover/edit.native.js @@ -40,8 +40,9 @@ import { MediaPlaceholder, MediaUpload, MediaUploadProgress, - withColors, - __experimentalUseGradient, + getColorObjectByColorValue, + getColorObjectByAttributeValues, + getGradientValueBySlug, useSetting, store as blockEditorStore, } from '@wordpress/block-editor'; @@ -83,13 +84,13 @@ const Cover = ( { getStylesFromColorScheme, isParentSelected, onFocus, - overlayColor, setAttributes, openGeneralSidebar, closeSettingsBottomSheet, isSelected, selectBlock, blockWidth, + hasInnerBlocks, } ) => { const { backgroundType, @@ -103,6 +104,9 @@ const Cover = ( { minHeightUnit = 'px', allowedBlocks, templateLock, + customGradient, + gradient, + overlayColor, } = attributes; const [ isScreenReaderEnabled, setIsScreenReaderEnabled ] = useState( false @@ -145,18 +149,24 @@ const Cover = ( { const coverDefaultPalette = { colors: colorsDefault.slice( 0, THEME_COLORS_COUNT ), }; - - const { gradientValue } = __experimentalUseGradient(); + const gradients = useSetting( 'color.gradients' ) || []; + const gradientValue = + customGradient || getGradientValueBySlug( gradients, gradient ); + const overlayColorValue = getColorObjectByAttributeValues( + colorsDefault, + overlayColor + ); const hasBackground = !! ( url || ( style && style.color && style.color.background ) || attributes.overlayColor || - overlayColor.color || + overlayColorValue.color || + customOverlayColor || gradientValue ); - const hasOnlyColorBackground = ! url && hasBackground; + const hasOnlyColorBackground = ! url && ( hasBackground || hasInnerBlocks ); const [ isCustomColorPickerShowing, @@ -225,10 +235,12 @@ const Cover = ( { }, [ closeSettingsBottomSheet ] ); function setColor( color ) { + const colorValue = getColorObjectByColorValue( colorsDefault, color ); + setAttributes( { // clear all related attributes (only one should be set) - overlayColor: undefined, - customOverlayColor: color, + overlayColor: colorValue?.slug ?? undefined, + customOverlayColor: ( ! colorValue?.slug && color ) ?? undefined, gradient: undefined, customGradient: undefined, } ); @@ -251,12 +263,12 @@ const Cover = ( { ! gradientValue && { backgroundColor: customOverlayColor || - overlayColor?.color || + overlayColorValue?.color || style?.color?.background || styles.overlay?.color, }, // While we don't support theme colors we add a default bg color - ! overlayColor.color && ! url ? backgroundColor : {}, + ! overlayColorValue.color && ! url ? backgroundColor : {}, isImage && isParentSelected && ! isUploadInProgress && @@ -432,7 +444,10 @@ const Cover = ( { ); - if ( ! hasBackground || isCustomColorPickerShowing ) { + if ( + ( ! hasBackground && ! hasInnerBlocks ) || + isCustomColorPickerShowing + ) { return ( { isCustomColorPickerShowing && colorPickerControls } @@ -575,17 +590,21 @@ const Cover = ( { }; export default compose( [ - withColors( { overlayColor: 'background-color' } ), withSelect( ( select, { clientId } ) => { - const { getSelectedBlockClientId } = select( blockEditorStore ); + const { getSelectedBlockClientId, getBlock } = select( + blockEditorStore + ); const selectedBlockClientId = getSelectedBlockClientId(); const { getSettings } = select( blockEditorStore ); + const hasInnerBlocks = getBlock( clientId )?.innerBlocks.length > 0; + return { settings: getSettings(), isParentSelected: selectedBlockClientId === clientId, + hasInnerBlocks, }; } ), withDispatch( ( dispatch, { clientId } ) => { diff --git a/packages/block-library/src/cover/overlay-color-settings.native.js b/packages/block-library/src/cover/overlay-color-settings.native.js index 08fa32878f4c6d..92e0739f119a42 100644 --- a/packages/block-library/src/cover/overlay-color-settings.native.js +++ b/packages/block-library/src/cover/overlay-color-settings.native.js @@ -74,6 +74,15 @@ function OverlayColorSettings( { } }; + const onColorCleared = () => { + setAttributes( { + overlayColor: undefined, + customOverlayColor: undefined, + gradient: undefined, + customGradient: undefined, + } ); + }; + return [ { label: __( 'Color' ), @@ -81,6 +90,7 @@ function OverlayColorSettings( { colorValue, gradientValue, onGradientChange, + onColorCleared, }, ]; }, [ colorValue, gradientValue, colors, gradients ] ); diff --git a/packages/block-library/src/cover/test/__snapshots__/edit.native.js.snap b/packages/block-library/src/cover/test/__snapshots__/edit.native.js.snap new file mode 100644 index 00000000000000..0e9e5ad155faff --- /dev/null +++ b/packages/block-library/src/cover/test/__snapshots__/edit.native.js.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`color settings clears the selected overlay color and mantains the inner blocks 1`] = ` +" +
+

+
+" +`; + +exports[`color settings sets a color for the overlay background when the placeholder is visible 1`] = ` +" +
+

+
+" +`; + +exports[`color settings sets a gradient overlay background when a solid background was already selected 1`] = ` +" +
+

+
+" +`; + +exports[`color settings toggles between solid colors and gradients 1`] = ` +" +
+

+
+" +`; diff --git a/packages/block-library/src/cover/test/edit.native.js b/packages/block-library/src/cover/test/edit.native.js index eef5346026a0ed..c09b21f3eb8ff3 100644 --- a/packages/block-library/src/cover/test/edit.native.js +++ b/packages/block-library/src/cover/test/edit.native.js @@ -1,15 +1,22 @@ /** * External dependencies */ -import { Image } from 'react-native'; -import { render, fireEvent, waitFor } from 'test/helpers'; +import { AccessibilityInfo, Image } from 'react-native'; +import { + getEditorHtml, + initializeEditor, + render, + fireEvent, + waitFor, + within, +} from 'test/helpers'; /** * WordPress dependencies */ import { BottomSheetSettings, BlockEdit } from '@wordpress/block-editor'; import { SlotFillProvider } from '@wordpress/components'; -import { registerBlockType, unregisterBlockType } from '@wordpress/blocks'; +import { setDefaultBlockName, unregisterBlockType } from '@wordpress/blocks'; import { requestMediaPicker, requestMediaEditor, @@ -19,7 +26,9 @@ import { * Internal dependencies */ import { IMAGE_BACKGROUND_TYPE } from '../shared'; -import { metadata, settings, name } from '../index'; +import * as paragraph from '../../paragraph'; +import * as cover from '..'; +import { registerBlock } from '../..'; // Avoid errors due to mocked stylesheet files missing required selectors jest.mock( '@wordpress/compose', () => ( { @@ -33,10 +42,25 @@ jest.mock( '@wordpress/compose', () => ( { ) ), } ) ); +const COVER_BLOCK_PLACEHOLDER_HTML = ` +
+`; +const COVER_BLOCK_SOLID_COLOR_HTML = ` +
+

+
+`; + +const COLOR_PINK = '#f78da7'; +const COLOR_RED = '#cf2e2e'; +const COLOR_GRAY = '#abb8c3'; +const GRADIENT_GREEN = + 'linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%)'; + // Simplified tree to render Cover edit within slot const CoverEdit = ( props ) => ( - + ); @@ -55,26 +79,24 @@ beforeAll( () => { const getSizeSpy = jest.spyOn( Image, 'getSize' ); getSizeSpy.mockImplementation( ( _url, callback ) => callback( 300, 200 ) ); + AccessibilityInfo.isScreenReaderEnabled.mockResolvedValue( + Promise.resolve( true ) + ); + // Register required blocks - registerBlockType( name, { - ...metadata, - ...settings, - } ); - registerBlockType( 'core/paragraph', { - category: 'text', - title: 'Paragraph', - edit: () => {}, - save: () => {}, - } ); + registerBlock( paragraph ); + registerBlock( cover ); + setDefaultBlockName( paragraph.name ); } ); afterAll( () => { // Restore mocks Image.getSize.mockRestore(); + AccessibilityInfo.isScreenReaderEnabled.mockReset(); // Clean up registered blocks - unregisterBlockType( name ); - unregisterBlockType( 'core/paragraph' ); + unregisterBlockType( paragraph.name ); + unregisterBlockType( cover.name ); } ); describe( 'when no media is attached', () => { @@ -270,3 +292,248 @@ describe( 'when an image is attached', () => { ); } ); } ); + +describe( 'color settings', () => { + it( 'sets a color for the overlay background when the placeholder is visible', async () => { + const { getByTestId, getByA11yLabel } = await initializeEditor( { + initialHtml: COVER_BLOCK_PLACEHOLDER_HTML, + } ); + + const block = await waitFor( () => + getByA11yLabel( 'Cover block. Empty' ) + ); + expect( block ).toBeDefined(); + + // Select a color from the placeholder palette + const colorPalette = await waitFor( () => + getByTestId( 'color-palette' ) + ); + const colorButton = within( colorPalette ).getByTestId( COLOR_PINK ); + + expect( colorButton ).toBeDefined(); + fireEvent.press( colorButton ); + + // Wait for the block to be created + const coverBlockWithOverlay = await waitFor( () => + getByA11yLabel( /Cover Block\. Row 1/ ) + ); + fireEvent.press( coverBlockWithOverlay ); + + // Open Block Settings + const settingsButton = await waitFor( () => + getByA11yLabel( 'Open Settings' ) + ); + fireEvent.press( settingsButton ); + + // Wait for Block Settings to be visible + const blockSettingsModal = getByTestId( 'block-settings-modal' ); + await waitFor( () => blockSettingsModal.props.isVisible ); + + // Open the overlay color settings + const colorOverlay = await waitFor( () => + getByA11yLabel( 'Color. Empty' ) + ); + expect( colorOverlay ).toBeDefined(); + fireEvent.press( colorOverlay ); + + // Find the selected color + const colorPaletteButton = await waitFor( () => + getByTestId( COLOR_PINK ) + ); + expect( colorPaletteButton ).toBeDefined(); + + // Select another color + const newColorButton = await waitFor( () => getByTestId( COLOR_RED ) ); + fireEvent.press( newColorButton ); + + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + + it( 'sets a gradient overlay background when a solid background was already selected', async () => { + const { getByTestId, getByA11yLabel } = await initializeEditor( { + initialHtml: COVER_BLOCK_SOLID_COLOR_HTML, + } ); + + // Wait for the block to be created + const coverBlock = await waitFor( () => + getByA11yLabel( /Cover Block\. Row 1/ ) + ); + expect( coverBlock ).toBeDefined(); + fireEvent.press( coverBlock ); + + // Open Block Settings + const settingsButton = await waitFor( () => + getByA11yLabel( 'Open Settings' ) + ); + fireEvent.press( settingsButton ); + + // Wait for Block Settings to be visible + const blockSettingsModal = getByTestId( 'block-settings-modal' ); + await waitFor( () => blockSettingsModal.props.isVisible ); + + // Open the overlay color settings + const colorOverlay = await waitFor( () => + getByA11yLabel( 'Color. Empty' ) + ); + expect( colorOverlay ).toBeDefined(); + fireEvent.press( colorOverlay ); + + // Find the selected color + const colorButton = await waitFor( () => getByTestId( COLOR_GRAY ) ); + expect( colorButton ).toBeDefined(); + + // Open the gradients + const gradientsButton = await waitFor( () => + getByA11yLabel( 'Gradient' ) + ); + expect( gradientsButton ).toBeDefined(); + + fireEvent( gradientsButton, 'layout', { + nativeEvent: { layout: { width: 80, height: 26 } }, + } ); + fireEvent.press( gradientsButton ); + + // Find the gradient color + const newGradientButton = await waitFor( () => + getByTestId( GRADIENT_GREEN ) + ); + expect( newGradientButton ).toBeDefined(); + fireEvent.press( newGradientButton ); + + // Dismiss the Block Settings modal + fireEvent( blockSettingsModal, 'backdropPress' ); + + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + + it( 'toggles between solid colors and gradients', async () => { + const { getByTestId, getByA11yLabel } = await initializeEditor( { + initialHtml: COVER_BLOCK_PLACEHOLDER_HTML, + } ); + + const block = await waitFor( () => + getByA11yLabel( 'Cover block. Empty' ) + ); + expect( block ).toBeDefined(); + + // Select a color from the placeholder palette + const colorPalette = await waitFor( () => + getByTestId( 'color-palette' ) + ); + const colorButton = within( colorPalette ).getByTestId( COLOR_PINK ); + + expect( colorButton ).toBeDefined(); + fireEvent.press( colorButton ); + + // Wait for the block to be created + const coverBlockWithOverlay = await waitFor( () => + getByA11yLabel( /Cover Block\. Row 1/ ) + ); + fireEvent.press( coverBlockWithOverlay ); + + // Open Block Settings + const settingsButton = await waitFor( () => + getByA11yLabel( 'Open Settings' ) + ); + fireEvent.press( settingsButton ); + + // Wait for Block Settings to be visible + const blockSettingsModal = getByTestId( 'block-settings-modal' ); + await waitFor( () => blockSettingsModal.props.isVisible ); + + // Open the overlay color settings + const colorOverlay = await waitFor( () => + getByA11yLabel( 'Color. Empty' ) + ); + expect( colorOverlay ).toBeDefined(); + fireEvent.press( colorOverlay ); + + // Find the selected color + const colorPaletteButton = await waitFor( () => + getByTestId( COLOR_PINK ) + ); + expect( colorPaletteButton ).toBeDefined(); + + // Select another color + const newColorButton = await waitFor( () => getByTestId( COLOR_RED ) ); + fireEvent.press( newColorButton ); + + // Open the gradients + const gradientsButton = await waitFor( () => + getByA11yLabel( 'Gradient' ) + ); + expect( gradientsButton ).toBeDefined(); + + fireEvent( gradientsButton, 'layout', { + nativeEvent: { layout: { width: 80, height: 26 } }, + } ); + fireEvent.press( gradientsButton ); + + // Find the gradient color + const newGradientButton = await waitFor( () => + getByTestId( GRADIENT_GREEN ) + ); + expect( newGradientButton ).toBeDefined(); + fireEvent.press( newGradientButton ); + + // Go back to the settings list + fireEvent.press( await waitFor( () => getByA11yLabel( 'Go back' ) ) ); + + // Find the color setting + const colorSetting = await waitFor( () => + getByA11yLabel( 'Color. Empty' ) + ); + expect( colorSetting ).toBeDefined(); + fireEvent.press( colorSetting ); + + // Dismiss the Block Settings modal + fireEvent( blockSettingsModal, 'backdropPress' ); + + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + + it( 'clears the selected overlay color and mantains the inner blocks', async () => { + const { + getByTestId, + getByA11yLabel, + getByText, + } = await initializeEditor( { + initialHtml: COVER_BLOCK_SOLID_COLOR_HTML, + } ); + + // Wait for the block to be created + const coverBlock = await waitFor( () => + getByA11yLabel( /Cover Block\. Row 1/ ) + ); + expect( coverBlock ).toBeDefined(); + fireEvent.press( coverBlock ); + + // Open Block Settings + const settingsButton = await waitFor( () => + getByA11yLabel( 'Open Settings' ) + ); + fireEvent.press( settingsButton ); + + // Wait for Block Settings to be visible + const blockSettingsModal = getByTestId( 'block-settings-modal' ); + await waitFor( () => blockSettingsModal.props.isVisible ); + + // Open the overlay color settings + const colorOverlay = await waitFor( () => + getByA11yLabel( 'Color. Empty' ) + ); + expect( colorOverlay ).toBeDefined(); + fireEvent.press( colorOverlay ); + + // Find the selected color + const colorButton = await waitFor( () => getByTestId( COLOR_GRAY ) ); + expect( colorButton ).toBeDefined(); + + // Reset the selected color + const resetButton = await waitFor( () => getByText( 'Reset' ) ); + expect( resetButton ).toBeDefined(); + fireEvent.press( resetButton ); + + expect( getEditorHtml() ).toMatchSnapshot(); + } ); +} ); diff --git a/packages/components/src/color-palette/index.native.js b/packages/components/src/color-palette/index.native.js index decfddf44b0ad7..940e0cbbd1d039 100644 --- a/packages/components/src/color-palette/index.native.js +++ b/packages/components/src/color-palette/index.native.js @@ -219,6 +219,7 @@ function ColorPalette( { onScrollBeginDrag={ () => shouldEnableBottomSheetScroll( false ) } onScrollEndDrag={ () => shouldEnableBottomSheetScroll( true ) } ref={ scrollViewRef } + testID="color-palette" > { shouldShowCustomIndicator && ( { @@ -44,6 +45,7 @@ const ColorSettingsMemo = memo( colorValue, gradientValue, onGradientChange, + onColorCleared, label, hideNavigation, } } diff --git a/packages/components/src/mobile/color-settings/palette.screen.native.js b/packages/components/src/mobile/color-settings/palette.screen.native.js index 9c6ae92486538a..011eddf8a6041d 100644 --- a/packages/components/src/mobile/color-settings/palette.screen.native.js +++ b/packages/components/src/mobile/color-settings/palette.screen.native.js @@ -36,6 +36,7 @@ const PaletteScreen = () => { label, onColorChange, onGradientChange, + onColorCleared, colorValue, defaultSettings, hideNavigation = false, @@ -85,6 +86,10 @@ const PaletteScreen = () => { } else { onGradientChange( '' ); } + + if ( onColorCleared ) { + onColorCleared(); + } } function onCustomPress() {