Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ColorPalette: make sure "key" is unique when iterating over color entries with same value #43096

Merged
merged 7 commits into from
Aug 16, 2022

Conversation

ciampo
Copy link
Contributor

@ciampo ciampo commented Aug 9, 2022

What?

While testing a feature in the editor, I noticed that there was an error in the console while opening my personal site's color palette — I realised that the issue was related to the fact that my specific color palette for that specific site has a duplicated color (ie. two separate entries with the same value and name).

When that happen, it causes 2 issues:

  • the aforementioned console error, due to the fact that there are duplicate key attributes
  • both instances of the color appear as "checked", since they technically are both representing the selected value

Why?

This is an edge case that was never considered before.

How?

  • The key issue has been solved by using the array index to compute the key
  • To avoid two instances of the same color appearing as selected, I've arbitrarily decided that only the first matching color will appear as selected. This is not ideal, but IMO it's better than the current behavior.

An alternative idea that came to mind is to filter the list of colors and remove duplicates, although that also poses a few challenges:

  • it modifies the palette, which could result in an unexpected behavior to the user
  • should we consider as duplicate two different entries with the same value but a difference name ? (note that currently we're not keeping track of the name associated to the color that was clicked by the user)

Testing Instructions

I've added a new Storybook example to showcase the situation described above — you can test the example with and without the changes introduced in this PR to confirm that the fix is working as expected.

@ciampo ciampo requested a review from ajitbohra as a code owner August 9, 2022 16:25
@ciampo ciampo requested review from mirka and chad1008 August 9, 2022 16:25
@ciampo ciampo self-assigned this Aug 9, 2022
@ciampo ciampo added [Type] Bug An existing feature does not function as intended [Feature] UI Components Impacts or related to the UI component system [Package] Components /packages/components labels Aug 9, 2022
@github-actions
Copy link

github-actions bot commented Aug 9, 2022

Size Change: +9 B (0%)

Total Size: 1.27 MB

Filename Size Change
build/components/index.min.js 231 kB +9 B (0%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 982 B
build/annotations/index.min.js 2.76 kB
build/api-fetch/index.min.js 2.26 kB
build/autop/index.min.js 2.14 kB
build/blob/index.min.js 475 B
build/block-directory/index.min.js 6.58 kB
build/block-directory/style-rtl.css 990 B
build/block-directory/style.css 991 B
build/block-editor/default-editor-styles-rtl.css 378 B
build/block-editor/default-editor-styles.css 378 B
build/block-editor/index.min.js 156 kB
build/block-editor/style-rtl.css 14.7 kB
build/block-editor/style.css 14.7 kB
build/block-library/blocks/archives/editor-rtl.css 61 B
build/block-library/blocks/archives/editor.css 60 B
build/block-library/blocks/archives/style-rtl.css 65 B
build/block-library/blocks/archives/style.css 65 B
build/block-library/blocks/audio/editor-rtl.css 150 B
build/block-library/blocks/audio/editor.css 150 B
build/block-library/blocks/audio/style-rtl.css 103 B
build/block-library/blocks/audio/style.css 103 B
build/block-library/blocks/audio/theme-rtl.css 110 B
build/block-library/blocks/audio/theme.css 110 B
build/block-library/blocks/avatar/editor-rtl.css 116 B
build/block-library/blocks/avatar/editor.css 116 B
build/block-library/blocks/avatar/style-rtl.css 59 B
build/block-library/blocks/avatar/style.css 59 B
build/block-library/blocks/block/editor-rtl.css 161 B
build/block-library/blocks/block/editor.css 161 B
build/block-library/blocks/button/editor-rtl.css 441 B
build/block-library/blocks/button/editor.css 441 B
build/block-library/blocks/button/style-rtl.css 539 B
build/block-library/blocks/button/style.css 539 B
build/block-library/blocks/buttons/editor-rtl.css 292 B
build/block-library/blocks/buttons/editor.css 292 B
build/block-library/blocks/buttons/style-rtl.css 275 B
build/block-library/blocks/buttons/style.css 275 B
build/block-library/blocks/calendar/style-rtl.css 207 B
build/block-library/blocks/calendar/style.css 207 B
build/block-library/blocks/categories/editor-rtl.css 84 B
build/block-library/blocks/categories/editor.css 83 B
build/block-library/blocks/categories/style-rtl.css 79 B
build/block-library/blocks/categories/style.css 79 B
build/block-library/blocks/code/style-rtl.css 103 B
build/block-library/blocks/code/style.css 103 B
build/block-library/blocks/code/theme-rtl.css 124 B
build/block-library/blocks/code/theme.css 124 B
build/block-library/blocks/columns/editor-rtl.css 108 B
build/block-library/blocks/columns/editor.css 108 B
build/block-library/blocks/columns/style-rtl.css 406 B
build/block-library/blocks/columns/style.css 406 B
build/block-library/blocks/comment-author-avatar/editor-rtl.css 125 B
build/block-library/blocks/comment-author-avatar/editor.css 125 B
build/block-library/blocks/comment-content/style-rtl.css 92 B
build/block-library/blocks/comment-content/style.css 92 B
build/block-library/blocks/comment-template/style-rtl.css 187 B
build/block-library/blocks/comment-template/style.css 185 B
build/block-library/blocks/comments-pagination-numbers/editor-rtl.css 123 B
build/block-library/blocks/comments-pagination-numbers/editor.css 121 B
build/block-library/blocks/comments-pagination/editor-rtl.css 222 B
build/block-library/blocks/comments-pagination/editor.css 209 B
build/block-library/blocks/comments-pagination/style-rtl.css 235 B
build/block-library/blocks/comments-pagination/style.css 231 B
build/block-library/blocks/comments-title/editor-rtl.css 75 B
build/block-library/blocks/comments-title/editor.css 75 B
build/block-library/blocks/comments/editor-rtl.css 834 B
build/block-library/blocks/comments/editor.css 832 B
build/block-library/blocks/comments/style-rtl.css 632 B
build/block-library/blocks/comments/style.css 630 B
build/block-library/blocks/cover/editor-rtl.css 615 B
build/block-library/blocks/cover/editor.css 616 B
build/block-library/blocks/cover/style-rtl.css 1.55 kB
build/block-library/blocks/cover/style.css 1.55 kB
build/block-library/blocks/embed/editor-rtl.css 293 B
build/block-library/blocks/embed/editor.css 293 B
build/block-library/blocks/embed/style-rtl.css 410 B
build/block-library/blocks/embed/style.css 410 B
build/block-library/blocks/embed/theme-rtl.css 110 B
build/block-library/blocks/embed/theme.css 110 B
build/block-library/blocks/file/editor-rtl.css 300 B
build/block-library/blocks/file/editor.css 300 B
build/block-library/blocks/file/style-rtl.css 253 B
build/block-library/blocks/file/style.css 254 B
build/block-library/blocks/file/view.min.js 346 B
build/block-library/blocks/freeform/editor-rtl.css 2.44 kB
build/block-library/blocks/freeform/editor.css 2.44 kB
build/block-library/blocks/gallery/editor-rtl.css 948 B
build/block-library/blocks/gallery/editor.css 950 B
build/block-library/blocks/gallery/style-rtl.css 1.53 kB
build/block-library/blocks/gallery/style.css 1.53 kB
build/block-library/blocks/gallery/theme-rtl.css 108 B
build/block-library/blocks/gallery/theme.css 108 B
build/block-library/blocks/group/editor-rtl.css 339 B
build/block-library/blocks/group/editor.css 339 B
build/block-library/blocks/group/style-rtl.css 57 B
build/block-library/blocks/group/style.css 57 B
build/block-library/blocks/group/theme-rtl.css 78 B
build/block-library/blocks/group/theme.css 78 B
build/block-library/blocks/heading/style-rtl.css 76 B
build/block-library/blocks/heading/style.css 76 B
build/block-library/blocks/html/editor-rtl.css 327 B
build/block-library/blocks/html/editor.css 329 B
build/block-library/blocks/image/editor-rtl.css 736 B
build/block-library/blocks/image/editor.css 737 B
build/block-library/blocks/image/style-rtl.css 627 B
build/block-library/blocks/image/style.css 630 B
build/block-library/blocks/image/theme-rtl.css 110 B
build/block-library/blocks/image/theme.css 110 B
build/block-library/blocks/latest-comments/style-rtl.css 284 B
build/block-library/blocks/latest-comments/style.css 284 B
build/block-library/blocks/latest-posts/editor-rtl.css 213 B
build/block-library/blocks/latest-posts/editor.css 212 B
build/block-library/blocks/latest-posts/style-rtl.css 463 B
build/block-library/blocks/latest-posts/style.css 462 B
build/block-library/blocks/list/style-rtl.css 88 B
build/block-library/blocks/list/style.css 88 B
build/block-library/blocks/media-text/editor-rtl.css 266 B
build/block-library/blocks/media-text/editor.css 263 B
build/block-library/blocks/media-text/style-rtl.css 493 B
build/block-library/blocks/media-text/style.css 490 B
build/block-library/blocks/more/editor-rtl.css 431 B
build/block-library/blocks/more/editor.css 431 B
build/block-library/blocks/navigation-link/editor-rtl.css 705 B
build/block-library/blocks/navigation-link/editor.css 703 B
build/block-library/blocks/navigation-link/style-rtl.css 115 B
build/block-library/blocks/navigation-link/style.css 115 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 296 B
build/block-library/blocks/navigation-submenu/editor.css 295 B
build/block-library/blocks/navigation-submenu/view.min.js 423 B
build/block-library/blocks/navigation/editor-rtl.css 2.03 kB
build/block-library/blocks/navigation/editor.css 2.04 kB
build/block-library/blocks/navigation/style-rtl.css 1.98 kB
build/block-library/blocks/navigation/style.css 1.97 kB
build/block-library/blocks/navigation/view-modal.min.js 2.78 kB
build/block-library/blocks/navigation/view.min.js 443 B
build/block-library/blocks/nextpage/editor-rtl.css 395 B
build/block-library/blocks/nextpage/editor.css 395 B
build/block-library/blocks/page-list/editor-rtl.css 363 B
build/block-library/blocks/page-list/editor.css 363 B
build/block-library/blocks/page-list/style-rtl.css 175 B
build/block-library/blocks/page-list/style.css 175 B
build/block-library/blocks/paragraph/editor-rtl.css 157 B
build/block-library/blocks/paragraph/editor.css 157 B
build/block-library/blocks/paragraph/style-rtl.css 260 B
build/block-library/blocks/paragraph/style.css 260 B
build/block-library/blocks/post-author/style-rtl.css 175 B
build/block-library/blocks/post-author/style.css 176 B
build/block-library/blocks/post-comments-form/editor-rtl.css 96 B
build/block-library/blocks/post-comments-form/editor.css 96 B
build/block-library/blocks/post-comments-form/style-rtl.css 493 B
build/block-library/blocks/post-comments-form/style.css 493 B
build/block-library/blocks/post-excerpt/editor-rtl.css 73 B
build/block-library/blocks/post-excerpt/editor.css 73 B
build/block-library/blocks/post-excerpt/style-rtl.css 69 B
build/block-library/blocks/post-excerpt/style.css 69 B
build/block-library/blocks/post-featured-image/editor-rtl.css 605 B
build/block-library/blocks/post-featured-image/editor.css 605 B
build/block-library/blocks/post-featured-image/style-rtl.css 153 B
build/block-library/blocks/post-featured-image/style.css 153 B
build/block-library/blocks/post-template/editor-rtl.css 99 B
build/block-library/blocks/post-template/editor.css 98 B
build/block-library/blocks/post-template/style-rtl.css 282 B
build/block-library/blocks/post-template/style.css 282 B
build/block-library/blocks/post-terms/style-rtl.css 73 B
build/block-library/blocks/post-terms/style.css 73 B
build/block-library/blocks/post-title/style-rtl.css 80 B
build/block-library/blocks/post-title/style.css 80 B
build/block-library/blocks/preformatted/style-rtl.css 103 B
build/block-library/blocks/preformatted/style.css 103 B
build/block-library/blocks/pullquote/editor-rtl.css 198 B
build/block-library/blocks/pullquote/editor.css 198 B
build/block-library/blocks/pullquote/style-rtl.css 370 B
build/block-library/blocks/pullquote/style.css 370 B
build/block-library/blocks/pullquote/theme-rtl.css 167 B
build/block-library/blocks/pullquote/theme.css 167 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 122 B
build/block-library/blocks/query-pagination-numbers/editor.css 121 B
build/block-library/blocks/query-pagination/editor-rtl.css 221 B
build/block-library/blocks/query-pagination/editor.css 211 B
build/block-library/blocks/query-pagination/style-rtl.css 282 B
build/block-library/blocks/query-pagination/style.css 278 B
build/block-library/blocks/query/editor-rtl.css 439 B
build/block-library/blocks/query/editor.css 439 B
build/block-library/blocks/quote/style-rtl.css 213 B
build/block-library/blocks/quote/style.css 213 B
build/block-library/blocks/quote/theme-rtl.css 223 B
build/block-library/blocks/quote/theme.css 226 B
build/block-library/blocks/read-more/style-rtl.css 132 B
build/block-library/blocks/read-more/style.css 132 B
build/block-library/blocks/rss/editor-rtl.css 202 B
build/block-library/blocks/rss/editor.css 204 B
build/block-library/blocks/rss/style-rtl.css 289 B
build/block-library/blocks/rss/style.css 288 B
build/block-library/blocks/search/editor-rtl.css 165 B
build/block-library/blocks/search/editor.css 165 B
build/block-library/blocks/search/style-rtl.css 396 B
build/block-library/blocks/search/style.css 393 B
build/block-library/blocks/search/theme-rtl.css 114 B
build/block-library/blocks/search/theme.css 114 B
build/block-library/blocks/separator/editor-rtl.css 146 B
build/block-library/blocks/separator/editor.css 146 B
build/block-library/blocks/separator/style-rtl.css 233 B
build/block-library/blocks/separator/style.css 233 B
build/block-library/blocks/separator/theme-rtl.css 194 B
build/block-library/blocks/separator/theme.css 194 B
build/block-library/blocks/shortcode/editor-rtl.css 464 B
build/block-library/blocks/shortcode/editor.css 464 B
build/block-library/blocks/site-logo/editor-rtl.css 708 B
build/block-library/blocks/site-logo/editor.css 708 B
build/block-library/blocks/site-logo/style-rtl.css 192 B
build/block-library/blocks/site-logo/style.css 192 B
build/block-library/blocks/site-tagline/editor-rtl.css 86 B
build/block-library/blocks/site-tagline/editor.css 86 B
build/block-library/blocks/site-title/editor-rtl.css 84 B
build/block-library/blocks/site-title/editor.css 84 B
build/block-library/blocks/social-link/editor-rtl.css 184 B
build/block-library/blocks/social-link/editor.css 184 B
build/block-library/blocks/social-links/editor-rtl.css 674 B
build/block-library/blocks/social-links/editor.css 673 B
build/block-library/blocks/social-links/style-rtl.css 1.39 kB
build/block-library/blocks/social-links/style.css 1.38 kB
build/block-library/blocks/spacer/editor-rtl.css 322 B
build/block-library/blocks/spacer/editor.css 322 B
build/block-library/blocks/spacer/style-rtl.css 48 B
build/block-library/blocks/spacer/style.css 48 B
build/block-library/blocks/table/editor-rtl.css 494 B
build/block-library/blocks/table/editor.css 494 B
build/block-library/blocks/table/style-rtl.css 611 B
build/block-library/blocks/table/style.css 609 B
build/block-library/blocks/table/theme-rtl.css 175 B
build/block-library/blocks/table/theme.css 175 B
build/block-library/blocks/tag-cloud/style-rtl.css 239 B
build/block-library/blocks/tag-cloud/style.css 239 B
build/block-library/blocks/template-part/editor-rtl.css 235 B
build/block-library/blocks/template-part/editor.css 235 B
build/block-library/blocks/template-part/theme-rtl.css 101 B
build/block-library/blocks/template-part/theme.css 101 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B
build/block-library/blocks/text-columns/editor.css 95 B
build/block-library/blocks/text-columns/style-rtl.css 166 B
build/block-library/blocks/text-columns/style.css 166 B
build/block-library/blocks/verse/style-rtl.css 87 B
build/block-library/blocks/verse/style.css 87 B
build/block-library/blocks/video/editor-rtl.css 561 B
build/block-library/blocks/video/editor.css 563 B
build/block-library/blocks/video/style-rtl.css 159 B
build/block-library/blocks/video/style.css 159 B
build/block-library/blocks/video/theme-rtl.css 110 B
build/block-library/blocks/video/theme.css 110 B
build/block-library/common-rtl.css 1.01 kB
build/block-library/common.css 1 kB
build/block-library/editor-elements-rtl.css 75 B
build/block-library/editor-elements.css 75 B
build/block-library/editor-rtl.css 10.9 kB
build/block-library/editor.css 10.9 kB
build/block-library/elements-rtl.css 54 B
build/block-library/elements.css 54 B
build/block-library/index.min.js 185 kB
build/block-library/reset-rtl.css 478 B
build/block-library/reset.css 478 B
build/block-library/style-rtl.css 11.9 kB
build/block-library/style.css 11.9 kB
build/block-library/theme-rtl.css 695 B
build/block-library/theme.css 700 B
build/block-serialization-default-parser/index.min.js 1.11 kB
build/block-serialization-spec-parser/index.min.js 2.83 kB
build/blocks/index.min.js 49 kB
build/components/style-rtl.css 14 kB
build/components/style.css 14 kB
build/compose/index.min.js 12 kB
build/core-data/index.min.js 15.4 kB
build/customize-widgets/index.min.js 11.3 kB
build/customize-widgets/style-rtl.css 1.4 kB
build/customize-widgets/style.css 1.4 kB
build/data-controls/index.min.js 653 B
build/data/index.min.js 8.03 kB
build/date/index.min.js 32 kB
build/deprecated/index.min.js 507 B
build/dom-ready/index.min.js 324 B
build/dom/index.min.js 4.69 kB
build/edit-navigation/index.min.js 16 kB
build/edit-navigation/style-rtl.css 4.02 kB
build/edit-navigation/style.css 4.03 kB
build/edit-post/classic-rtl.css 546 B
build/edit-post/classic.css 547 B
build/edit-post/index.min.js 30.5 kB
build/edit-post/style-rtl.css 6.94 kB
build/edit-post/style.css 6.94 kB
build/edit-site/index.min.js 56.9 kB
build/edit-site/style-rtl.css 8.23 kB
build/edit-site/style.css 8.22 kB
build/edit-widgets/index.min.js 16.5 kB
build/edit-widgets/style-rtl.css 4.35 kB
build/edit-widgets/style.css 4.35 kB
build/editor/index.min.js 41.4 kB
build/editor/style-rtl.css 3.66 kB
build/editor/style.css 3.65 kB
build/element/index.min.js 4.68 kB
build/escape-html/index.min.js 537 B
build/format-library/index.min.js 6.75 kB
build/format-library/style-rtl.css 571 B
build/format-library/style.css 571 B
build/hooks/index.min.js 1.64 kB
build/html-entities/index.min.js 448 B
build/i18n/index.min.js 3.77 kB
build/is-shallow-equal/index.min.js 527 B
build/keyboard-shortcuts/index.min.js 1.78 kB
build/keycodes/index.min.js 1.79 kB
build/list-reusable-blocks/index.min.js 1.74 kB
build/list-reusable-blocks/style-rtl.css 835 B
build/list-reusable-blocks/style.css 835 B
build/media-utils/index.min.js 2.93 kB
build/notices/index.min.js 953 B
build/nux/index.min.js 2.05 kB
build/nux/style-rtl.css 732 B
build/nux/style.css 728 B
build/plugins/index.min.js 1.94 kB
build/preferences-persistence/index.min.js 2.22 kB
build/preferences/index.min.js 1.3 kB
build/primitives/index.min.js 933 B
build/priority-queue/index.min.js 612 B
build/react-i18n/index.min.js 696 B
build/react-refresh-entry/index.min.js 8.44 kB
build/react-refresh-runtime/index.min.js 7.31 kB
build/redux-routine/index.min.js 2.74 kB
build/reusable-blocks/index.min.js 2.21 kB
build/reusable-blocks/style-rtl.css 256 B
build/reusable-blocks/style.css 256 B
build/rich-text/index.min.js 11.1 kB
build/server-side-render/index.min.js 1.61 kB
build/shortcode/index.min.js 1.53 kB
build/token-list/index.min.js 644 B
build/url/index.min.js 3.61 kB
build/vendors/react-dom.min.js 38.5 kB
build/vendors/react.min.js 4.34 kB
build/viewport/index.min.js 1.08 kB
build/warning/index.min.js 268 B
build/widgets/index.min.js 7.19 kB
build/widgets/style-rtl.css 1.16 kB
build/widgets/style.css 1.16 kB
build/wordcount/index.min.js 1.06 kB

compressed-size-action

@ciampo
Copy link
Contributor Author

ciampo commented Aug 10, 2022

Hey @marceloaof, what you're flagging is definitely related to the same issue that this PR is trying to mitigate, and it actually highlighted another edge case that I didn't consider (duplicated colors across separate palettes)

  • in your example, there are two separate entries in the color palette sharing the same color value (ie. #ffffff)
  • when clicking one of these two values, the selected color become #ffffff
  • prior to this PR, the ColorPalette component would simply compare each of its entries to the selected value, and if the two vales are the same, it would mark that specific entry as selected. As you can imagine, this means that all color entries with that value will be flagged as selected.

The solution currently proposed in this PR currently doesn't fix this scenario, which makes think that we should be probably looking at a more systematic solution.


Here's some reflections:

  • What qualifies as a "duplicate entry"?
    • Entries with the same color value but a different name should be considered duplicates?
    • Entries with the same color value and same color name should be considered duplicates?
    • Should we somewhat add an id to each entry, guaranteed to be unique to that color entry?
  • If we agree that "duplicate entries" are a problem that needs to be dealt with:
    • We should probably put additional validation check in every place where it's possible to edit such values (I assume both in Gutenberg's UIs, and in the theme.json validation?) so that it's not possible to add a duplicate color entry?
    • We should also agree on the behavior of the ColorPalette component in case it receives "duplicate" colors:
      • should the component keep all instances of the duplicate entries, and show them all as selected if they match the currently selected value ? (current behavior)
      • should the component filter out and hide all duplicate entries, apart from the first instance?
      • should we look into changing what the component expects as the value ? E.g. instead of passing only the color's value (eg the rgb or hex code), we could pass the whole color object (which includes name, value) or even a unique id (in case we force each color "value" to have one)
      • any other suggestion?
  • How would this translate for gradients?

Personally, I believe that the "safest" way to handle this issue would be to add a unique id to each color passed to ColorPalette, although I'm not sure about how feasible this is (should these ids be generated automatically? And at what point? Would they be automatically persisted?)

Cc'ing also few folks who may help with this (@mtias @jorgefilipecosta @ntsekouras @andrewserong @aaronrobertshaw @jasmussen )

@jasmussen
Copy link
Contributor

Great ticket. I actually ran into this issue a while back, but ended up not ticketing since my specific use case is a bit of a lazy hack. This is a much older GIF, but it shows the issue:
older color bug

Here's what's going on:

  • I have two Black colors. "Black" (#000) and "Interface mode black" (also #000)
  • The first "Black" is always pure black. But the second one is black only in light mode, but gets inverted in dark mode.

Like so:

html body {
	@media (prefers-color-scheme: dark) {
		--wp--preset--color--interface-mode-black: #fff;
		--wp--preset--color--interface-mode-white: #000;
		--wp--preset--color--interface-mode-gray: #333;
	}
}

Yes, it's a bit of a hack to transform the colors like that. But the net result is that I can selectively apply colors on my site that I know will invert in dark mode, as I traverse the site editor.

While a hack, it does illustrate one opportunity for theme developers to be creative. For that reason, I would personally compare active colors only on their slug, and not their value. What do you think?

@ciampo
Copy link
Contributor Author

ciampo commented Aug 10, 2022

Yes, it's a bit of a hack to transform the colors like that. But the net result is that I can selectively apply colors on my site that I know will invert in dark mode, as I traverse the site editor.

While a hack, it does illustrate one opportunity for theme developers to be creative.

Thank you for sharing additional perspective — your point of view makes a lot of sense!

For that reason, I would personally compare active colors only on their slug, and not their value. What do you think?

If by slug you mean the color's name (what in your example were "Black" and "Interface mode black"), using the color's name will only mitigate the problem, but not solve it completely / systematically. This is because I believe it's possible define two color entries that have the same name and color.

That's why I was suggesting that we consider adding a new unique id field to these color objects that is supposed to be unique across the whole list of predefined colors for the site — although I'm aware that this will pose some technical challenges that someone with more experience in how this works in the backend should assess.

If we follow your suggestion and rely on color's name and value only, we'll still need to factor in the edge-case scenario in the ColorPalette component where two separate color entries will share the same name and color. What do you think we should do, in that case? Behave like we do currently? Show only the first instance of that color, and hide the duplicate entries?

@jasmussen
Copy link
Contributor

Ah, I meant that "Interface mode black" gives me --wp--preset--color--interface-mode-black as a variable, whereas "Black" gives me --wp--preset--color--interface-mode-black. I don't know if that's any better than the name, though, as it sounds like you could also have two identical CSS variable names here.

@ciampo
Copy link
Contributor Author

ciampo commented Aug 10, 2022

When I refer to "color entries" having a name and a color value , I refer to the data format that the ColorPalette components expects to receive.

You can see it in action by visiting the Storybook example and clicking on the "Docs" tab in the top of the screen. There, you should see code snippet for different examples where the list of colors is an array of objects containing name and value — which means that it's all what the ColorPalette component can currently use to distinguish duplicate entries.

Copy link
Member

@mirka mirka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this behavior is probably the lesser of all evils, in that the UI is honest about its current limitations. It's a bit confusing when you click Blue2 and Blue1 gets selected, but ultimately that is going to be the color slug you see when you reload the page.

So until we get backend changes that allow more than a raw color value to be persisted, I think this is a compromise we have to live with. (And the backend part should be discussed as a separate issue.)

@@ -2,7 +2,6 @@
/**
* External dependencies
*/
import { map } from 'lodash';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👋 Bye bye

Copy link
Member

@mirka mirka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid two instances of the same color appearing as selected, I've arbitrarily decided that only the first matching color will appear as selected. This is not ideal, but IMO it's better than the current behavior.

I just realized we can't do this because it doesn't make sense when you're using a screen reader. When you select the second instance of the color, it sounds like the UI is unresponsive. Maybe we should keep this PR to the key fix only?

Copy link
Contributor

@aaronrobertshaw aaronrobertshaw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for getting the discussion going on solutions to this issue @ciampo 👍

@mirka makes a great point regarding the accessibility problem with the PR's current approach. +1 to keeping this PR limited to the key fix. Tackling the bigger problem should probably be explored alongside what changes might need to be made to the editors, block supports, theme.json, and global styles.

(note that currently we're not keeping track of the name associated to the color that was clicked by the user)

I believe our editors actually attempt to map the color value back to a preset and store the slug in block attributes etc. For example, textColor or backgroundColor. This slug (name) is used to apply appropriate classnames, CSS var strings in the form of var:preset|color|${ slug }, and create the actual CSS vars themselves. Given that, duplicate slugs or names are likely also a problem in their own right.

Any solution to uniquely identifying color presets in the ColorPalette component will, in all likelihood, be required by our editors.

@andrewserong and @ramonjd will have more insight into how potentially storing additional color data might impact, or be impacted by, the style engine.

@ramonjd
Copy link
Member

ramonjd commented Aug 11, 2022

This slug (name) is used to apply appropriate classnames, CSS var strings in the form of var:preset|color|${ slug }, and create the actual CSS vars themselves. Given that, duplicate slugs or names are likely also a problem in their own right.

@andrewserong and @ramonjd will have more insight into how potentially storing additional color data might impact, or be impacted by, the style engine.

Thanks for the ping!

It depends on whether any additional data appears in a block's attributes and that data has any relationship to the block's actual text/background/gradient value.

For presets, the style engine does expect a value format of var:preset|color|${ slug } as mentioned. In the backend we're constructing those from the attributes themselves. For example, see: https://github.com/WordPress/gutenberg/blob/trunk/lib/block-supports/colors.php#L99

Is there anywhere suggesting that block attribute values such as backgroundColor would change?

For all other purposes, the style engine mainly cares about the block attribute's style property, so if that model doesn't change we're golden.

If there are multiple properties with the same value we'd dedupe them anyway.

Not sure if that even helps, but 🍺

@andrewserong
Copy link
Contributor

andrewserong commented Aug 11, 2022

Thanks for getting the discussion going on solutions to this issue @ciampo 👍

+1 thanks for the discussion here, it's a pretty nuanced issue! The only other thing I'd add is to this comment:

Entries with the same color value but a different name should be considered duplicates?

I'm probably just echoing what's already been discussed, but I think ideally these wouldn't be duplicates, so that someone selecting "Foreground" in a theme color can then switch to a different theme variation where that color is then swapped out for a different color value, versus selecting a color that shouldn't (or probably won't) change on a theme switch, like the named "Black" from the default palette. TwentyTwentyTwo's variations are probably a good example as "Foreground" is set to black in the "Default" variation but takes on a different color in each of the others.

@ciampo
Copy link
Contributor Author

ciampo commented Aug 11, 2022

Thank you all for the replies!

I'll go ahead and make this PR only focused on solving the key issue.

I will also open a new issue where the matter with duplicated color entries can be discussed separately

@ciampo ciampo force-pushed the fix/color-palette-resilient-against-duplicate-colors branch from 7f94b11 to ccb9bf9 Compare August 11, 2022 14:18
@ciampo ciampo force-pushed the fix/color-palette-resilient-against-duplicate-colors branch from ccb9bf9 to 7ff5559 Compare August 11, 2022 14:21
@ciampo ciampo requested a review from mirka August 11, 2022 14:21
@ciampo ciampo changed the title ColorPalette: make component more resilient against duplicate colors ColorPalette: make sure "key" is unique when iterating over color entries with same value Aug 12, 2022
@ciampo
Copy link
Contributor Author

ciampo commented Aug 12, 2022

I will also open a new issue where the matter with duplicated color entries can be discussed separately

Opened #43197

@ciampo ciampo requested a review from aaronrobertshaw August 12, 2022 15:48
Copy link
Member

@mirka mirka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! 🚢

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] UI Components Impacts or related to the UI component system [Package] Components /packages/components [Type] Bug An existing feature does not function as intended
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants