diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index f65160f6e3f427..eb5d15a335887d 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -2,9 +2,11 @@ ## Unreleased -### Enhancements +### Bug Fixes -- `Guide`: Update finish button to use the new default size ([#65680](https://github.com/WordPress/gutenberg/pull/65680)). +- `PaletteEdit`: dedupe palette element slugs ([#65772](https://github.com/WordPress/gutenberg/pull/65772)). + +## 28.9.0 (2024-10-03) ### Bug Fixes @@ -15,6 +17,10 @@ - `Composite`: make items tabbable if active element gets removed ([#65720](https://github.com/WordPress/gutenberg/pull/65720)). - `DatePicker`: Use compact button size. ([#65653](https://github.com/WordPress/gutenberg/pull/65653)). +### Enhancements + +- `Guide`: Update finish button to use the new default size ([#65680](https://github.com/WordPress/gutenberg/pull/65680)). + ## 28.8.0 (2024-09-19) ### Bug Fixes diff --git a/packages/components/src/palette-edit/index.tsx b/packages/components/src/palette-edit/index.tsx index 601ea758b75bba..8bfcb7240b9ea4 100644 --- a/packages/components/src/palette-edit/index.tsx +++ b/packages/components/src/palette-edit/index.tsx @@ -49,7 +49,6 @@ import { kebabCase } from '../utils/strings'; import type { Color, ColorPickerPopoverProps, - Gradient, NameInputProps, OptionProps, PaletteEditListViewProps, @@ -70,6 +69,28 @@ function NameInput( { value, onChange, label }: NameInputProps ) { ); } +/* + * Deduplicates the slugs of the provided elements. + */ +export function deduplicateElementSlugs< T extends PaletteElement >( + elements: T[] +) { + const slugCounts: { [ slug: string ]: number } = {}; + + return elements.map( ( element ) => { + let newSlug: string | undefined; + + const { slug } = element; + slugCounts[ slug ] = ( slugCounts[ slug ] || 0 ) + 1; + + if ( slugCounts[ slug ] > 1 ) { + newSlug = `${ slug }-${ slugCounts[ slug ] - 1 }`; + } + + return { ...element, slug: newSlug ?? slug }; + } ); +} + /** * Returns a name and slug for a palette item. The name takes the format "Color + id". * To ensure there are no duplicate ids, this function checks all slugs. @@ -109,7 +130,7 @@ export function getNameAndSlugForPosition( }; } -function ColorPickerPopover< T extends Color | Gradient >( { +function ColorPickerPopover< T extends PaletteElement >( { isGradient, element, onChange, @@ -167,7 +188,7 @@ function ColorPickerPopover< T extends Color | Gradient >( { ); } -function Option< T extends Color | Gradient >( { +function Option< T extends PaletteElement >( { canOnlyChangeValues, element, onChange, @@ -265,7 +286,7 @@ function Option< T extends Color | Gradient >( { ); } -function PaletteEditListView< T extends Color | Gradient >( { +function PaletteEditListView< T extends PaletteElement >( { elements, onChange, canOnlyChangeValues, @@ -280,7 +301,11 @@ function PaletteEditListView< T extends Color | Gradient >( { elementsReferenceRef.current = elements; }, [ elements ] ); - const debounceOnChange = useDebounce( onChange, 100 ); + const debounceOnChange = useDebounce( + ( updatedElements: T[] ) => + onChange( deduplicateElementSlugs( updatedElements ) ), + 100 + ); return ( diff --git a/packages/components/src/palette-edit/test/index.tsx b/packages/components/src/palette-edit/test/index.tsx index 980630633b97f9..7dc00dbba22042 100644 --- a/packages/components/src/palette-edit/test/index.tsx +++ b/packages/components/src/palette-edit/test/index.tsx @@ -7,7 +7,10 @@ import { click, type, press } from '@ariakit/test'; /** * Internal dependencies */ -import PaletteEdit, { getNameAndSlugForPosition } from '..'; +import PaletteEdit, { + getNameAndSlugForPosition, + deduplicateElementSlugs, +} from '..'; import type { PaletteElement } from '../types'; const noop = () => {}; @@ -97,6 +100,52 @@ describe( 'getNameAndSlugForPosition', () => { } ); } ); +describe( 'deduplicateElementSlugs', () => { + it( 'should not change the slugs if they are unique', () => { + const elements: PaletteElement[] = [ + { + slug: 'test-color-1', + color: '#ffffff', + name: 'Test Color 1', + }, + { + slug: 'test-color-2', + color: '#1a4548', + name: 'Test Color 2', + }, + ]; + + expect( deduplicateElementSlugs( elements ) ).toEqual( elements ); + } ); + it( 'should change the slugs if they are not unique', () => { + const elements: PaletteElement[] = [ + { + slug: 'test-color-1', + color: '#ffffff', + name: 'Test Color 1', + }, + { + slug: 'test-color-1', + color: '#1a4548', + name: 'Test Color 2', + }, + ]; + + expect( deduplicateElementSlugs( elements ) ).toEqual( [ + { + slug: 'test-color-1', + color: '#ffffff', + name: 'Test Color 1', + }, + { + slug: 'test-color-1-1', + color: '#1a4548', + name: 'Test Color 2', + }, + ] ); + } ); +} ); + describe( 'PaletteEdit', () => { const defaultProps = { paletteLabel: 'Test label',