From 673837144d8d16ca4de62f7b80b91ecb1b53c9e4 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Wed, 22 Feb 2023 08:32:10 -0500 Subject: [PATCH 01/56] Remove from ts-config exclude, rename files to .ts[x] --- .../src/custom-gradient-picker/{constants.js => constants.ts} | 0 .../gradient-bar/{constants.js => constants.ts} | 0 .../gradient-bar/{control-points.js => control-points.tsx} | 0 .../custom-gradient-picker/gradient-bar/{index.js => index.tsx} | 0 .../gradient-bar/test/{utils.js => utils.ts} | 0 .../custom-gradient-picker/gradient-bar/{utils.js => utils.ts} | 0 .../src/custom-gradient-picker/{index.js => index.tsx} | 0 .../src/custom-gradient-picker/{serializer.js => serializer.ts} | 0 .../src/custom-gradient-picker/stories/{index.js => index.tsx} | 2 +- ...dient-picker-styles.js => custom-gradient-picker-styles.tsx} | 0 .../test/{serializer.js => serializer.ts} | 0 .../src/custom-gradient-picker/{utils.js => utils.ts} | 0 12 files changed, 1 insertion(+), 1 deletion(-) rename packages/components/src/custom-gradient-picker/{constants.js => constants.ts} (100%) rename packages/components/src/custom-gradient-picker/gradient-bar/{constants.js => constants.ts} (100%) rename packages/components/src/custom-gradient-picker/gradient-bar/{control-points.js => control-points.tsx} (100%) rename packages/components/src/custom-gradient-picker/gradient-bar/{index.js => index.tsx} (100%) rename packages/components/src/custom-gradient-picker/gradient-bar/test/{utils.js => utils.ts} (100%) rename packages/components/src/custom-gradient-picker/gradient-bar/{utils.js => utils.ts} (100%) rename packages/components/src/custom-gradient-picker/{index.js => index.tsx} (100%) rename packages/components/src/custom-gradient-picker/{serializer.js => serializer.ts} (100%) rename packages/components/src/custom-gradient-picker/stories/{index.js => index.tsx} (93%) rename packages/components/src/custom-gradient-picker/styles/{custom-gradient-picker-styles.js => custom-gradient-picker-styles.tsx} (100%) rename packages/components/src/custom-gradient-picker/test/{serializer.js => serializer.ts} (100%) rename packages/components/src/custom-gradient-picker/{utils.js => utils.ts} (100%) diff --git a/packages/components/src/custom-gradient-picker/constants.js b/packages/components/src/custom-gradient-picker/constants.ts similarity index 100% rename from packages/components/src/custom-gradient-picker/constants.js rename to packages/components/src/custom-gradient-picker/constants.ts diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/constants.js b/packages/components/src/custom-gradient-picker/gradient-bar/constants.ts similarity index 100% rename from packages/components/src/custom-gradient-picker/gradient-bar/constants.js rename to packages/components/src/custom-gradient-picker/gradient-bar/constants.ts diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.js b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx similarity index 100% rename from packages/components/src/custom-gradient-picker/gradient-bar/control-points.js rename to packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/index.js b/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx similarity index 100% rename from packages/components/src/custom-gradient-picker/gradient-bar/index.js rename to packages/components/src/custom-gradient-picker/gradient-bar/index.tsx diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/test/utils.js b/packages/components/src/custom-gradient-picker/gradient-bar/test/utils.ts similarity index 100% rename from packages/components/src/custom-gradient-picker/gradient-bar/test/utils.js rename to packages/components/src/custom-gradient-picker/gradient-bar/test/utils.ts diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/utils.js b/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts similarity index 100% rename from packages/components/src/custom-gradient-picker/gradient-bar/utils.js rename to packages/components/src/custom-gradient-picker/gradient-bar/utils.ts diff --git a/packages/components/src/custom-gradient-picker/index.js b/packages/components/src/custom-gradient-picker/index.tsx similarity index 100% rename from packages/components/src/custom-gradient-picker/index.js rename to packages/components/src/custom-gradient-picker/index.tsx diff --git a/packages/components/src/custom-gradient-picker/serializer.js b/packages/components/src/custom-gradient-picker/serializer.ts similarity index 100% rename from packages/components/src/custom-gradient-picker/serializer.js rename to packages/components/src/custom-gradient-picker/serializer.ts diff --git a/packages/components/src/custom-gradient-picker/stories/index.js b/packages/components/src/custom-gradient-picker/stories/index.tsx similarity index 93% rename from packages/components/src/custom-gradient-picker/stories/index.js rename to packages/components/src/custom-gradient-picker/stories/index.tsx index 9e56d1534955d..3d70862e10486 100644 --- a/packages/components/src/custom-gradient-picker/stories/index.js +++ b/packages/components/src/custom-gradient-picker/stories/index.tsx @@ -6,7 +6,7 @@ import { useState } from '@wordpress/element'; /** * Internal dependencies */ -import CustomGradientPicker from '../'; +import CustomGradientPicker from '..'; export default { title: 'Components/CustomGradientPicker', diff --git a/packages/components/src/custom-gradient-picker/styles/custom-gradient-picker-styles.js b/packages/components/src/custom-gradient-picker/styles/custom-gradient-picker-styles.tsx similarity index 100% rename from packages/components/src/custom-gradient-picker/styles/custom-gradient-picker-styles.js rename to packages/components/src/custom-gradient-picker/styles/custom-gradient-picker-styles.tsx diff --git a/packages/components/src/custom-gradient-picker/test/serializer.js b/packages/components/src/custom-gradient-picker/test/serializer.ts similarity index 100% rename from packages/components/src/custom-gradient-picker/test/serializer.js rename to packages/components/src/custom-gradient-picker/test/serializer.ts diff --git a/packages/components/src/custom-gradient-picker/utils.js b/packages/components/src/custom-gradient-picker/utils.ts similarity index 100% rename from packages/components/src/custom-gradient-picker/utils.js rename to packages/components/src/custom-gradient-picker/utils.ts From 229f3a95cd01e55d5c85fbdc55dcf4047be452cb Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Wed, 22 Feb 2023 08:32:45 -0500 Subject: [PATCH 02/56] Add types.ts --- packages/components/src/custom-gradient-picker/types.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 packages/components/src/custom-gradient-picker/types.ts diff --git a/packages/components/src/custom-gradient-picker/types.ts b/packages/components/src/custom-gradient-picker/types.ts new file mode 100644 index 0000000000000..e69de29bb2d1d From 8e233ab28b9962d4060e4b51e7c21a5d7ddc7b4e Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Wed, 22 Feb 2023 10:45:39 -0500 Subject: [PATCH 03/56] Add initial component types --- .../src/custom-gradient-picker/index.tsx | 5 ++-- .../src/custom-gradient-picker/types.ts | 28 +++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/index.tsx b/packages/components/src/custom-gradient-picker/index.tsx index db47f25c88d94..a5b2e92e998bc 100644 --- a/packages/components/src/custom-gradient-picker/index.tsx +++ b/packages/components/src/custom-gradient-picker/index.tsx @@ -36,6 +36,7 @@ import { AccessoryWrapper, SelectWrapper, } from './styles/custom-gradient-picker-styles'; +import type { CustomGradientPickerProps } from './types'; const GradientAnglePicker = ( { gradientAST, hasGradient, onChange } ) => { const angle = @@ -112,8 +113,8 @@ export default function CustomGradientPicker( { __nextHasNoMargin = false, value, onChange, - __experimentalIsRenderedInSidebar, -} ) { + __experimentalIsRenderedInSidebar = false, +}: CustomGradientPickerProps ) { const gradientAST = getGradientAstWithDefault( value ); // On radial gradients the bar should display a linear gradient. // On radial gradients the bar represents a slice of the gradient from the center until the outside. diff --git a/packages/components/src/custom-gradient-picker/types.ts b/packages/components/src/custom-gradient-picker/types.ts index e69de29bb2d1d..590c0f7f6fb75 100644 --- a/packages/components/src/custom-gradient-picker/types.ts +++ b/packages/components/src/custom-gradient-picker/types.ts @@ -0,0 +1,28 @@ +export type CustomGradientPickerProps = { + /** + * Start opting in to the new margin-free styles that will become the default + * in a future version, currently scheduled to be WordPress 6.4. (The prop + * can be safely removed once this happens.) + * + * @default false + */ + __nextHasNoMargin?: boolean; + /** + * The current value of the gradient. Pass a css gradient string (See default value for example). + * Optionally pass in a `null` value to specify no gradient is currently selected. + * + * @default 'linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%)' + */ + value?: string; + /** + * The function called when a new gradient has been defined. It is passed to + * the `currentGradient` as an arugment. + */ + onChange: ( currentGradient: string | undefined ) => void; + /** + * Whether this is rendered in the sidebar. + * + * @default false + */ + __experimentalIsRenderedInSidebar?: boolean; +}; From f1a16e54f936ef56e015ca074652d62e57ec3438 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Thu, 23 Feb 2023 08:29:53 -0500 Subject: [PATCH 04/56] install `@types/gradient-parser` --- package-lock.json | 5 +++++ package.json | 1 + 2 files changed, 6 insertions(+) diff --git a/package-lock.json b/package-lock.json index 0ad5466ceb106..46bad766de3ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15260,6 +15260,11 @@ "@types/node": "*" } }, + "@types/gradient-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/gradient-parser/-/gradient-parser-0.1.2.tgz", + "integrity": "sha512-e2s1svCYJY8JDr2t/OoB/H4aWZy4sXUOAZ0NdSSHjKACw1jeU54gf4xj38di0AgVIObgzN1JSikJ5oSo3vxwgA==" + }, "@types/hammerjs": { "version": "2.0.41", "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.41.tgz", diff --git a/package.json b/package.json index 16b1e5478354c..f3bba32b7a50a 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "IS_GUTENBERG_PLUGIN": true }, "dependencies": { + "@types/gradient-parser": "0.1.2", "@wordpress/a11y": "file:packages/a11y", "@wordpress/annotations": "file:packages/annotations", "@wordpress/api-fetch": "file:packages/api-fetch", From 589f72820c3286cc41b7abd5830a0a8ffd692011 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Thu, 23 Feb 2023 12:54:58 -0500 Subject: [PATCH 05/56] Add typing to `getGradientAstWithDefault` --- .../src/custom-gradient-picker/index.tsx | 5 +-- .../src/custom-gradient-picker/serializer.ts | 11 ++++-- .../src/custom-gradient-picker/utils.ts | 36 +++++++++++-------- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/index.tsx b/packages/components/src/custom-gradient-picker/index.tsx index a5b2e92e998bc..361144590b7d9 100644 --- a/packages/components/src/custom-gradient-picker/index.tsx +++ b/packages/components/src/custom-gradient-picker/index.tsx @@ -115,12 +115,13 @@ export default function CustomGradientPicker( { onChange, __experimentalIsRenderedInSidebar = false, }: CustomGradientPickerProps ) { - const gradientAST = getGradientAstWithDefault( value ); + const { gradientAST, gradientAstValue } = + getGradientAstWithDefault( value ); // On radial gradients the bar should display a linear gradient. // On radial gradients the bar represents a slice of the gradient from the center until the outside. // On liner gradients the bar represents the color stops from left to right independently of the angle. const background = getLinearGradientRepresentation( gradientAST ); - const hasGradient = gradientAST.value !== DEFAULT_GRADIENT; + const hasGradient = gradientAstValue !== DEFAULT_GRADIENT; // Control points color option may be hex from presets, custom colors will be rgb. // The position should always be a percentage. const controlPoints = gradientAST.colorStops.map( ( colorStop ) => ( { diff --git a/packages/components/src/custom-gradient-picker/serializer.ts b/packages/components/src/custom-gradient-picker/serializer.ts index 934cf392ddd1e..a6c1fd4df4f5d 100644 --- a/packages/components/src/custom-gradient-picker/serializer.ts +++ b/packages/components/src/custom-gradient-picker/serializer.ts @@ -1,4 +1,7 @@ -// @ts-nocheck +/** + * External dependencies + */ +import type { GradientNode } from 'gradient-parser'; export function serializeGradientColor( { type, value } ) { if ( type === 'literal' ) { @@ -32,7 +35,11 @@ export function serializeGradientOrientation( orientation ) { return `${ orientation.value }deg`; } -export function serializeGradient( { type, orientation, colorStops } ) { +export function serializeGradient( { + type, + orientation, + colorStops, +}: GradientNode ) { const serializedOrientation = serializeGradientOrientation( orientation ); const serializedColorStops = colorStops .sort( ( colorStop1, colorStop2 ) => { diff --git a/packages/components/src/custom-gradient-picker/utils.ts b/packages/components/src/custom-gradient-picker/utils.ts index 69d8223fbdb87..e57f54d1b82b2 100644 --- a/packages/components/src/custom-gradient-picker/utils.ts +++ b/packages/components/src/custom-gradient-picker/utils.ts @@ -27,44 +27,52 @@ export function getLinearGradientRepresentation( gradientAST ) { } ); } -function hasUnsupportedLength( item ) { +function hasUnsupportedLength( item: gradientParser.ColorStop ) { return item.length === undefined || item.length.type !== '%'; } -export function getGradientAstWithDefault( value ) { +export function getGradientAstWithDefault( value?: string ) { // gradientAST will contain the gradient AST as parsed by gradient-parser npm module. // More information of its structure available at https://www.npmjs.com/package/gradient-parser#ast. - let gradientAST; + let gradientAST: gradientParser.GradientNode; + let gradientAstValue: string | undefined; + + const valueToParse = value ?? DEFAULT_GRADIENT; try { - gradientAST = gradientParser.parse( value )[ 0 ]; - gradientAST.value = value; + gradientAST = gradientParser.parse( valueToParse )[ 0 ]; + gradientAstValue = valueToParse; } catch ( error ) { gradientAST = gradientParser.parse( DEFAULT_GRADIENT )[ 0 ]; - gradientAST.value = DEFAULT_GRADIENT; + gradientAstValue = DEFAULT_GRADIENT; } - if ( gradientAST.orientation?.type === 'directional' ) { - gradientAST.orientation.type = 'angular'; - gradientAST.orientation.value = - DIRECTIONAL_ORIENTATION_ANGLE_MAP[ + if ( + ! Array.isArray( gradientAST.orientation ) && + gradientAST.orientation?.type === 'directional' + ) { + gradientAST.orientation = { + type: 'angular', + value: DIRECTIONAL_ORIENTATION_ANGLE_MAP[ gradientAST.orientation.value - ].toString(); + ].toString(), + }; } if ( gradientAST.colorStops.some( hasUnsupportedLength ) ) { + // NTS: Followup - `colorStops` is destructured before being mutated, but it's never reinserted into `gradientAST`. This could mean the original object might still have unspoorted lengths. Investigate. const { colorStops } = gradientAST; const step = 100 / ( colorStops.length - 1 ); colorStops.forEach( ( stop, index ) => { stop.length = { - value: step * index, + value: `${ step * index }`, type: '%', }; } ); - gradientAST.value = serializeGradient( gradientAST ); + gradientAstValue = serializeGradient( gradientAST ); } - return gradientAST; + return { gradientAST, gradientAstValue }; } export function getGradientAstWithControlPoints( From 538a0a51d071cfd4d93d8345d22a92257591e416 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Thu, 23 Feb 2023 12:56:15 -0500 Subject: [PATCH 06/56] Assert that `colorStop.length` is no longer null, due to the `hasUnsupportedValue` check that has already run' --- .../components/src/custom-gradient-picker/index.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/index.tsx b/packages/components/src/custom-gradient-picker/index.tsx index 361144590b7d9..26041e19460a1 100644 --- a/packages/components/src/custom-gradient-picker/index.tsx +++ b/packages/components/src/custom-gradient-picker/index.tsx @@ -124,10 +124,15 @@ export default function CustomGradientPicker( { const hasGradient = gradientAstValue !== DEFAULT_GRADIENT; // Control points color option may be hex from presets, custom colors will be rgb. // The position should always be a percentage. - const controlPoints = gradientAST.colorStops.map( ( colorStop ) => ( { - color: getStopCssColor( colorStop ), - position: parseInt( colorStop.length.value ), - } ) ); + const controlPoints = gradientAST.colorStops.map( ( colorStop ) => { + // Although it's already been checked by `hasUnsupportedLength` in `getGradientAstWithDefault`, + // TypeScript doesn't know that `colorStop.length` is not undefined here. + //NTS: Is there may be a more elegant way to help TS see that for itself? 🤔 + return { + color: getStopCssColor( colorStop ), + position: parseInt( colorStop.length!.value ), + }; + } ); if ( ! __nextHasNoMargin ) { deprecated( From da497b566a09997e388249826b0b83d9624b715a Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Thu, 23 Feb 2023 16:06:24 -0500 Subject: [PATCH 07/56] Add typing to `GradientAnglePicker` --- .../src/custom-gradient-picker/index.tsx | 15 +++++++++++---- .../src/custom-gradient-picker/types.ts | 13 +++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/index.tsx b/packages/components/src/custom-gradient-picker/index.tsx index 26041e19460a1..8baf910d1f8a5 100644 --- a/packages/components/src/custom-gradient-picker/index.tsx +++ b/packages/components/src/custom-gradient-picker/index.tsx @@ -36,18 +36,25 @@ import { AccessoryWrapper, SelectWrapper, } from './styles/custom-gradient-picker-styles'; -import type { CustomGradientPickerProps } from './types'; +import type { + CustomGradientPickerProps, + GradientAnglePickerProps, +} from './types'; -const GradientAnglePicker = ( { gradientAST, hasGradient, onChange } ) => { +const GradientAnglePicker = ( { + gradientAST, + hasGradient, + onChange, +}: GradientAnglePickerProps ) => { const angle = gradientAST?.orientation?.value ?? DEFAULT_LINEAR_GRADIENT_ANGLE; - const onAngleChange = ( newAngle ) => { + const onAngleChange = ( newAngle: number ) => { onChange( serializeGradient( { ...gradientAST, orientation: { type: 'angular', - value: newAngle, + value: `${ newAngle }`, }, } ) ); diff --git a/packages/components/src/custom-gradient-picker/types.ts b/packages/components/src/custom-gradient-picker/types.ts index 590c0f7f6fb75..f4485b868f544 100644 --- a/packages/components/src/custom-gradient-picker/types.ts +++ b/packages/components/src/custom-gradient-picker/types.ts @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import type gradientParser from 'gradient-parser'; + export type CustomGradientPickerProps = { /** * Start opting in to the new margin-free styles that will become the default @@ -26,3 +31,11 @@ export type CustomGradientPickerProps = { */ __experimentalIsRenderedInSidebar?: boolean; }; + +export type GradientAnglePickerProps = { + gradientAST: + | gradientParser.LinearGradientNode + | gradientParser.RepeatingLinearGradientNode; + hasGradient: boolean; + onChange: ( gradient: string ) => void; +}; From 108e75cbc28d0809a25cc63698b96973a21cc34a Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Mon, 27 Feb 2023 11:09:25 -0500 Subject: [PATCH 08/56] Add typing for `gradientTypePicker` --- .../src/custom-gradient-picker/index.tsx | 23 ++++++++++++++----- .../src/custom-gradient-picker/types.ts | 6 +++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/index.tsx b/packages/components/src/custom-gradient-picker/index.tsx index 8baf910d1f8a5..c58d9aebb7d3b 100644 --- a/packages/components/src/custom-gradient-picker/index.tsx +++ b/packages/components/src/custom-gradient-picker/index.tsx @@ -4,6 +4,7 @@ * External dependencies */ import classnames from 'classnames'; +import type gradientParser from 'gradient-parser'; /** * WordPress dependencies @@ -39,6 +40,7 @@ import { import type { CustomGradientPickerProps, GradientAnglePickerProps, + GradientTypePickerProps, } from './types'; const GradientAnglePicker = ( { @@ -68,8 +70,13 @@ const GradientAnglePicker = ( { ); }; -const GradientTypePicker = ( { gradientAST, hasGradient, onChange } ) => { +const GradientTypePicker = ( { + gradientAST, + hasGradient, + onChange, +}: GradientTypePickerProps ) => { const { type } = gradientAST; + const onSetLinearGradient = () => { onChange( serializeGradient( { @@ -78,7 +85,8 @@ const GradientTypePicker = ( { gradientAST, hasGradient, onChange } ) => { ? {} : { orientation: HORIZONTAL_GRADIENT_ORIENTATION } ), type: 'linear-gradient', - } ) + // NTS: this isn't very elegant, but the `orientation` props between linear and radial gradients don't overlap (according to DefinitelyTyped) so TypeScript gets confused by this manual change. + } as gradientParser.LinearGradientNode ) ); }; @@ -92,11 +100,13 @@ const GradientTypePicker = ( { gradientAST, hasGradient, onChange } ) => { ); }; - const handleOnChange = ( next ) => { - if ( next === 'linear-gradient' ) { + const handleOnChange = ( next: string | string[] ) => { + // NTS: Because `SelectControl.onChange` (below) can accept an array, we need to accept one here. We shouldn't ever see an array here, but I'm adding a check just in case. + const nextValue = Array.isArray( next ) ? next[ 0 ] : next; + if ( nextValue === 'linear-gradient' ) { onSetLinearGradient(); } - if ( next === 'radial-gradient' ) { + if ( nextValue === 'radial-gradient' ) { onSetRadialGradient(); } }; @@ -110,7 +120,8 @@ const GradientTypePicker = ( { gradientAST, hasGradient, onChange } ) => { onChange={ handleOnChange } options={ GRADIENT_OPTIONS } size="__unstable-large" - value={ hasGradient && type } + // NTS: Small runtime change. Previously, if `type` was undefined (which I don't think should ever happen) this check would return `false`, which would be an invalid value. + value={ hasGradient ? type : undefined } /> ); }; diff --git a/packages/components/src/custom-gradient-picker/types.ts b/packages/components/src/custom-gradient-picker/types.ts index f4485b868f544..d8a345345c502 100644 --- a/packages/components/src/custom-gradient-picker/types.ts +++ b/packages/components/src/custom-gradient-picker/types.ts @@ -39,3 +39,9 @@ export type GradientAnglePickerProps = { hasGradient: boolean; onChange: ( gradient: string ) => void; }; + +export type GradientTypePickerProps = { + gradientAST: gradientParser.GradientNode; + hasGradient: boolean; + onChange: ( gradient: string ) => void; +}; From cfb04f02546a4b0cf0f41224c815cc1484350b13 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Mon, 27 Feb 2023 12:17:45 -0500 Subject: [PATCH 09/56] Introduce `ControlPoint` type and apply to `CustomGradientPicker.onChange`' --- packages/components/src/custom-gradient-picker/index.tsx | 2 +- packages/components/src/custom-gradient-picker/types.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/components/src/custom-gradient-picker/index.tsx b/packages/components/src/custom-gradient-picker/index.tsx index c58d9aebb7d3b..3ee0dfba3cf08 100644 --- a/packages/components/src/custom-gradient-picker/index.tsx +++ b/packages/components/src/custom-gradient-picker/index.tsx @@ -177,7 +177,7 @@ export default function CustomGradientPicker( { background={ background } hasGradient={ hasGradient } value={ controlPoints } - onChange={ ( newControlPoints ) => { + onChange={ ( newControlPoints: ControlPoint[] ) => { onChange( serializeGradient( getGradientAstWithControlPoints( diff --git a/packages/components/src/custom-gradient-picker/types.ts b/packages/components/src/custom-gradient-picker/types.ts index d8a345345c502..be32e6121afc9 100644 --- a/packages/components/src/custom-gradient-picker/types.ts +++ b/packages/components/src/custom-gradient-picker/types.ts @@ -45,3 +45,5 @@ export type GradientTypePickerProps = { hasGradient: boolean; onChange: ( gradient: string ) => void; }; + +export type ControlPoint = { color: string; position: number }; From 6cec73c7d06250b41a48957b39d938fa27ec8312 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Mon, 27 Feb 2023 14:32:42 -0500 Subject: [PATCH 10/56] Add typing to main component utils --- .../src/custom-gradient-picker/serializer.ts | 4 ++-- .../src/custom-gradient-picker/utils.ts | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/serializer.ts b/packages/components/src/custom-gradient-picker/serializer.ts index a6c1fd4df4f5d..54544f65046c6 100644 --- a/packages/components/src/custom-gradient-picker/serializer.ts +++ b/packages/components/src/custom-gradient-picker/serializer.ts @@ -1,7 +1,7 @@ /** * External dependencies */ -import type { GradientNode } from 'gradient-parser'; +import type gradientParser from 'gradient-parser'; export function serializeGradientColor( { type, value } ) { if ( type === 'literal' ) { @@ -39,7 +39,7 @@ export function serializeGradient( { type, orientation, colorStops, -}: GradientNode ) { +}: gradientParser.GradientNode ) { const serializedOrientation = serializeGradientOrientation( orientation ); const serializedColorStops = colorStops .sort( ( colorStop1, colorStop2 ) => { diff --git a/packages/components/src/custom-gradient-picker/utils.ts b/packages/components/src/custom-gradient-picker/utils.ts index e57f54d1b82b2..ae1e0df5c4825 100644 --- a/packages/components/src/custom-gradient-picker/utils.ts +++ b/packages/components/src/custom-gradient-picker/utils.ts @@ -16,15 +16,22 @@ import { DIRECTIONAL_ORIENTATION_ANGLE_MAP, } from './constants'; import { serializeGradient } from './serializer'; +import type { ControlPoint } from './types'; extend( [ namesPlugin ] ); -export function getLinearGradientRepresentation( gradientAST ) { +export function getLinearGradientRepresentation( + gradientAST: gradientParser.GradientNode +) { return serializeGradient( { type: 'linear-gradient', - orientation: HORIZONTAL_GRADIENT_ORIENTATION, + orientation: { + ...HORIZONTAL_GRADIENT_ORIENTATION, + value: `${ HORIZONTAL_GRADIENT_ORIENTATION.value }`, + }, colorStops: gradientAST.colorStops, - } ); + // NTS: As noted elsewhere, gradientParser's DefinetelyTyped values provide no overlap between the different `orientation` values for linear vs radial nodes. + } as gradientParser.LinearGradientNode ); } function hasUnsupportedLength( item: gradientParser.ColorStop ) { @@ -76,8 +83,8 @@ export function getGradientAstWithDefault( value?: string ) { } export function getGradientAstWithControlPoints( - gradientAST, - newControlPoints + gradientAST: gradientParser.GradientNode, + newControlPoints: ControlPoint[] ) { return { ...gradientAST, @@ -95,7 +102,7 @@ export function getGradientAstWithControlPoints( }; } -export function getStopCssColor( colorStop ) { +export function getStopCssColor( colorStop: gradientParser.ColorStop ) { switch ( colorStop.type ) { case 'hex': return `#${ colorStop.value }`; From b82267a4226358ebacc34913486333947835b62f Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Thu, 2 Mar 2023 13:03:31 -0500 Subject: [PATCH 11/56] Add serializer typing --- .../src/custom-gradient-picker/serializer.ts | 43 +++++++++++++++---- .../src/custom-gradient-picker/types.ts | 8 ++++ 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/serializer.ts b/packages/components/src/custom-gradient-picker/serializer.ts index 54544f65046c6..4c7b23d6f26d7 100644 --- a/packages/components/src/custom-gradient-picker/serializer.ts +++ b/packages/components/src/custom-gradient-picker/serializer.ts @@ -2,8 +2,15 @@ * External dependencies */ import type gradientParser from 'gradient-parser'; +/** + * Internal dependencies + */ +import type { ColorStopTypeAndValue } from './types'; -export function serializeGradientColor( { type, value } ) { +export function serializeGradientColor( { + type, + value, +}: ColorStopTypeAndValue ) { if ( type === 'literal' ) { return value; } @@ -13,7 +20,9 @@ export function serializeGradientColor( { type, value } ) { return `${ type }(${ value.join( ',' ) })`; } -export function serializeGradientPosition( position ) { +export function serializeGradientPosition( + position: gradientParser.ColorStop[ 'length' ] +) { if ( ! position ) { return ''; } @@ -21,15 +30,25 @@ export function serializeGradientPosition( position ) { return `${ value }${ type }`; } -export function serializeGradientColorStop( { type, value, length } ) { +export function serializeGradientColorStop( { + type, + value, + length, +}: gradientParser.ColorStop ) { return `${ serializeGradientColor( { type, value, - } ) } ${ serializeGradientPosition( length ) }`; + } as ColorStopTypeAndValue ) } ${ serializeGradientPosition( length ) }`; } -export function serializeGradientOrientation( orientation ) { - if ( ! orientation || orientation.type !== 'angular' ) { +export function serializeGradientOrientation( + orientation: gradientParser.GradientNode[ 'orientation' ] +) { + if ( + Array.isArray( orientation ) || + ! orientation || + orientation.type !== 'angular' + ) { return; } return `${ orientation.value }deg`; @@ -43,9 +62,17 @@ export function serializeGradient( { const serializedOrientation = serializeGradientOrientation( orientation ); const serializedColorStops = colorStops .sort( ( colorStop1, colorStop2 ) => { + const getNumericStopValue = ( + colorStop: gradientParser.ColorStop + ) => { + return colorStop?.length?.value === undefined + ? 0 + : parseInt( colorStop?.length?.value ); + }; + return ( - ( colorStop1?.length?.value ?? 0 ) - - ( colorStop2?.length?.value ?? 0 ) + getNumericStopValue( colorStop1 ) - + getNumericStopValue( colorStop2 ) ); } ) .map( serializeGradientColorStop ); diff --git a/packages/components/src/custom-gradient-picker/types.ts b/packages/components/src/custom-gradient-picker/types.ts index be32e6121afc9..c5179b82b9b82 100644 --- a/packages/components/src/custom-gradient-picker/types.ts +++ b/packages/components/src/custom-gradient-picker/types.ts @@ -47,3 +47,11 @@ export type GradientTypePickerProps = { }; export type ControlPoint = { color: string; position: number }; + +type DistributiveOmit< T, K extends keyof any > = T extends any + ? Omit< T, K > + : never; +export type ColorStopTypeAndValue = DistributiveOmit< + gradientParser.ColorStop, + 'length' +>; From 93977655968229bb993ea51e64984270ca4b657b Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Thu, 2 Mar 2023 13:04:28 -0500 Subject: [PATCH 12/56] Fix union conflict on `getGradientAstWithControlPoints` --- packages/components/src/custom-gradient-picker/utils.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/utils.ts b/packages/components/src/custom-gradient-picker/utils.ts index ae1e0df5c4825..0b9e16be24bf8 100644 --- a/packages/components/src/custom-gradient-picker/utils.ts +++ b/packages/components/src/custom-gradient-picker/utils.ts @@ -96,10 +96,14 @@ export function getGradientAstWithControlPoints( value: position?.toString(), }, type: a < 1 ? 'rgba' : 'rgb', - value: a < 1 ? [ r, g, b, a ] : [ r, g, b ], + value: + a < 1 + ? [ `${ r }`, `${ g }`, `${ b }`, `${ a }` ] + : [ `${ r }`, `${ g }`, `${ b }` ], }; } ), - }; + // NTS: there are a few `as` calls throughout the changes. Some are to avoid conflicts between union members. Note that in your description. + } as gradientParser.GradientNode; } export function getStopCssColor( colorStop: gradientParser.ColorStop ) { From ad502da5de51e096976d328ad68478eb7d2d6807 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Thu, 2 Mar 2023 17:34:34 -0500 Subject: [PATCH 13/56] Add `CustomGradientBar` subcomponent typing --- .../gradient-bar/index.tsx | 16 +++++++++++++--- .../src/custom-gradient-picker/index.tsx | 2 +- .../src/custom-gradient-picker/types.ts | 10 ++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx b/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx index 19b5746847e4b..997588a4b36e0 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx +++ b/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx @@ -16,6 +16,8 @@ import { useRef, useReducer } from '@wordpress/element'; import ControlPoints from './control-points'; import { getHorizontalRelativeGradientPosition } from './utils'; import { MINIMUM_DISTANCE_BETWEEN_INSERTER_AND_POINT } from './constants'; +import type { CustomGradientBarProps } from '../types'; +import type { LegacyRef, MouseEventHandler } from 'react'; function customGradientBarReducer( state, action ) { switch ( action.type ) { @@ -76,19 +78,27 @@ export default function CustomGradientBar( { disableInserter = false, disableAlpha = false, __experimentalIsRenderedInSidebar, -} ) { - const gradientMarkersContainerDomRef = useRef(); +}: CustomGradientBarProps ) { + const gradientMarkersContainerDomRef: LegacyRef< HTMLDivElement > = + useRef( null ); const [ gradientBarState, gradientBarStateDispatch ] = useReducer( customGradientBarReducer, customGradientBarReducerInitialState ); - const onMouseEnterAndMove = ( event ) => { + const onMouseEnterAndMove: MouseEventHandler< HTMLDivElement > = ( + event + ) => { const insertPosition = getHorizontalRelativeGradientPosition( event.clientX, gradientMarkersContainerDomRef.current ); + // NTS: `getHorizontalRelativeGradientPosition` will return `undefined` if the `containerElement` arg is falsey. + if ( insertPosition === undefined ) { + return; + } + // If the insert point is close to an existing control point don't show it. if ( controlPoints.some( ( { position } ) => { diff --git a/packages/components/src/custom-gradient-picker/index.tsx b/packages/components/src/custom-gradient-picker/index.tsx index 3ee0dfba3cf08..c58d9aebb7d3b 100644 --- a/packages/components/src/custom-gradient-picker/index.tsx +++ b/packages/components/src/custom-gradient-picker/index.tsx @@ -177,7 +177,7 @@ export default function CustomGradientPicker( { background={ background } hasGradient={ hasGradient } value={ controlPoints } - onChange={ ( newControlPoints: ControlPoint[] ) => { + onChange={ ( newControlPoints ) => { onChange( serializeGradient( getGradientAstWithControlPoints( diff --git a/packages/components/src/custom-gradient-picker/types.ts b/packages/components/src/custom-gradient-picker/types.ts index c5179b82b9b82..b2707230191f0 100644 --- a/packages/components/src/custom-gradient-picker/types.ts +++ b/packages/components/src/custom-gradient-picker/types.ts @@ -55,3 +55,13 @@ export type ColorStopTypeAndValue = DistributiveOmit< gradientParser.ColorStop, 'length' >; + +export type CustomGradientBarProps = { + background: React.CSSProperties[ 'background' ]; + hasGradient: boolean; + value: ControlPoint[]; + onChange: ( newControlPoints: ControlPoint[] ) => void; + disableInserter?: boolean; + disableAlpha?: boolean; + __experimentalIsRenderedInSidebar: boolean; +}; From 6504d13309a7fd9ac76c06c0d01ab9ca8af018c9 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Mon, 6 Mar 2023 12:04:58 -0500 Subject: [PATCH 14/56] Add typing to `customGradientBarReducer` --- .../gradient-bar/index.tsx | 18 ++++++++++--- .../src/custom-gradient-picker/types.ts | 25 +++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx b/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx index 997588a4b36e0..750ec711dba3c 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx +++ b/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx @@ -16,10 +16,18 @@ import { useRef, useReducer } from '@wordpress/element'; import ControlPoints from './control-points'; import { getHorizontalRelativeGradientPosition } from './utils'; import { MINIMUM_DISTANCE_BETWEEN_INSERTER_AND_POINT } from './constants'; -import type { CustomGradientBarProps } from '../types'; +import type { + CustomGradientBarProps, + CustomGradientBarReducerState, + CustomGradientBarReducerAction, + CustomGradientBarIdleState, +} from '../types'; import type { LegacyRef, MouseEventHandler } from 'react'; -function customGradientBarReducer( state, action ) { +const customGradientBarReducer = ( + state: CustomGradientBarReducerState, + action: CustomGradientBarReducerAction +): CustomGradientBarReducerState => { switch ( action.type ) { case 'MOVE_INSERTER': if ( state.id === 'IDLE' || state.id === 'MOVING_INSERTER' ) { @@ -67,8 +75,10 @@ function customGradientBarReducer( state, action ) { break; } return state; -} -const customGradientBarReducerInitialState = { id: 'IDLE' }; +}; +const customGradientBarReducerInitialState: CustomGradientBarIdleState = { + id: 'IDLE', +}; export default function CustomGradientBar( { background, diff --git a/packages/components/src/custom-gradient-picker/types.ts b/packages/components/src/custom-gradient-picker/types.ts index b2707230191f0..c2944d8e6549d 100644 --- a/packages/components/src/custom-gradient-picker/types.ts +++ b/packages/components/src/custom-gradient-picker/types.ts @@ -65,3 +65,28 @@ export type CustomGradientBarProps = { disableAlpha?: boolean; __experimentalIsRenderedInSidebar: boolean; }; + +export type CustomGradientBarIdleState = { id: 'IDLE' }; +type CustomGradientBarMovingInserterState = { + id: 'MOVING_INSERTER'; + insertPosition: number; +}; +type CustomGradientBarInsertingControlPointState = { + id: 'INSERTING_CONTROL_POINT'; + insertPosition: number; +}; +type CustomGradientBarMovingControlPointState = { id: 'MOVING_CONTROL_POINT' }; + +export type CustomGradientBarReducerState = + | CustomGradientBarIdleState + | CustomGradientBarMovingInserterState + | CustomGradientBarInsertingControlPointState + | CustomGradientBarMovingControlPointState; + +export type CustomGradientBarReducerAction = + | { type: 'MOVE_INSERTER'; insertPosition: number } + | { type: 'STOP_INSERTER_MOVE' } + | { type: 'OPEN_INSERTER' } + | { type: 'CLOSE_INSERTER' } + | { type: 'START_CONTROL_CHANGE' } + | { type: 'STOP_CONTROL_CHANGE' }; From c62b3cc396e81a3298ad82f6512a905e1d75515f Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Mon, 6 Mar 2023 15:53:48 -0500 Subject: [PATCH 15/56] Add typing for `GradientBar` utils --- .../gradient-bar/utils.ts | 143 ++++-------------- 1 file changed, 32 insertions(+), 111 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts b/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts index 24b552e0c149f..ca0d2ce50f2bc 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts +++ b/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts @@ -4,50 +4,16 @@ * Internal dependencies */ import { MINIMUM_DISTANCE_BETWEEN_POINTS } from './constants'; +import type { ControlPoint } from '../types'; -/** - * Control point for the gradient bar. - * - * @typedef {Object} ControlPoint - * @property {string} color Color of the control point. - * @property {number} position Integer position of the control point as a percentage. - */ - -/** - * Color as parsed from the gradient by gradient-parser. - * - * @typedef {Object} Color - * @property {string} r Red component. - * @property {string} g Green component. - * @property {string} b Green component. - * @property {string} [a] Optional alpha component. - */ - -/** - * Clamps a number between 0 and 100. - * - * @param {number} value Value to clamp. - * - * @return {number} Value clamped between 0 and 100. - */ -export function clampPercent( value ) { +export function clampPercent( value: number ) { return Math.max( 0, Math.min( 100, value ) ); } -/** - * Check if a control point is overlapping with another. - * - * @param {ControlPoint[]} value Array of control points. - * @param {number} initialIndex Index of the position to test. - * @param {number} newPosition New position of the control point. - * @param {number} minDistance Distance considered to be overlapping. - * - * @return {boolean} True if the point is overlapping. - */ export function isOverlapping( - value, - initialIndex, - newPosition, + value: ControlPoint[], + initialIndex: number, + newPosition: number, minDistance = MINIMUM_DISTANCE_BETWEEN_POINTS ) { const initialPosition = value[ initialIndex ].position; @@ -63,16 +29,11 @@ export function isOverlapping( } ); } -/** - * Adds a control point from an array and returns the new array. - * - * @param {ControlPoint[]} points Array of control points. - * @param {number} position Position to insert the new point. - * @param {Color} color Color to update the control point at index. - * - * @return {ControlPoint[]} New array of control points. - */ -export function addControlPoint( points, position, color ) { +export function addControlPoint( + points: ControlPoint[], + position: number, + color: string +) { const nextIndex = points.findIndex( ( point ) => point.position > position ); @@ -82,45 +43,27 @@ export function addControlPoint( points, position, color ) { return newPoints; } -/** - * Removes a control point from an array and returns the new array. - * - * @param {ControlPoint[]} points Array of control points. - * @param {number} index Index to remove. - * - * @return {ControlPoint[]} New array of control points. - */ -export function removeControlPoint( points, index ) { - return points.filter( ( point, pointIndex ) => { +export function removeControlPoint( points: ControlPoint[], index: number ) { + return points.filter( ( _point, pointIndex ) => { return pointIndex !== index; } ); } -/** - * Updates a control point from an array and returns the new array. - * - * @param {ControlPoint[]} points Array of control points. - * @param {number} index Index to update. - * @param {ControlPoint[]} newPoint New control point to replace the index. - * - * @return {ControlPoint[]} New array of control points. - */ -export function updateControlPoint( points, index, newPoint ) { +export function updateControlPoint( + points: ControlPoint[], + index: number, + newPoint: ControlPoint +) { const newValue = points.slice(); newValue[ index ] = newPoint; return newValue; } -/** - * Updates the position of a control point from an array and returns the new array. - * - * @param {ControlPoint[]} points Array of control points. - * @param {number} index Index to update. - * @param {number} newPosition Position to move the control point at index. - * - * @return {ControlPoint[]} New array of control points. - */ -export function updateControlPointPosition( points, index, newPosition ) { +export function updateControlPointPosition( + points: ControlPoint[], + index: number, + newPosition: ControlPoint[ 'position' ] +) { if ( isOverlapping( points, index, newPosition ) ) { return points; } @@ -131,16 +74,11 @@ export function updateControlPointPosition( points, index, newPosition ) { return updateControlPoint( points, index, newPoint ); } -/** - * Updates the position of a control point from an array and returns the new array. - * - * @param {ControlPoint[]} points Array of control points. - * @param {number} index Index to update. - * @param {Color} newColor Color to update the control point at index. - * - * @return {ControlPoint[]} New array of control points. - */ -export function updateControlPointColor( points, index, newColor ) { +export function updateControlPointColor( + points: ControlPoint[], + index: number, + newColor: string +) { const newPoint = { ...points[ index ], color: newColor, @@ -148,35 +86,18 @@ export function updateControlPointColor( points, index, newColor ) { return updateControlPoint( points, index, newPoint ); } -/** - * Updates the position of a control point from an array and returns the new array. - * - * @param {ControlPoint[]} points Array of control points. - * @param {number} position Position of the color stop. - * @param {string} newColor Color to update the control point at index. - * - * @return {ControlPoint[]} New array of control points. - */ export function updateControlPointColorByPosition( - points, - position, - newColor + points: ControlPoint[], + position: ControlPoint[ 'position' ], + newColor: string ) { const index = points.findIndex( ( point ) => point.position === position ); return updateControlPointColor( points, index, newColor ); } -/** - * Gets the horizontal coordinate when dragging a control point with the mouse. - * - * @param {number} mouseXCoordinate Horizontal coordinate of the mouse position. - * @param {Element} containerElement Container for the gradient picker. - * - * @return {number | undefined} Whole number percentage from the left. - */ export function getHorizontalRelativeGradientPosition( - mouseXCoordinate, - containerElement + mouseXCoordinate: number, + containerElement: HTMLDivElement | null ) { if ( ! containerElement ) { return; From 62d1bff1a0395b15b4d5d39aaa8dfe772dcb9554 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Tue, 7 Mar 2023 11:09:39 -0500 Subject: [PATCH 16/56] Add typing to `ControlPoints` --- .../gradient-bar/control-points.tsx | 49 +++++++++++++++---- .../src/custom-gradient-picker/types.ts | 35 +++++++++++++ 2 files changed, 74 insertions(+), 10 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx index 6b964d047b2a5..3a143b4a00006 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx +++ b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx @@ -36,8 +36,22 @@ import { MINIMUM_SIGNIFICANT_MOVE, KEYBOARD_CONTROL_POINT_VARIATION, } from './constants'; +import type { WordPressComponentProps } from '../../ui/context'; +import type { + ControlPointButtonProps, + ControlPointMoveState, + ControlPointsProps, + InsertPointProps, +} from '../types'; +import type { DropdownProps } from '../../dropdown/types'; +import type { CustomColorPickerDropdownProps } from '../../color-palette/types'; -function ControlPointButton( { isOpen, position, color, ...additionalProps } ) { +function ControlPointButton( { + isOpen, + position, + color, + ...additionalProps +}: WordPressComponentProps< ControlPointButtonProps, 'button', true > ) { const instanceId = useInstanceId( ControlPointButton ); const descriptionId = `components-custom-gradient-picker__control-point-button-description-${ instanceId }`; return ( @@ -75,9 +89,9 @@ function GradientColorPickerDropdown( { isRenderedInSidebar, className, ...props -} ) { +}: WordPressComponentProps< CustomColorPickerDropdownProps, 'div', true > ) { // Open the popover below the gradient control/insertion point - const popoverProps = useMemo( + const popoverProps: DropdownProps[ 'popoverProps' ] = useMemo( () => ( { placement: 'bottom', offset: 8, @@ -110,16 +124,29 @@ function ControlPoints( { onStartControlPointChange, onStopControlPointChange, __experimentalIsRenderedInSidebar, -} ) { - const controlPointMoveState = useRef(); +}: ControlPointsProps ) { + const controlPointMoveState: React.MutableRefObject< + ControlPointMoveState | undefined + > = useRef(); - const onMouseMove = ( event ) => { + const onMouseMove = ( event: MouseEvent ) => { const relativePosition = getHorizontalRelativeGradientPosition( event.clientX, gradientPickerDomRef.current ); + const { initialPosition, index, significantMoveHappened } = - controlPointMoveState.current; + controlPointMoveState?.current ?? {}; + + if ( + initialPosition === undefined || + relativePosition === undefined || + index === undefined || + controlPointMoveState.current === undefined + ) { + return; + } + if ( ! significantMoveHappened && Math.abs( initialPosition - relativePosition ) >= @@ -150,12 +177,14 @@ function ControlPoints( { // Adding `cleanEventListeners` to the dependency array below requires the function itself to be wrapped in a `useCallback` // This memoization would prevent the event listeners from being properly cleaned. // Instead, we'll pass a ref to the function in our `useEffect` so `cleanEventListeners` itself is no longer a dependency. - const cleanEventListenersRef = useRef(); + const cleanEventListenersRef: React.MutableRefObject< + ( () => void ) | undefined + > = useRef(); cleanEventListenersRef.current = cleanEventListeners; useEffect( () => { return () => { - cleanEventListenersRef.current(); + cleanEventListenersRef.current?.(); }; }, [] ); @@ -296,7 +325,7 @@ function InsertPoint( { insertPosition, disableAlpha, __experimentalIsRenderedInSidebar, -} ) { +}: InsertPointProps ) { const [ alreadyInsertedPoint, setAlreadyInsertedPoint ] = useState( false ); return ( ; + ignoreMarkerPosition?: number; + value: ControlPoint[]; + onChange: ( conrolPoints: ControlPoint[] ) => void; + onStartControlPointChange: () => void; + onStopControlPointChange: () => void; + __experimentalIsRenderedInSidebar: boolean; +}; + +export type ControlPointMoveState = { + initialPosition: number; + index: number; + significantMoveHappened: boolean; + listenersActivated: boolean; +}; + +export type InsertPointProps = { + value: ControlPoint[]; + onChange: ( controlPoints: ControlPoint[] ) => void; + onOpenInserter: () => void; + onCloseInserter: () => void; + insertPosition: number; + disableAlpha: boolean; + __experimentalIsRenderedInSidebar: boolean; +}; From 11fca9b43fe4fbd13fc6d3f0d4308c1dd8e38e25 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Tue, 7 Mar 2023 13:16:03 -0500 Subject: [PATCH 17/56] Refactor `ControlPoints` return value to a single JSX element --- .../gradient-bar/control-points.tsx | 256 ++++++++++-------- 1 file changed, 136 insertions(+), 120 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx index 3a143b4a00006..6b8a7cf464752 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx +++ b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx @@ -188,133 +188,149 @@ function ControlPoints( { }; }, [] ); - return controlPoints.map( ( point, index ) => { - const initialPosition = point?.position; - return ( - ignoreMarkerPosition !== initialPosition && ( - ( - + { controlPoints.map( ( point, index ) => { + const initialPosition = point?.position; + return ( + ignoreMarkerPosition !== initialPosition && ( + { - if ( - controlPointMoveState.current && - controlPointMoveState.current - .significantMoveHappened - ) { - return; - } - if ( isOpen ) { - onStopControlPointChange(); - } else { - onStartControlPointChange(); - } - onToggle(); - } } - onMouseDown={ () => { - if ( window && window.addEventListener ) { - controlPointMoveState.current = { - initialPosition, - index, - significantMoveHappened: false, - listenersActivated: true, - }; - onStartControlPointChange(); - window.addEventListener( - 'mousemove', - onMouseMove - ); - window.addEventListener( - 'mouseup', - cleanEventListeners - ); - } - } } - onKeyDown={ ( event ) => { - if ( event.code === 'ArrowLeft' ) { - // Stop propagation of the key press event to avoid focus moving - // to another editor area. - event.stopPropagation(); - onChange( - updateControlPointPosition( - controlPoints, - index, - clampPercent( - point.position - - KEYBOARD_CONTROL_POINT_VARIATION - ) - ) - ); - } else if ( event.code === 'ArrowRight' ) { - // Stop propagation of the key press event to avoid focus moving - // to another editor area. - event.stopPropagation(); - onChange( - updateControlPointPosition( - controlPoints, - index, - clampPercent( - point.position + - KEYBOARD_CONTROL_POINT_VARIATION - ) - ) - ); - } - } } - isOpen={ isOpen } - position={ point.position } - color={ point.color } - /> - ) } - renderContent={ ( { onClose } ) => ( - <> - { - onChange( - updateControlPointColor( - controlPoints, - index, - colord( color ).toRgbString() - ) - ); - } } - /> - { ! disableRemove && controlPoints.length > 2 && ( - - - + /> + { ! disableRemove && + controlPoints.length > 2 && ( + + + + ) } + ) } - - ) } - style={ { - left: `${ point.position }%`, - transform: 'translateX( -50% )', - } } - /> - ) - ); - } ); + style={ { + left: `${ point.position }%`, + transform: 'translateX( -50% )', + } } + /> + ) + ); + } ) } + + ); } function InsertPoint( { From 6f30b088a87f8fa4f45d8f7104a478c2119ed7ed Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Tue, 7 Mar 2023 15:08:55 -0500 Subject: [PATCH 18/56] Update tests to align with `gradientParser` typing --- .../custom-gradient-picker/test/serializer.ts | 73 ++++++++++--------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/test/serializer.ts b/packages/components/src/custom-gradient-picker/test/serializer.ts index 3b11e41f7c91d..c0422127bbbfb 100644 --- a/packages/components/src/custom-gradient-picker/test/serializer.ts +++ b/packages/components/src/custom-gradient-picker/test/serializer.ts @@ -12,24 +12,31 @@ import { describe( 'It should serialize a gradient', () => { test( 'serializeGradientColor', () => { expect( - serializeGradientColor( { type: 'rgba', value: [ 1, 2, 3, 0.5 ] } ) + serializeGradientColor( { + type: 'rgba', + //NTS: For entire file - our tests were using numbers, but `gradientParser` (according to DefinitelyTyped) expects strings. I've updated the tests accordingly. Alternatively we could add some logic to our serializer so it accepts both and converts numbers to strings on the fly. + value: [ '1', '2', '3', '0.5' ], + } ) ).toBe( 'rgba(1,2,3,0.5)' ); expect( - serializeGradientColor( { type: 'rgb', value: [ 255, 0, 0 ] } ) + serializeGradientColor( { + type: 'rgb', + value: [ '255', '0', '0' ], + } ) ).toBe( 'rgb(255,0,0)' ); } ); test( 'serializeGradientPosition', () => { - expect( serializeGradientPosition( { type: '%', value: 70 } ) ).toBe( + expect( serializeGradientPosition( { type: '%', value: '70' } ) ).toBe( '70%' ); - expect( serializeGradientPosition( { type: '%', value: 0 } ) ).toBe( + expect( serializeGradientPosition( { type: '%', value: '0' } ) ).toBe( '0%' ); - expect( serializeGradientPosition( { type: 'px', value: 4 } ) ).toBe( + expect( serializeGradientPosition( { type: 'px', value: '4' } ) ).toBe( '4px' ); } ); @@ -38,35 +45,35 @@ describe( 'It should serialize a gradient', () => { expect( serializeGradientColorStop( { type: 'rgba', - value: [ 1, 2, 3, 0.5 ], - length: { type: '%', value: 70 }, + value: [ '1', '2', '3', ' 0.5' ], + length: { type: '%', value: '70' }, } ) ).toBe( 'rgba(1,2,3,0.5) 70%' ); expect( serializeGradientColorStop( { type: 'rgb', - value: [ 255, 0, 0 ], - length: { type: '%', value: 0 }, + value: [ '255', '0', '0' ], + length: { type: '%', value: '0' }, } ) ).toBe( 'rgb(255,0,0) 0%' ); expect( serializeGradientColorStop( { type: 'rgba', - value: [ 1, 2, 3, 0.5 ], - length: { type: 'px', value: 100 }, + value: [ '1', '2', '3', ' 0.5' ], + length: { type: 'px', value: '100' }, } ) ).toBe( 'rgba(1,2,3,0.5) 100px' ); } ); test( 'serializeGradientOrientation', () => { expect( - serializeGradientOrientation( { type: 'angular', value: 40 } ) + serializeGradientOrientation( { type: 'angular', value: '40' } ) ).toBe( '40deg' ); expect( - serializeGradientOrientation( { type: 'angular', value: 0 } ) + serializeGradientOrientation( { type: 'angular', value: '0' } ) ).toBe( '0deg' ); } ); @@ -74,17 +81,17 @@ describe( 'It should serialize a gradient', () => { expect( serializeGradient( { type: 'linear-gradient', - orientation: { type: 'angular', value: 40 }, + orientation: { type: 'angular', value: '40' }, colorStops: [ { type: 'rgba', - value: [ 1, 2, 3, 0.5 ], - length: { type: '%', value: 70 }, + value: [ '1', '2', '3', '0.5' ], + length: { type: '%', value: '70' }, }, { type: 'rgba', - value: [ 255, 1, 1, 0.9 ], - length: { type: '%', value: 40 }, + value: [ '255', '1', '1', '0.9' ], + length: { type: '%', value: '40' }, }, ], } ) @@ -98,13 +105,13 @@ describe( 'It should serialize a gradient', () => { colorStops: [ { type: 'rgba', - value: [ 1, 2, 3, 0.5 ], - length: { type: '%', value: 70 }, + value: [ '1', '2', '3', '0.5' ], + length: { type: '%', value: '70' }, }, { type: 'rgba', - value: [ 255, 1, 1, 0.9 ], - length: { type: '%', value: 40 }, + value: [ '255', '1', '1', '0.9' ], + length: { type: '%', value: '40' }, }, ], } ) @@ -117,12 +124,12 @@ describe( 'It should serialize a gradient', () => { { type: 'hex', value: '000', - length: { type: '%', value: 70 }, + length: { type: '%', value: '70' }, }, { type: 'hex', value: 'fff', - length: { type: '%', value: 40 }, + length: { type: '%', value: '40' }, }, ], } ) @@ -131,27 +138,27 @@ describe( 'It should serialize a gradient', () => { expect( serializeGradient( { type: 'linear-gradient', - orientation: { type: 'angular', value: 0 }, + orientation: { type: 'angular', value: '0' }, colorStops: [ { type: 'rgba', - value: [ 1, 2, 3, 0.5 ], - length: { type: '%', value: 0 }, + value: [ '1', '2', '3', '0.5' ], + length: { type: '%', value: '0' }, }, { type: 'rgba', - value: [ 255, 1, 1, 0.9 ], - length: { type: '%', value: 40 }, + value: [ '255', '1', '1', '0.9' ], + length: { type: '%', value: '40' }, }, { type: 'rgba', - value: [ 1, 2, 3, 0.5 ], - length: { type: '%', value: 100 }, + value: [ '1', '2', '3', '0.5' ], + length: { type: '%', value: '100' }, }, { type: 'rgba', - value: [ 10, 20, 30, 0.5 ], - length: { type: '%', value: 20 }, + value: [ '10', '20', '30', '0.5' ], + length: { type: '%', value: '20' }, }, ], } ) From 6a44f5b41d596588856a01dcf35e01ca6e0f66a0 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Tue, 7 Mar 2023 15:24:42 -0500 Subject: [PATCH 19/56] Update util tests to match typing --- .../gradient-bar/test/utils.ts | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/test/utils.ts b/packages/components/src/custom-gradient-picker/gradient-bar/test/utils.ts index 525d075a0fd0a..6c1a527387388 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/test/utils.ts +++ b/packages/components/src/custom-gradient-picker/gradient-bar/test/utils.ts @@ -5,12 +5,13 @@ import { getHorizontalRelativeGradientPosition } from '../utils'; describe( 'getHorizontalRelativeGradientPosition', () => { it( 'should return relative percentage position', () => { - const containerElement = { - getBoundingClientRect: () => ( { + const containerElement = document.createElement( 'div' ); + Object.defineProperty( containerElement, 'getBoudingClientRect', { + value: { x: 0, width: 1000, - } ), - }; + }, + } ); expect( getHorizontalRelativeGradientPosition( 500, containerElement ) @@ -18,12 +19,13 @@ describe( 'getHorizontalRelativeGradientPosition', () => { } ); it( 'should subtract the x position of the container from the mouse position', () => { - const containerElement = { - getBoundingClientRect: () => ( { + const containerElement = document.createElement( 'div' ); + Object.defineProperty( containerElement, 'getBoudingClientRect', { + value: { x: 50, width: 1000, - } ), - }; + }, + } ); expect( getHorizontalRelativeGradientPosition( 550, containerElement ) @@ -31,12 +33,13 @@ describe( 'getHorizontalRelativeGradientPosition', () => { } ); it( 'should clamp to a whole percentage number', () => { - const containerElement = { - getBoundingClientRect: () => ( { + const containerElement = document.createElement( 'div' ); + Object.defineProperty( containerElement, 'getBoudingClientRect', { + value: { x: 0, width: 1000, - } ), - }; + }, + } ); expect( getHorizontalRelativeGradientPosition( 333, containerElement ) @@ -44,12 +47,13 @@ describe( 'getHorizontalRelativeGradientPosition', () => { } ); it( 'should clamp to zero when mouse position is less the x position', () => { - const containerElement = { - getBoundingClientRect: () => ( { + const containerElement = document.createElement( 'div' ); + Object.defineProperty( containerElement, 'getBoudingClientRect', { + value: { x: 50, width: 1000, - } ), - }; + }, + } ); expect( getHorizontalRelativeGradientPosition( 2, containerElement ) @@ -57,12 +61,13 @@ describe( 'getHorizontalRelativeGradientPosition', () => { } ); it( 'should clamp to 100 when mouse position is greater than width', () => { - const containerElement = { - getBoundingClientRect: () => ( { + const containerElement = document.createElement( 'div' ); + Object.defineProperty( containerElement, 'getBoudingClientRect', { + value: { x: 0, width: 1000, - } ), - }; + }, + } ); expect( getHorizontalRelativeGradientPosition( 1500, containerElement ) @@ -70,7 +75,7 @@ describe( 'getHorizontalRelativeGradientPosition', () => { } ); it( 'should return undefined if no containerElement is provided', () => { - const containerElement = undefined; + const containerElement = null; expect( getHorizontalRelativeGradientPosition( 1500, containerElement ) From 566efa4a66828e8ddd4da60acd3cd864e9208b08 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Wed, 8 Mar 2023 08:04:16 -0500 Subject: [PATCH 20/56] Update Storybook --- .../src/custom-gradient-picker/index.tsx | 25 ++++++++++++++++++- .../custom-gradient-picker/stories/index.tsx | 20 ++++++++++----- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/index.tsx b/packages/components/src/custom-gradient-picker/index.tsx index c58d9aebb7d3b..809389967df45 100644 --- a/packages/components/src/custom-gradient-picker/index.tsx +++ b/packages/components/src/custom-gradient-picker/index.tsx @@ -126,7 +126,28 @@ const GradientTypePicker = ( { ); }; -export default function CustomGradientPicker( { +/** + * CustomGradientPicker is a React component that renders a UI for specifying + * linear or radial gradients. Radial gradients are displayed in the picker as + * a slice of the gradient from the center to the outside. + * + * ```jsx + * import { CustomGradientPicker } from '@wordpress/components'; + * import { useState } from '@wordpress/element'; + * + * const MyCustomGradientPicker = () => { + * const [ gradient, setGradient ] = useState(); + * + * return ( + * + * ); + * }; + * ``` + */ +export function CustomGradientPicker( { /** Start opting into the new margin-free styles that will become the default in a future version. */ __nextHasNoMargin = false, value, @@ -212,3 +233,5 @@ export default function CustomGradientPicker( { ); } + +export default CustomGradientPicker; diff --git a/packages/components/src/custom-gradient-picker/stories/index.tsx b/packages/components/src/custom-gradient-picker/stories/index.tsx index 3d70862e10486..36d931aaeadc1 100644 --- a/packages/components/src/custom-gradient-picker/stories/index.tsx +++ b/packages/components/src/custom-gradient-picker/stories/index.tsx @@ -1,3 +1,7 @@ +/** + * External dependencies + */ +import type { ComponentMeta, ComponentStory } from '@storybook/react'; /** * WordPress dependencies */ @@ -6,18 +10,22 @@ import { useState } from '@wordpress/element'; /** * Internal dependencies */ -import CustomGradientPicker from '..'; +import CustomGradientPicker from '../'; -export default { +const meta: ComponentMeta< typeof CustomGradientPicker > = { title: 'Components/CustomGradientPicker', component: CustomGradientPicker, - argTypes: { - __nextHasNoMargin: { control: { type: 'boolean' } }, + parameters: { + controls: { expanded: true }, + docs: { source: { state: 'open' } }, }, }; +export default meta; -const CustomGradientPickerWithState = ( props ) => { - const [ gradient, setGradient ] = useState(); +const CustomGradientPickerWithState: ComponentStory< + typeof CustomGradientPicker +> = ( props ) => { + const [ gradient, setGradient ] = useState< string | undefined >(); return ( Date: Wed, 8 Mar 2023 08:43:26 -0500 Subject: [PATCH 21/56] Self-review notes --- .../src/custom-gradient-picker/gradient-bar/control-points.tsx | 2 +- packages/components/src/custom-gradient-picker/serializer.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx index 6b8a7cf464752..0b0e52a02c19e 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx +++ b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx @@ -137,7 +137,7 @@ function ControlPoints( { const { initialPosition, index, significantMoveHappened } = controlPointMoveState?.current ?? {}; - + // NTS: `getHorizontalRelativeGradientPosition` will return `undefined` if the `containerElement` arg is falsey. if ( initialPosition === undefined || relativePosition === undefined || diff --git a/packages/components/src/custom-gradient-picker/serializer.ts b/packages/components/src/custom-gradient-picker/serializer.ts index 4c7b23d6f26d7..af8eb42b27e1e 100644 --- a/packages/components/src/custom-gradient-picker/serializer.ts +++ b/packages/components/src/custom-gradient-picker/serializer.ts @@ -45,6 +45,7 @@ export function serializeGradientOrientation( orientation: gradientParser.GradientNode[ 'orientation' ] ) { if ( + // NTS: Because we're narrowing to `orientation.type !== 'angular'`, ruling out arrays should have no runtime impact Array.isArray( orientation ) || ! orientation || orientation.type !== 'angular' From 2a51a79d3fafb0b34b9c8ecf0f5fc64f1e8e6f3c Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Wed, 8 Mar 2023 10:08:52 -0500 Subject: [PATCH 22/56] Comment/TODO cleanup --- .../custom-gradient-picker/gradient-bar/control-points.tsx | 5 ++--- .../src/custom-gradient-picker/gradient-bar/index.tsx | 1 - packages/components/src/custom-gradient-picker/index.tsx | 4 ---- .../components/src/custom-gradient-picker/test/serializer.ts | 1 - packages/components/src/custom-gradient-picker/utils.ts | 3 --- 5 files changed, 2 insertions(+), 12 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx index 0b0e52a02c19e..a46f41dd09980 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx +++ b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx @@ -137,12 +137,11 @@ function ControlPoints( { const { initialPosition, index, significantMoveHappened } = controlPointMoveState?.current ?? {}; - // NTS: `getHorizontalRelativeGradientPosition` will return `undefined` if the `containerElement` arg is falsey. if ( + controlPointMoveState.current === undefined || initialPosition === undefined || - relativePosition === undefined || index === undefined || - controlPointMoveState.current === undefined + relativePosition === undefined ) { return; } diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx b/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx index 750ec711dba3c..7a893fa5aa007 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx +++ b/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx @@ -104,7 +104,6 @@ export default function CustomGradientBar( { gradientMarkersContainerDomRef.current ); - // NTS: `getHorizontalRelativeGradientPosition` will return `undefined` if the `containerElement` arg is falsey. if ( insertPosition === undefined ) { return; } diff --git a/packages/components/src/custom-gradient-picker/index.tsx b/packages/components/src/custom-gradient-picker/index.tsx index 809389967df45..a726204b81373 100644 --- a/packages/components/src/custom-gradient-picker/index.tsx +++ b/packages/components/src/custom-gradient-picker/index.tsx @@ -85,7 +85,6 @@ const GradientTypePicker = ( { ? {} : { orientation: HORIZONTAL_GRADIENT_ORIENTATION } ), type: 'linear-gradient', - // NTS: this isn't very elegant, but the `orientation` props between linear and radial gradients don't overlap (according to DefinitelyTyped) so TypeScript gets confused by this manual change. } as gradientParser.LinearGradientNode ) ); }; @@ -101,7 +100,6 @@ const GradientTypePicker = ( { }; const handleOnChange = ( next: string | string[] ) => { - // NTS: Because `SelectControl.onChange` (below) can accept an array, we need to accept one here. We shouldn't ever see an array here, but I'm adding a check just in case. const nextValue = Array.isArray( next ) ? next[ 0 ] : next; if ( nextValue === 'linear-gradient' ) { onSetLinearGradient(); @@ -120,7 +118,6 @@ const GradientTypePicker = ( { onChange={ handleOnChange } options={ GRADIENT_OPTIONS } size="__unstable-large" - // NTS: Small runtime change. Previously, if `type` was undefined (which I don't think should ever happen) this check would return `false`, which would be an invalid value. value={ hasGradient ? type : undefined } /> ); @@ -166,7 +163,6 @@ export function CustomGradientPicker( { const controlPoints = gradientAST.colorStops.map( ( colorStop ) => { // Although it's already been checked by `hasUnsupportedLength` in `getGradientAstWithDefault`, // TypeScript doesn't know that `colorStop.length` is not undefined here. - //NTS: Is there may be a more elegant way to help TS see that for itself? 🤔 return { color: getStopCssColor( colorStop ), position: parseInt( colorStop.length!.value ), diff --git a/packages/components/src/custom-gradient-picker/test/serializer.ts b/packages/components/src/custom-gradient-picker/test/serializer.ts index c0422127bbbfb..8ce8a69b22164 100644 --- a/packages/components/src/custom-gradient-picker/test/serializer.ts +++ b/packages/components/src/custom-gradient-picker/test/serializer.ts @@ -14,7 +14,6 @@ describe( 'It should serialize a gradient', () => { expect( serializeGradientColor( { type: 'rgba', - //NTS: For entire file - our tests were using numbers, but `gradientParser` (according to DefinitelyTyped) expects strings. I've updated the tests accordingly. Alternatively we could add some logic to our serializer so it accepts both and converts numbers to strings on the fly. value: [ '1', '2', '3', '0.5' ], } ) ).toBe( 'rgba(1,2,3,0.5)' ); diff --git a/packages/components/src/custom-gradient-picker/utils.ts b/packages/components/src/custom-gradient-picker/utils.ts index 0b9e16be24bf8..69e07294f285c 100644 --- a/packages/components/src/custom-gradient-picker/utils.ts +++ b/packages/components/src/custom-gradient-picker/utils.ts @@ -30,7 +30,6 @@ export function getLinearGradientRepresentation( value: `${ HORIZONTAL_GRADIENT_ORIENTATION.value }`, }, colorStops: gradientAST.colorStops, - // NTS: As noted elsewhere, gradientParser's DefinetelyTyped values provide no overlap between the different `orientation` values for linear vs radial nodes. } as gradientParser.LinearGradientNode ); } @@ -67,7 +66,6 @@ export function getGradientAstWithDefault( value?: string ) { } if ( gradientAST.colorStops.some( hasUnsupportedLength ) ) { - // NTS: Followup - `colorStops` is destructured before being mutated, but it's never reinserted into `gradientAST`. This could mean the original object might still have unspoorted lengths. Investigate. const { colorStops } = gradientAST; const step = 100 / ( colorStops.length - 1 ); colorStops.forEach( ( stop, index ) => { @@ -102,7 +100,6 @@ export function getGradientAstWithControlPoints( : [ `${ r }`, `${ g }`, `${ b }` ], }; } ), - // NTS: there are a few `as` calls throughout the changes. Some are to avoid conflicts between union members. Note that in your description. } as gradientParser.GradientNode; } From 1229def1062c722f154e2e5a66a37ec79bb04b3a Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Wed, 8 Mar 2023 10:10:00 -0500 Subject: [PATCH 23/56] update CHANGELOG --- packages/components/CHANGELOG.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index eae4f3355741a..ef5a7bcccdb46 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Internal + +- `CustomGradientPicker`: Convert to TypeScript ([#48929](https://github.com/WordPress/gutenberg/pull/48929)). + ## 23.6.0 (2023-03-15) ### Enhancements @@ -12,8 +16,8 @@ ### Bug Fix -- `ResponsiveWrapper`: use `aspect-ratio` CSS prop, add support for `SVG` elements ([#48573](https://github.com/WordPress/gutenberg/pull/48573). -- `ResizeTooltip`: Use `default.fontFamily` on tooltip ([#48805](https://github.com/WordPress/gutenberg/pull/48805). +- `ResponsiveWrapper`: use `aspect-ratio` CSS prop, add support for `SVG` elements ([#48573](https://github.com/WordPress/gutenberg/pull/48573). +- `ResizeTooltip`: Use `default.fontFamily` on tooltip ([#48805](https://github.com/WordPress/gutenberg/pull/48805). ### Internal @@ -37,7 +41,7 @@ ### Enhancements -- `ToolsPanel`: Separate reset all filter registration from items registration and support global resets ([#48123](https://github.com/WordPress/gutenberg/pull/48123)). +- `ToolsPanel`: Separate reset all filter registration from items registration and support global resets ([#48123](https://github.com/WordPress/gutenberg/pull/48123)). ### Internal From 3a9fe8e05fb9242ac795909d8e1d85096c72a55d Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Wed, 8 Mar 2023 10:54:29 -0500 Subject: [PATCH 24/56] TODO removal --- packages/components/src/custom-gradient-picker/serializer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/components/src/custom-gradient-picker/serializer.ts b/packages/components/src/custom-gradient-picker/serializer.ts index af8eb42b27e1e..4c7b23d6f26d7 100644 --- a/packages/components/src/custom-gradient-picker/serializer.ts +++ b/packages/components/src/custom-gradient-picker/serializer.ts @@ -45,7 +45,6 @@ export function serializeGradientOrientation( orientation: gradientParser.GradientNode[ 'orientation' ] ) { if ( - // NTS: Because we're narrowing to `orientation.type !== 'angular'`, ruling out arrays should have no runtime impact Array.isArray( orientation ) || ! orientation || orientation.type !== 'angular' From de5098290a370972e6a8ae70f42f505e1f63b7a3 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Wed, 8 Mar 2023 11:35:22 -0500 Subject: [PATCH 25/56] partial unit test fixes --- .../gradient-bar/test/utils.ts | 80 ++++++++++++------- .../custom-gradient-picker/test/serializer.ts | 4 +- 2 files changed, 52 insertions(+), 32 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/test/utils.ts b/packages/components/src/custom-gradient-picker/gradient-bar/test/utils.ts index 6c1a527387388..925c9c06584c6 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/test/utils.ts +++ b/packages/components/src/custom-gradient-picker/gradient-bar/test/utils.ts @@ -6,12 +6,16 @@ import { getHorizontalRelativeGradientPosition } from '../utils'; describe( 'getHorizontalRelativeGradientPosition', () => { it( 'should return relative percentage position', () => { const containerElement = document.createElement( 'div' ); - Object.defineProperty( containerElement, 'getBoudingClientRect', { - value: { - x: 0, - width: 1000, - }, - } ); + Object.defineProperty( + containerElement, + 'getBoundingClientRect', + () => ( { + value: { + x: 0, + width: 1000, + }, + } ) + ); expect( getHorizontalRelativeGradientPosition( 500, containerElement ) @@ -20,12 +24,16 @@ describe( 'getHorizontalRelativeGradientPosition', () => { it( 'should subtract the x position of the container from the mouse position', () => { const containerElement = document.createElement( 'div' ); - Object.defineProperty( containerElement, 'getBoudingClientRect', { - value: { - x: 50, - width: 1000, - }, - } ); + Object.defineProperty( + containerElement, + 'getBoundingClientRect', + () => ( { + value: { + x: 50, + width: 1000, + }, + } ) + ); expect( getHorizontalRelativeGradientPosition( 550, containerElement ) @@ -34,12 +42,16 @@ describe( 'getHorizontalRelativeGradientPosition', () => { it( 'should clamp to a whole percentage number', () => { const containerElement = document.createElement( 'div' ); - Object.defineProperty( containerElement, 'getBoudingClientRect', { - value: { - x: 0, - width: 1000, - }, - } ); + Object.defineProperty( + containerElement, + 'getBoundingClientRect', + () => ( { + value: { + x: 0, + width: 1000, + }, + } ) + ); expect( getHorizontalRelativeGradientPosition( 333, containerElement ) @@ -48,12 +60,16 @@ describe( 'getHorizontalRelativeGradientPosition', () => { it( 'should clamp to zero when mouse position is less the x position', () => { const containerElement = document.createElement( 'div' ); - Object.defineProperty( containerElement, 'getBoudingClientRect', { - value: { - x: 50, - width: 1000, - }, - } ); + Object.defineProperty( + containerElement, + 'getBoundingClientRect', + () => ( { + value: { + x: 50, + width: 1000, + }, + } ) + ); expect( getHorizontalRelativeGradientPosition( 2, containerElement ) @@ -62,12 +78,16 @@ describe( 'getHorizontalRelativeGradientPosition', () => { it( 'should clamp to 100 when mouse position is greater than width', () => { const containerElement = document.createElement( 'div' ); - Object.defineProperty( containerElement, 'getBoudingClientRect', { - value: { - x: 0, - width: 1000, - }, - } ); + Object.defineProperty( + containerElement, + 'getBoundingClientRect', + () => ( { + value: { + x: 0, + width: 1000, + }, + } ) + ); expect( getHorizontalRelativeGradientPosition( 1500, containerElement ) diff --git a/packages/components/src/custom-gradient-picker/test/serializer.ts b/packages/components/src/custom-gradient-picker/test/serializer.ts index 8ce8a69b22164..3c14194fc7279 100644 --- a/packages/components/src/custom-gradient-picker/test/serializer.ts +++ b/packages/components/src/custom-gradient-picker/test/serializer.ts @@ -44,7 +44,7 @@ describe( 'It should serialize a gradient', () => { expect( serializeGradientColorStop( { type: 'rgba', - value: [ '1', '2', '3', ' 0.5' ], + value: [ '1', '2', '3', '0.5' ], length: { type: '%', value: '70' }, } ) ).toBe( 'rgba(1,2,3,0.5) 70%' ); @@ -60,7 +60,7 @@ describe( 'It should serialize a gradient', () => { expect( serializeGradientColorStop( { type: 'rgba', - value: [ '1', '2', '3', ' 0.5' ], + value: [ '1', '2', '3', '0.5' ], length: { type: 'px', value: '100' }, } ) ).toBe( 'rgba(1,2,3,0.5) 100px' ); From 9c33aedfac96dca3164541e5fc38569b6b7df2db Mon Sep 17 00:00:00 2001 From: Marco Ciampini Date: Wed, 8 Mar 2023 18:03:49 +0100 Subject: [PATCH 26/56] Use jest mocks for getBoundingClientRect --- .../gradient-bar/test/utils.ts | 84 ++++++++++--------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/test/utils.ts b/packages/components/src/custom-gradient-picker/gradient-bar/test/utils.ts index 925c9c06584c6..01396d94f0581 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/test/utils.ts +++ b/packages/components/src/custom-gradient-picker/gradient-bar/test/utils.ts @@ -5,71 +5,72 @@ import { getHorizontalRelativeGradientPosition } from '../utils'; describe( 'getHorizontalRelativeGradientPosition', () => { it( 'should return relative percentage position', () => { - const containerElement = document.createElement( 'div' ); - Object.defineProperty( - containerElement, - 'getBoundingClientRect', - () => ( { - value: { + jest.spyOn( + window.HTMLElement.prototype, + 'getBoundingClientRect' + ).mockImplementationOnce( + () => + ( { x: 0, width: 1000, - }, - } ) + } as DOMRect ) ); + const containerElement = document.createElement( 'div' ); expect( getHorizontalRelativeGradientPosition( 500, containerElement ) ).toBe( 50 ); } ); it( 'should subtract the x position of the container from the mouse position', () => { - const containerElement = document.createElement( 'div' ); - Object.defineProperty( - containerElement, - 'getBoundingClientRect', - () => ( { - value: { + jest.spyOn( + window.HTMLElement.prototype, + 'getBoundingClientRect' + ).mockImplementationOnce( + () => + ( { x: 50, width: 1000, - }, - } ) + } as DOMRect ) ); + const containerElement = document.createElement( 'div' ); expect( getHorizontalRelativeGradientPosition( 550, containerElement ) ).toBe( 50 ); } ); it( 'should clamp to a whole percentage number', () => { - const containerElement = document.createElement( 'div' ); - Object.defineProperty( - containerElement, - 'getBoundingClientRect', - () => ( { - value: { + jest.spyOn( + window.HTMLElement.prototype, + 'getBoundingClientRect' + ).mockImplementationOnce( + () => + ( { x: 0, width: 1000, - }, - } ) + } as DOMRect ) ); + const containerElement = document.createElement( 'div' ); + expect( getHorizontalRelativeGradientPosition( 333, containerElement ) ).toBe( 33 ); } ); it( 'should clamp to zero when mouse position is less the x position', () => { - const containerElement = document.createElement( 'div' ); - Object.defineProperty( - containerElement, - 'getBoundingClientRect', - () => ( { - value: { + jest.spyOn( + window.HTMLElement.prototype, + 'getBoundingClientRect' + ).mockImplementationOnce( + () => + ( { x: 50, width: 1000, - }, - } ) + } as DOMRect ) ); + const containerElement = document.createElement( 'div' ); expect( getHorizontalRelativeGradientPosition( 2, containerElement ) @@ -77,18 +78,19 @@ describe( 'getHorizontalRelativeGradientPosition', () => { } ); it( 'should clamp to 100 when mouse position is greater than width', () => { - const containerElement = document.createElement( 'div' ); - Object.defineProperty( - containerElement, - 'getBoundingClientRect', - () => ( { - value: { - x: 0, + jest.spyOn( + window.HTMLElement.prototype, + 'getBoundingClientRect' + ).mockImplementationOnce( + () => + ( { + x: 50, width: 1000, - }, - } ) + } as DOMRect ) ); + const containerElement = document.createElement( 'div' ); + expect( getHorizontalRelativeGradientPosition( 1500, containerElement ) ).toBe( 100 ); From 9eaf378fe9d4064822ff7059a3de32987440ed14 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Wed, 8 Mar 2023 16:48:57 -0500 Subject: [PATCH 27/56] Clean up typing on `useRef` calls --- .../gradient-bar/control-points.tsx | 8 ++------ .../src/custom-gradient-picker/gradient-bar/index.tsx | 5 ++--- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx index a46f41dd09980..b39b32370f7d2 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx +++ b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx @@ -125,9 +125,7 @@ function ControlPoints( { onStopControlPointChange, __experimentalIsRenderedInSidebar, }: ControlPointsProps ) { - const controlPointMoveState: React.MutableRefObject< - ControlPointMoveState | undefined - > = useRef(); + const controlPointMoveState = useRef< ControlPointMoveState | undefined >(); const onMouseMove = ( event: MouseEvent ) => { const relativePosition = getHorizontalRelativeGradientPosition( @@ -176,9 +174,7 @@ function ControlPoints( { // Adding `cleanEventListeners` to the dependency array below requires the function itself to be wrapped in a `useCallback` // This memoization would prevent the event listeners from being properly cleaned. // Instead, we'll pass a ref to the function in our `useEffect` so `cleanEventListeners` itself is no longer a dependency. - const cleanEventListenersRef: React.MutableRefObject< - ( () => void ) | undefined - > = useRef(); + const cleanEventListenersRef = useRef< ( () => void ) | undefined >(); cleanEventListenersRef.current = cleanEventListeners; useEffect( () => { diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx b/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx index 7a893fa5aa007..14875dcc6000b 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx +++ b/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx @@ -22,7 +22,7 @@ import type { CustomGradientBarReducerAction, CustomGradientBarIdleState, } from '../types'; -import type { LegacyRef, MouseEventHandler } from 'react'; +import type { MouseEventHandler } from 'react'; const customGradientBarReducer = ( state: CustomGradientBarReducerState, @@ -89,8 +89,7 @@ export default function CustomGradientBar( { disableAlpha = false, __experimentalIsRenderedInSidebar, }: CustomGradientBarProps ) { - const gradientMarkersContainerDomRef: LegacyRef< HTMLDivElement > = - useRef( null ); + const gradientMarkersContainerDomRef = useRef< HTMLDivElement >( null ); const [ gradientBarState, gradientBarStateDispatch ] = useReducer( customGradientBarReducer, From f344c226630e46093fbc0ea70204e6e3c45d94bc Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Mon, 13 Mar 2023 09:53:46 -0400 Subject: [PATCH 28/56] fix `GradienttypePicker` `onSetLinearGradient`. --- packages/components/src/custom-gradient-picker/constants.ts | 4 ++-- packages/components/src/custom-gradient-picker/index.tsx | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/constants.ts b/packages/components/src/custom-gradient-picker/constants.ts index 9924ff2b3d686..842aea9dcb638 100644 --- a/packages/components/src/custom-gradient-picker/constants.ts +++ b/packages/components/src/custom-gradient-picker/constants.ts @@ -10,8 +10,8 @@ export const DEFAULT_LINEAR_GRADIENT_ANGLE = 180; export const HORIZONTAL_GRADIENT_ORIENTATION = { type: 'angular', - value: 90, -}; + value: '90', +} as const; export const GRADIENT_OPTIONS = [ { value: 'linear-gradient', label: __( 'Linear' ) }, diff --git a/packages/components/src/custom-gradient-picker/index.tsx b/packages/components/src/custom-gradient-picker/index.tsx index a726204b81373..1be70d1354004 100644 --- a/packages/components/src/custom-gradient-picker/index.tsx +++ b/packages/components/src/custom-gradient-picker/index.tsx @@ -81,9 +81,9 @@ const GradientTypePicker = ( { onChange( serializeGradient( { ...gradientAST, - ...( gradientAST.orientation - ? {} - : { orientation: HORIZONTAL_GRADIENT_ORIENTATION } ), + orientation: gradientAST.orientation + ? undefined + : HORIZONTAL_GRADIENT_ORIENTATION, type: 'linear-gradient', } as gradientParser.LinearGradientNode ) ); From d25daa630e445f60be9179613136cab2a021991e Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Mon, 13 Mar 2023 10:46:59 -0400 Subject: [PATCH 29/56] Improve `GradientTypePicker`s `handleOnChange` type handling to align with updates to `SelectCotnrol` --- packages/components/src/custom-gradient-picker/index.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/index.tsx b/packages/components/src/custom-gradient-picker/index.tsx index 1be70d1354004..b6daf3a717c23 100644 --- a/packages/components/src/custom-gradient-picker/index.tsx +++ b/packages/components/src/custom-gradient-picker/index.tsx @@ -99,12 +99,11 @@ const GradientTypePicker = ( { ); }; - const handleOnChange = ( next: string | string[] ) => { - const nextValue = Array.isArray( next ) ? next[ 0 ] : next; - if ( nextValue === 'linear-gradient' ) { + const handleOnChange = ( next: string ) => { + if ( next === 'linear-gradient' ) { onSetLinearGradient(); } - if ( nextValue === 'radial-gradient' ) { + if ( next === 'radial-gradient' ) { onSetRadialGradient(); } }; From 134b10e90107cb9d9d92f40ca72a21a04a6d99d1 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Mon, 13 Mar 2023 12:52:25 -0400 Subject: [PATCH 30/56] Remove unneeded `WordPressComponentProps` Co-authored-by: Marco Ciampini --- .../src/custom-gradient-picker/gradient-bar/control-points.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx index b39b32370f7d2..77139be657a2f 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx +++ b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx @@ -89,7 +89,7 @@ function GradientColorPickerDropdown( { isRenderedInSidebar, className, ...props -}: WordPressComponentProps< CustomColorPickerDropdownProps, 'div', true > ) { +}: CustomColorPickerDropdownProps ) { // Open the popover below the gradient control/insertion point const popoverProps: DropdownProps[ 'popoverProps' ] = useMemo( () => ( { From d3e82417890bc1a7624b0991c3f16ac863efbe6c Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Mon, 13 Mar 2023 12:54:32 -0400 Subject: [PATCH 31/56] `as const` instead of importing prop types from `Dropdown` --- .../gradient-bar/control-points.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx index 77139be657a2f..b53476dce9929 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx +++ b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx @@ -43,7 +43,6 @@ import type { ControlPointsProps, InsertPointProps, } from '../types'; -import type { DropdownProps } from '../../dropdown/types'; import type { CustomColorPickerDropdownProps } from '../../color-palette/types'; function ControlPointButton( { @@ -91,11 +90,12 @@ function GradientColorPickerDropdown( { ...props }: CustomColorPickerDropdownProps ) { // Open the popover below the gradient control/insertion point - const popoverProps: DropdownProps[ 'popoverProps' ] = useMemo( - () => ( { - placement: 'bottom', - offset: 8, - } ), + const popoverProps = useMemo( + () => + ( { + placement: 'bottom', + offset: 8, + } as const ), [] ); From 0362e85da9b1eb01076db6a49db0e79b039cbd8e Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Mon, 13 Mar 2023 12:55:57 -0400 Subject: [PATCH 32/56] Typo Co-authored-by: Marco Ciampini --- packages/components/src/custom-gradient-picker/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/custom-gradient-picker/types.ts b/packages/components/src/custom-gradient-picker/types.ts index 68d026eb7fbaf..fd92ff0208ffb 100644 --- a/packages/components/src/custom-gradient-picker/types.ts +++ b/packages/components/src/custom-gradient-picker/types.ts @@ -103,7 +103,7 @@ export type ControlPointsProps = { gradientPickerDomRef: React.RefObject< HTMLDivElement >; ignoreMarkerPosition?: number; value: ControlPoint[]; - onChange: ( conrolPoints: ControlPoint[] ) => void; + onChange: ( controlPoints: ControlPoint[] ) => void; onStartControlPointChange: () => void; onStopControlPointChange: () => void; __experimentalIsRenderedInSidebar: boolean; From c1d14b05a3d7d7a8fd7c12e52108cd41dba82a8e Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Mon, 13 Mar 2023 12:58:17 -0400 Subject: [PATCH 33/56] Remove unneeded `undefined` typing --- .../src/custom-gradient-picker/gradient-bar/control-points.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx index b53476dce9929..eee46eab7adfb 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx +++ b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx @@ -125,7 +125,7 @@ function ControlPoints( { onStopControlPointChange, __experimentalIsRenderedInSidebar, }: ControlPointsProps ) { - const controlPointMoveState = useRef< ControlPointMoveState | undefined >(); + const controlPointMoveState = useRef< ControlPointMoveState >(); const onMouseMove = ( event: MouseEvent ) => { const relativePosition = getHorizontalRelativeGradientPosition( From 74a02c5e87e61c845f18efa4c6a4759c5ae4fd3b Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Mon, 13 Mar 2023 20:24:48 -0400 Subject: [PATCH 34/56] Improve `ControlPoints` typeguarding logic --- .../gradient-bar/control-points.tsx | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx index eee46eab7adfb..5649691caef4c 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx +++ b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx @@ -128,22 +128,21 @@ function ControlPoints( { const controlPointMoveState = useRef< ControlPointMoveState >(); const onMouseMove = ( event: MouseEvent ) => { - const relativePosition = getHorizontalRelativeGradientPosition( - event.clientX, - gradientPickerDomRef.current - ); - - const { initialPosition, index, significantMoveHappened } = - controlPointMoveState?.current ?? {}; if ( controlPointMoveState.current === undefined || - initialPosition === undefined || - index === undefined || - relativePosition === undefined + gradientPickerDomRef.current === null ) { return; } + const relativePosition = getHorizontalRelativeGradientPosition( + event.clientX, + gradientPickerDomRef.current + ) as number; + + const { initialPosition, index, significantMoveHappened } = + controlPointMoveState.current; + if ( ! significantMoveHappened && Math.abs( initialPosition - relativePosition ) >= From 0561fd4c083e9c4d299620eb473270baf9ddc4e9 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Mon, 13 Mar 2023 20:36:37 -0400 Subject: [PATCH 35/56] Add comment describing `DistributiveOmit` --- packages/components/src/custom-gradient-picker/types.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/components/src/custom-gradient-picker/types.ts b/packages/components/src/custom-gradient-picker/types.ts index fd92ff0208ffb..0b7c2d6906eb6 100644 --- a/packages/components/src/custom-gradient-picker/types.ts +++ b/packages/components/src/custom-gradient-picker/types.ts @@ -48,6 +48,14 @@ export type GradientTypePickerProps = { export type ControlPoint = { color: string; position: number }; +// When dealing with unions of objects, using `Omit` to omit a prop will result +// in a new type each prop is a union of the values for that prop from all of +// the origional union members. This does not maintain the specific combinations +// of props present in the original union. +// To avoid this, the `DistributiveOmit` type will "distribute" the `Omit` across +// the union. This removes the undesired prop from each member individually, +// maintaining the relationships between the remaining props. +// https://stackoverflow.com/questions/57103834/typescript-omit-a-property-from-all-interfaces-in-a-union-but-keep-the-union-s type DistributiveOmit< T, K extends keyof any > = T extends any ? Omit< T, K > : never; From ef96a5b95d33835f3253090cbdcb03b83a08f484 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Mon, 13 Mar 2023 20:38:24 -0400 Subject: [PATCH 36/56] Remove unneeded `undefined` typing Co-authored-by: Marco Ciampini --- .../src/custom-gradient-picker/gradient-bar/control-points.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx index 5649691caef4c..bbbba037ee6e1 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx +++ b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx @@ -173,7 +173,7 @@ function ControlPoints( { // Adding `cleanEventListeners` to the dependency array below requires the function itself to be wrapped in a `useCallback` // This memoization would prevent the event listeners from being properly cleaned. // Instead, we'll pass a ref to the function in our `useEffect` so `cleanEventListeners` itself is no longer a dependency. - const cleanEventListenersRef = useRef< ( () => void ) | undefined >(); + const cleanEventListenersRef = useRef< () => void >(); cleanEventListenersRef.current = cleanEventListeners; useEffect( () => { From 06939b15538f2de9ffab0e922ab6721619fe9c0c Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Tue, 14 Mar 2023 09:10:20 -0400 Subject: [PATCH 37/56] Restore original gradient bar util JSDoc comments, minus type info --- .../gradient-bar/utils.ts | 79 ++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts b/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts index ca0d2ce50f2bc..cdfb65747656e 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts +++ b/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts @@ -6,10 +6,27 @@ import { MINIMUM_DISTANCE_BETWEEN_POINTS } from './constants'; import type { ControlPoint } from '../types'; +/** + * Clamps a number between 0 and 100. + * + * @param value Value to clamp. + * + * @return Value clamped between 0 and 100. + */ export function clampPercent( value: number ) { return Math.max( 0, Math.min( 100, value ) ); } +/** + * Check if a control point is overlapping with another. + * + * @param value Array of control points. + * @param initialIndex Index of the position to test. + * @param newPosition New position of the control point. + * @param minDistance Distance considered to be overlapping. + * + * @return True if the point is overlapping. + */ export function isOverlapping( value: ControlPoint[], initialIndex: number, @@ -29,6 +46,15 @@ export function isOverlapping( } ); } +/** + * Adds a control point from an array and returns the new array. + * + * @param points Array of control points. + * @param position Position to insert the new point. + * @param color Color to update the control point at index. + * + * @return New array of control points. + */ export function addControlPoint( points: ControlPoint[], position: number, @@ -43,12 +69,28 @@ export function addControlPoint( return newPoints; } +/** + * Removes a control point from an array and returns the new array. + * + * @param points Array of control points. + * @param index Index to remove. + * + * @return New array of control points. + */ export function removeControlPoint( points: ControlPoint[], index: number ) { return points.filter( ( _point, pointIndex ) => { return pointIndex !== index; } ); } - +/** + * Updates a control point from an array and returns the new array. + * + * @param points Array of control points. + * @param index Index to update. + * @param newPoint New control point to replace the index. + * + * @return New array of control points. + */ export function updateControlPoint( points: ControlPoint[], index: number, @@ -59,6 +101,15 @@ export function updateControlPoint( return newValue; } +/** + * Updates the position of a control point from an array and returns the new array. + * + * @param points Array of control points. + * @param index Index to update. + * @param newPosition Position to move the control point at index. + * + * @return New array of control points. + */ export function updateControlPointPosition( points: ControlPoint[], index: number, @@ -74,6 +125,15 @@ export function updateControlPointPosition( return updateControlPoint( points, index, newPoint ); } +/** + * Updates the position of a control point from an array and returns the new array. + * + * @param points Array of control points. + * @param index Index to update. + * @param newColor Color to update the control point at index. + * + * @return New array of control points. + */ export function updateControlPointColor( points: ControlPoint[], index: number, @@ -86,6 +146,15 @@ export function updateControlPointColor( return updateControlPoint( points, index, newPoint ); } +/** + * Updates the position of a control point from an array and returns the new array. + * + * @param points Array of control points. + * @param position Position of the color stop. + * @param newColor Color to update the control point at index. + * + * @return New array of control points. + */ export function updateControlPointColorByPosition( points: ControlPoint[], position: ControlPoint[ 'position' ], @@ -95,6 +164,14 @@ export function updateControlPointColorByPosition( return updateControlPointColor( points, index, newColor ); } +/** + * Gets the horizontal coordinate when dragging a control point with the mouse. + * + * @param mouseXCoordinate Horizontal coordinate of the mouse position. + * @param containerElement Container for the gradient picker. + * + * @return Whole number percentage from the left. + */ export function getHorizontalRelativeGradientPosition( mouseXCoordinate: number, containerElement: HTMLDivElement | null From ee86ba99f013b600a33a140de0136003d9ae14e7 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Tue, 14 Mar 2023 10:48:18 -0400 Subject: [PATCH 38/56] In utils, type `color` props as `ControlPoint[color]` --- .../src/custom-gradient-picker/gradient-bar/utils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts b/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts index cdfb65747656e..12d1985e9a81a 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts +++ b/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts @@ -58,7 +58,7 @@ export function isOverlapping( export function addControlPoint( points: ControlPoint[], position: number, - color: string + color: ControlPoint[ 'color' ] ) { const nextIndex = points.findIndex( ( point ) => point.position > position @@ -137,7 +137,7 @@ export function updateControlPointPosition( export function updateControlPointColor( points: ControlPoint[], index: number, - newColor: string + newColor: ControlPoint[ 'color' ] ) { const newPoint = { ...points[ index ], @@ -158,7 +158,7 @@ export function updateControlPointColor( export function updateControlPointColorByPosition( points: ControlPoint[], position: ControlPoint[ 'position' ], - newColor: string + newColor: ControlPoint[ 'color' ] ) { const index = points.findIndex( ( point ) => point.position === position ); return updateControlPointColor( points, index, newColor ); From 7ef9d2337f5b5eff3ca43226580f4f623d55c124 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Tue, 14 Mar 2023 12:08:21 -0400 Subject: [PATCH 39/56] Convert `ColorStopTypeAndValue` to a `Pick`-based approach --- .../src/custom-gradient-picker/types.ts | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/types.ts b/packages/components/src/custom-gradient-picker/types.ts index 0b7c2d6906eb6..09b0b30dd09eb 100644 --- a/packages/components/src/custom-gradient-picker/types.ts +++ b/packages/components/src/custom-gradient-picker/types.ts @@ -48,20 +48,25 @@ export type GradientTypePickerProps = { export type ControlPoint = { color: string; position: number }; -// When dealing with unions of objects, using `Omit` to omit a prop will result -// in a new type each prop is a union of the values for that prop from all of -// the origional union members. This does not maintain the specific combinations -// of props present in the original union. -// To avoid this, the `DistributiveOmit` type will "distribute" the `Omit` across -// the union. This removes the undesired prop from each member individually, -// maintaining the relationships between the remaining props. +// When dealing with unions of objects, using `Omit` or `Pick` will result +// in a new type where each desired prop is a union of the values for that prop +// from all of the origional union members. This does not maintain the specific +// combinations of props present in the original union. +// To avoid this, the `DistributiveOmit` and `DistributiveOmit` type will +// "distribute" the `Omit`/`Pick` across the union. This allows the `Omit`/`Pick` +// to act on each member individually, maintaining the relationships between the +// resulting remaining props. // https://stackoverflow.com/questions/57103834/typescript-omit-a-property-from-all-interfaces-in-a-union-but-keep-the-union-s type DistributiveOmit< T, K extends keyof any > = T extends any ? Omit< T, K > : never; -export type ColorStopTypeAndValue = DistributiveOmit< +type DistributivePick< T, K extends keyof T > = T extends any + ? Pick< T, K > + : never; + +export type ColorStopTypeAndValue = DistributivePick< gradientParser.ColorStop, - 'length' + 'type' | 'value' >; export type CustomGradientBarProps = { From 0d2c6a9000acdeca3ab9ef602114f082e2c2dbd9 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Tue, 14 Mar 2023 12:21:21 -0400 Subject: [PATCH 40/56] remove extra optional chaining --- packages/components/src/custom-gradient-picker/serializer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/custom-gradient-picker/serializer.ts b/packages/components/src/custom-gradient-picker/serializer.ts index 4c7b23d6f26d7..226348dadc66c 100644 --- a/packages/components/src/custom-gradient-picker/serializer.ts +++ b/packages/components/src/custom-gradient-picker/serializer.ts @@ -67,7 +67,7 @@ export function serializeGradient( { ) => { return colorStop?.length?.value === undefined ? 0 - : parseInt( colorStop?.length?.value ); + : parseInt( colorStop.length.value ); }; return ( From cb4ffbff8cdc01dc38769caa1827ecf2bb441be4 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Tue, 14 Mar 2023 12:23:51 -0400 Subject: [PATCH 41/56] remove unneeded `undefined` typing --- .../components/src/custom-gradient-picker/stories/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/custom-gradient-picker/stories/index.tsx b/packages/components/src/custom-gradient-picker/stories/index.tsx index 36d931aaeadc1..98c50b778bf77 100644 --- a/packages/components/src/custom-gradient-picker/stories/index.tsx +++ b/packages/components/src/custom-gradient-picker/stories/index.tsx @@ -25,7 +25,7 @@ export default meta; const CustomGradientPickerWithState: ComponentStory< typeof CustomGradientPicker > = ( props ) => { - const [ gradient, setGradient ] = useState< string | undefined >(); + const [ gradient, setGradient ] = useState< string >(); return ( Date: Tue, 14 Mar 2023 12:28:26 -0400 Subject: [PATCH 42/56] include option `null` type for `CustomGradientPickerProps.value` --- packages/components/src/custom-gradient-picker/types.ts | 2 +- packages/components/src/custom-gradient-picker/utils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/types.ts b/packages/components/src/custom-gradient-picker/types.ts index 09b0b30dd09eb..197c04b6fd2c7 100644 --- a/packages/components/src/custom-gradient-picker/types.ts +++ b/packages/components/src/custom-gradient-picker/types.ts @@ -18,7 +18,7 @@ export type CustomGradientPickerProps = { * * @default 'linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%)' */ - value?: string; + value?: string | null; /** * The function called when a new gradient has been defined. It is passed to * the `currentGradient` as an arugment. diff --git a/packages/components/src/custom-gradient-picker/utils.ts b/packages/components/src/custom-gradient-picker/utils.ts index 69e07294f285c..98cbcb7ead288 100644 --- a/packages/components/src/custom-gradient-picker/utils.ts +++ b/packages/components/src/custom-gradient-picker/utils.ts @@ -37,7 +37,7 @@ function hasUnsupportedLength( item: gradientParser.ColorStop ) { return item.length === undefined || item.length.type !== '%'; } -export function getGradientAstWithDefault( value?: string ) { +export function getGradientAstWithDefault( value?: string | null ) { // gradientAST will contain the gradient AST as parsed by gradient-parser npm module. // More information of its structure available at https://www.npmjs.com/package/gradient-parser#ast. let gradientAST: gradientParser.GradientNode; From 5448a450cf7fa7107d7d8f38dace09db24f05f64 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Tue, 14 Mar 2023 12:30:50 -0400 Subject: [PATCH 43/56] Code cleanup Co-authored-by: Marco Ciampini --- packages/components/src/custom-gradient-picker/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/custom-gradient-picker/types.ts b/packages/components/src/custom-gradient-picker/types.ts index 197c04b6fd2c7..0554efa018e53 100644 --- a/packages/components/src/custom-gradient-picker/types.ts +++ b/packages/components/src/custom-gradient-picker/types.ts @@ -23,7 +23,7 @@ export type CustomGradientPickerProps = { * The function called when a new gradient has been defined. It is passed to * the `currentGradient` as an arugment. */ - onChange: ( currentGradient: string | undefined ) => void; + onChange: ( currentGradient?: string ) => void; /** * Whether this is rendered in the sidebar. * From 18ec48d6f2e89f2f001c9ada7a4ed41df3a9711b Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Wed, 15 Mar 2023 16:11:35 -0400 Subject: [PATCH 44/56] fix typos --- packages/components/src/custom-gradient-picker/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/types.ts b/packages/components/src/custom-gradient-picker/types.ts index 0554efa018e53..3fdb010755813 100644 --- a/packages/components/src/custom-gradient-picker/types.ts +++ b/packages/components/src/custom-gradient-picker/types.ts @@ -21,7 +21,7 @@ export type CustomGradientPickerProps = { value?: string | null; /** * The function called when a new gradient has been defined. It is passed to - * the `currentGradient` as an arugment. + * the `currentGradient` as an argument. */ onChange: ( currentGradient?: string ) => void; /** @@ -50,7 +50,7 @@ export type ControlPoint = { color: string; position: number }; // When dealing with unions of objects, using `Omit` or `Pick` will result // in a new type where each desired prop is a union of the values for that prop -// from all of the origional union members. This does not maintain the specific +// from all of the original union members. This does not maintain the specific // combinations of props present in the original union. // To avoid this, the `DistributiveOmit` and `DistributiveOmit` type will // "distribute" the `Omit`/`Pick` across the union. This allows the `Omit`/`Pick` From b4fa9e14df7bda56a80b6c5075444fee509da7f0 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Wed, 15 Mar 2023 16:18:07 -0400 Subject: [PATCH 45/56] replace `!` with `@ts-expect-error` in `CustomGradientPicker` `controlPoints` declaration --- packages/components/src/custom-gradient-picker/index.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/index.tsx b/packages/components/src/custom-gradient-picker/index.tsx index b6daf3a717c23..d775c67da2931 100644 --- a/packages/components/src/custom-gradient-picker/index.tsx +++ b/packages/components/src/custom-gradient-picker/index.tsx @@ -160,11 +160,12 @@ export function CustomGradientPicker( { // Control points color option may be hex from presets, custom colors will be rgb. // The position should always be a percentage. const controlPoints = gradientAST.colorStops.map( ( colorStop ) => { - // Although it's already been checked by `hasUnsupportedLength` in `getGradientAstWithDefault`, - // TypeScript doesn't know that `colorStop.length` is not undefined here. return { color: getStopCssColor( colorStop ), - position: parseInt( colorStop.length!.value ), + // Although it's already been checked by `hasUnsupportedLength` in `getGradientAstWithDefault`, + // TypeScript doesn't know that `colorStop.length` is not undefined here. + // @ts-expect-error + position: parseInt( colorStop.length.value ), }; } ); From af4f889d5b9cfe951177c6525d84d7ab5018e700 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Wed, 15 Mar 2023 16:25:34 -0400 Subject: [PATCH 46/56] Apply overrides to clarify `getHorizontalRelativeGradientPosition` return type --- .../gradient-bar/control-points.tsx | 2 +- .../custom-gradient-picker/gradient-bar/utils.ts | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx index bbbba037ee6e1..25fa5f4ab1d69 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx +++ b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx @@ -138,7 +138,7 @@ function ControlPoints( { const relativePosition = getHorizontalRelativeGradientPosition( event.clientX, gradientPickerDomRef.current - ) as number; + ); const { initialPosition, index, significantMoveHappened } = controlPointMoveState.current; diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts b/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts index 12d1985e9a81a..000871e627473 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts +++ b/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts @@ -167,11 +167,23 @@ export function updateControlPointColorByPosition( /** * Gets the horizontal coordinate when dragging a control point with the mouse. * - * @param mouseXCoordinate Horizontal coordinate of the mouse position. + * @param mouseXcoordinate Horizontal coordinate of the mouse position. * @param containerElement Container for the gradient picker. * * @return Whole number percentage from the left. */ +export function getHorizontalRelativeGradientPosition( + mouseXcoordinate: number, + containerElement: HTMLDivElement +): number; +export function getHorizontalRelativeGradientPosition( + mouseXcoordinate: number, + containerElement: null +): undefined; +export function getHorizontalRelativeGradientPosition( + mouseXcoordinate: number, + containerElement: HTMLDivElement | null +): number | undefined; export function getHorizontalRelativeGradientPosition( mouseXCoordinate: number, containerElement: HTMLDivElement | null From 66f4f40f60a53c9cb231cb5b5aac3d94c05d75c7 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Wed, 15 Mar 2023 16:30:31 -0400 Subject: [PATCH 47/56] remove `DistributiveOmit` in favor of `DistributivePick` --- .../components/src/custom-gradient-picker/types.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/types.ts b/packages/components/src/custom-gradient-picker/types.ts index 3fdb010755813..3d9d570382f89 100644 --- a/packages/components/src/custom-gradient-picker/types.ts +++ b/packages/components/src/custom-gradient-picker/types.ts @@ -48,18 +48,15 @@ export type GradientTypePickerProps = { export type ControlPoint = { color: string; position: number }; -// When dealing with unions of objects, using `Omit` or `Pick` will result +// When dealing with unions of objects, using `Pick` will result // in a new type where each desired prop is a union of the values for that prop -// from all of the original union members. This does not maintain the specific +// across all of the original union members. This does not maintain the specific // combinations of props present in the original union. -// To avoid this, the `DistributiveOmit` and `DistributiveOmit` type will -// "distribute" the `Omit`/`Pick` across the union. This allows the `Omit`/`Pick` +// To avoid this, the `DistributivePick` type will +// "distribute" the `Pick` across the union. This allows the `Pick` // to act on each member individually, maintaining the relationships between the -// resulting remaining props. +// resulting props. // https://stackoverflow.com/questions/57103834/typescript-omit-a-property-from-all-interfaces-in-a-union-but-keep-the-union-s -type DistributiveOmit< T, K extends keyof any > = T extends any - ? Omit< T, K > - : never; type DistributivePick< T, K extends keyof T > = T extends any ? Pick< T, K > : never; From 1182a2868cdb4d7af481d0821f813c519c820516 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Wed, 15 Mar 2023 16:52:17 -0400 Subject: [PATCH 48/56] Remove `@ts-nocheck`s --- .../src/custom-gradient-picker/gradient-bar/control-points.tsx | 2 -- .../src/custom-gradient-picker/gradient-bar/index.tsx | 2 -- .../components/src/custom-gradient-picker/gradient-bar/utils.ts | 2 -- packages/components/src/custom-gradient-picker/index.tsx | 2 -- packages/components/src/custom-gradient-picker/utils.ts | 2 -- 5 files changed, 10 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx index 25fa5f4ab1d69..e761df24823b9 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx +++ b/packages/components/src/custom-gradient-picker/gradient-bar/control-points.tsx @@ -1,5 +1,3 @@ -// @ts-nocheck - /** * External dependencies */ diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx b/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx index 14875dcc6000b..38ddb6434737d 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx +++ b/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx @@ -1,5 +1,3 @@ -// @ts-nocheck - /** * External dependencies */ diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts b/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts index 000871e627473..7f039b36f4ff6 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts +++ b/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts @@ -1,5 +1,3 @@ -// @ts-nocheck - /** * Internal dependencies */ diff --git a/packages/components/src/custom-gradient-picker/index.tsx b/packages/components/src/custom-gradient-picker/index.tsx index d775c67da2931..5be6bdc8f5c12 100644 --- a/packages/components/src/custom-gradient-picker/index.tsx +++ b/packages/components/src/custom-gradient-picker/index.tsx @@ -1,5 +1,3 @@ -// @ts-nocheck - /** * External dependencies */ diff --git a/packages/components/src/custom-gradient-picker/utils.ts b/packages/components/src/custom-gradient-picker/utils.ts index 98cbcb7ead288..c1d61a2b3dc71 100644 --- a/packages/components/src/custom-gradient-picker/utils.ts +++ b/packages/components/src/custom-gradient-picker/utils.ts @@ -1,5 +1,3 @@ -// @ts-nocheck - /** * External dependencies */ From 7595d97dcc2569332eed97b6b2a99b1677bc2bd4 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Wed, 15 Mar 2023 17:04:24 -0400 Subject: [PATCH 49/56] Mark `Gradient.gradient` from `PaletteEdit` as optional --- packages/components/src/palette-edit/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/palette-edit/types.ts b/packages/components/src/palette-edit/types.ts index 5513b94553778..d5e0384c324c4 100644 --- a/packages/components/src/palette-edit/types.ts +++ b/packages/components/src/palette-edit/types.ts @@ -16,7 +16,7 @@ export type Color = { }; export type Gradient = { - gradient: string; + gradient?: string; name: string; slug: string; color?: never; From 748d17aeb4d57da7d790eda618e2e7e0f5f381fc Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Thu, 16 Mar 2023 12:25:13 -0400 Subject: [PATCH 50/56] Type `minDistance` as `number` Co-authored-by: Marco Ciampini --- .../components/src/custom-gradient-picker/gradient-bar/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts b/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts index 7f039b36f4ff6..6e9417328cdbf 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts +++ b/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts @@ -29,7 +29,7 @@ export function isOverlapping( value: ControlPoint[], initialIndex: number, newPosition: number, - minDistance = MINIMUM_DISTANCE_BETWEEN_POINTS + minDistance: number = MINIMUM_DISTANCE_BETWEEN_POINTS ) { const initialPosition = value[ initialIndex ].position; const minPosition = Math.min( initialPosition, newPosition ); From 8f9e1887b43b5333daab9d336422d05b8a0df081 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Thu, 16 Mar 2023 12:24:30 -0400 Subject: [PATCH 51/56] Mark `CustomGradientBar.__experimentalIsRenderedInSidebar` as optional with `false` default --- .../src/custom-gradient-picker/gradient-bar/index.tsx | 2 +- packages/components/src/custom-gradient-picker/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx b/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx index 38ddb6434737d..526ee53bff5f0 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx +++ b/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx @@ -85,7 +85,7 @@ export default function CustomGradientBar( { onChange, disableInserter = false, disableAlpha = false, - __experimentalIsRenderedInSidebar, + __experimentalIsRenderedInSidebar = false, }: CustomGradientBarProps ) { const gradientMarkersContainerDomRef = useRef< HTMLDivElement >( null ); diff --git a/packages/components/src/custom-gradient-picker/types.ts b/packages/components/src/custom-gradient-picker/types.ts index 3d9d570382f89..4927d9c5183f8 100644 --- a/packages/components/src/custom-gradient-picker/types.ts +++ b/packages/components/src/custom-gradient-picker/types.ts @@ -73,7 +73,7 @@ export type CustomGradientBarProps = { onChange: ( newControlPoints: ControlPoint[] ) => void; disableInserter?: boolean; disableAlpha?: boolean; - __experimentalIsRenderedInSidebar: boolean; + __experimentalIsRenderedInSidebar?: boolean; }; export type CustomGradientBarIdleState = { id: 'IDLE' }; From 5b3b080fdcfe8c5a28bf9d0af8bf2b8880c5ea3c Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Thu, 16 Mar 2023 12:34:59 -0400 Subject: [PATCH 52/56] Revert string casting that is no longer needed --- packages/components/src/custom-gradient-picker/utils.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/utils.ts b/packages/components/src/custom-gradient-picker/utils.ts index c1d61a2b3dc71..cdfd8dfc95cf0 100644 --- a/packages/components/src/custom-gradient-picker/utils.ts +++ b/packages/components/src/custom-gradient-picker/utils.ts @@ -23,10 +23,7 @@ export function getLinearGradientRepresentation( ) { return serializeGradient( { type: 'linear-gradient', - orientation: { - ...HORIZONTAL_GRADIENT_ORIENTATION, - value: `${ HORIZONTAL_GRADIENT_ORIENTATION.value }`, - }, + orientation: HORIZONTAL_GRADIENT_ORIENTATION, colorStops: gradientAST.colorStops, } as gradientParser.LinearGradientNode ); } From ce0003120424f9fbf6da1063ff38160789890823 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Thu, 16 Mar 2023 12:38:38 -0400 Subject: [PATCH 53/56] Remove unneeded type assertion --- packages/components/src/custom-gradient-picker/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/components/src/custom-gradient-picker/utils.ts b/packages/components/src/custom-gradient-picker/utils.ts index cdfd8dfc95cf0..f90420e9a222b 100644 --- a/packages/components/src/custom-gradient-picker/utils.ts +++ b/packages/components/src/custom-gradient-picker/utils.ts @@ -25,7 +25,7 @@ export function getLinearGradientRepresentation( type: 'linear-gradient', orientation: HORIZONTAL_GRADIENT_ORIENTATION, colorStops: gradientAST.colorStops, - } as gradientParser.LinearGradientNode ); + } ); } function hasUnsupportedLength( item: gradientParser.ColorStop ) { From ef06890dee8a260ccd63460129b6904c4b4e476d Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Thu, 16 Mar 2023 13:23:22 -0400 Subject: [PATCH 54/56] Require a value for the input of `CustomGradientPickerProps.onChange` to better align with `ColorPickerPopover` types --- packages/components/src/custom-gradient-picker/types.ts | 2 +- packages/components/src/palette-edit/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/types.ts b/packages/components/src/custom-gradient-picker/types.ts index 4927d9c5183f8..93407fd77ddcf 100644 --- a/packages/components/src/custom-gradient-picker/types.ts +++ b/packages/components/src/custom-gradient-picker/types.ts @@ -23,7 +23,7 @@ export type CustomGradientPickerProps = { * The function called when a new gradient has been defined. It is passed to * the `currentGradient` as an argument. */ - onChange: ( currentGradient?: string ) => void; + onChange: ( currentGradient: string ) => void; /** * Whether this is rendered in the sidebar. * diff --git a/packages/components/src/palette-edit/index.tsx b/packages/components/src/palette-edit/index.tsx index 7882950431bba..9b54c5ed0c14c 100644 --- a/packages/components/src/palette-edit/index.tsx +++ b/packages/components/src/palette-edit/index.tsx @@ -133,7 +133,7 @@ function ColorPickerPopover< T extends Color | Gradient >( { __nextHasNoMargin __experimentalIsRenderedInSidebar value={ element.gradient } - onChange={ ( newGradient: Gradient[ 'gradient' ] ) => { + onChange={ ( newGradient ) => { onChange( { ...element, gradient: newGradient, From d0f1d81195036b24d8802b067ce940c7ac29c01e Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Thu, 16 Mar 2023 13:26:09 -0400 Subject: [PATCH 55/56] Add support for Storybook callback logging --- .../src/custom-gradient-picker/stories/index.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/stories/index.tsx b/packages/components/src/custom-gradient-picker/stories/index.tsx index 98c50b778bf77..3648db3abe571 100644 --- a/packages/components/src/custom-gradient-picker/stories/index.tsx +++ b/packages/components/src/custom-gradient-picker/stories/index.tsx @@ -16,6 +16,7 @@ const meta: ComponentMeta< typeof CustomGradientPicker > = { title: 'Components/CustomGradientPicker', component: CustomGradientPicker, parameters: { + actions: { argTypesRegex: '^on.*' }, controls: { expanded: true }, docs: { source: { state: 'open' } }, }, @@ -24,13 +25,16 @@ export default meta; const CustomGradientPickerWithState: ComponentStory< typeof CustomGradientPicker -> = ( props ) => { +> = ( { onChange, ...props } ) => { const [ gradient, setGradient ] = useState< string >(); return ( { + setGradient( newGradient ); + onChange( newGradient ); + } } /> ); }; From 465a59d285bfd7b0ca91f0a11a9794dcdaa26c12 Mon Sep 17 00:00:00 2001 From: chad1008 <13856531+chad1008@users.noreply.github.com> Date: Fri, 17 Mar 2023 11:02:27 -0400 Subject: [PATCH 56/56] Apply Marco's brilliance --- .../src/custom-gradient-picker/gradient-bar/index.tsx | 8 ++++---- .../src/custom-gradient-picker/gradient-bar/utils.ts | 4 ---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx b/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx index 526ee53bff5f0..225a5fb75754d 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx +++ b/packages/components/src/custom-gradient-picker/gradient-bar/index.tsx @@ -96,15 +96,15 @@ export default function CustomGradientBar( { const onMouseEnterAndMove: MouseEventHandler< HTMLDivElement > = ( event ) => { + if ( ! gradientMarkersContainerDomRef.current ) { + return; + } + const insertPosition = getHorizontalRelativeGradientPosition( event.clientX, gradientMarkersContainerDomRef.current ); - if ( insertPosition === undefined ) { - return; - } - // If the insert point is close to an existing control point don't show it. if ( controlPoints.some( ( { position } ) => { diff --git a/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts b/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts index 6e9417328cdbf..88daf2b2c19d6 100644 --- a/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts +++ b/packages/components/src/custom-gradient-picker/gradient-bar/utils.ts @@ -178,10 +178,6 @@ export function getHorizontalRelativeGradientPosition( mouseXcoordinate: number, containerElement: null ): undefined; -export function getHorizontalRelativeGradientPosition( - mouseXcoordinate: number, - containerElement: HTMLDivElement | null -): number | undefined; export function getHorizontalRelativeGradientPosition( mouseXCoordinate: number, containerElement: HTMLDivElement | null