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

Block Variations: Have getActiveBlockVariation return variation with highest specificity #62031

Conversation

ockham
Copy link
Contributor

@ockham ockham commented May 27, 2024

What?

Change getActiveBlockVariation such that if multiple variations' isActive arrays match the given block's attributes, the variation with the highest specificity (i.e. matching the largest number of attributes) is returned.

Why?

See the "Caveats to using isActive" section of the Block Variation docs.

Fixes part of #41303 (see #41303 (comment)).

How?

By assigning a specificity score to each match, corresponding to the number of attributes specified by isActive, and returning the match with the highest specificity. Take the example from the docs:

wp.blocks.registerBlockVariation(
	'core/paragraph',
	{
		name: 'paragraph-red',
		title: 'Red Paragraph',
		attributes: {
			textColor: 'vivid-red',
		},
		isActive: [ 'textColor' ],
	}
);

wp.blocks.registerBlockVariation(
	'core/paragraph',
	{
		name: 'paragraph-red-grey',
		title: 'Red/Grey Paragraph',
		attributes: {
			textColor: 'vivid-red',
			backgroundColor: 'cyan-bluish-gray'
		},
		isActive: [ 'textColor', 'backgroundColor' ]
	}
);

The specificity (i.e. isActive.length) of the paragraph-red variation is 1, whereas for the paragraph-red-grey variation, it is 2. If the current block matches both variations, the one with the higher specificity (here: paragraph-red-grey) is returned.

Note that if any matching variation uses a function rather than an array for its isActive field, a specificity is not assigned; as a result, the match cannot be compared to the other matches. In this case, the first matching variation will be returned instead (much like getActiveBlockVariation did anyway, prior to this PR). (See #62068 for a related issue; fixing it should make it possible to use arrays rather than functions for in most cases.)

Testing Instructions

Do some some testing with block variations in the editor.
Other than that, refer to automated tests.

@ockham ockham added [Feature] Block Variations Block variations, including introducing new variations and variations as a feature [Type] Iteration Scoped iteration of an effort from a tracking issue or overview issue ideally for a major release. labels May 27, 2024
@ockham ockham self-assigned this May 27, 2024
Copy link

github-actions bot commented May 27, 2024

Size Change: +295 B (+0.02%)

Total Size: 1.74 MB

Filename Size Change
build/block-editor/index.min.js 261 kB +146 B (+0.06%)
build/block-library/index.min.js 219 kB +40 B (+0.02%)
build/blocks/index.min.js 51.8 kB +37 B (+0.07%)
build/edit-site/index.min.js 209 kB +2 B (0%)
build/editor/index.min.js 95.6 kB +70 B (+0.07%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 955 B
build/annotations/index.min.js 2.27 kB
build/api-fetch/index.min.js 2.32 kB
build/autop/index.min.js 2.1 kB
build/blob/index.min.js 578 B
build/block-directory/index.min.js 7.29 kB
build/block-directory/style-rtl.css 1.03 kB
build/block-directory/style.css 1.03 kB
build/block-editor/content-rtl.css 4.58 kB
build/block-editor/content.css 4.57 kB
build/block-editor/default-editor-styles-rtl.css 395 B
build/block-editor/default-editor-styles.css 395 B
build/block-editor/style-rtl.css 15.6 kB
build/block-editor/style.css 15.6 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 90 B
build/block-library/blocks/archives/style.css 90 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 122 B
build/block-library/blocks/audio/style.css 122 B
build/block-library/blocks/audio/theme-rtl.css 126 B
build/block-library/blocks/audio/theme.css 126 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 104 B
build/block-library/blocks/avatar/style.css 104 B
build/block-library/blocks/button/editor-rtl.css 307 B
build/block-library/blocks/button/editor.css 307 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 337 B
build/block-library/blocks/buttons/editor.css 337 B
build/block-library/blocks/buttons/style-rtl.css 332 B
build/block-library/blocks/buttons/style.css 332 B
build/block-library/blocks/calendar/style-rtl.css 239 B
build/block-library/blocks/calendar/style.css 239 B
build/block-library/blocks/categories/editor-rtl.css 113 B
build/block-library/blocks/categories/editor.css 112 B
build/block-library/blocks/categories/style-rtl.css 124 B
build/block-library/blocks/categories/style.css 124 B
build/block-library/blocks/code/editor-rtl.css 53 B
build/block-library/blocks/code/editor.css 53 B
build/block-library/blocks/code/style-rtl.css 121 B
build/block-library/blocks/code/style.css 121 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 421 B
build/block-library/blocks/columns/style.css 421 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 199 B
build/block-library/blocks/comment-template/style.css 198 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 840 B
build/block-library/blocks/comments/editor.css 839 B
build/block-library/blocks/comments/style-rtl.css 637 B
build/block-library/blocks/comments/style.css 636 B
build/block-library/blocks/cover/editor-rtl.css 667 B
build/block-library/blocks/cover/editor.css 670 B
build/block-library/blocks/cover/style-rtl.css 1.62 kB
build/block-library/blocks/cover/style.css 1.61 kB
build/block-library/blocks/details/editor-rtl.css 65 B
build/block-library/blocks/details/editor.css 65 B
build/block-library/blocks/details/style-rtl.css 86 B
build/block-library/blocks/details/style.css 86 B
build/block-library/blocks/embed/editor-rtl.css 312 B
build/block-library/blocks/embed/editor.css 312 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 126 B
build/block-library/blocks/embed/theme.css 126 B
build/block-library/blocks/file/editor-rtl.css 326 B
build/block-library/blocks/file/editor.css 327 B
build/block-library/blocks/file/style-rtl.css 280 B
build/block-library/blocks/file/style.css 281 B
build/block-library/blocks/file/view.min.js 324 B
build/block-library/blocks/footnotes/style-rtl.css 201 B
build/block-library/blocks/footnotes/style.css 199 B
build/block-library/blocks/form-input/editor-rtl.css 227 B
build/block-library/blocks/form-input/editor.css 227 B
build/block-library/blocks/form-input/style-rtl.css 343 B
build/block-library/blocks/form-input/style.css 343 B
build/block-library/blocks/form-submission-notification/editor-rtl.css 340 B
build/block-library/blocks/form-submission-notification/editor.css 340 B
build/block-library/blocks/form-submit-button/style-rtl.css 69 B
build/block-library/blocks/form-submit-button/style.css 69 B
build/block-library/blocks/form/view.min.js 471 B
build/block-library/blocks/freeform/editor-rtl.css 2.61 kB
build/block-library/blocks/freeform/editor.css 2.61 kB
build/block-library/blocks/gallery/editor-rtl.css 962 B
build/block-library/blocks/gallery/editor.css 965 B
build/block-library/blocks/gallery/style-rtl.css 1.72 kB
build/block-library/blocks/gallery/style.css 1.72 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 403 B
build/block-library/blocks/group/editor.css 403 B
build/block-library/blocks/group/style-rtl.css 103 B
build/block-library/blocks/group/style.css 103 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 189 B
build/block-library/blocks/heading/style.css 189 B
build/block-library/blocks/html/editor-rtl.css 346 B
build/block-library/blocks/html/editor.css 347 B
build/block-library/blocks/image/editor-rtl.css 891 B
build/block-library/blocks/image/editor.css 891 B
build/block-library/blocks/image/style-rtl.css 1.52 kB
build/block-library/blocks/image/style.css 1.52 kB
build/block-library/blocks/image/theme-rtl.css 137 B
build/block-library/blocks/image/theme.css 137 B
build/block-library/blocks/image/view.min.js 1.54 kB
build/block-library/blocks/latest-comments/style-rtl.css 357 B
build/block-library/blocks/latest-comments/style.css 357 B
build/block-library/blocks/latest-posts/editor-rtl.css 205 B
build/block-library/blocks/latest-posts/editor.css 205 B
build/block-library/blocks/latest-posts/style-rtl.css 512 B
build/block-library/blocks/latest-posts/style.css 512 B
build/block-library/blocks/list/style-rtl.css 102 B
build/block-library/blocks/list/style.css 102 B
build/block-library/blocks/media-text/editor-rtl.css 306 B
build/block-library/blocks/media-text/editor.css 305 B
build/block-library/blocks/media-text/style-rtl.css 505 B
build/block-library/blocks/media-text/style.css 503 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 668 B
build/block-library/blocks/navigation-link/editor.css 669 B
build/block-library/blocks/navigation-link/style-rtl.css 193 B
build/block-library/blocks/navigation-link/style.css 192 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/editor-rtl.css 2.26 kB
build/block-library/blocks/navigation/editor.css 2.26 kB
build/block-library/blocks/navigation/style-rtl.css 2.26 kB
build/block-library/blocks/navigation/style.css 2.25 kB
build/block-library/blocks/navigation/view.min.js 1.03 kB
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 377 B
build/block-library/blocks/page-list/editor.css 377 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 235 B
build/block-library/blocks/paragraph/editor.css 235 B
build/block-library/blocks/paragraph/style-rtl.css 341 B
build/block-library/blocks/paragraph/style.css 341 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 508 B
build/block-library/blocks/post-comments-form/style.css 508 B
build/block-library/blocks/post-content/editor-rtl.css 74 B
build/block-library/blocks/post-content/editor.css 74 B
build/block-library/blocks/post-date/style-rtl.css 61 B
build/block-library/blocks/post-date/style.css 61 B
build/block-library/blocks/post-excerpt/editor-rtl.css 71 B
build/block-library/blocks/post-excerpt/editor.css 71 B
build/block-library/blocks/post-excerpt/style-rtl.css 141 B
build/block-library/blocks/post-excerpt/style.css 141 B
build/block-library/blocks/post-featured-image/editor-rtl.css 734 B
build/block-library/blocks/post-featured-image/editor.css 732 B
build/block-library/blocks/post-featured-image/style-rtl.css 342 B
build/block-library/blocks/post-featured-image/style.css 342 B
build/block-library/blocks/post-navigation-link/style-rtl.css 215 B
build/block-library/blocks/post-navigation-link/style.css 214 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 397 B
build/block-library/blocks/post-template/style.css 396 B
build/block-library/blocks/post-terms/style-rtl.css 96 B
build/block-library/blocks/post-terms/style.css 96 B
build/block-library/blocks/post-time-to-read/style-rtl.css 69 B
build/block-library/blocks/post-time-to-read/style.css 69 B
build/block-library/blocks/post-title/style-rtl.css 100 B
build/block-library/blocks/post-title/style.css 100 B
build/block-library/blocks/preformatted/style-rtl.css 125 B
build/block-library/blocks/preformatted/style.css 125 B
build/block-library/blocks/pullquote/editor-rtl.css 135 B
build/block-library/blocks/pullquote/editor.css 135 B
build/block-library/blocks/pullquote/style-rtl.css 344 B
build/block-library/blocks/pullquote/style.css 343 B
build/block-library/blocks/pullquote/theme-rtl.css 168 B
build/block-library/blocks/pullquote/theme.css 168 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 288 B
build/block-library/blocks/query-pagination/style.css 284 B
build/block-library/blocks/query-title/style-rtl.css 63 B
build/block-library/blocks/query-title/style.css 63 B
build/block-library/blocks/query/editor-rtl.css 486 B
build/block-library/blocks/query/editor.css 486 B
build/block-library/blocks/query/view.min.js 958 B
build/block-library/blocks/quote/style-rtl.css 237 B
build/block-library/blocks/quote/style.css 237 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 140 B
build/block-library/blocks/read-more/style.css 140 B
build/block-library/blocks/rss/editor-rtl.css 101 B
build/block-library/blocks/rss/editor.css 101 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 184 B
build/block-library/blocks/search/editor.css 184 B
build/block-library/blocks/search/style-rtl.css 690 B
build/block-library/blocks/search/style.css 689 B
build/block-library/blocks/search/theme-rtl.css 114 B
build/block-library/blocks/search/theme.css 114 B
build/block-library/blocks/search/view.min.js 478 B
build/block-library/blocks/separator/editor-rtl.css 99 B
build/block-library/blocks/separator/editor.css 99 B
build/block-library/blocks/separator/style-rtl.css 248 B
build/block-library/blocks/separator/style.css 248 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 286 B
build/block-library/blocks/shortcode/editor.css 286 B
build/block-library/blocks/site-logo/editor-rtl.css 805 B
build/block-library/blocks/site-logo/editor.css 805 B
build/block-library/blocks/site-logo/style-rtl.css 218 B
build/block-library/blocks/site-logo/style.css 218 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 124 B
build/block-library/blocks/site-title/editor.css 124 B
build/block-library/blocks/site-title/style-rtl.css 70 B
build/block-library/blocks/site-title/style.css 70 B
build/block-library/blocks/social-link/editor-rtl.css 335 B
build/block-library/blocks/social-link/editor.css 335 B
build/block-library/blocks/social-links/editor-rtl.css 683 B
build/block-library/blocks/social-links/editor.css 681 B
build/block-library/blocks/social-links/style-rtl.css 1.51 kB
build/block-library/blocks/social-links/style.css 1.51 kB
build/block-library/blocks/spacer/editor-rtl.css 350 B
build/block-library/blocks/spacer/editor.css 350 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 395 B
build/block-library/blocks/table/editor.css 395 B
build/block-library/blocks/table/style-rtl.css 639 B
build/block-library/blocks/table/style.css 639 B
build/block-library/blocks/table/theme-rtl.css 146 B
build/block-library/blocks/table/theme.css 146 B
build/block-library/blocks/tag-cloud/style-rtl.css 265 B
build/block-library/blocks/tag-cloud/style.css 266 B
build/block-library/blocks/template-part/editor-rtl.css 393 B
build/block-library/blocks/template-part/editor.css 393 B
build/block-library/blocks/template-part/theme-rtl.css 112 B
build/block-library/blocks/template-part/theme.css 112 B
build/block-library/blocks/term-description/style-rtl.css 111 B
build/block-library/blocks/term-description/style.css 111 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 99 B
build/block-library/blocks/verse/style.css 99 B
build/block-library/blocks/video/editor-rtl.css 552 B
build/block-library/blocks/video/editor.css 555 B
build/block-library/blocks/video/style-rtl.css 185 B
build/block-library/blocks/video/style.css 185 B
build/block-library/blocks/video/theme-rtl.css 126 B
build/block-library/blocks/video/theme.css 126 B
build/block-library/classic-rtl.css 179 B
build/block-library/classic.css 179 B
build/block-library/common-rtl.css 1.11 kB
build/block-library/common.css 1.11 kB
build/block-library/editor-elements-rtl.css 75 B
build/block-library/editor-elements.css 75 B
build/block-library/editor-rtl.css 12 kB
build/block-library/editor.css 12 kB
build/block-library/elements-rtl.css 54 B
build/block-library/elements.css 54 B
build/block-library/reset-rtl.css 472 B
build/block-library/reset.css 472 B
build/block-library/style-rtl.css 14.6 kB
build/block-library/style.css 14.6 kB
build/block-library/theme-rtl.css 703 B
build/block-library/theme.css 706 B
build/block-serialization-default-parser/index.min.js 1.12 kB
build/block-serialization-spec-parser/index.min.js 2.87 kB
build/commands/index.min.js 15.2 kB
build/commands/style-rtl.css 953 B
build/commands/style.css 951 B
build/components/index.min.js 222 kB
build/components/style-rtl.css 12 kB
build/components/style.css 12 kB
build/compose/index.min.js 12.8 kB
build/core-commands/index.min.js 2.71 kB
build/core-data/index.min.js 72.5 kB
build/customize-widgets/index.min.js 10.9 kB
build/customize-widgets/style-rtl.css 1.36 kB
build/customize-widgets/style.css 1.36 kB
build/data-controls/index.min.js 640 B
build/data/index.min.js 9.01 kB
build/date/index.min.js 17.9 kB
build/deprecated/index.min.js 451 B
build/dom-ready/index.min.js 324 B
build/dom/index.min.js 4.65 kB
build/edit-post/classic-rtl.css 578 B
build/edit-post/classic.css 578 B
build/edit-post/index.min.js 12.5 kB
build/edit-post/style-rtl.css 2.35 kB
build/edit-post/style.css 2.35 kB
build/edit-site/style-rtl.css 11.8 kB
build/edit-site/style.css 11.8 kB
build/edit-widgets/index.min.js 17.6 kB
build/edit-widgets/style-rtl.css 4.21 kB
build/edit-widgets/style.css 4.21 kB
build/editor/style-rtl.css 9.22 kB
build/editor/style.css 9.23 kB
build/element/index.min.js 4.83 kB
build/escape-html/index.min.js 537 B
build/format-library/index.min.js 8.05 kB
build/format-library/style-rtl.css 493 B
build/format-library/style.css 492 B
build/hooks/index.min.js 1.55 kB
build/html-entities/index.min.js 448 B
build/i18n/index.min.js 3.58 kB
build/interactivity/debug.min.js 16.5 kB
build/interactivity/file.min.js 447 B
build/interactivity/image.min.js 1.67 kB
build/interactivity/index.min.js 13.4 kB
build/interactivity/navigation.min.js 1.17 kB
build/interactivity/query.min.js 740 B
build/interactivity/router.min.js 2.81 kB
build/interactivity/search.min.js 618 B
build/is-shallow-equal/index.min.js 527 B
build/keyboard-shortcuts/index.min.js 1.31 kB
build/keycodes/index.min.js 1.46 kB
build/list-reusable-blocks/index.min.js 2.14 kB
build/list-reusable-blocks/style-rtl.css 851 B
build/list-reusable-blocks/style.css 851 B
build/media-utils/index.min.js 2.92 kB
build/modules/importmap-polyfill.min.js 12.2 kB
build/notices/index.min.js 948 B
build/nux/index.min.js 1.58 kB
build/nux/style-rtl.css 748 B
build/nux/style.css 744 B
build/patterns/index.min.js 6.51 kB
build/patterns/style-rtl.css 595 B
build/patterns/style.css 595 B
build/plugins/index.min.js 1.81 kB
build/preferences-persistence/index.min.js 2.06 kB
build/preferences/index.min.js 2.9 kB
build/preferences/style-rtl.css 719 B
build/preferences/style.css 721 B
build/primitives/index.min.js 831 B
build/priority-queue/index.min.js 1.52 kB
build/private-apis/index.min.js 1 kB
build/react-i18n/index.min.js 629 B
build/react-refresh-entry/index.min.js 9.47 kB
build/react-refresh-runtime/index.min.js 6.78 kB
build/redux-routine/index.min.js 2.7 kB
build/reusable-blocks/index.min.js 2.72 kB
build/reusable-blocks/style-rtl.css 256 B
build/reusable-blocks/style.css 256 B
build/rich-text/index.min.js 10.1 kB
build/router/index.min.js 1.96 kB
build/server-side-render/index.min.js 1.96 kB
build/shortcode/index.min.js 1.39 kB
build/style-engine/index.min.js 2.02 kB
build/token-list/index.min.js 582 B
build/url/index.min.js 3.74 kB
build/vendors/react-dom.min.js 42.8 kB
build/vendors/react-jsx-runtime.min.js 554 B
build/vendors/react.min.js 2.65 kB
build/viewport/index.min.js 964 B
build/warning/index.min.js 249 B
build/widgets/index.min.js 7.13 kB
build/widgets/style-rtl.css 1.17 kB
build/widgets/style.css 1.17 kB
build/wordcount/index.min.js 1.02 kB

compressed-size-action

@ockham ockham changed the title Block Variations: Have getActiveBlockVariation return variation with highest specificity May 28, 2024
@ockham ockham marked this pull request as ready for review May 28, 2024 14:25
Copy link

github-actions bot commented May 28, 2024

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: ockham <bernhard-reiter@git.wordpress.org>
Co-authored-by: ntsekouras <ntsekouras@git.wordpress.org>
Co-authored-by: gziolo <gziolo@git.wordpress.org>
Co-authored-by: ndiego <ndiego@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@ockham ockham requested review from gziolo and tjcafferkey May 28, 2024 14:26
@ockham ockham added [Type] Enhancement A suggestion for improvement. and removed [Type] Iteration Scoped iteration of an effort from a tracking issue or overview issue ideally for a major release. labels May 28, 2024
@ntsekouras
Copy link
Contributor

ntsekouras commented May 28, 2024

Thanks for the PR!

I commented on the Group block variations issue and it seems really related:

That's a hard problem indeed. We had some explorations in the past for this to see how it would be best to extend a core block and its variations. We ended up adding a 'dumb' attribute(namespace) in Query Loop block that's been taken into account in isActive function and can be used in filters, hooks, php, etc.. While it might not be ideal, this approach seems reliable and is similar to the workaround suggested above(className, etc..).

Maybe something like that could be a reliable solution, by injecting in all blocks an attribute(like the namespace) with a supports flag? 🤔

This approach still has some shortcomings with the isActive being a function for one. The most important thing probably though is that sometimes with variations we want to more(like adding extra controls with filters etc..), without actually having many more attributes to define that the variation is active. An example would be the Posts List variation of Query Loop and a possible new variation that just wants to add more controls, but still cares only if the postType is post. Makes sense?

Sorry if I'm focusing more on examples of Query Loop and object attributes, but that's the use case I've discussed/explored most..

@ockham
Copy link
Contributor Author

ockham commented May 29, 2024

Sorry if I'm focusing more on examples of Query Loop and object attributes, but that's the use case I've discussed/explored most..

No, that's fine! It's actually helpful to see other use cases that I hadn't taken into account 🙂

Maybe something like that could be a reliable solution, by injecting in all blocks an attribute(like the namespace) with a supports flag? 🤔

So basically a "silent" attribute that's used to differentiate variations, but doesn't have any other effect on the block? During our explorations related to #43743, @tjcafferkey and I considered adding a metadata.variation attribute to a block to store its currently active variations. Ultimately, we decided against this (at least for now), as we were hoping to infer the variation from attributes instead (to avoid having to sync the attribute, among other things). As a consequence, we're trying to improve the logic that we're using to infer the active variation, with the goal of getting incremental improvements (like this PR, or WordPress/wordpress-develop#6602) into WP 6.6 🙂

Now while this PR cannot detect the active variation in the situation you're describing, I believe it would cover the specific use case described in #41303 (if we also implement the idea in #62068) and AFAICS, there wouldn't be any adverse side effects. Do you think it's viable to land those two things before we consider adding a "silent" attribute (like the namespace in this example) to all variations?

@ockham ockham force-pushed the fix/get-active-block-variation-return-match-with-highest-specificity branch 2 times, most recently from e8bd468 to 58ce75c Compare May 30, 2024 14:28

```js
isActive: [ 'providerNameSlug' ]
isActive: [ 'query.postType' ]
Copy link
Member

Choose a reason for hiding this comment

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

@justintadlock and @bacoords, I think you will like these change in relation to Block Bindings 😄

Copy link
Member

Choose a reason for hiding this comment

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

This is a fantastic PR overall. 🙌

@ntsekouras ntsekouras force-pushed the fix/get-active-block-variation-return-match-with-highest-specificity branch from 58ce75c to ec6a1c6 Compare May 31, 2024 12:35
Copy link
Member

@gziolo gziolo left a comment

Choose a reason for hiding this comment

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

Code-wise, this looks good to me. I will give it some testing as I wanted to confirm it plays nicely with block bindings :)

} );

return match;
let candidate = [ undefined, 0 ];
Copy link
Member

Choose a reason for hiding this comment

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

This part of the code is hard to follow because of the tuple-like array. However, it works as expected.

@@ -237,10 +237,17 @@ export const getBlockVariations = createSelector(
export function getActiveBlockVariation( state, blockName, attributes, scope ) {
const variations = getBlockVariations( state, blockName, scope );

const match = variations?.find( ( variation ) => {
if ( ! variations ) {
Copy link
Member

Choose a reason for hiding this comment

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

I wasn't sure about this like but getBlockVariations returns a non-empty array or undefined after converting the array into the return value.

@gziolo
Copy link
Member

gziolo commented May 31, 2024

I'm seeing some small inconsistencies after inserting a variation. It gets recognized correctly upon insertion, but after reloading the page, it no longer can be recognized as a variation. Maybe the comparison is based on the object reference which is the same upon insertion but will differ after the page reloads?

Screen.Recording.2024-05-31.at.15.00.59.mov

This is the modification that I applied to the Paragraph block:

diff --git a/packages/block-library/src/paragraph/block.json b/packages/block-library/src/paragraph/block.json
index 6f11baf838..8bd350fb1e 100644
--- a/packages/block-library/src/paragraph/block.json
+++ b/packages/block-library/src/paragraph/block.json
@@ -70,5 +70,25 @@
 		}
 	},
 	"editorStyle": "wp-block-paragraph-editor",
-	"style": "wp-block-paragraph"
+	"style": "wp-block-paragraph",
+	"variations": [
+		{
+			"name": "warning",
+			"title": "Warning",
+			"icon": "warning",
+			"attributes": {
+				"metadata": {
+					"bindings": {
+						"content": {
+							"source": "core/post-meta",
+							"args": {
+								"key": "text_field"
+							}
+						}
+					}
+				}
+			},
+			"isActive": [ "metadata.bindings.content" ]
+		}
+	]
 }
Copy link
Contributor

@ntsekouras ntsekouras left a comment

Choose a reason for hiding this comment

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

Thank you @ockham! Code wise looks good! I'm trying something small if we can simplify a bit and @gziolo comment is probably something to look into. I haven't work on bindings, so it would be better for folks who have, to test better.


The function version of this property accepts a block instance's `blockAttributes` as the first argument, and the `variationAttributes` declared for a variation as the second argument. These arguments can be used to determine if a variation is active by comparing them and returning a `true` or `false` (indicating whether this variation is inactive for this block instance).
The `string[]` version is used to declare which of the block instance's attributes should be compared to the given variation's. Each attribute will be checked and the variation will be active if all of them match.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
The `string[]` version is used to declare which of the block instance's attributes should be compared to the given variation's. Each attribute will be checked and the variation will be active if all of them match.
The `string[]` version is used to declare which of the block instance's attributes should be compared to each variation's attributes. Each attribute will be checked and the variation will be active if all of them match.
```

Nested object paths are also supported. For example, consider a block variation that has a `query` object as an attribute. It is possible to determine if the variation is active solely based on that object's `postType` property (while ignoring all its other properties):
The function version of this property accepts a block instance's `blockAttributes` as the first argument, and the `variationAttributes` declared for a variation as the second argument. These arguments can be used to determine if a variation is active by comparing them and returning a `true` or `false` (indicating whether this variation is inactive for this block instance).
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
The function version of this property accepts a block instance's `blockAttributes` as the first argument, and the `variationAttributes` declared for a variation as the second argument. These arguments can be used to determine if a variation is active by comparing them and returning a `true` or `false` (indicating whether this variation is inactive for this block instance).
The function version of this property accepts a block instance's `blockAttributes` as the first argument, and the `variationAttributes` declared for a variation as the second argument. These arguments can be used to determine if a variation is active by comparing them and returning `true` or `false` (indicating whether this variation is inactive for this block instance).

The `isActive` property can return false positives if multiple variations exist for a specific block and the `isActive` checks are not specific enough. To demonstrate this, consider the following example:
If there are multiple variations whose `isActive` check matches a given block instance, and all of them are string arrays, then the variation with the highest _specificity_ will be chosen. Consider the following example:
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Maybe make bold the all of them?

@gziolo
Copy link
Member

gziolo commented May 31, 2024

I'm trying to test this variation added to the Paragraph block:

"variations": [
	{
		"name": "warning-with-color",
		"title": "Warning with color",
		"icon": "warning",
		"attributes": {
			"content": "This is a warning message.",
			"backgroundColor": "luminous-vivid-amber"
		},
		"isActive": [ "content", "backgroundColor" ]
	}
]

It isn't recognized as active for some reason I don't fully understand for both trunk and this branch. I was wondering if it ever worked this way or regressed at some point. In my testing, I figured out that it works correctly for:

"isActive": [ "backgroundColor" ]

but not

"isActive": [ "content" ]

In effect, I think we need to fix it separately for attributes represented as RichText objects and cast them to string values when comparing.

Copy link
Member

@gziolo gziolo left a comment

Choose a reason for hiding this comment

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

Very excited about this functionality, we will have to follow-up with some small fixes to ensure that objects are compared by value, and RichText value properly casted to string value. The same issues existed before so it isn't directly related to this improved functionality but rather related to the way I'm testing it.

@ntsekouras
Copy link
Contributor

Hope you don't mind, but pushed a commit that I think it simplifies things a bit and also keeps the best match if a variation has defined an isActive function (see the test change). Feel free to revert of course 😄

@gziolo
Copy link
Member

gziolo commented May 31, 2024

@ntsekouras, feel free to land it when CI passes so we have it ready on time for WP 6.6 Beta 1 😄

@ntsekouras ntsekouras enabled auto-merge (squash) May 31, 2024 14:04
@ntsekouras ntsekouras merged commit 4ca6528 into trunk May 31, 2024
62 checks passed
@ntsekouras ntsekouras deleted the fix/get-active-block-variation-return-match-with-highest-specificity branch May 31, 2024 14:39
@github-actions github-actions bot added this to the Gutenberg 18.5 milestone May 31, 2024
@gziolo
Copy link
Member

gziolo commented May 31, 2024

I'm seeing some small inconsistencies after inserting a variation. It gets recognized correctly upon insertion, but after reloading the page, it no longer can be recognized as a variation. Maybe the comparison is based on the object reference which is the same upon insertion but will differ after the page reloads?

I wanted to report back on my findings. I did some more testing, and everything works as in trunk when defining the isActive array correctly with the new notation. We can compare at the moment only using the primitive values. I solved the issues reported previously by using more specific selectors for comparison:

"isActive": [
	"backgroundColor",
	"metadata.bindings.content.source",
	"metadata.bindings.content.args.key"
]

This is how it looks in action for a few variations correctly defined:

Screen.Recording.2024-05-31.at.16.49.19.mov
@ockham
Copy link
Contributor Author

ockham commented Jun 3, 2024

Thank you both for approving and landing this! 🎉

Hope you don't mind, but pushed a commit that I think it simplifies things a bit and also keeps the best match if a variation has defined an isActive function (see the test change). Feel free to revert of course 😄

@ntsekouras I was initially considering a similar approach but ultimately decided against. IIUC, your change returns the best string[] isActive based match if an isActive function is found. This silently assumes that the specificity of the string array isActive is better than the function one.

It seemed to me that we couldn't make that assumption, as an isActive function is kind of opaque. Internally, it could compare one or ten attributes, in which case its de-facto specificity would be very low, or very high; I didn't want to assume either way. Instead, I concluded that the only consistent behavior would be to discard the specificity sorting if there were any isActive properties that matched the criterion.

Granted, it is probably an edge case to have a mix of string[] and function isActive properties, and defaulting to the first match (as I did) is also somewhat arbitrary. I can't seem to come up with a case where the current logic yields "worse" result than the previous method, so it might be just fine 🙂

@ockham
Copy link
Contributor Author

ockham commented Jun 3, 2024

@gziolo I'll file issues and/or PRs to address object and RichText comparison in the next couple of days!

carstingaxion pushed a commit to carstingaxion/gutenberg that referenced this pull request Jun 4, 2024
…h highest specificity (WordPress#62031)

Co-authored-by: ockham <bernhard-reiter@git.wordpress.org>
Co-authored-by: ntsekouras <ntsekouras@git.wordpress.org>
Co-authored-by: gziolo <gziolo@git.wordpress.org>
Co-authored-by: ndiego <ndiego@git.wordpress.org>
patil-vipul pushed a commit to patil-vipul/gutenberg that referenced this pull request Jun 17, 2024
…h highest specificity (WordPress#62031)

Co-authored-by: ockham <bernhard-reiter@git.wordpress.org>
Co-authored-by: ntsekouras <ntsekouras@git.wordpress.org>
Co-authored-by: gziolo <gziolo@git.wordpress.org>
Co-authored-by: ndiego <ndiego@git.wordpress.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Block Variations Block variations, including introducing new variations and variations as a feature [Type] Enhancement A suggestion for improvement.
4 participants