From 4db74c8ce4cf0020882162a7d9d2cfe5487f5baf Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 24 Jan 2023 17:06:19 +0100 Subject: [PATCH 1/8] Move ColorPalette utils to separate file --- .../components/src/color-palette/index.tsx | 79 ++--------------- .../src/color-palette/test/utils.ts | 2 +- .../components/src/color-palette/utils.ts | 86 +++++++++++++++++++ 3 files changed, 94 insertions(+), 73 deletions(-) create mode 100644 packages/components/src/color-palette/utils.ts diff --git a/packages/components/src/color-palette/index.tsx b/packages/components/src/color-palette/index.tsx index ca61884d5f708..65692e7af9a22 100644 --- a/packages/components/src/color-palette/index.tsx +++ b/packages/components/src/color-palette/index.tsx @@ -1,7 +1,7 @@ /** * External dependencies */ -import type { ForwardedRef, RefObject } from 'react'; +import type { ForwardedRef } from 'react'; import { colord, extend } from 'colord'; import namesPlugin from 'colord/plugins/names'; import a11yPlugin from 'colord/plugins/a11y'; @@ -33,6 +33,12 @@ import type { } from './types'; import type { WordPressComponentProps } from '../ui/context'; import type { DropdownProps } from '../dropdown/types'; +import { + extractColorNameFromCurrentValue, + areColorsMultiplePalette, + normalizeColorValue, + showTransparentBackground, +} from './utils'; extend( [ namesPlugin, a11yPlugin ] ); @@ -164,77 +170,6 @@ export function CustomColorPickerDropdown( { ); } -export const extractColorNameFromCurrentValue = ( - currentValue?: ColorPaletteProps[ 'value' ], - colors: ColorPaletteProps[ 'colors' ] = [], - showMultiplePalettes: boolean = false -) => { - if ( ! currentValue ) { - return ''; - } - - const currentValueIsCssVariable = /^var\(/.test( currentValue ); - const normalizedCurrentValue = currentValueIsCssVariable - ? currentValue - : colord( currentValue ).toHex(); - - // Normalize format of `colors` to simplify the following loop - type normalizedPaletteObject = { colors: ColorObject[] }; - const colorPalettes: normalizedPaletteObject[] = showMultiplePalettes - ? ( colors as PaletteObject[] ) - : [ { colors: colors as ColorObject[] } ]; - for ( const { colors: paletteColors } of colorPalettes ) { - for ( const { name: colorName, color: colorValue } of paletteColors ) { - const normalizedColorValue = currentValueIsCssVariable - ? colorValue - : colord( colorValue ).toHex(); - - if ( normalizedCurrentValue === normalizedColorValue ) { - return colorName; - } - } - } - - // translators: shown when the user has picked a custom color (i.e not in the palette of colors). - return __( 'Custom' ); -}; - -export const showTransparentBackground = ( currentValue?: string ) => { - if ( typeof currentValue === 'undefined' ) { - return true; - } - return colord( currentValue ).alpha() === 0; -}; - -const areColorsMultiplePalette = ( - colors: NonNullable< ColorPaletteProps[ 'colors' ] > -): colors is PaletteObject[] => { - return colors.every( ( colorObj ) => - Array.isArray( ( colorObj as PaletteObject ).colors ) - ); -}; - -const normalizeColorValue = ( - value: string | undefined, - ref: RefObject< HTMLElement > | null -) => { - const currentValueIsCssVariable = /^var\(/.test( value ?? '' ); - - if ( ! currentValueIsCssVariable || ! ref?.current ) { - return value; - } - - const { ownerDocument } = ref.current; - const { defaultView } = ownerDocument; - const computedBackgroundColor = defaultView?.getComputedStyle( - ref.current - ).backgroundColor; - - return computedBackgroundColor - ? colord( computedBackgroundColor ).toHex() - : value; -}; - function UnforwardedColorPalette( props: WordPressComponentProps< ColorPaletteProps, 'div' >, forwardedRef: ForwardedRef< any > diff --git a/packages/components/src/color-palette/test/utils.ts b/packages/components/src/color-palette/test/utils.ts index bbb0d82c6e91d..c65b52739f996 100644 --- a/packages/components/src/color-palette/test/utils.ts +++ b/packages/components/src/color-palette/test/utils.ts @@ -4,7 +4,7 @@ import { extractColorNameFromCurrentValue, showTransparentBackground, -} from '../'; +} from '../utils'; describe( 'ColorPalette: Utils', () => { describe( 'extractColorNameFromCurrentValue', () => { diff --git a/packages/components/src/color-palette/utils.ts b/packages/components/src/color-palette/utils.ts new file mode 100644 index 0000000000000..61d0c1d1d6251 --- /dev/null +++ b/packages/components/src/color-palette/utils.ts @@ -0,0 +1,86 @@ +/** + * External dependencies + */ +import type { RefObject } from 'react'; +import { colord } from 'colord'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import type { ColorObject, ColorPaletteProps, PaletteObject } from './types'; + +export const extractColorNameFromCurrentValue = ( + currentValue?: ColorPaletteProps[ 'value' ], + colors: ColorPaletteProps[ 'colors' ] = [], + showMultiplePalettes: boolean = false +) => { + if ( ! currentValue ) { + return ''; + } + + const currentValueIsCssVariable = /^var\(/.test( currentValue ); + const normalizedCurrentValue = currentValueIsCssVariable + ? currentValue + : colord( currentValue ).toHex(); + + // Normalize format of `colors` to simplify the following loop + type normalizedPaletteObject = { colors: ColorObject[] }; + const colorPalettes: normalizedPaletteObject[] = showMultiplePalettes + ? ( colors as PaletteObject[] ) + : [ { colors: colors as ColorObject[] } ]; + for ( const { colors: paletteColors } of colorPalettes ) { + for ( const { name: colorName, color: colorValue } of paletteColors ) { + const normalizedColorValue = currentValueIsCssVariable + ? colorValue + : colord( colorValue ).toHex(); + + if ( normalizedCurrentValue === normalizedColorValue ) { + return colorName; + } + } + } + + // translators: shown when the user has picked a custom color (i.e not in the palette of colors). + return __( 'Custom' ); +}; + +export const showTransparentBackground = ( currentValue?: string ) => { + if ( typeof currentValue === 'undefined' ) { + return true; + } + return colord( currentValue ).alpha() === 0; +}; + +export const areColorsMultiplePalette = ( + colors: NonNullable< ColorPaletteProps[ 'colors' ] > +): colors is PaletteObject[] => { + return colors.every( ( colorObj ) => + Array.isArray( ( colorObj as PaletteObject ).colors ) + ); +}; + +export const normalizeColorValue = ( + value: string | undefined, + ref: RefObject< HTMLElement > | null +) => { + const currentValueIsCssVariable = /^var\(/.test( value ?? '' ); + + if ( ! currentValueIsCssVariable || ! ref?.current ) { + return value; + } + + const { ownerDocument } = ref.current; + const { defaultView } = ownerDocument; + const computedBackgroundColor = defaultView?.getComputedStyle( + ref.current + ).backgroundColor; + + return computedBackgroundColor + ? colord( computedBackgroundColor ).toHex() + : value; +}; From b212fe722f6d7000bdaeb379e7f1e12ee7fa6698 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 24 Jan 2023 17:07:48 +0100 Subject: [PATCH 2/8] Rewrite multiple palette object/array check --- .../components/src/color-palette/index.tsx | 4 ++-- packages/components/src/color-palette/utils.ts | 18 +++++++++++++----- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/components/src/color-palette/index.tsx b/packages/components/src/color-palette/index.tsx index 65692e7af9a22..73ec589fe4d67 100644 --- a/packages/components/src/color-palette/index.tsx +++ b/packages/components/src/color-palette/index.tsx @@ -35,7 +35,7 @@ import type { WordPressComponentProps } from '../ui/context'; import type { DropdownProps } from '../dropdown/types'; import { extractColorNameFromCurrentValue, - areColorsMultiplePalette, + isMultiplePaletteArray, normalizeColorValue, showTransparentBackground, } from './utils'; @@ -203,7 +203,7 @@ function UnforwardedColorPalette( // Make sure that the `colors` array has a valid format. if ( colors.length > 0 && - hasMultipleColorOrigins !== areColorsMultiplePalette( colors ) + hasMultipleColorOrigins !== isMultiplePaletteArray( colors ) ) { // eslint-disable-next-line no-console console.warn( diff --git a/packages/components/src/color-palette/utils.ts b/packages/components/src/color-palette/utils.ts index 61d0c1d1d6251..310e4728cce4e 100644 --- a/packages/components/src/color-palette/utils.ts +++ b/packages/components/src/color-palette/utils.ts @@ -56,11 +56,19 @@ export const showTransparentBackground = ( currentValue?: string ) => { return colord( currentValue ).alpha() === 0; }; -export const areColorsMultiplePalette = ( - colors: NonNullable< ColorPaletteProps[ 'colors' ] > -): colors is PaletteObject[] => { - return colors.every( ( colorObj ) => - Array.isArray( ( colorObj as PaletteObject ).colors ) +// The PaletteObject type has a `colors` property (an array of ColorObject), +// while the ColorObject type has a `color` property (the CSS color value). +export const isMultiplePaletteObject = ( + obj: PaletteObject | ColorObject +): obj is PaletteObject => + Array.isArray( ( obj as PaletteObject ).colors ) && ! ( 'color' in obj ); + +export const isMultiplePaletteArray = ( + arr: ( PaletteObject | ColorObject )[] +): arr is PaletteObject[] => { + return ( + arr.length > 0 && + arr.every( ( colorObj ) => isMultiplePaletteObject( colorObj ) ) ); }; From 5e172cd5ac42d301c1adb8c7e5f7324aec0b9a68 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 24 Jan 2023 17:09:00 +0100 Subject: [PATCH 3/8] Remove unnecessary check --- packages/components/src/color-palette/index.tsx | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/packages/components/src/color-palette/index.tsx b/packages/components/src/color-palette/index.tsx index 73ec589fe4d67..c967703141ea3 100644 --- a/packages/components/src/color-palette/index.tsx +++ b/packages/components/src/color-palette/index.tsx @@ -187,9 +187,7 @@ function UnforwardedColorPalette( } = props; const clearColor = useCallback( () => onChange( undefined ), [ onChange ] ); - const hasMultipleColorOrigins = - colors.length > 0 && - ( colors as PaletteObject[] )[ 0 ].colors !== undefined; + const hasMultipleColorOrigins = isMultiplePaletteArray( colors ); const buttonLabelName = useMemo( () => extractColorNameFromCurrentValue( @@ -200,18 +198,6 @@ function UnforwardedColorPalette( [ value, colors, hasMultipleColorOrigins ] ); - // Make sure that the `colors` array has a valid format. - if ( - colors.length > 0 && - hasMultipleColorOrigins !== isMultiplePaletteArray( colors ) - ) { - // eslint-disable-next-line no-console - console.warn( - 'wp.components.ColorPalette: please specify a valid format for the `colors` prop. ' - ); - return null; - } - const renderCustomColorPicker = () => ( Date: Tue, 24 Jan 2023 17:09:28 +0100 Subject: [PATCH 4/8] Tweak type to more correct version --- packages/components/src/color-palette/README.md | 2 +- packages/components/src/color-palette/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/src/color-palette/README.md b/packages/components/src/color-palette/README.md index f5689cb0af036..870a5ef2d52f1 100644 --- a/packages/components/src/color-palette/README.md +++ b/packages/components/src/color-palette/README.md @@ -43,7 +43,7 @@ Whether the palette should have a clearing button. - Required: No - Default: `true` -### `colors`: `( PaletteObject | ColorObject )[]` +### `colors`: `PaletteObject[] | ColorObject[]` Array with the colors to be shown. When displaying multiple color palettes to choose from, the format of the array changes from an array of colors objects, to an array of color palettes. diff --git a/packages/components/src/color-palette/types.ts b/packages/components/src/color-palette/types.ts index 1f9c66c0fb604..58f55ad8f758f 100644 --- a/packages/components/src/color-palette/types.ts +++ b/packages/components/src/color-palette/types.ts @@ -55,7 +55,7 @@ export type ColorPaletteProps = Pick< PaletteProps, 'onChange' > & { * * @default [] */ - colors?: ( PaletteObject | ColorObject )[]; + colors?: PaletteObject[] | ColorObject[]; /** * Whether to allow the user to pick a custom color on top of the predefined * choices (defined via the `colors` prop). From 715da3ee8c5ce0072b450d0c123884b30eb3dad7 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 24 Jan 2023 17:10:13 +0100 Subject: [PATCH 5/8] Reuse multiple origin check in BorderControl --- .../border-control-dropdown/component.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/components/src/border-control/border-control-dropdown/component.tsx b/packages/components/src/border-control/border-control-dropdown/component.tsx index 1f5bef1a54c1c..b10788cb1638a 100644 --- a/packages/components/src/border-control/border-control-dropdown/component.tsx +++ b/packages/components/src/border-control/border-control-dropdown/component.tsx @@ -24,7 +24,8 @@ import { useBorderControlDropdown } from './hook'; import { StyledLabel } from '../../base-control/styles/base-control-styles'; import DropdownContentWrapper from '../../dropdown/dropdown-content-wrapper'; -import type { ColorObject, PaletteObject } from '../../color-palette/types'; +import type { ColorObject } from '../../color-palette/types'; +import { isMultiplePaletteArray } from '../../color-palette/utils'; import type { DropdownProps as DropdownComponentProps } from '../../dropdown/types'; import type { ColorProps, DropdownProps } from '../types'; @@ -32,14 +33,15 @@ const getColorObject = ( colorValue: CSSProperties[ 'borderColor' ], colors: ColorProps[ 'colors' ] | undefined ) => { - if ( ! colorValue || ! colors || colors.length === 0 ) { + if ( ! colorValue || ! colors ) { return; } - if ( ( colors as PaletteObject[] )[ 0 ].colors !== undefined ) { + if ( isMultiplePaletteArray( colors ) ) { + // Multiple origins let matchedColor; - ( colors as PaletteObject[] ).some( ( origin ) => + colors.some( ( origin ) => origin.colors.some( ( color ) => { if ( color.color === colorValue ) { matchedColor = color; @@ -53,9 +55,8 @@ const getColorObject = ( return matchedColor; } - return ( colors as ColorObject[] ).find( - ( color ) => color.color === colorValue - ); + // Single origin + return colors.find( ( color ) => color.color === colorValue ); }; const getToggleAriaLabel = ( From 03acd11bc5f8e16905c237b420ee81f9615904d7 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 24 Jan 2023 17:10:30 +0100 Subject: [PATCH 6/8] Apply similar logic for GradientPicker --- .../components/src/gradient-picker/index.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/components/src/gradient-picker/index.js b/packages/components/src/gradient-picker/index.js index 8a16f0d79e640..1a5338e459165 100644 --- a/packages/components/src/gradient-picker/index.js +++ b/packages/components/src/gradient-picker/index.js @@ -14,6 +14,18 @@ import { VStack } from '../v-stack'; import { ColorHeading } from '../color-palette/styles'; import { Spacer } from '../spacer'; +// The Multiple Origin Gradients have a `gradients` property (an array of +// gradient objects), while Single Origin ones have a `gradient` property. +const isMultipleOriginObject = ( obj ) => + Array.isArray( obj.gradients ) && ! ( 'gradient' in obj ); + +const isMultipleOriginArray = ( arr ) => { + return ( + arr.length > 0 && + arr.every( ( gradientObj ) => isMultipleOriginObject( gradientObj ) ) + ); +}; + function SingleOrigin( { className, clearGradient, @@ -105,10 +117,9 @@ export default function GradientPicker( { () => onChange( undefined ), [ onChange ] ); - const Component = - gradients?.length && gradients[ 0 ].gradients - ? MultipleOrigin - : SingleOrigin; + const Component = isMultipleOriginArray( gradients ) + ? MultipleOrigin + : SingleOrigin; if ( ! __nextHasNoMargin ) { deprecated( 'Outer margin styles for wp.components.GradientPicker', { From 726764d4e1e4bdd1c762455f747cf92fb070d611 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 24 Jan 2023 17:27:00 +0100 Subject: [PATCH 7/8] CHANGELOG --- packages/components/CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 118e3b8cbaa22..ac3606a8de517 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -14,6 +14,8 @@ - `DropdownMenu`: migrate Storybook to controls ([47149](https://github.com/WordPress/gutenberg/pull/47149)). - Removed deprecated `@storybook/addon-knobs` dependency from the package ([47152](https://github.com/WordPress/gutenberg/pull/47152)). - `ColorListPicker`: Convert to TypeScript ([#46358](https://github.com/WordPress/gutenberg/pull/46358)). +- `ColorPalette`, `BorderControl`, `GradientPicker`: refine types and logic around single vs multiple palettes + ([#47384](https://github.com/WordPress/gutenberg/pull/47384)). ### Bug Fix @@ -38,6 +40,10 @@ ## 23.1.0 (2023-01-02) +### Breaking Changes + +- `ColorPalette`: The experimental `__experimentalHasMultipleOrigins` prop has been removed ([#46315](https://github.com/WordPress/gutenberg/pull/46315)). + ## 23.0.0 (2022-12-14) ### Breaking Changes From 7f1cd2a45c188e01d9a3ed029e58c39cba61a215 Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Tue, 24 Jan 2023 17:34:14 +0100 Subject: [PATCH 8/8] Initialize colord plugins in utils file --- packages/components/src/color-palette/utils.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/components/src/color-palette/utils.ts b/packages/components/src/color-palette/utils.ts index 310e4728cce4e..8a70d25a701e9 100644 --- a/packages/components/src/color-palette/utils.ts +++ b/packages/components/src/color-palette/utils.ts @@ -2,7 +2,9 @@ * External dependencies */ import type { RefObject } from 'react'; -import { colord } from 'colord'; +import { colord, extend } from 'colord'; +import namesPlugin from 'colord/plugins/names'; +import a11yPlugin from 'colord/plugins/a11y'; /** * WordPress dependencies @@ -14,6 +16,8 @@ import { __ } from '@wordpress/i18n'; */ import type { ColorObject, ColorPaletteProps, PaletteObject } from './types'; +extend( [ namesPlugin, a11yPlugin ] ); + export const extractColorNameFromCurrentValue = ( currentValue?: ColorPaletteProps[ 'value' ], colors: ColorPaletteProps[ 'colors' ] = [],