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

Buttons: Preserve styling from adjacent button blocks when inserting a new button block #37905

Merged

Conversation

andrewserong
Copy link
Contributor

@andrewserong andrewserong commented Jan 12, 2022

Description

Fixes #37904

This PR updates the Inserter logic for the defaultBlock to attempt to use attributes from an adjacent similar block when inserting a new block directly between two blocks. The use case is primarily for the Buttons block, where clicking between two individual Button blocks inserts a new button. For layouts or patterns that use full-width buttons (e.g. a Link in Bio pattern), or buttons with particular styling, it might make it smoother for users if in the inserted button we use those existing attributes.

I'd love feedback on whether or not this is universal enough to be added to the defaultBlock logic added by @tellthemachines in #34899.

The logic in this PR includes:

  • In the inserter, when a block is being directly inserted via the inserter, attempt to get the attributes from the previous block if it is of the same block type, and the directInsertBlock has an attributesToCopy array with matching attribute names.
  • If that isn't possible (e.g. you're clicking the last inserter in the Buttons block) then attempt to get the attributes from the last of the inner blocks.
  • I've refactored the directInsertBlock to be an object instead of an array (to allow for the attributesToCopy prop) and added a typedef

For a precedent surrounding the suggested behaviour when inserting the button block, currently if you hit "enter" on a button block to fire the onSplit function, it uses the existing attributes when creating the new button block.

How has this been tested?

  1. Insert a Buttons block and add a few buttons. Set them to 100% width and / or some color styles.
  2. Hover between the blocks and click the inserter to add a Button block. The styling of the previous Button block should be preserved.
  3. Select the parent Buttons block and click the inserter at the end of the list of Button blocks. The newly inserted Button block should preserve the styling/attributes of the last block in the list.
  4. Ensure that link / text is not preserved in the inserted block.
  5. Double-check that this change doesn't introduce regressions for those blocks that already use the __experimentalDefaultBlock option when registering the block (e.g. Navigation and Navigation Submenu blocks)

Screenshots

Kapture.2022-01-13.at.17.21.59.mp4

Types of changes

Somewhere between a bug fix and an enhancement (I'd argue it's an enhancement but it will likely feel like a bug fix to some users)

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 (please manually search all *.native.js files for terms that need renaming or removal).
  • I've updated related schemas if appropriate.

@andrewserong andrewserong added [Type] Enhancement A suggestion for improvement. [Block] Buttons Affects the Buttons Block labels Jan 12, 2022
@andrewserong andrewserong self-assigned this Jan 12, 2022
@github-actions
Copy link

github-actions bot commented Jan 12, 2022

Size Change: +955 B (0%)

Total Size: 1.13 MB

Filename Size Change
build/block-editor/index.min.js 141 kB +280 B (0%)
build/block-library/blocks/navigation/editor-rtl.css 1.99 kB +57 B (+3%)
build/block-library/blocks/navigation/editor.css 2 kB +59 B (+3%)
build/block-library/blocks/navigation/style-rtl.css 1.85 kB +21 B (+1%)
build/block-library/blocks/navigation/style.css 1.84 kB +25 B (+1%)
build/block-library/blocks/navigation/view.min.js 2.81 kB -11 B (0%)
build/block-library/editor-rtl.css 10.1 kB +44 B (0%)
build/block-library/editor.css 10.1 kB +42 B (0%)
build/block-library/index.min.js 165 kB +401 B (0%)
build/block-library/style-rtl.css 10.8 kB +18 B (0%)
build/block-library/style.css 10.8 kB +18 B (0%)
build/blocks/index.min.js 46.4 kB +13 B (0%)
build/components/index.min.js 215 kB +7 B (0%)
build/edit-post/index.min.js 29.6 kB +16 B (0%)
build/edit-post/style-rtl.css 7.15 kB -14 B (0%)
build/edit-post/style.css 7.14 kB -15 B (0%)
build/edit-site/index.min.js 37.7 kB -10 B (0%)
build/editor/index.min.js 38.4 kB +4 B (0%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 960 B
build/admin-manifest/index.min.js 1.1 kB
build/annotations/index.min.js 2.75 kB
build/api-fetch/index.min.js 2.21 kB
build/autop/index.min.js 2.12 kB
build/blob/index.min.js 459 B
build/block-directory/index.min.js 6.28 kB
build/block-directory/style-rtl.css 1.01 kB
build/block-directory/style.css 1.01 kB
build/block-editor/default-editor-styles-rtl.css 378 B
build/block-editor/default-editor-styles.css 378 B
build/block-editor/style-rtl.css 14.6 kB
build/block-editor/style.css 14.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 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 111 B
build/block-library/blocks/audio/style.css 111 B
build/block-library/blocks/audio/theme-rtl.css 125 B
build/block-library/blocks/audio/theme.css 125 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 470 B
build/block-library/blocks/button/editor.css 470 B
build/block-library/blocks/button/style-rtl.css 560 B
build/block-library/blocks/button/style.css 560 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 90 B
build/block-library/blocks/code/style.css 90 B
build/block-library/blocks/code/theme-rtl.css 131 B
build/block-library/blocks/code/theme.css 131 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-template/style-rtl.css 127 B
build/block-library/blocks/comment-template/style.css 127 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/cover/editor-rtl.css 546 B
build/block-library/blocks/cover/editor.css 547 B
build/block-library/blocks/cover/style-rtl.css 1.22 kB
build/block-library/blocks/cover/style.css 1.22 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 417 B
build/block-library/blocks/embed/style.css 417 B
build/block-library/blocks/embed/theme-rtl.css 124 B
build/block-library/blocks/embed/theme.css 124 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 255 B
build/block-library/blocks/file/style.css 255 B
build/block-library/blocks/file/view.min.js 322 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 965 B
build/block-library/blocks/gallery/editor.css 967 B
build/block-library/blocks/gallery/style-rtl.css 1.6 kB
build/block-library/blocks/gallery/style.css 1.6 kB
build/block-library/blocks/gallery/theme-rtl.css 122 B
build/block-library/blocks/gallery/theme.css 122 B
build/block-library/blocks/group/editor-rtl.css 159 B
build/block-library/blocks/group/editor.css 159 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 114 B
build/block-library/blocks/heading/style.css 114 B
build/block-library/blocks/html/editor-rtl.css 332 B
build/block-library/blocks/html/editor.css 333 B
build/block-library/blocks/image/editor-rtl.css 810 B
build/block-library/blocks/image/editor.css 809 B
build/block-library/blocks/image/style-rtl.css 507 B
build/block-library/blocks/image/style.css 511 B
build/block-library/blocks/image/theme-rtl.css 124 B
build/block-library/blocks/image/theme.css 124 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 137 B
build/block-library/blocks/latest-posts/editor.css 137 B
build/block-library/blocks/latest-posts/style-rtl.css 528 B
build/block-library/blocks/latest-posts/style.css 527 B
build/block-library/blocks/list/style-rtl.css 94 B
build/block-library/blocks/list/style.css 94 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 649 B
build/block-library/blocks/navigation-link/editor.css 650 B
build/block-library/blocks/navigation-link/style-rtl.css 94 B
build/block-library/blocks/navigation-link/style.css 94 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 299 B
build/block-library/blocks/navigation-submenu/editor.css 299 B
build/block-library/blocks/navigation-submenu/view.min.js 343 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 401 B
build/block-library/blocks/page-list/editor.css 402 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 273 B
build/block-library/blocks/paragraph/style.css 273 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/style-rtl.css 446 B
build/block-library/blocks/post-comments-form/style.css 446 B
build/block-library/blocks/post-comments/style-rtl.css 521 B
build/block-library/blocks/post-comments/style.css 521 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 721 B
build/block-library/blocks/post-featured-image/editor.css 721 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 305 B
build/block-library/blocks/post-template/style.css 305 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 389 B
build/block-library/blocks/pullquote/style.css 388 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 234 B
build/block-library/blocks/query-pagination/style.css 231 B
build/block-library/blocks/query/editor-rtl.css 131 B
build/block-library/blocks/query/editor.css 132 B
build/block-library/blocks/quote/style-rtl.css 187 B
build/block-library/blocks/quote/style.css 187 B
build/block-library/blocks/quote/theme-rtl.css 223 B
build/block-library/blocks/quote/theme.css 226 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 397 B
build/block-library/blocks/search/style.css 398 B
build/block-library/blocks/search/theme-rtl.css 64 B
build/block-library/blocks/search/theme.css 64 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 245 B
build/block-library/blocks/separator/style.css 245 B
build/block-library/blocks/separator/theme-rtl.css 172 B
build/block-library/blocks/separator/theme.css 172 B
build/block-library/blocks/shortcode/editor-rtl.css 474 B
build/block-library/blocks/shortcode/editor.css 474 B
build/block-library/blocks/site-logo/editor-rtl.css 744 B
build/block-library/blocks/site-logo/editor.css 744 B
build/block-library/blocks/site-logo/style-rtl.css 181 B
build/block-library/blocks/site-logo/style.css 181 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 177 B
build/block-library/blocks/social-link/editor.css 177 B
build/block-library/blocks/social-links/editor-rtl.css 670 B
build/block-library/blocks/social-links/editor.css 669 B
build/block-library/blocks/social-links/style-rtl.css 1.32 kB
build/block-library/blocks/social-links/style.css 1.32 kB
build/block-library/blocks/spacer/editor-rtl.css 332 B
build/block-library/blocks/spacer/editor.css 332 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 471 B
build/block-library/blocks/table/editor.css 472 B
build/block-library/blocks/table/style-rtl.css 481 B
build/block-library/blocks/table/style.css 481 B
build/block-library/blocks/table/theme-rtl.css 188 B
build/block-library/blocks/table/theme.css 188 B
build/block-library/blocks/tag-cloud/style-rtl.css 214 B
build/block-library/blocks/tag-cloud/style.css 215 B
build/block-library/blocks/template-part/editor-rtl.css 560 B
build/block-library/blocks/template-part/editor.css 559 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 571 B
build/block-library/blocks/video/editor.css 572 B
build/block-library/blocks/video/style-rtl.css 173 B
build/block-library/blocks/video/style.css 173 B
build/block-library/blocks/video/theme-rtl.css 124 B
build/block-library/blocks/video/theme.css 124 B
build/block-library/common-rtl.css 908 B
build/block-library/common.css 905 B
build/block-library/reset-rtl.css 474 B
build/block-library/reset.css 474 B
build/block-library/theme-rtl.css 672 B
build/block-library/theme.css 676 B
build/block-serialization-default-parser/index.min.js 1.09 kB
build/block-serialization-spec-parser/index.min.js 2.79 kB
build/components/style-rtl.css 15.5 kB
build/components/style.css 15.5 kB
build/compose/index.min.js 11.2 kB
build/core-data/index.min.js 13.3 kB
build/customize-widgets/index.min.js 11.4 kB
build/customize-widgets/style-rtl.css 1.5 kB
build/customize-widgets/style.css 1.49 kB
build/data-controls/index.min.js 631 B
build/data/index.min.js 7.49 kB
build/date/index.min.js 31.9 kB
build/deprecated/index.min.js 485 B
build/dom-ready/index.min.js 304 B
build/dom/index.min.js 4.5 kB
build/edit-navigation/index.min.js 16 kB
build/edit-navigation/style-rtl.css 3.76 kB
build/edit-navigation/style.css 3.76 kB
build/edit-post/classic-rtl.css 546 B
build/edit-post/classic.css 547 B
build/edit-site/style-rtl.css 6.85 kB
build/edit-site/style.css 6.84 kB
build/edit-widgets/index.min.js 16.5 kB
build/edit-widgets/style-rtl.css 4.17 kB
build/edit-widgets/style.css 4.17 kB
build/editor/style-rtl.css 3.71 kB
build/editor/style.css 3.71 kB
build/element/index.min.js 3.29 kB
build/escape-html/index.min.js 517 B
build/format-library/index.min.js 6.58 kB
build/format-library/style-rtl.css 571 B
build/format-library/style.css 571 B
build/hooks/index.min.js 1.63 kB
build/html-entities/index.min.js 424 B
build/i18n/index.min.js 3.75 kB
build/is-shallow-equal/index.min.js 501 B
build/keyboard-shortcuts/index.min.js 1.8 kB
build/keycodes/index.min.js 1.39 kB
build/list-reusable-blocks/index.min.js 1.72 kB
build/list-reusable-blocks/style-rtl.css 838 B
build/list-reusable-blocks/style.css 838 B
build/media-utils/index.min.js 2.92 kB
build/notices/index.min.js 925 B
build/nux/index.min.js 2.08 kB
build/nux/style-rtl.css 747 B
build/nux/style.css 743 B
build/plugins/index.min.js 1.84 kB
build/primitives/index.min.js 924 B
build/priority-queue/index.min.js 582 B
build/react-i18n/index.min.js 671 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.65 kB
build/reusable-blocks/index.min.js 2.22 kB
build/reusable-blocks/style-rtl.css 256 B
build/reusable-blocks/style.css 256 B
build/rich-text/index.min.js 11 kB
build/server-side-render/index.min.js 1.58 kB
build/shortcode/index.min.js 1.49 kB
build/token-list/index.min.js 639 B
build/url/index.min.js 1.9 kB
build/viewport/index.min.js 1.05 kB
build/warning/index.min.js 248 B
build/widgets/index.min.js 7.15 kB
build/widgets/style-rtl.css 1.16 kB
build/widgets/style.css 1.16 kB
build/wordcount/index.min.js 1.04 kB

compressed-size-action

@andrewserong andrewserong added the [Feature] Inserter The main way to insert blocks using the + button in the editing interface label Jan 13, 2022
@andrewserong andrewserong marked this pull request as ready for review January 13, 2022 06:38
@andrewserong
Copy link
Contributor Author

@tellthemachines I've added you as a reviewer since you worked on adding the defaultBlock behaviour to the inserter and have more experience with the Navigation block. This isn't at all urgent, though, so no worries if you don't have time to review!

Copy link
Contributor

@stacimc stacimc left a comment

Choose a reason for hiding this comment

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

This would be such a welcome change! IMO it dramatically improves the experience of working with the Buttons block. I'm interested to hear what others think, but I also like its addition to the defaultBlock logic and its potential to be used more universally. I appreciate there's already handling for inner blocks of different types :)

This tests great for me with the Buttons block! I've tried adding all possible attributes, and inserting buttons in different positions. Attributes are correctly taken from the previous block, and text/url are not copied. This feels so much more intuitive ✨

button-inserter.mov

I just caught one regression with the Navigation block. When you add a submenu and attempt to insert links, you'll get the inserter on the first link. If you try to add additional links, though, it just copies the first link over rather than opening the inserter. I think you'll just need to mark some link attributes like url as undefined so they don't get copied over -- looks like maybe in the navigation-submenu DEFAULT_BLOCK?

navigation-submenu.mov

Other than that, Navigation also looks good to me!

@andrewserong
Copy link
Contributor Author

Thanks for reviewing @stacimc! Good catch with the Navigation and Navigation Submenu blocks — I think from the looks of it the Navigation Link block mostly has attributes that we wouldn't want to copy over when inserting a block. However, I quite like the default behaviour of copying across attributes, so in 29774c9 I've had a go at setting the Navigation Link's DEFAULT_BLOCK to use all default or undefined attributes. It works, but it might not be the tidiest way to do it, particularly if it's more common that blocks will have lots of attributes that shouldn't be copied over 🤔

Happy for any feedback if folks think there's a better way to handle it (particularly from folks who've done more work on the Navigation block) — I was trying to avoid adding another flag to switch on the behaviour in this PR, but there might be a trade-off to be found if the approach of setting defaults is too unwieldy!

Anyway, here's a screengrab of the navigation link insertion after the above commit was pushed (I think it's working properly now):

Kapture.2022-01-14.at.12.11.08.mp4

@andrewserong andrewserong added the [Block] Navigation Affects the Navigation Block label Jan 14, 2022
Copy link
Contributor

@tellthemachines tellthemachines 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 addressing this issue! I left a few comments below; essentially I think we should make it so existing instances of __experimentalDefaultBlock don't have to be changed at all.

...( directInsertBlock[ 1 ] || {} ),
},
]
: directInsertBlock;
Copy link
Contributor

Choose a reason for hiding this comment

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

This ternary is a little long for comfortable reading; could we turn it into an if else? Perhaps we can conflate it with the ternary below and just have something like

let blockToInsert
if ( directInsertBlock?.length ) 
   blockToInsert =  createBlock( // get attribs etc )
else
  blockToInsert =  createBlock( allowedBlockType.name )

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks that's a good suggestion, I'll have a play with that. I struggled a little with this PR in trying to keep the logic neat (I mostly hacked it together and then tried in vain to get it presentable 😅), so I very much appreciate the extra set of 👀!

} ),
{}
),
},
Copy link
Contributor

Choose a reason for hiding this comment

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

It's not great that we have to add exceptions to copied attributes in every single place we use direct insert. We may not be using it in a lot of core blocks (and there probably won't be many more), but if/when it becomes stable, we want third party block developers to find it easy to use.

One alternative would be to specify in DEFAULT_BLOCK a list of attributes we want copied, and have getAdjacentBlockAttributes only return those; then by default if nothing is specified nothing is copied.

Or we could add an __experimentalCopyStyleAttributes instead of leveraging direct insert, but what we're doing here is close enough to direct insert that it kind of makes sense to have it there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's not great that we have to add exceptions to copied attributes in every single place we use direct insert.

Definitely not, thanks for taking a look here!

One alternative would be to specify in DEFAULT_BLOCK a list of attributes we want copied, and have getAdjacentBlockAttributes only return those; then by default if nothing is specified nothing is copied.

Oh, I like that idea — I felt a bit uneasy just copying everything, and I think it makes it cleaner if we can keep it a part of DEFAULT_BLOCK if possible without adding an extra flag.

At the moment DEFAULT_BLOCK is an array so that it matches the arguments we pass to createBlock. I was wondering if it'd make sense to switch it to an object, so that we could add an extra key for the attributes to be copied, and have something like the following?

const DEFAULT_BLOCK = {
  name: 'core/my-block',
  attributes: {
    myDefaultAttribute: 'default value to set'
  },
  attributesToCopy: {
    myCopiedAttribute: true
  }
}

I'll keep chipping away at this PR next week, but let me know if you have a particular preference for how we implement it. Either way, explicitly flagging when we'd like to copy attributes sounds safer than assuming and having block authors have to add exceptions 🙂

@andrewserong
Copy link
Contributor Author

@tellthemachines and @stacimc, I've had a go at refactoring this so that it's opt-in to copy over attributes from adjacent blocks rather than opt-out, so that 🤞 it's a little more straightforward for either existing blocks, or 3rd party block authors to work with (i.e. they don't need to know to exclude attributes from being copied).

To get there, I've also refactored usage of the directInsertBlock to be an object with props name, and optional props attributes and attributesToCopy — I think this makes it a little cleaner than the array that's tied to the signature of createBlock, but do let me know if you'd prefer we don't change this! For clarity over its usage, I've added a @typedef for the WPDirectInsertBlock to the __experimentalGetDirectInsertBlock selector.

Based on a comment from @gwwar on the earlier PR that introduced the directInsertBlock I've left off innerBlocks from the type and the call to createBlock, but that should be easy enough to add back in if folks prefer we keep it, too. Apologies if I've missed some context there!

I'm pretty sure this PR is now ready for another review, but it's the end of my day now so I haven't tested it fully just yet. Will do some more testing tomorrow!

@andrewserong andrewserong force-pushed the update/buttons-block-inserter-button-to-inherit-styles branch from 294f18f to f21abdf Compare January 18, 2022 00:57
@andrewserong
Copy link
Contributor Author

Alrighty, I think this PR is working correctly now (fixed a regression with the Navigation block inserter) 👍

Copy link
Contributor

@stacimc stacimc 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 looking good to me! Copying nothing by default and requiring configuration of attributes to copy in the WPDirectInsertBlock seems very reasonable; it is more intentional and probably less likely to cause error if misconfigured.

I'll defer to those more familiar with the Navigation block and the directInsertBlock implementation, but the use of the object looks okay to me with the addition of the typedef. If I read the linked conversation correctly, there's no current use case for including the innerBlocks and it's safer to omit it for now, so I would hazard you've got the right idea there as well.


  • Style attributes are copied from the previous button block
  • Url & text attributes are not copied
  • Works when inserting between two existing buttons, or at the end of the list

I re-tested Navigation as well:

  • Links are added by default
  • No attributes are copied over as new links are added
  • Submenus can be added, and links are added to them by default

As soon as you add a non-link block to a Navigation menu (for example, if you add the Site Logo), clicking Insert will bring up the full block inserter menu. This is what's happening on trunk as well, and I believe that's the intended behavior.


blockToInsert = createBlock( directInsertBlock.name, {
...newAttributes,
...( directInsertBlock.attributes || {} ),
Copy link
Contributor

Choose a reason for hiding this comment

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

Just for my curiosity and to see if I missed a test case, did you have a use case in mind where the directInsertBlock would override attributes of the adjacent block?

Copy link
Contributor

Choose a reason for hiding this comment

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

Good question. directInsertBlock could have attributes of its own defined; in case we define an attribute and also want to grab the same attribute from an adjacent block, it might make more sense for the adjacent one to override the initial.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed, thanks for asking the question! I wasn't too sure of a use case, but thought it'd still be good to allow attributes to be defined in the directInsertBlock. Allowing the adjacent ones to override sounds good to me, I've swapped the order here 👍

Copy link
Contributor

@tellthemachines tellthemachines 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 the quick iteration! It's much improved; just a few more comments below.

adjacentBlockAttributes[ attributeName ];
}
}
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, this will cycle through all the adjacent block attributes even if we don't mean to copy any of them. I'm thinking it would be better to return early if there are no attributes to be copied. A couple ways we could do this:

  • Wrap this logic inside another if statement checking for directInsertBlock.attributesToCopy; or
  • pass the attributes to copy to getAdjacentBlockAttributes and have it return only those attributes; in that case we can return early from that function if nothing is passed to it. I'm more inclined towards this option as it would tidy all the extra attribute logic inside getAdjacentBlockAttributes. What do you think?

Copy link
Contributor Author

@andrewserong andrewserong Jan 19, 2022

Choose a reason for hiding this comment

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

Great points! I've moved the logic up to go inside getAdjacentBlockAttributes. That function is a little long now, but it feels a bit cleaner putting it there, and also only iterating over the specified attributesToCopy if provided rather than every attribute of the adjacent block 👍

Let me know if you think it needs further tidying (or refactoring to another file now that it's a bit longer!)


blockToInsert = createBlock( directInsertBlock.name, {
...newAttributes,
...( directInsertBlock.attributes || {} ),
Copy link
Contributor

Choose a reason for hiding this comment

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

Good question. directInsertBlock could have attributes of its own defined; in case we define an attribute and also want to grab the same attribute from an adjacent block, it might make more sense for the adjacent one to override the initial.

className: true,
gradient: true,
textColor: true,
width: true,
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we also want to copy border and spacing styles?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, yes, and font settings, too! I totally forgot about those attributes provided by block supports. I've added a few more here and it seems to be working well:

Kapture 2022-01-19 at 16 52 11

@andrewserong
Copy link
Contributor Author

Thanks again for the reviews @stacimc and @tellthemachines! I think I've implemented all the feedback, but let me know if I've missed anything 😀

// Copy over only those attributes flagged to be copied.
for ( const attribute in attributesToCopy ) {
if (
attributesToCopy[ attribute ] &&
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we remove this and just check:

if ( adjacentAttributes.hasOwnProperty( attribute ) )?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for re-testing @stacimc, another good question! I wasn't too sure about this one — the attributesToCopy is currently an object where each attribute is set to a boolean (true in all the cases in the Buttons block). So, if someone were to theoretically set one of them to false it might be odd if it still copied the attribute.

I initially had it an object so that look up was easy when looping over the adjacent block's attributes, so an object look up was preferable to an array. Now that it's switched the other way (we're looping over the attributesToCopy) the object structure doesn't seem to be quite as necessary. I kinda like the parallel, though, with having both attributes and attributesToCopy being objects.

So, a couple of follow-up questions! 😀

Is it better for attributesToCopy to be an object or an array? And if we keep it as an object, should it be possible for someone to set one of the values to false to switch off copying of that attribute (there's no real use case for this, but I think it determines whether or not we should remove this line 🤔)

Apologies if I'm overthinking this!

Copy link
Contributor

@stacimc stacimc Jan 20, 2022

Choose a reason for hiding this comment

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

Oh of course, attributesToCopy is an object now 🤦‍♀️ You're right, this is a bit tricky! Provided attributesToCopy remains an object, I'd leave it as you have it. I don't think anyone ought to explicitly set a value to false within that object, but I think you're right to safeguard against it.

To me it does feel slightly less clear when reading -- maybe just a naming thing, but I wouldn't expect a parameter named attributesToCopy to contain data for attributes that shouldn't be copied. I may be the one overthinking it here, though 😄 I don't feel strongly about changing it and I don't think it should block the PR, so up to you!

Copy link
Contributor

Choose a reason for hiding this comment

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

Making attributesToCopy an array would make it less verbose to add those attributes, and semantically makes more sense because we wouldn't ever expect them to be set to false (there's no point adding an attribute to copy if we don't want it copied, and there's no way of toggling those attributes after they're set).

I agree it's not a blocking issue for this PR though; we can always address it in a follow up.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks @tellthemachines, that's a great point. I've updated it to be an array in 401358f (and updated the typedef so that it's an array of strings), and that looks much clearer to me. Cheers!

I'm out for the next few hours, but will 🤞 merge in this PR tonight once the tests pass, unless there are any objections 🙂

Copy link
Contributor

@stacimc stacimc left a comment

Choose a reason for hiding this comment

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

+1 for moving the logic into getAdjacentAttributes, which also has the advantage of making getInsertionIndex more readable.

I neglected to test all attributes on my second run-through, sorry! Tested again and all style attributes are being copied over, including attrs from block supports and additional CSS classes 😄

Just one question, but LGTM!

Copy link
Contributor

@stacimc stacimc 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 testing well and makes a big impact on the usability of the Buttons block 🥳 Very excited to see this one go in!

Copy link
Contributor

@tellthemachines tellthemachines 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 looking good now 🎉

// Copy over only those attributes flagged to be copied.
for ( const attribute in attributesToCopy ) {
if (
attributesToCopy[ attribute ] &&
Copy link
Contributor

Choose a reason for hiding this comment

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

Making attributesToCopy an array would make it less verbose to add those attributes, and semantically makes more sense because we wouldn't ever expect them to be set to false (there's no point adding an attribute to copy if we don't want it copied, and there's no way of toggling those attributes after they're set).

I agree it's not a blocking issue for this PR though; we can always address it in a follow up.

@andrewserong andrewserong changed the title Buttons: Attempt to preserve styling from existing button blocks when inserting a new button block Buttons: Preserve styling from adjacent button blocks when inserting a new button block Jan 20, 2022
@andrewserong andrewserong merged commit df341a8 into trunk Jan 20, 2022
@andrewserong andrewserong deleted the update/buttons-block-inserter-button-to-inherit-styles branch January 20, 2022 06:29
@github-actions github-actions bot added this to the Gutenberg 12.5 milestone Jan 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Block] Buttons Affects the Buttons Block [Block] Navigation Affects the Navigation Block [Feature] Inserter The main way to insert blocks using the + button in the editing interface [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Buttons Block: Clicking inserter icon to add a button should use styling of an existing button in the list
3 participants