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

Suggest Pattern transformations that are contextual to the currently selected blocks #29890

Closed
wants to merge 14 commits into from

Conversation

ntsekouras
Copy link
Contributor

@ntsekouras ntsekouras commented Mar 16, 2021

Description

Resolves: #28736
Part of: #27575

This is a POC for suggesting block pattern transformations that are contextual to the currently selected blocks.

First of all we need to decide what type of transformations we will suggest/support and we have to keep in mind flows for template parts and any other special cases we can think of. You can find discussions on the above issues and especially here.

Rationale behind the current decisions

  1. While it makes sense to show patterns for grouping blocks (like Group or Columns) during insertion in Placeholder (explored here), it doesn't make sense to suggest any pattern to transform, as a user could have anything inside as content. So if any selected block falls into this category, do not suggest any transforms.
  2. The only exception to the above would be Template Parts (at least from what I thought) and we should have a replacing all contents flow probably.
  3. I have skipped any check about transforming selected blocks to available block transforms inside patterns, as I think it would be very confusing to have a really different suggested pattern. An example would be to have a paragraph selected and show patterns that do not include paragraph but have transformed the paragraph to a quote block.
  4. To show a suggested pattern, every selected block must have matched with a pattern's block . This doesn't necessarily mean that the order will be preserved. You can test this in this PR by selecting a paragraph and two headings (in this order) and see the suggested pattern that has the following order: [heading, paragraph, heading]. Also shown at the gif.
  5. If a suggested pattern has more blocks than the selected blocks, add them as are from the pattern. Every pattern block that matched with a selected block will be transformed. IMO this is the main reason for doing this and having block patterns in general. We want to inspire/show users alternative designs.
  6. When we have a match with inner blocks like Buttons, keep only the blocks from the selected block and skip the inner blocks from the pattern. You can test this with a test Buttons pattern I have in this PR.

I have many comments in my code trying to explain as good as I can the flows.

Testing instructions

  1. Test with the demo patterns I created in lib/test-block-patterns.php or register one yourselves
  2. Create some paragraphs or headings and make different selections (single or multiple) to observe the previews from the selected blocks.
  3. Only if all of the selected blocks can be matched in pattern's blocks, it will be shown as an option

Screenshots

patternTransforms

Notes

  1. I would like some feedback/thoughts about the handling of patterns transformation first, so design wise focus on a higher level and not details that can be fixed quite easily.
  2. There has been some POC work for Template Parts to take into account active variations, but the flow is not complete. We will probably need to create a new Template Part on a selection.

I would love your thoughts and feedback on this!

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows the accessibility standards.
  • I've tested my changes with keyboard and screen readers.
  • My code has proper inline documentation.
  • I've included developer documentation if appropriate.
  • I've updated all React Native files affected by any refactorings/renamings in this PR.

@ntsekouras ntsekouras requested review from mtias, jameskoster, kjellr, jasmussen and a team March 16, 2021 12:56
@github-actions
Copy link

github-actions bot commented Mar 16, 2021

Size Change: +1.53 kB (0%)

Total Size: 1.42 MB

Filename Size Change
build/annotations/index.js 3.78 kB +2 B (0%)
build/api-fetch/index.js 3.42 kB +1 B (0%)
build/block-directory/index.js 8.63 kB -1 B (0%)
build/block-editor/index.js 129 kB +1.22 kB (+1%)
build/block-editor/style-rtl.css 12.5 kB +97 B (+1%)
build/block-editor/style.css 12.5 kB +96 B (+1%)
build/block-library/index.js 151 kB +4 B (0%)
build/blocks/index.js 48.5 kB +109 B (0%)
build/components/index.js 286 kB -3 B (0%)
build/core-data/index.js 16.8 kB +1 B (0%)
build/customize-widgets/index.js 7.33 kB -1 B (0%)
build/edit-post/index.js 307 kB -2 B (0%)
build/edit-site/index.js 27.5 kB +3 B (0%)
build/edit-widgets/index.js 15.7 kB -1 B (0%)
build/editor/index.js 42.7 kB +3 B (0%)
build/element/index.js 4.62 kB -1 B (0%)
build/format-library/index.js 6.76 kB -1 B (0%)
build/keyboard-shortcuts/index.js 2.53 kB -1 B (0%)
build/nux/index.js 3.42 kB -1 B (0%)
build/primitives/index.js 1.42 kB +1 B (0%)
build/server-side-render/index.js 2.59 kB -1 B (0%)
ℹ️ View Unchanged
Filename Size Change
build/a11y/index.js 1.14 kB 0 B
build/autop/index.js 2.82 kB 0 B
build/blob/index.js 665 B 0 B
build/block-directory/style-rtl.css 1 kB 0 B
build/block-directory/style.css 1.01 kB 0 B
build/block-library/blocks/archives/editor-rtl.css 61 B 0 B
build/block-library/blocks/archives/editor.css 60 B 0 B
build/block-library/blocks/audio/editor-rtl.css 58 B 0 B
build/block-library/blocks/audio/editor.css 58 B 0 B
build/block-library/blocks/audio/style-rtl.css 112 B 0 B
build/block-library/blocks/audio/style.css 112 B 0 B
build/block-library/blocks/block/editor-rtl.css 161 B 0 B
build/block-library/blocks/block/editor.css 161 B 0 B
build/block-library/blocks/button/editor-rtl.css 475 B 0 B
build/block-library/blocks/button/editor.css 474 B 0 B
build/block-library/blocks/button/style-rtl.css 489 B 0 B
build/block-library/blocks/button/style.css 488 B 0 B
build/block-library/blocks/buttons/editor-rtl.css 315 B 0 B
build/block-library/blocks/buttons/editor.css 315 B 0 B
build/block-library/blocks/buttons/style-rtl.css 364 B 0 B
build/block-library/blocks/buttons/style.css 363 B 0 B
build/block-library/blocks/calendar/style-rtl.css 208 B 0 B
build/block-library/blocks/calendar/style.css 208 B 0 B
build/block-library/blocks/categories/editor-rtl.css 84 B 0 B
build/block-library/blocks/categories/editor.css 83 B 0 B
build/block-library/blocks/categories/style-rtl.css 79 B 0 B
build/block-library/blocks/categories/style.css 79 B 0 B
build/block-library/blocks/code/style-rtl.css 90 B 0 B
build/block-library/blocks/code/style.css 90 B 0 B
build/block-library/blocks/columns/editor-rtl.css 190 B 0 B
build/block-library/blocks/columns/editor.css 190 B 0 B
build/block-library/blocks/columns/style-rtl.css 436 B 0 B
build/block-library/blocks/columns/style.css 435 B 0 B
build/block-library/blocks/cover/editor-rtl.css 605 B 0 B
build/block-library/blocks/cover/editor.css 605 B 0 B
build/block-library/blocks/cover/style-rtl.css 1.23 kB 0 B
build/block-library/blocks/cover/style.css 1.23 kB 0 B
build/block-library/blocks/embed/editor-rtl.css 486 B 0 B
build/block-library/blocks/embed/editor.css 486 B 0 B
build/block-library/blocks/embed/style-rtl.css 401 B 0 B
build/block-library/blocks/embed/style.css 400 B 0 B
build/block-library/blocks/file/editor-rtl.css 175 B 0 B
build/block-library/blocks/file/editor.css 174 B 0 B
build/block-library/blocks/file/style-rtl.css 248 B 0 B
build/block-library/blocks/file/style.css 248 B 0 B
build/block-library/blocks/freeform/editor-rtl.css 2.44 kB 0 B
build/block-library/blocks/freeform/editor.css 2.44 kB 0 B
build/block-library/blocks/gallery/editor-rtl.css 704 B 0 B
build/block-library/blocks/gallery/editor.css 705 B 0 B
build/block-library/blocks/gallery/style-rtl.css 1.09 kB 0 B
build/block-library/blocks/gallery/style.css 1.09 kB 0 B
build/block-library/blocks/group/editor-rtl.css 160 B 0 B
build/block-library/blocks/group/editor.css 160 B 0 B
build/block-library/blocks/group/style-rtl.css 57 B 0 B
build/block-library/blocks/group/style.css 57 B 0 B
build/block-library/blocks/heading/editor-rtl.css 129 B 0 B
build/block-library/blocks/heading/editor.css 129 B 0 B
build/block-library/blocks/heading/style-rtl.css 76 B 0 B
build/block-library/blocks/heading/style.css 76 B 0 B
build/block-library/blocks/html/editor-rtl.css 281 B 0 B
build/block-library/blocks/html/editor.css 281 B 0 B
build/block-library/blocks/image/editor-rtl.css 717 B 0 B
build/block-library/blocks/image/editor.css 716 B 0 B
build/block-library/blocks/image/style-rtl.css 476 B 0 B
build/block-library/blocks/image/style.css 478 B 0 B
build/block-library/blocks/latest-comments/style-rtl.css 281 B 0 B
build/block-library/blocks/latest-comments/style.css 282 B 0 B
build/block-library/blocks/latest-posts/editor-rtl.css 137 B 0 B
build/block-library/blocks/latest-posts/editor.css 137 B 0 B
build/block-library/blocks/latest-posts/style-rtl.css 523 B 0 B
build/block-library/blocks/latest-posts/style.css 522 B 0 B
build/block-library/blocks/legacy-widget/editor-rtl.css 398 B 0 B
build/block-library/blocks/legacy-widget/editor.css 399 B 0 B
build/block-library/blocks/list/style-rtl.css 63 B 0 B
build/block-library/blocks/list/style.css 63 B 0 B
build/block-library/blocks/media-text/editor-rtl.css 191 B 0 B
build/block-library/blocks/media-text/editor.css 191 B 0 B
build/block-library/blocks/media-text/style-rtl.css 535 B 0 B
build/block-library/blocks/media-text/style.css 532 B 0 B
build/block-library/blocks/more/editor-rtl.css 434 B 0 B
build/block-library/blocks/more/editor.css 434 B 0 B
build/block-library/blocks/navigation-link/editor-rtl.css 634 B 0 B
build/block-library/blocks/navigation-link/editor.css 635 B 0 B
build/block-library/blocks/navigation-link/style-rtl.css 908 B 0 B
build/block-library/blocks/navigation-link/style.css 908 B 0 B
build/block-library/blocks/navigation/editor-rtl.css 1.13 kB 0 B
build/block-library/blocks/navigation/editor.css 1.13 kB 0 B
build/block-library/blocks/navigation/style-rtl.css 204 B 0 B
build/block-library/blocks/navigation/style.css 205 B 0 B
build/block-library/blocks/nextpage/editor-rtl.css 395 B 0 B
build/block-library/blocks/nextpage/editor.css 395 B 0 B
build/block-library/blocks/page-list/editor-rtl.css 170 B 0 B
build/block-library/blocks/page-list/editor.css 170 B 0 B
build/block-library/blocks/page-list/style-rtl.css 167 B 0 B
build/block-library/blocks/page-list/style.css 167 B 0 B
build/block-library/blocks/paragraph/editor-rtl.css 157 B 0 B
build/block-library/blocks/paragraph/editor.css 157 B 0 B
build/block-library/blocks/paragraph/style-rtl.css 247 B 0 B
build/block-library/blocks/paragraph/style.css 248 B 0 B
build/block-library/blocks/post-author/editor-rtl.css 209 B 0 B
build/block-library/blocks/post-author/editor.css 209 B 0 B
build/block-library/blocks/post-author/style-rtl.css 183 B 0 B
build/block-library/blocks/post-author/style.css 184 B 0 B
build/block-library/blocks/post-comments-form/style-rtl.css 250 B 0 B
build/block-library/blocks/post-comments-form/style.css 250 B 0 B
build/block-library/blocks/post-content/editor-rtl.css 139 B 0 B
build/block-library/blocks/post-content/editor.css 139 B 0 B
build/block-library/blocks/post-excerpt/editor-rtl.css 73 B 0 B
build/block-library/blocks/post-excerpt/editor.css 73 B 0 B
build/block-library/blocks/post-featured-image/editor-rtl.css 338 B 0 B
build/block-library/blocks/post-featured-image/editor.css 338 B 0 B
build/block-library/blocks/post-featured-image/style-rtl.css 100 B 0 B
build/block-library/blocks/post-featured-image/style.css 100 B 0 B
build/block-library/blocks/preformatted/style-rtl.css 103 B 0 B
build/block-library/blocks/preformatted/style.css 103 B 0 B
build/block-library/blocks/pullquote/editor-rtl.css 183 B 0 B
build/block-library/blocks/pullquote/editor.css 183 B 0 B
build/block-library/blocks/pullquote/style-rtl.css 318 B 0 B
build/block-library/blocks/pullquote/style.css 318 B 0 B
build/block-library/blocks/query-loop/editor-rtl.css 83 B 0 B
build/block-library/blocks/query-loop/editor.css 82 B 0 B
build/block-library/blocks/query-loop/style-rtl.css 315 B 0 B
build/block-library/blocks/query-loop/style.css 317 B 0 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 122 B 0 B
build/block-library/blocks/query-pagination-numbers/editor.css 121 B 0 B
build/block-library/blocks/query-pagination/editor-rtl.css 270 B 0 B
build/block-library/blocks/query-pagination/editor.css 262 B 0 B
build/block-library/blocks/query-pagination/style-rtl.css 168 B 0 B
build/block-library/blocks/query-pagination/style.css 168 B 0 B
build/block-library/blocks/query-title/editor-rtl.css 86 B 0 B
build/block-library/blocks/query-title/editor.css 86 B 0 B
build/block-library/blocks/query/editor-rtl.css 795 B 0 B
build/block-library/blocks/query/editor.css 794 B 0 B
build/block-library/blocks/quote/style-rtl.css 169 B 0 B
build/block-library/blocks/quote/style.css 169 B 0 B
build/block-library/blocks/rss/editor-rtl.css 201 B 0 B
build/block-library/blocks/rss/editor.css 202 B 0 B
build/block-library/blocks/rss/style-rtl.css 290 B 0 B
build/block-library/blocks/rss/style.css 290 B 0 B
build/block-library/blocks/search/editor-rtl.css 165 B 0 B
build/block-library/blocks/search/editor.css 165 B 0 B
build/block-library/blocks/search/style-rtl.css 342 B 0 B
build/block-library/blocks/search/style.css 344 B 0 B
build/block-library/blocks/separator/editor-rtl.css 99 B 0 B
build/block-library/blocks/separator/editor.css 99 B 0 B
build/block-library/blocks/separator/style-rtl.css 251 B 0 B
build/block-library/blocks/separator/style.css 251 B 0 B
build/block-library/blocks/shortcode/editor-rtl.css 512 B 0 B
build/block-library/blocks/shortcode/editor.css 512 B 0 B
build/block-library/blocks/site-logo/editor-rtl.css 201 B 0 B
build/block-library/blocks/site-logo/editor.css 201 B 0 B
build/block-library/blocks/site-logo/style-rtl.css 115 B 0 B
build/block-library/blocks/site-logo/style.css 115 B 0 B
build/block-library/blocks/social-link/editor-rtl.css 164 B 0 B
build/block-library/blocks/social-link/editor.css 165 B 0 B
build/block-library/blocks/social-links/editor-rtl.css 776 B 0 B
build/block-library/blocks/social-links/editor.css 776 B 0 B
build/block-library/blocks/social-links/style-rtl.css 1.32 kB 0 B
build/block-library/blocks/social-links/style.css 1.33 kB 0 B
build/block-library/blocks/spacer/editor-rtl.css 317 B 0 B
build/block-library/blocks/spacer/editor.css 317 B 0 B
build/block-library/blocks/spacer/style-rtl.css 48 B 0 B
build/block-library/blocks/spacer/style.css 48 B 0 B
build/block-library/blocks/table/editor-rtl.css 478 B 0 B
build/block-library/blocks/table/editor.css 478 B 0 B
build/block-library/blocks/table/style-rtl.css 402 B 0 B
build/block-library/blocks/table/style.css 402 B 0 B
build/block-library/blocks/tag-cloud/editor-rtl.css 118 B 0 B
build/block-library/blocks/tag-cloud/editor.css 118 B 0 B
build/block-library/blocks/tag-cloud/style-rtl.css 94 B 0 B
build/block-library/blocks/tag-cloud/style.css 94 B 0 B
build/block-library/blocks/template-part/editor-rtl.css 552 B 0 B
build/block-library/blocks/template-part/editor.css 551 B 0 B
build/block-library/blocks/term-description/editor-rtl.css 90 B 0 B
build/block-library/blocks/term-description/editor.css 90 B 0 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B 0 B
build/block-library/blocks/text-columns/editor.css 95 B 0 B
build/block-library/blocks/text-columns/style-rtl.css 166 B 0 B
build/block-library/blocks/text-columns/style.css 166 B 0 B
build/block-library/blocks/verse/editor-rtl.css 50 B 0 B
build/block-library/blocks/verse/editor.css 50 B 0 B
build/block-library/blocks/verse/style-rtl.css 87 B 0 B
build/block-library/blocks/verse/style.css 87 B 0 B
build/block-library/blocks/video/editor-rtl.css 504 B 0 B
build/block-library/blocks/video/editor.css 503 B 0 B
build/block-library/blocks/video/style-rtl.css 173 B 0 B
build/block-library/blocks/video/style.css 173 B 0 B
build/block-library/common-rtl.css 1.1 kB 0 B
build/block-library/common.css 1.1 kB 0 B
build/block-library/editor-rtl.css 9.54 kB 0 B
build/block-library/editor.css 9.53 kB 0 B
build/block-library/reset-rtl.css 375 B 0 B
build/block-library/reset.css 376 B 0 B
build/block-library/style-rtl.css 8.98 kB 0 B
build/block-library/style.css 8.99 kB 0 B
build/block-library/theme-rtl.css 692 B 0 B
build/block-library/theme.css 693 B 0 B
build/block-serialization-default-parser/index.js 1.87 kB 0 B
build/block-serialization-spec-parser/index.js 3.06 kB 0 B
build/components/style-rtl.css 16.2 kB 0 B
build/components/style.css 16.2 kB 0 B
build/compose/index.js 11.2 kB 0 B
build/customize-widgets/style-rtl.css 676 B 0 B
build/customize-widgets/style.css 677 B 0 B
build/data-controls/index.js 838 B 0 B
build/data/index.js 8.88 kB 0 B
build/date/index.js 31.9 kB 0 B
build/deprecated/index.js 787 B 0 B
build/dom-ready/index.js 577 B 0 B
build/dom/index.js 4.99 kB 0 B
build/edit-navigation/index.js 17 kB 0 B
build/edit-navigation/style-rtl.css 2.68 kB 0 B
build/edit-navigation/style.css 2.68 kB 0 B
build/edit-post/style-rtl.css 7.04 kB 0 B
build/edit-post/style.css 7.03 kB 0 B
build/edit-site/style-rtl.css 4.5 kB 0 B
build/edit-site/style.css 4.5 kB 0 B
build/edit-widgets/style-rtl.css 2.97 kB 0 B
build/edit-widgets/style.css 2.98 kB 0 B
build/editor/style-rtl.css 3.95 kB 0 B
build/editor/style.css 3.95 kB 0 B
build/escape-html/index.js 735 B 0 B
build/format-library/style-rtl.css 637 B 0 B
build/format-library/style.css 639 B 0 B
build/hooks/index.js 2.28 kB 0 B
build/html-entities/index.js 622 B 0 B
build/i18n/index.js 4.01 kB 0 B
build/is-shallow-equal/index.js 699 B 0 B
build/keycodes/index.js 1.96 kB 0 B
build/list-reusable-blocks/index.js 3.19 kB 0 B
build/list-reusable-blocks/style-rtl.css 629 B 0 B
build/list-reusable-blocks/style.css 628 B 0 B
build/media-utils/index.js 5.39 kB 0 B
build/notices/index.js 1.85 kB 0 B
build/nux/style-rtl.css 731 B 0 B
build/nux/style.css 727 B 0 B
build/plugins/index.js 2.95 kB 0 B
build/priority-queue/index.js 790 B 0 B
build/react-i18n/index.js 1.46 kB 0 B
build/redux-routine/index.js 2.84 kB 0 B
build/reusable-blocks/index.js 3.79 kB 0 B
build/reusable-blocks/style-rtl.css 225 B 0 B
build/reusable-blocks/style.css 225 B 0 B
build/rich-text/index.js 13.5 kB 0 B
build/shortcode/index.js 1.7 kB 0 B
build/token-list/index.js 1.27 kB 0 B
build/url/index.js 3.02 kB 0 B
build/viewport/index.js 1.86 kB 0 B
build/warning/index.js 1.14 kB 0 B
build/wordcount/index.js 1.22 kB 0 B

compressed-size-action

@jasmussen
Copy link
Contributor

So cool.

I tested this as is without any additional patterns registered. Keeping that in mind, I'm seeing some duplication in the menu:

patterns

And some tall stuff here:

Screenshot 2021-03-16 at 14 18 02

But the principle is cool: the text I transform is applied inside the pattern, the added stuff just lands also. I think for some blocks, like galleries, cover, media & text and columns, this feature will be a big help.

There are also some menu item polish tweaks that need to happen, happy to help here. But as a proof of concept, even now it feels promising. It will also be useful, I suspect, for a different paginated view that Jay and I have been discussing.

@jameskoster
Copy link
Contributor

every selected block must have matched with a pattern's block

This seems like a very sensible way to approach this for now 👍

One small potential issue I spotted: If you transform some blocks to a pattern, ungroup the blocks, reselect them all, open the transform menu, I don't see the patterns menu. Should I? Video to explain:

pattern.mp4

I suppose perhaps not, since the blocks already match the pattern.

Another problem which seems to be a bug; if you delete one of the blocks from a transformed pattern, select those that remain and open the pattern menu and try to select a pattern, it seems to fail, and eventually crash:

re-pattern.mp4

I don't know if this qualifies as a "design detail" you asked us to ignore, but is the plan to invoke the carousel/grid ui from #28736 (comment) down the road?

@ntsekouras
Copy link
Contributor Author

Keeping that in mind, I'm seeing some duplication in the menu:
And some tall stuff here:

It's not duplicated by fault, I have the same content in two patterns with the difference that one has one more heading (multi/v1, multi/v2). Also the tall stuff is because of the styles (padding etc) and the available space. It seems weird that in your preview @jasmussen it doesn't show the background colors that I have in pattern.

There are also some menu item polish tweaks that need to happen, happy to help here.

Appreciate your offer to help and will take it :). I'll ping you when the time comes.

I don't know if this qualifies as a "design detail" you asked us to ignore, but is the plan to invoke the carousel/grid ui from #28736 (comment) down the road?

@jameskoster that's not a detail. I think this is a design decision that needs to be made and then I'll change accordingly the code. Of course we will need to first land the PR that introduces the carousel: #29602.

I'll check the bugs you mention above.

Thanks for testing both!

@jasmussen
Copy link
Contributor

I tested with a theme that does not register any patterns, so the default patterns are used.

@ntsekouras ntsekouras self-assigned this Mar 17, 2021
@ntsekouras ntsekouras added [Feature] Patterns A collection of blocks that can be synced (previously reusable blocks) or unsynced [Type] Experimental Experimental feature or API. labels Mar 17, 2021
@mtias
Copy link
Member

mtias commented Mar 18, 2021

This is excellent! Super excited to see this coming through.

@ntsekouras ntsekouras force-pushed the try/patterns-transforms branch from d3f7208 to 5ecd95b Compare March 18, 2021 10:21
@ntsekouras
Copy link
Contributor Author

One small potential issue I spotted: If you transform some blocks to a pattern, ungroup the blocks, reselect them all, open the transform menu, I don't see the patterns menu. Should I? Video to explain:

This happens because in my test pattern the paragraph is also in a Group. So when you ungroup the parent Group, then you have selected a Group so no transforms are shown.

I suppose perhaps not, since the blocks already match the pattern.

This is not handled - the check if selected blocks are identical in terms of content/attributes/InnerBlocks with pattern blocks.

Another problem which seems to be a bug; if you delete one of the blocks from a transformed pattern, select those that remain and open the pattern menu and try to select a pattern, it seems to fail, and eventually crash:

This was a bug with the cloning and now is fixed.

I don't know if this qualifies as a "design detail" you asked us to ignore, but is the plan to invoke the carousel/grid ui from #28736 (comment) down the road?

Do anyone have any thoughts on that? Because if that's the case we should focus first on landing the PR that introduces the carousel: #29602.

-cc @jameskoster, @mtias , @jasmussen , @mcsf , @Addison-Stavlo

@youknowriad
Copy link
Contributor

@ntsekouras How do you ensure the "content" is preserved and not replace with the pattern's attributes?

@ntsekouras
Copy link
Contributor Author

@ntsekouras How do you ensure the "content" is preserved and not replace with the pattern's attributes?

I merge the attributes of the selected block and the matched pattern block and prioritize the selected block's attributes. Regarding blocks with InnerBlocks like Buttons I keep only the blocks from the selected block and skip the inner blocks from the pattern. These are decisions that I've made though through my explorations and are debatable 😄

https://github.com/WordPress/gutenberg/pull/29890/files#diff-5954312493301eefb1d6a19052098cf8475c57e7372d1ed20e0ec6fbe8fb0694R107

That was what you were asking, no?

@youknowriad
Copy link
Contributor

yes, this answers my questions, it does sound a bit arbitrary though and a bit hard to reason about.

For instance say I have a simple unstyled paragraph block and two patterns that have some random content and different background colors:

  • When I transform and pick pattern A: the result is that the current block content is kept and a background color is added.
  • When I transform and pick pattern B after that: nothing will change.

If I do the opposite (start with pattern B), I'll get a different result.

That said, I'm not sure I can come up with a good algorithm to be honest, it sounds too specific to the pattern itself that it's hard to come up with the right transformation in a generic way.

@ntsekouras
Copy link
Contributor Author

For instance say I have a simple unstyled paragraph block and two patterns that have some random content and different background colors: ......

Exactly! That is something I'm still thinking about and I'm not sure either how it should be handled the best. I don't think there is an easy way to be too smart and more importantly if we should try to..

@mtias
Copy link
Member

mtias commented Mar 18, 2021

Do anyone have any thoughts on that? Because if that's the case we should focus first on landing the PR that introduces the carousel: #29602.

I'm fine going with this one separately. The carousel has a place, but needs a bit more work in my mind, and a simpler transform for patterns will still be needed, I think.

@Addison-Stavlo
Copy link
Contributor

Addison-Stavlo commented Mar 18, 2021

I tested this out a bit and it is a really neat idea that I think could become a very powerful tool for the block editor! 🎉

I don't know if this qualifies as a "design detail" you asked us to ignore, but is the plan to invoke the carousel/grid ui from #28736 (comment) down the road?

Do anyone have any thoughts on that? Because if that's the case we should focus first on landing the PR that introduces the carousel: #29602.

I feel like the carousel layout may make a bit more sense for Template Parts specifically. Here they feel a little bit out of place as they behave a bit differently from the other flows with selections of normal blocks: they are limited to single selection, do not preserve content, and do not surface patterns contextually based off of the inner blocks they contain. Those behaviors make sense for template parts, but they are also inconsistent with the flows with standard blocks. I feel like the carousel / placeholder pattern would fit better for these behaviors, and prefer a different UI for a different behavioral flow (as opposed to reusing the same UI across a different behavioral flow resulting in the inconsistent feel in UX).

I'm fine going with this one separately. The carousel has a place, but needs a bit more work in my mind, and a simpler transform for patterns will still be needed, I think.

@mtias - Im curious about how the carousel would need a lot more work than this. From a technical standpoint, it seems like it would be much less complex: determining which patterns to show in that circumstance is quite simple and it has no requirement for preserving content. It seems like it would make a lot of sense for Template Parts specifically, and if we can limit it to that block at first we can get user feedback from a more forgiving beta environment that is currently undergoing a series of testing cycles.

@jasmussen
Copy link
Contributor

Jays feedback works well here:

now

Note that tall heading is my own fault:

Screenshot 2021-03-23 at 09 52 40

It feels a little weird that we have "Transform to" as a subheading on the 2nd group, when a pattern is also a transform:

Screenshot 2021-03-23 at 09 56 55

@jameskoster is there a design where block transformations and pattern transformations coexist in the same section? Or at least share the same heading?

I pushed some polish to paddings and hover styles:

Screenshot 2021-03-23 at 09 56 55

@ntsekouras ntsekouras force-pushed the try/patterns-transforms branch from 3eee2e5 to f8e8e76 Compare March 23, 2021 09:30
@mtias
Copy link
Member

mtias commented Mar 23, 2021

Im curious about how the carousel would need a lot more work than this. From a technical standpoint, it seems like it would be much less complex: determining which patterns to show in that circumstance is quite simple and it has no requirement for preserving content.

@Addison-Stavlo It needs more work from a design perspective. We cannot use the block canvas carousel in all contexts due to space constrains, so we need to explore a way to work well with that — either isolated template part or modal. Also the double toolbar aspect is tricky. It's also not super clear yet that it's a selection step and not just the content of the block. I think we need all of that in a good place to try with template parts.

@ntsekouras ntsekouras force-pushed the try/patterns-transforms branch from 5fcf92a to 5f64abb Compare March 24, 2021 09:27
@ntsekouras ntsekouras force-pushed the try/patterns-transforms branch from 48d2ff5 to 796fe41 Compare March 26, 2021 16:46
@ntsekouras ntsekouras force-pushed the try/patterns-transforms branch from 796fe41 to 8bc5a9f Compare March 26, 2021 18:05
Copy link
Contributor

@mcsf mcsf left a comment

Choose a reason for hiding this comment

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

This is some really cool work. There are lots of little pieces to unpack, so it took me a while to review.

First of all, don't pay too much attention to my inline comments: they tend to be micro-level observations.

As for the overall approach:

  • Personally I'm on board with the "role": "content" trait in the block schema, and letting the transforms system make decisions based on that.
  • I like the focus on the user that shines through. You're clearly exploring different methods of transformations to see what would offer the most value.
  • However, that comes at the cost of complexity and arbitrariness in our components (e.g. replaceInnerBlocksMode, blocksToSkip).
  • There are enough parallels and distinctions between "regular" block-wise transforms and pattern-wise transforms that we need to ponder whether to consolidate the two, or whether to set them clearly apart. Right now, pattern-wise transforms are piggybacking off block-wise transforms.
  • A lot of juggling is being done between state selectors and different layers of UI components, which makes it very hard to reason about the data flowing through.

Given some of the issues raised above, is it possible to break apart some of this work so that:

  • The first PR only does one kind of transformation (i.e. no replaceInnerBlocksMode) and tries to do it as neatly as possible, based on `"role": "content", and focusing on the data flow.
  • Different kinds of transformations can then be explored each in its own PR (e.g. the special handling of Template Parts). Similarly, we can debate any introduction of "magic" in dedicated PRs.
  • Bonus: can we measure the performance of opening the block switcher against trunk?

packages/block-editor/src/store/selectors.js Show resolved Hide resolved
Comment on lines +1961 to +1965
// If a selected block is nested (with InnerBlocks like Group or Columns)
// return an EMPTY_ARRAY, as it doesn't make sense to try to be too smart.
// In such blocks a user could have anything inside as content, so don't show
// any transforms. In these blocks it only makes sense to show `block` scoped
// patterns during insertion, where no content exists yet.
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens with other container blocks? What's the difference with just checking for inner blocks?

if (blocks.some(b => b.innerBlocks.length)) return EMPTY_ARRAY;

And do we need to treat certain containers differently? For example (and this isn't meant to further block/complicate this PR!) what might change once the Quote block supports nested content? Can the role: 'content' concept help in those cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The difference between such blocks is that some of them are mean more as grouping blocks. Examples are Group, Columns, Template Part and:

// In such blocks a user could have anything inside as content, so don't show
// any transforms. In these blocks it only makes sense to show block scoped
// patterns during insertion, where no content exists yet.

This could work differently in simpler(more semantic) blocks with InnerBlocks like Buttons, Quote, Cover. While the user could still have anything inside it's still more targeted to specific semantics and the implementation at this point kept all the selected block's InnerBlocks.

This is a great point that I wasn't sure and wanted feedback and maybe it should be handled not by the framework but instead be explicitly declared in the block somewhere(?)

packages/blocks/src/api/utils.js Show resolved Hide resolved
Comment on lines +82 to +86
_patterns = statePatterns.map( ( statePattern ) => ( {
...statePattern,
transformedBlocks: statePattern.blocks.map( ( block ) =>
cloneBlock( block )
),
Copy link
Contributor

Choose a reason for hiding this comment

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

This is one of the crucial spots, for me, where it becomes apparent that these different data structures moving around become confusing: WPEditorTransformItem, { ...WPEditorTransform, transformedBlocks }, plus some of the bindings (transformedBlocks -> patternBlocks`).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

WPEditorTransformItem this is wrong type from jsdoc paste :)

Comment on lines +52 to +54
// We have found a matched block type, so
// add it to the transformed blocks Set and return it.
transformedBlocks.add( clientId );
Copy link
Contributor

Choose a reason for hiding this comment

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

Correct me if I'm wrong, but I don't think we should mutate this set inside this function. It's probably up to the consumer of that function to provide the right set of excluded block names. Since the consumer will probably be an iterative algorithm, it will roughly look like this:

consumedBlocks := Set()
for patternBlock in patternBlocksToProcess:
    matchingBlock := findMatchingBlockInPattern(
        patternBlock,
        selectedBlockName,
        consumedBlocks)
    if matchingBlock:
        consumedBlocks.add(matchingBlock.clientId)

Comment on lines +101 to +103
// Recurse through every pattern block
// to find matches with each selected block,
// and transform these blocks (we mutate patternBlocks).
Copy link
Contributor

Choose a reason for hiding this comment

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

we mutate patternBlocks

I can see why we would prefer to mutate in this case: it can be a lot of data to duplicate, a lot of allocation, and the input is already the result of cloneBlocks, so we might as well transform it in place. However, we should extract all of this logic so that it's clear where the boundaries of data mutation are. IMO, the body of the useMemo call should be much smaller and just call a couple of well tested helpers — I do realise that your request for feedback was about the whole approach and not these implementation details, and you were probably going to streamline this module anyway. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I do realise that your request for feedback was about the whole approach and not these implementation details, and you were probably going to streamline this module anyway. :)

Exactly, although your comments are helpful :)

@ntsekouras
Copy link
Contributor Author

Thanks for reviewing @mcsf ! I hear your concerns about the complexity and arbitrariness and this is indeed a complex problem to find the right balance between magic and APIs.

The first PR only does one kind of transformation (i.e. no replaceInnerBlocksMode) and tries to do it as neatly as possible, based on `"role": "content", and focusing on the data flow.

I'll spin off a new PR for that.

ntsekouras added a commit that referenced this pull request Apr 13, 2021
…ently selected 'simple' blocks (no InnerBlocks) (#30469)

* spin off from #29890 for simple blocks without InnerBlocks

* __experimentalGetBlockAttributesNamesByRole tests

* update patterns

* useTransformedPatterns and refactoring part 1

* getMatchingBlockByName tests + and other jsdoc

* getPatternTransformedBlocks + transformMatchingBlock tests

* add role:content to more blocks + a couple test patterns

* pattern list padding

* rename role to __experimentalRole

* Update docs suggestion

Co-authored-by: Miguel Fonseca <[email protected]>

* fix tests + docs

* change patterns

* update social pattern

Co-authored-by: James Koster <[email protected]>
Co-authored-by: Miguel Fonseca <[email protected]>
@ntsekouras ntsekouras closed this Apr 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Patterns A collection of blocks that can be synced (previously reusable blocks) or unsynced [Type] Experimental Experimental feature or API.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Create a UI that suggests patterns that are contextual to the currently selected block
7 participants