From 98dee61619e8871d8f37fc5f65e26ebbe6f36cae Mon Sep 17 00:00:00 2001 From: Gerardo Date: Wed, 19 Apr 2023 10:05:52 +0200 Subject: [PATCH 01/13] Mobile - Update ColorPanel: Move it to the global styles folder and update code to latest changes from the color hook --- .../global-styles/color-panel.native.js | 207 ++++++++++++++++++ .../src/hooks/color-panel.native.js | 63 ------ 2 files changed, 207 insertions(+), 63 deletions(-) create mode 100644 packages/block-editor/src/components/global-styles/color-panel.native.js delete mode 100644 packages/block-editor/src/hooks/color-panel.native.js diff --git a/packages/block-editor/src/components/global-styles/color-panel.native.js b/packages/block-editor/src/components/global-styles/color-panel.native.js new file mode 100644 index 0000000000000..f329ad0369b46 --- /dev/null +++ b/packages/block-editor/src/components/global-styles/color-panel.native.js @@ -0,0 +1,207 @@ +/** + * WordPress dependencies + */ +import { useSelect } from '@wordpress/data'; +import { useEffect, useState, useMemo, useCallback } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { useGlobalStyles } from '@wordpress/components'; +import { store as blockEditorStore } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import PanelColorGradientSettings from '../colors-gradients/panel-color-gradient-settings'; +import { useColorsPerOrigin, useGradientsPerOrigin } from './hooks'; +import { getValueFromVariable } from './utils'; +import { immutableSet } from '../../utils/object'; +import ContrastChecker from '../contrast-checker'; +import InspectorControls from '../inspector-controls'; +import { + useHasColorPanel, + useHasTextPanel, + useHasBackgroundPanel, +} from './color-panel.js'; + +const ColorPanel = ( { + value, + inheritedValue = value, + onChange, + settings, +} ) => { + const colors = useColorsPerOrigin( settings ); + const gradients = useGradientsPerOrigin( settings ); + const globalStyles = useGlobalStyles(); + + const [ detectedBackgroundColor, setDetectedBackgroundColor ] = useState(); + const [ detectedTextColor, setDetectedTextColor ] = useState(); + + const { baseGlobalStyles } = useSelect( ( select ) => { + const { getSettings } = select( blockEditorStore ); + return { + baseGlobalStyles: + getSettings()?.__experimentalGlobalStylesBaseStyles?.color, + }; + } ); + + const decodeValue = ( rawValue ) => + getValueFromVariable( { settings }, '', rawValue ); + const encodeColorValue = useCallback( + ( colorValue ) => { + const allColors = colors.flatMap( + ( { colors: originColors } ) => originColors + ); + const colorObject = allColors.find( + ( { color } ) => color === colorValue + ); + return colorObject + ? 'var:preset|color|' + colorObject.slug + : colorValue; + }, + [ colors ] + ); + const encodeGradientValue = useCallback( + ( gradientValue ) => { + const allGradients = gradients.flatMap( + ( { gradients: originGradients } ) => originGradients + ); + const gradientObject = allGradients.find( + ( { gradient } ) => gradient === gradientValue + ); + return gradientObject + ? 'var:preset|gradient|' + gradientObject.slug + : gradientValue; + }, + [ gradients ] + ); + + // Text Color + const showTextPanel = useHasTextPanel( settings ); + const textColor = decodeValue( inheritedValue?.color?.text ); + const setTextColor = useCallback( + ( newColor ) => { + onChange( + immutableSet( + value, + [ 'color', 'text' ], + encodeColorValue( newColor ) + ) + ); + }, + [ encodeColorValue, onChange, value ] + ); + const resetTextColor = useCallback( + () => setTextColor( undefined ), + [ setTextColor ] + ); + + // BackgroundColor + const showBackgroundPanel = useHasBackgroundPanel( settings ); + const backgroundColor = decodeValue( inheritedValue?.color?.background ); + const gradient = decodeValue( inheritedValue?.color?.gradient ); + const setBackgroundColor = useCallback( + ( newColor ) => { + const newValue = immutableSet( + value, + [ 'color', 'background' ], + encodeColorValue( newColor ) + ); + newValue.color.gradient = undefined; + onChange( newValue ); + }, + [ encodeColorValue, onChange, value ] + ); + const setGradient = useCallback( + ( newGradient ) => { + const newValue = immutableSet( + value, + [ 'color', 'gradient' ], + encodeGradientValue( newGradient ) + ); + newValue.color.background = undefined; + onChange( newValue ); + }, + [ encodeGradientValue, onChange, value ] + ); + const resetBackground = useCallback( () => { + const newValue = immutableSet( + value, + [ 'color', 'background' ], + undefined + ); + newValue.color.gradient = undefined; + onChange( newValue ); + }, [ onChange, value ] ); + const currentGradients = settings?.color?.gradients; + const withoutGradientsSupport = + Array.isArray( currentGradients ) && currentGradients.length === 0; + + const items = useMemo( + () => + [ + showTextPanel && { + label: __( 'Text' ), + colorValue: textColor, + onColorChange: setTextColor, + onColorCleared: resetTextColor, + }, + showBackgroundPanel && { + label: __( 'Background' ), + colorValue: backgroundColor, + onColorChange: setBackgroundColor, + onColorCleared: resetBackground, + onGradientChange: ! withoutGradientsSupport + ? setGradient + : undefined, + gradientValue: gradient, + }, + ].filter( Boolean ), + [ + backgroundColor, + gradient, + resetBackground, + resetTextColor, + setBackgroundColor, + setGradient, + setTextColor, + showBackgroundPanel, + showTextPanel, + textColor, + withoutGradientsSupport, + ] + ); + + useEffect( () => { + // The following logic is used to determine current text/background colors: + // 1. The globalStyles object is queried to determine whether a color has been + // set via a block's settings. + // 2. If a block-based theme is in use and no globalStyles exist, the theme's + // default/base colors are used. + // 3. If no globalStyles exist and a theme isn't block-based, there is no way + // to determine the default text/background color and the checker won't run. + const currentDetectedTextColor = + globalStyles?.color || baseGlobalStyles?.text; + const currentDetectedBackgroundColor = + globalStyles?.backgroundColor || baseGlobalStyles?.background; + + setDetectedTextColor( currentDetectedTextColor ); + setDetectedBackgroundColor( currentDetectedBackgroundColor ); + }, [ globalStyles, baseGlobalStyles ] ); + + return ( + + + + + + ); +}; + +export { useHasColorPanel }; +export default ColorPanel; diff --git a/packages/block-editor/src/hooks/color-panel.native.js b/packages/block-editor/src/hooks/color-panel.native.js deleted file mode 100644 index ff994899bbf06..0000000000000 --- a/packages/block-editor/src/hooks/color-panel.native.js +++ /dev/null @@ -1,63 +0,0 @@ -/** - * WordPress dependencies - */ -import { useSelect } from '@wordpress/data'; -import { useEffect, useState } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; -import { useGlobalStyles } from '@wordpress/components'; -import { store as blockEditorStore } from '@wordpress/block-editor'; - -/** - * Internal dependencies - */ -import PanelColorGradientSettings from '../components/colors-gradients/panel-color-gradient-settings'; -import ContrastChecker from '../components/contrast-checker'; -import InspectorControls from '../components/inspector-controls'; - -const ColorPanel = ( { settings } ) => { - const globalStyles = useGlobalStyles(); - - const [ detectedBackgroundColor, setDetectedBackgroundColor ] = useState(); - const [ detectedTextColor, setDetectedTextColor ] = useState(); - - const { baseGlobalStyles } = useSelect( ( select ) => { - const { getSettings } = select( blockEditorStore ); - return { - baseGlobalStyles: - getSettings()?.__experimentalGlobalStylesBaseStyles?.color, - }; - } ); - - useEffect( () => { - // The following logic is used to determine current text/background colors: - // 1. The globalStyles object is queried to determine whether a color has been - // set via a block's settings. - // 2. If a block-based theme is in use and no globalStyles exist, the theme's - // default/base colors are used. - // 3. If no globalStyles exist and a theme isn't block-based, there is no way - // to determine the default text/background color and the checker won't run. - const textColor = globalStyles?.color || baseGlobalStyles?.text; - const backgroundColor = - globalStyles?.backgroundColor || baseGlobalStyles?.background; - - setDetectedTextColor( textColor ); - setDetectedBackgroundColor( backgroundColor ); - }, [ globalStyles, baseGlobalStyles ] ); - - return ( - - - - - - ); -}; - -export default ColorPanel; From 7c6a8e4f4c0ff3b552f2e6e5a27a629ef7833c0a Mon Sep 17 00:00:00 2001 From: Gerardo Date: Wed, 19 Apr 2023 10:06:50 +0200 Subject: [PATCH 02/13] Mobile - ColorSettings Palette: Avoid resetting color/gradient changes since this is now being handled by the ColorPanel component --- .../src/mobile/color-settings/palette.screen.native.js | 7 ------- 1 file changed, 7 deletions(-) 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 021df19ee4db5..bc7187fd092b8 100644 --- a/packages/components/src/mobile/color-settings/palette.screen.native.js +++ b/packages/components/src/mobile/color-settings/palette.screen.native.js @@ -78,22 +78,15 @@ const PaletteScreen = () => { setCurrentValue( color ); if ( isSolidSegment && onColorChange && onGradientChange ) { onColorChange( color ); - onGradientChange( '' ); } else if ( isSolidSegment && onColorChange ) { onColorChange( color ); } else if ( ! isSolidSegment && onGradientChange ) { onGradientChange( color ); - onColorChange( '' ); } }; function onClear() { setCurrentValue( undefined ); - if ( isSolidSegment ) { - onColorChange( '' ); - } else { - onGradientChange( '' ); - } if ( onColorCleared ) { onColorCleared(); From 7473e6db06ec15b83e2c12a312520085e61e5a62 Mon Sep 17 00:00:00 2001 From: Gerardo Date: Wed, 19 Apr 2023 10:07:54 +0200 Subject: [PATCH 03/13] Mobile - CustomGradientPicker - Update code to get the correct gradient data due to the latest color hook changes --- .../components/src/custom-gradient-picker/index.native.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/components/src/custom-gradient-picker/index.native.js b/packages/components/src/custom-gradient-picker/index.native.js index 474df2bd18b9b..b2e0b4432f0ca 100644 --- a/packages/components/src/custom-gradient-picker/index.native.js +++ b/packages/components/src/custom-gradient-picker/index.native.js @@ -25,7 +25,7 @@ function CustomGradientPicker( { setColor, currentValue, isGradientColor } ) { const [ currentColor, setCurrentColor ] = useState( currentValue ); const { getGradientType, gradients, gradientOptions } = colorsUtils; - const gradientAST = getGradientAstWithDefault( currentColor ); + const gradientWithDefault = getGradientAstWithDefault( currentColor ); const gradientType = getGradientType( currentColor ); function isLinearGradient( type ) { @@ -33,6 +33,7 @@ function CustomGradientPicker( { setColor, currentValue, isGradientColor } ) { } function getGradientColor( type ) { + const { gradientAST } = gradientWithDefault; const { orientation, ...restGradientAST } = gradientAST; if ( orientation ) { @@ -64,6 +65,7 @@ function CustomGradientPicker( { setColor, currentValue, isGradientColor } ) { } function setGradientAngle( value ) { + const { gradientAST } = gradientWithDefault; const gradientColor = serializeGradient( { ...gradientAST, orientation: { @@ -79,6 +81,7 @@ function CustomGradientPicker( { setColor, currentValue, isGradientColor } ) { } function getGradientAngle() { + const { gradientAST } = gradientWithDefault; return gradientAST?.orientation?.value ?? DEFAULT_LINEAR_GRADIENT_ANGLE; } return ( From d3326ef2150dae4ecb73e3639a8e26ce17f8aec9 Mon Sep 17 00:00:00 2001 From: Gerardo Date: Wed, 19 Apr 2023 13:00:25 +0200 Subject: [PATCH 04/13] Mobile - ColorPalette - Add accessibilityLabel with the color value --- packages/components/src/color-palette/index.native.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/components/src/color-palette/index.native.js b/packages/components/src/color-palette/index.native.js index ffb063f7f23f4..70f41447c5f92 100644 --- a/packages/components/src/color-palette/index.native.js +++ b/packages/components/src/color-palette/index.native.js @@ -260,6 +260,7 @@ function ColorPalette( { selected: isSelected( color ), } } accessibilityHint={ color } + accessibilityLabel={ color } testID={ color } > Date: Wed, 19 Apr 2023 13:00:48 +0200 Subject: [PATCH 05/13] Mobile - Segmented Control - Add optional chaining --- .../components/src/mobile/segmented-control/index.native.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/src/mobile/segmented-control/index.native.js b/packages/components/src/mobile/segmented-control/index.native.js index b3270e37120e3..1a218fcece100 100644 --- a/packages/components/src/mobile/segmented-control/index.native.js +++ b/packages/components/src/mobile/segmented-control/index.native.js @@ -140,8 +140,8 @@ const SegmentedControls = ( { styles.selectedDark ); - const width = segmentsDimensions[ activeSegmentIndex ].width; - const height = segmentsDimensions[ activeSegmentIndex ].height; + const width = segmentsDimensions[ activeSegmentIndex ]?.width; + const height = segmentsDimensions[ activeSegmentIndex ]?.height; const outlineStyle = [ styles.outline, isIOS && styles.outlineIOS ]; From d74891ef1c78032bb719a9de812fddd33c5866cf Mon Sep 17 00:00:00 2001 From: Gerardo Date: Wed, 19 Apr 2023 13:01:12 +0200 Subject: [PATCH 06/13] Mobile - Paragraph block - Add tests for color customization --- .../src/paragraph/test/edit.native.js | 201 ++++++++++++++++++ 1 file changed, 201 insertions(+) diff --git a/packages/block-library/src/paragraph/test/edit.native.js b/packages/block-library/src/paragraph/test/edit.native.js index a4dcfdac92041..2fb48e97be92e 100644 --- a/packages/block-library/src/paragraph/test/edit.native.js +++ b/packages/block-library/src/paragraph/test/edit.native.js @@ -11,6 +11,7 @@ import { initializeEditor, render, setupCoreBlocks, + waitFor, within, } from 'test/helpers'; import Clipboard from '@react-native-clipboard/clipboard'; @@ -374,4 +375,204 @@ describe( 'Paragraph block', () => { " ` ); } ); + + it( 'should set a text color', async () => { + // Arrange + const screen = await initializeEditor(); + await addBlock( screen, 'Paragraph' ); + + // Act + const paragraphBlock = getBlock( screen, 'Paragraph' ); + fireEvent.press( paragraphBlock ); + const paragraphTextInput = + within( paragraphBlock ).getByPlaceholderText( 'Start writing…' ); + typeInRichText( + paragraphTextInput, + 'A quick brown fox jumps over the lazy dog.' + ); + // Open Block Settings. + fireEvent.press( screen.getByLabelText( 'Open Settings' ) ); + + // Wait for Block Settings to be visible. + const blockSettingsModal = screen.getByTestId( 'block-settings-modal' ); + await waitFor( () => blockSettingsModal.props.isVisible ); + + // Open Text color settings + fireEvent.press( screen.getByLabelText( 'Text, Default' ) ); + + // Tap one color + fireEvent.press( screen.getByLabelText( '#f78da7' ) ); + + // Dismiss the Block Settings modal. + fireEvent( blockSettingsModal, 'backdropPress' ); + + // Assert + expect( getEditorHtml() ).toMatchInlineSnapshot( ` + " +

A quick brown fox jumps over the lazy dog.

+ " + ` ); + } ); + + it( 'should set a background color', async () => { + // Arrange + const screen = await initializeEditor(); + await addBlock( screen, 'Paragraph' ); + + // Act + const paragraphBlock = getBlock( screen, 'Paragraph' ); + fireEvent.press( paragraphBlock ); + const paragraphTextInput = + within( paragraphBlock ).getByPlaceholderText( 'Start writing…' ); + typeInRichText( + paragraphTextInput, + 'A quick brown fox jumps over the lazy dog.' + ); + // Open Block Settings. + fireEvent.press( screen.getByLabelText( 'Open Settings' ) ); + + // Wait for Block Settings to be visible. + const blockSettingsModal = screen.getByTestId( 'block-settings-modal' ); + await waitFor( () => blockSettingsModal.props.isVisible ); + + // Open Background color settings + fireEvent.press( screen.getByLabelText( 'Background, Default' ) ); + + // Tap one color + fireEvent.press( screen.getByLabelText( '#ff6900' ) ); + + // Dismiss the Block Settings modal. + fireEvent( blockSettingsModal, 'backdropPress' ); + + // Assert + expect( getEditorHtml() ).toMatchInlineSnapshot( ` + " +

A quick brown fox jumps over the lazy dog.

+ " + ` ); + } ); + + it( 'should set a text and background color', async () => { + // Arrange + const screen = await initializeEditor(); + await addBlock( screen, 'Paragraph' ); + + // Act + const paragraphBlock = getBlock( screen, 'Paragraph' ); + fireEvent.press( paragraphBlock ); + const paragraphTextInput = + within( paragraphBlock ).getByPlaceholderText( 'Start writing…' ); + typeInRichText( + paragraphTextInput, + 'A quick brown fox jumps over the lazy dog.' + ); + // Open Block Settings. + fireEvent.press( screen.getByLabelText( 'Open Settings' ) ); + + // Wait for Block Settings to be visible. + const blockSettingsModal = screen.getByTestId( 'block-settings-modal' ); + await waitFor( () => blockSettingsModal.props.isVisible ); + + // Open Text color settings + fireEvent.press( screen.getByLabelText( 'Text, Default' ) ); + + // Tap one color + fireEvent.press( screen.getByLabelText( '#ffffff' ) ); + + // Go back to the settings menu + fireEvent.press( screen.getByLabelText( 'Go back' ) ); + + // Open Background color settings + fireEvent.press( screen.getByLabelText( 'Background, Default' ) ); + + // Tap one color + fireEvent.press( screen.getByLabelText( '#ff6900' ) ); + + // Dismiss the Block Settings modal. + fireEvent( blockSettingsModal, 'backdropPress' ); + + // Assert + expect( getEditorHtml() ).toMatchInlineSnapshot( ` + " +

A quick brown fox jumps over the lazy dog.

+ " + ` ); + } ); + + it( 'should remove text and background colors', async () => { + // Arrange + const screen = await initializeEditor( { + initialHtml: ` +

A quick brown fox jumps over the lazy dog.

+ `, + } ); + + // Act + const paragraphBlock = getBlock( screen, 'Paragraph' ); + fireEvent.press( paragraphBlock ); + + // Open Block Settings. + fireEvent.press( screen.getByLabelText( 'Open Settings' ) ); + + // Wait for Block Settings to be visible. + const blockSettingsModal = screen.getByTestId( 'block-settings-modal' ); + await waitFor( () => blockSettingsModal.props.isVisible ); + + // Open Text color settings + fireEvent.press( screen.getByLabelText( 'Text. Empty' ) ); + + // Reset color + fireEvent.press( await screen.findByText( 'Reset' ) ); + + // Go back to the settings menu + fireEvent.press( screen.getByLabelText( 'Go back' ) ); + + // Open Background color settings + fireEvent.press( screen.getByLabelText( 'Background. Empty' ) ); + + // Reset color + fireEvent.press( await screen.findByText( 'Reset' ) ); + + // Dismiss the Block Settings modal. + fireEvent( blockSettingsModal, 'backdropPress' ); + + // Assert + expect( getEditorHtml() ).toMatchInlineSnapshot( ` + " +

A quick brown fox jumps over the lazy dog.

+ " + ` ); + } ); + + it( 'should not have a gradient background color option', async () => { + // Arrange + const screen = await initializeEditor(); + await addBlock( screen, 'Paragraph' ); + + // Act + const paragraphBlock = getBlock( screen, 'Paragraph' ); + fireEvent.press( paragraphBlock ); + const paragraphTextInput = + within( paragraphBlock ).getByPlaceholderText( 'Start writing…' ); + typeInRichText( + paragraphTextInput, + 'A quick brown fox jumps over the lazy dog.' + ); + // Open Block Settings. + fireEvent.press( screen.getByLabelText( 'Open Settings' ) ); + + // Wait for Block Settings to be visible. + const blockSettingsModal = screen.getByTestId( 'block-settings-modal' ); + await waitFor( () => blockSettingsModal.props.isVisible ); + + // Open Background color settings + fireEvent.press( screen.getByLabelText( 'Background, Default' ) ); + + // Assert + const colorButton = screen.getByLabelText( '#ff6900' ); + expect( colorButton ).toBeDefined(); + + const gradientButton = screen.queryByLabelText( 'Gradient' ); + expect( gradientButton ).toBeNull(); + } ); } ); From 5200b4310a3b675a33376859f096465681b8de93 Mon Sep 17 00:00:00 2001 From: Gerardo Date: Wed, 19 Apr 2023 13:01:25 +0200 Subject: [PATCH 07/13] Mobile - Heading block - Add tests for color customization --- .../test/__snapshots__/index.native.js.snap | 12 ++++ .../src/heading/test/index.native.js | 71 +++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/packages/block-library/src/heading/test/__snapshots__/index.native.js.snap b/packages/block-library/src/heading/test/__snapshots__/index.native.js.snap index 0e0febb84d549..308aa8ac729bf 100644 --- a/packages/block-library/src/heading/test/__snapshots__/index.native.js.snap +++ b/packages/block-library/src/heading/test/__snapshots__/index.native.js.snap @@ -5,3 +5,15 @@ exports[`Heading block inserts block 1`] = `

" `; + +exports[`Heading block should set a background color 1`] = ` +" +

A quick brown fox jumps over the lazy dog.

+" +`; + +exports[`Heading block should set a text color 1`] = ` +" +

A quick brown fox jumps over the lazy dog.

+" +`; diff --git a/packages/block-library/src/heading/test/index.native.js b/packages/block-library/src/heading/test/index.native.js index fce294cf9c992..d3df80a3c96c7 100644 --- a/packages/block-library/src/heading/test/index.native.js +++ b/packages/block-library/src/heading/test/index.native.js @@ -7,6 +7,9 @@ import { initializeEditor, addBlock, getBlock, + typeInRichText, + waitFor, + within, } from 'test/helpers'; /** @@ -41,4 +44,72 @@ describe( 'Heading block', () => { expect( getEditorHtml() ).toMatchSnapshot(); } ); + + it( 'should set a text color', async () => { + // Arrange + const screen = await initializeEditor(); + await addBlock( screen, 'Heading' ); + + // Act + const headingBlock = getBlock( screen, 'Heading' ); + fireEvent.press( headingBlock ); + const headingTextInput = + within( headingBlock ).getByPlaceholderText( 'Heading' ); + typeInRichText( + headingTextInput, + 'A quick brown fox jumps over the lazy dog.' + ); + // Open Block Settings. + fireEvent.press( screen.getByLabelText( 'Open Settings' ) ); + + // Wait for Block Settings to be visible. + const blockSettingsModal = screen.getByTestId( 'block-settings-modal' ); + await waitFor( () => blockSettingsModal.props.isVisible ); + + // Open Text color settings + fireEvent.press( screen.getByLabelText( 'Text, Default' ) ); + + // Tap one color + fireEvent.press( screen.getByLabelText( '#f78da7' ) ); + + // Dismiss the Block Settings modal. + fireEvent( blockSettingsModal, 'backdropPress' ); + + // Assert + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + + it( 'should set a background color', async () => { + // Arrange + const screen = await initializeEditor(); + await addBlock( screen, 'Heading' ); + + // Act + const headingBlock = getBlock( screen, 'Heading' ); + fireEvent.press( headingBlock ); + const headingTextInput = + within( headingBlock ).getByPlaceholderText( 'Heading' ); + typeInRichText( + headingTextInput, + 'A quick brown fox jumps over the lazy dog.' + ); + // Open Block Settings. + fireEvent.press( screen.getByLabelText( 'Open Settings' ) ); + + // Wait for Block Settings to be visible. + const blockSettingsModal = screen.getByTestId( 'block-settings-modal' ); + await waitFor( () => blockSettingsModal.props.isVisible ); + + // Open Background color settings + fireEvent.press( screen.getByLabelText( 'Background, Default' ) ); + + // Tap one color + fireEvent.press( screen.getByLabelText( '#ff6900' ) ); + + // Dismiss the Block Settings modal. + fireEvent( blockSettingsModal, 'backdropPress' ); + + // Assert + expect( getEditorHtml() ).toMatchSnapshot(); + } ); } ); From 29cad0586f31b91e58ba5faa0f9b807bd5b8203e Mon Sep 17 00:00:00 2001 From: Gerardo Date: Wed, 19 Apr 2023 13:01:55 +0200 Subject: [PATCH 08/13] Mobile - Buttons block - Add tests for color customization --- .../test/__snapshots__/edit.native.js.snap | 18 +++ .../src/buttons/test/edit.native.js | 123 ++++++++++++++++++ 2 files changed, 141 insertions(+) diff --git a/packages/block-library/src/buttons/test/__snapshots__/edit.native.js.snap b/packages/block-library/src/buttons/test/__snapshots__/edit.native.js.snap index 617edecbe4b0c..1a55c807225d9 100644 --- a/packages/block-library/src/buttons/test/__snapshots__/edit.native.js.snap +++ b/packages/block-library/src/buttons/test/__snapshots__/edit.native.js.snap @@ -1,5 +1,23 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Buttons block color customization sets a background color 1`] = ` +" +
+" +`; + +exports[`Buttons block color customization sets a gradient background color 1`] = ` +" +
+" +`; + +exports[`Buttons block color customization sets a text color 1`] = ` +" +
+" +`; + exports[`Buttons block justify content sets Justify items center option 1`] = ` "
diff --git a/packages/block-library/src/buttons/test/edit.native.js b/packages/block-library/src/buttons/test/edit.native.js index 788c1b1e40e4b..abed217d2243e 100644 --- a/packages/block-library/src/buttons/test/edit.native.js +++ b/packages/block-library/src/buttons/test/edit.native.js @@ -2,12 +2,15 @@ * External dependencies */ import { + addBlock, fireEvent, getEditorHtml, within, getBlock, initializeEditor, + triggerBlockListLayout, typeInRichText, + waitFor, } from 'test/helpers'; /** @@ -271,4 +274,124 @@ describe( 'Buttons block', () => { } ) ); } ); + + describe( 'color customization', () => { + it( 'sets a text color', async () => { + // Arrange + const screen = await initializeEditor(); + await addBlock( screen, 'Buttons' ); + + // Act + const buttonsBlock = getBlock( screen, 'Buttons' ); + fireEvent.press( buttonsBlock ); + + // Trigger onLayout for the list + await triggerBlockListLayout( buttonsBlock ); + + const buttonBlock = await getBlock( screen, 'Button' ); + fireEvent.press( buttonBlock ); + + // Open Block Settings. + fireEvent.press( screen.getByLabelText( 'Open Settings' ) ); + + // Wait for Block Settings to be visible. + const blockSettingsModal = screen.getByTestId( + 'block-settings-modal' + ); + await waitFor( () => blockSettingsModal.props.isVisible ); + + // Open Text color settings + fireEvent.press( screen.getByLabelText( 'Text, Default' ) ); + + // Tap one color + fireEvent.press( screen.getByLabelText( '#f78da7' ) ); + + // Dismiss the Block Settings modal. + fireEvent( blockSettingsModal, 'backdropPress' ); + + // Assert + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + + it( 'sets a background color', async () => { + // Arrange + const screen = await initializeEditor(); + await addBlock( screen, 'Buttons' ); + + // Act + const buttonsBlock = getBlock( screen, 'Buttons' ); + fireEvent.press( buttonsBlock ); + + // Trigger onLayout for the list + await triggerBlockListLayout( buttonsBlock ); + + const buttonBlock = await getBlock( screen, 'Button' ); + fireEvent.press( buttonBlock ); + + // Open Block Settings. + fireEvent.press( screen.getByLabelText( 'Open Settings' ) ); + + // Wait for Block Settings to be visible. + const blockSettingsModal = screen.getByTestId( + 'block-settings-modal' + ); + await waitFor( () => blockSettingsModal.props.isVisible ); + + // Open Text color settings + fireEvent.press( screen.getByLabelText( 'Background, Default' ) ); + + // Tap one color + fireEvent.press( screen.getByLabelText( '#fcb900' ) ); + + // Dismiss the Block Settings modal. + fireEvent( blockSettingsModal, 'backdropPress' ); + + // Assert + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + + it( 'sets a gradient background color', async () => { + // Arrange + const screen = await initializeEditor(); + await addBlock( screen, 'Buttons' ); + + // Act + const buttonsBlock = getBlock( screen, 'Buttons' ); + fireEvent.press( buttonsBlock ); + + // Trigger onLayout for the list + await triggerBlockListLayout( buttonsBlock ); + + const buttonBlock = await getBlock( screen, 'Button' ); + fireEvent.press( buttonBlock ); + + // Open Block Settings. + fireEvent.press( screen.getByLabelText( 'Open Settings' ) ); + + // Wait for Block Settings to be visible. + const blockSettingsModal = screen.getByTestId( + 'block-settings-modal' + ); + await waitFor( () => blockSettingsModal.props.isVisible ); + + // Open Text color settings + fireEvent.press( screen.getByLabelText( 'Background, Default' ) ); + + // Tap on the gradient segment + fireEvent.press( screen.getByLabelText( 'Gradient' ) ); + + // Tap one gradient color + fireEvent.press( + screen.getByLabelText( + 'linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%)' + ) + ); + + // Dismiss the Block Settings modal. + fireEvent( blockSettingsModal, 'backdropPress' ); + + // Assert + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + } ); } ); From fec37660132e6f07d68d180b5137f1e6c318f5c3 Mon Sep 17 00:00:00 2001 From: Gerardo Date: Wed, 19 Apr 2023 17:24:42 +0200 Subject: [PATCH 09/13] Mobile - Integration tests - Add getGlobalStyles - To pass theme data to the editor for tests --- .../get-global-styles.js | 55 +++++++++++++++++++ .../initialize-editor.js | 17 ++++-- 2 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 test/native/integration-test-helpers/get-global-styles.js diff --git a/test/native/integration-test-helpers/get-global-styles.js b/test/native/integration-test-helpers/get-global-styles.js new file mode 100644 index 0000000000000..1156018358c3c --- /dev/null +++ b/test/native/integration-test-helpers/get-global-styles.js @@ -0,0 +1,55 @@ +const GLOBAL_STYLES_RAW_FEATURES = { + color: { + text: true, + background: true, + defaultPalette: true, + defaultGradients: false, + palette: { + default: [ + { + color: '#f78da7', + name: 'Pale pink', + slug: 'pale-pink', + }, + { + color: '#cf2e2e', + name: 'Vivid red', + slug: 'vivid-red', + }, + { + color: '#ff6900', + name: 'Luminous vivid orange', + slug: 'luminous-vivid-orange', + }, + ], + theme: [ + { + color: '#e2d8ff', + name: 'Foreground', + slug: 'foreground', + }, + { + color: '#2f1ab2', + name: 'Background', + slug: 'background', + }, + { + color: '#2411a4', + name: 'Tertiary', + slug: 'tertiary', + }, + ], + }, + }, +}; + +/** + * Returns some global styles data to test with the editor. + * + * @return {Object} Editor features. + */ +export function getGlobalStyles() { + return { + rawFeatures: JSON.stringify( GLOBAL_STYLES_RAW_FEATURES ), + }; +} diff --git a/test/native/integration-test-helpers/initialize-editor.js b/test/native/integration-test-helpers/initialize-editor.js index 440d69550ae0e..54ece9d347a07 100644 --- a/test/native/integration-test-helpers/initialize-editor.js +++ b/test/native/integration-test-helpers/initialize-editor.js @@ -15,14 +15,16 @@ import { initializeEditor as internalInitializeEditor } from '@wordpress/edit-po * Internal dependencies */ import { waitForStoreResolvers } from './wait-for-store-resolvers'; +import { getGlobalStyles } from './get-global-styles'; /** * Initialize an editor for test assertions. * - * @param {Object} props Properties passed to the editor component. - * @param {string} props.initialHtml String of block editor HTML to parse and render. - * @param {Object} [options] Configuration options for the editor. - * @param {import('react').ReactNode} [options.component] A specific editor component to render. + * @param {Object} props Properties passed to the editor component. + * @param {string} props.initialHtml String of block editor HTML to parse and render. + * @param {string} props.withGlobalStyles Boolean to pass global styles data to the editor. + * @param {Object} [options] Configuration options for the editor. + * @param {import('react').ReactNode} [options.component] A specific editor component to render. * @return {import('@testing-library/react-native').RenderAPI} A Testing Library screen. */ export async function initializeEditor( props, { component } = {} ) { @@ -31,7 +33,11 @@ export async function initializeEditor( props, { component } = {} ) { const postType = 'post'; return waitForStoreResolvers( () => { - const { screenWidth = 320, ...rest } = props || {}; + const { + screenWidth = 320, + withGlobalStyles = false, + ...rest + } = props || {}; const editorElement = component ? createElement( component, { postType, postId } ) : internalInitializeEditor( uniqueId, postType, postId ); @@ -39,6 +45,7 @@ export async function initializeEditor( props, { component } = {} ) { const screen = render( cloneElement( editorElement, { initialTitle: 'test', + ...( withGlobalStyles ? getGlobalStyles() : {} ), ...rest, } ) ); From d034df2407c7e134d852b23c44af7fe645929ef3 Mon Sep 17 00:00:00 2001 From: Gerardo Date: Wed, 19 Apr 2023 17:25:25 +0200 Subject: [PATCH 10/13] Mobile - Paragraph block - Test setting a theme text color and for the contrast check --- .../src/paragraph/test/edit.native.js | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/packages/block-library/src/paragraph/test/edit.native.js b/packages/block-library/src/paragraph/test/edit.native.js index 2fb48e97be92e..931f2812746c0 100644 --- a/packages/block-library/src/paragraph/test/edit.native.js +++ b/packages/block-library/src/paragraph/test/edit.native.js @@ -575,4 +575,68 @@ describe( 'Paragraph block', () => { const gradientButton = screen.queryByLabelText( 'Gradient' ); expect( gradientButton ).toBeNull(); } ); + + it( 'should set a theme text color', async () => { + // Arrange + const screen = await initializeEditor( { withGlobalStyles: true } ); + await addBlock( screen, 'Paragraph' ); + + // Act + const paragraphBlock = getBlock( screen, 'Paragraph' ); + fireEvent.press( paragraphBlock ); + const paragraphTextInput = + within( paragraphBlock ).getByPlaceholderText( 'Start writing…' ); + typeInRichText( + paragraphTextInput, + 'A quick brown fox jumps over the lazy dog.' + ); + // Open Block Settings. + fireEvent.press( screen.getByLabelText( 'Open Settings' ) ); + + // Wait for Block Settings to be visible. + const blockSettingsModal = screen.getByTestId( 'block-settings-modal' ); + await waitFor( () => blockSettingsModal.props.isVisible ); + + // Open Text color settings + fireEvent.press( screen.getByLabelText( 'Text, Default' ) ); + + // Tap one color + fireEvent.press( screen.getByLabelText( '#2411a4' ) ); + + // Dismiss the Block Settings modal. + fireEvent( blockSettingsModal, 'backdropPress' ); + + // Assert + expect( getEditorHtml() ).toMatchInlineSnapshot( ` + " +

A quick brown fox jumps over the lazy dog.

+ " + ` ); + } ); + + it( 'should show the contrast check warning', async () => { + // Arrange + const screen = await initializeEditor( { + initialHtml: ` +

A quick brown fox jumps over the lazy dog.

+ `, + } ); + + // Act + const paragraphBlock = getBlock( screen, 'Paragraph' ); + fireEvent.press( paragraphBlock ); + + // Open Block Settings. + fireEvent.press( screen.getByLabelText( 'Open Settings' ) ); + + // Wait for Block Settings to be visible. + const blockSettingsModal = screen.getByTestId( 'block-settings-modal' ); + await waitFor( () => blockSettingsModal.props.isVisible ); + + // Assert + const contrastCheckElement = screen.getByText( + /This color combination/ + ); + expect( contrastCheckElement ).toBeDefined(); + } ); } ); From 6e8f955c394384de4d1f29684ac12408b30d35ee Mon Sep 17 00:00:00 2001 From: Gerardo Date: Thu, 20 Apr 2023 13:35:46 +0200 Subject: [PATCH 11/13] Mobile - ColorPalette - Use color name as the accessibility label --- .../block-library/src/buttons/test/edit.native.js | 8 +++----- .../src/heading/test/index.native.js | 4 ++-- .../src/paragraph/test/edit.native.js | 12 ++++++------ .../components/src/color-palette/index.native.js | 15 ++++++++++++++- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/packages/block-library/src/buttons/test/edit.native.js b/packages/block-library/src/buttons/test/edit.native.js index abed217d2243e..ff79be61b9290 100644 --- a/packages/block-library/src/buttons/test/edit.native.js +++ b/packages/block-library/src/buttons/test/edit.native.js @@ -304,7 +304,7 @@ describe( 'Buttons block', () => { fireEvent.press( screen.getByLabelText( 'Text, Default' ) ); // Tap one color - fireEvent.press( screen.getByLabelText( '#f78da7' ) ); + fireEvent.press( screen.getByLabelText( 'Pale pink' ) ); // Dismiss the Block Settings modal. fireEvent( blockSettingsModal, 'backdropPress' ); @@ -341,7 +341,7 @@ describe( 'Buttons block', () => { fireEvent.press( screen.getByLabelText( 'Background, Default' ) ); // Tap one color - fireEvent.press( screen.getByLabelText( '#fcb900' ) ); + fireEvent.press( screen.getByLabelText( 'Luminous vivid amber' ) ); // Dismiss the Block Settings modal. fireEvent( blockSettingsModal, 'backdropPress' ); @@ -382,9 +382,7 @@ describe( 'Buttons block', () => { // Tap one gradient color fireEvent.press( - screen.getByLabelText( - 'linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%)' - ) + screen.getByLabelText( 'Light green cyan to vivid green cyan' ) ); // Dismiss the Block Settings modal. diff --git a/packages/block-library/src/heading/test/index.native.js b/packages/block-library/src/heading/test/index.native.js index d3df80a3c96c7..34fdc00c032ed 100644 --- a/packages/block-library/src/heading/test/index.native.js +++ b/packages/block-library/src/heading/test/index.native.js @@ -70,7 +70,7 @@ describe( 'Heading block', () => { fireEvent.press( screen.getByLabelText( 'Text, Default' ) ); // Tap one color - fireEvent.press( screen.getByLabelText( '#f78da7' ) ); + fireEvent.press( screen.getByLabelText( 'Pale pink' ) ); // Dismiss the Block Settings modal. fireEvent( blockSettingsModal, 'backdropPress' ); @@ -104,7 +104,7 @@ describe( 'Heading block', () => { fireEvent.press( screen.getByLabelText( 'Background, Default' ) ); // Tap one color - fireEvent.press( screen.getByLabelText( '#ff6900' ) ); + fireEvent.press( screen.getByLabelText( 'Luminous vivid orange' ) ); // Dismiss the Block Settings modal. fireEvent( blockSettingsModal, 'backdropPress' ); diff --git a/packages/block-library/src/paragraph/test/edit.native.js b/packages/block-library/src/paragraph/test/edit.native.js index 931f2812746c0..d3ff59c0e42c2 100644 --- a/packages/block-library/src/paragraph/test/edit.native.js +++ b/packages/block-library/src/paragraph/test/edit.native.js @@ -401,7 +401,7 @@ describe( 'Paragraph block', () => { fireEvent.press( screen.getByLabelText( 'Text, Default' ) ); // Tap one color - fireEvent.press( screen.getByLabelText( '#f78da7' ) ); + fireEvent.press( screen.getByLabelText( 'Pale pink' ) ); // Dismiss the Block Settings modal. fireEvent( blockSettingsModal, 'backdropPress' ); @@ -439,7 +439,7 @@ describe( 'Paragraph block', () => { fireEvent.press( screen.getByLabelText( 'Background, Default' ) ); // Tap one color - fireEvent.press( screen.getByLabelText( '#ff6900' ) ); + fireEvent.press( screen.getByLabelText( 'Luminous vivid orange' ) ); // Dismiss the Block Settings modal. fireEvent( blockSettingsModal, 'backdropPress' ); @@ -477,7 +477,7 @@ describe( 'Paragraph block', () => { fireEvent.press( screen.getByLabelText( 'Text, Default' ) ); // Tap one color - fireEvent.press( screen.getByLabelText( '#ffffff' ) ); + fireEvent.press( screen.getByLabelText( 'White' ) ); // Go back to the settings menu fireEvent.press( screen.getByLabelText( 'Go back' ) ); @@ -486,7 +486,7 @@ describe( 'Paragraph block', () => { fireEvent.press( screen.getByLabelText( 'Background, Default' ) ); // Tap one color - fireEvent.press( screen.getByLabelText( '#ff6900' ) ); + fireEvent.press( screen.getByLabelText( 'Luminous vivid orange' ) ); // Dismiss the Block Settings modal. fireEvent( blockSettingsModal, 'backdropPress' ); @@ -569,7 +569,7 @@ describe( 'Paragraph block', () => { fireEvent.press( screen.getByLabelText( 'Background, Default' ) ); // Assert - const colorButton = screen.getByLabelText( '#ff6900' ); + const colorButton = screen.getByLabelText( 'Luminous vivid orange' ); expect( colorButton ).toBeDefined(); const gradientButton = screen.queryByLabelText( 'Gradient' ); @@ -601,7 +601,7 @@ describe( 'Paragraph block', () => { fireEvent.press( screen.getByLabelText( 'Text, Default' ) ); // Tap one color - fireEvent.press( screen.getByLabelText( '#2411a4' ) ); + fireEvent.press( screen.getByLabelText( 'Tertiary' ) ); // Dismiss the Block Settings modal. fireEvent( blockSettingsModal, 'backdropPress' ); diff --git a/packages/components/src/color-palette/index.native.js b/packages/components/src/color-palette/index.native.js index 70f41447c5f92..1d4e8bb2b1454 100644 --- a/packages/components/src/color-palette/index.native.js +++ b/packages/components/src/color-palette/index.native.js @@ -175,6 +175,17 @@ function ColorPalette( { } } + function getColorGradientName( value ) { + const foundColorName = isGradientSegment + ? defaultSettings.gradients.find( + ( gradient ) => gradient.gradient === value + ) + : defaultSettings.allColors.find( + ( color ) => color.color === value + ); + return foundColorName ? foundColorName?.name : ''; + } + function onColorPress( color ) { deselectCustomGradient(); performAnimation( color ); @@ -251,6 +262,8 @@ function ColorPalette( { const scaleValue = isSelected( color ) ? scaleInterpolation : 1; + const colorName = getColorGradientName( color ); + return ( Date: Thu, 20 Apr 2023 15:25:00 +0200 Subject: [PATCH 12/13] Mobile - CustomGradientPicker - Simplify code by destructuring gradientAST when calling getGradientAstWithDefault --- .../components/src/custom-gradient-picker/index.native.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/index.native.js b/packages/components/src/custom-gradient-picker/index.native.js index b2e0b4432f0ca..d10ade0aea7c2 100644 --- a/packages/components/src/custom-gradient-picker/index.native.js +++ b/packages/components/src/custom-gradient-picker/index.native.js @@ -25,7 +25,7 @@ function CustomGradientPicker( { setColor, currentValue, isGradientColor } ) { const [ currentColor, setCurrentColor ] = useState( currentValue ); const { getGradientType, gradients, gradientOptions } = colorsUtils; - const gradientWithDefault = getGradientAstWithDefault( currentColor ); + const { gradientAST } = getGradientAstWithDefault( currentColor ); const gradientType = getGradientType( currentColor ); function isLinearGradient( type ) { @@ -33,7 +33,6 @@ function CustomGradientPicker( { setColor, currentValue, isGradientColor } ) { } function getGradientColor( type ) { - const { gradientAST } = gradientWithDefault; const { orientation, ...restGradientAST } = gradientAST; if ( orientation ) { @@ -65,7 +64,6 @@ function CustomGradientPicker( { setColor, currentValue, isGradientColor } ) { } function setGradientAngle( value ) { - const { gradientAST } = gradientWithDefault; const gradientColor = serializeGradient( { ...gradientAST, orientation: { @@ -81,7 +79,6 @@ function CustomGradientPicker( { setColor, currentValue, isGradientColor } ) { } function getGradientAngle() { - const { gradientAST } = gradientWithDefault; return gradientAST?.orientation?.value ?? DEFAULT_LINEAR_GRADIENT_ANGLE; } return ( From 39943888e5b8344f27fa1ba364dc3f25c8f70b05 Mon Sep 17 00:00:00 2001 From: Gerardo Date: Thu, 20 Apr 2023 15:26:30 +0200 Subject: [PATCH 13/13] Mobile - ColorPalette - Add optional chaining for allColors and gradients, in some cases these values can be undefined, depending on the theme colors/configuration. It also adds a fallback name for colors that don't have a name value --- .../components/src/color-palette/index.native.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/components/src/color-palette/index.native.js b/packages/components/src/color-palette/index.native.js index 1d4e8bb2b1454..51a61785df9af 100644 --- a/packages/components/src/color-palette/index.native.js +++ b/packages/components/src/color-palette/index.native.js @@ -15,7 +15,7 @@ import { /** * WordPress dependencies */ -import { __ } from '@wordpress/i18n'; +import { __, sprintf } from '@wordpress/i18n'; import { useRef, useEffect } from '@wordpress/element'; import { usePreferredColorSchemeStyle } from '@wordpress/compose'; @@ -176,14 +176,19 @@ function ColorPalette( { } function getColorGradientName( value ) { + const fallbackName = sprintf( + /* translators: %s: the hex color value */ + __( 'Unlabeled color. %s' ), + value + ); const foundColorName = isGradientSegment - ? defaultSettings.gradients.find( + ? defaultSettings.gradients?.find( ( gradient ) => gradient.gradient === value ) - : defaultSettings.allColors.find( + : defaultSettings.allColors?.find( ( color ) => color.color === value ); - return foundColorName ? foundColorName?.name : ''; + return foundColorName ? foundColorName?.name : fallbackName; } function onColorPress( color ) {