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 00000000000000..f329ad0369b468 --- /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 ff994899bbf06b..00000000000000 --- 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; 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 617edecbe4b0cb..1a55c807225d9d 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 788c1b1e40e4bb..ff79be61b92909 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,122 @@ 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( 'Pale pink' ) ); + + // 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( 'Luminous vivid amber' ) ); + + // 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( 'Light green cyan to vivid green cyan' ) + ); + + // Dismiss the Block Settings modal. + fireEvent( blockSettingsModal, 'backdropPress' ); + + // Assert + expect( getEditorHtml() ).toMatchSnapshot(); + } ); + } ); } ); 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 0e0febb84d549f..308aa8ac729bff 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 fce294cf9c9920..34fdc00c032ed8 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( 'Pale pink' ) ); + + // 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( 'Luminous vivid orange' ) ); + + // Dismiss the Block Settings modal. + fireEvent( blockSettingsModal, 'backdropPress' ); + + // Assert + expect( getEditorHtml() ).toMatchSnapshot(); + } ); } ); diff --git a/packages/block-library/src/paragraph/test/edit.native.js b/packages/block-library/src/paragraph/test/edit.native.js index a4dcfdac92041b..d3ff59c0e42c29 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,268 @@ 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( 'Pale pink' ) ); + + // 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( 'Luminous vivid orange' ) ); + + // 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( 'White' ) ); + + // 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( 'Luminous vivid orange' ) ); + + // 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( 'Luminous vivid orange' ); + expect( colorButton ).toBeDefined(); + + 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( 'Tertiary' ) ); + + // 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(); + } ); } ); diff --git a/packages/components/src/color-palette/index.native.js b/packages/components/src/color-palette/index.native.js index ffb063f7f23f49..51a61785df9afe 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'; @@ -175,6 +175,22 @@ function ColorPalette( { } } + function getColorGradientName( value ) { + const fallbackName = sprintf( + /* translators: %s: the hex color value */ + __( 'Unlabeled color. %s' ), + value + ); + const foundColorName = isGradientSegment + ? defaultSettings.gradients?.find( + ( gradient ) => gradient.gradient === value + ) + : defaultSettings.allColors?.find( + ( color ) => color.color === value + ); + return foundColorName ? foundColorName?.name : fallbackName; + } + function onColorPress( color ) { deselectCustomGradient(); performAnimation( color ); @@ -251,6 +267,8 @@ function ColorPalette( { const scaleValue = isSelected( color ) ? scaleInterpolation : 1; + const colorName = getColorGradientName( color ); + return ( { 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(); diff --git a/packages/components/src/mobile/segmented-control/index.native.js b/packages/components/src/mobile/segmented-control/index.native.js index b3270e37120e3b..1a218fcece100f 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 ]; 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 00000000000000..1156018358c3ca --- /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 440d69550ae0e6..54ece9d347a07c 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, } ) );