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

Show warning on critical block removal #51145

Merged
merged 28 commits into from
Jun 22, 2023
Merged

Show warning on critical block removal #51145

merged 28 commits into from
Jun 22, 2023

Conversation

tellthemachines
Copy link
Contributor

@tellthemachines tellthemachines commented Jun 1, 2023

What?

Fixes #40618.

(edited by @mcsf:)

Show a modal asking the user for confirmation when attempting to remove Post Content and Query blocks, but only in the site editor.

I have questions regarding implementation, mainly: should this initial version be extensible? If so, perhaps it would be better to register block criticality, as well as the message to display, in the block metadata. That would mean that third parties would be able to start using it immediately. For now, keep everything private using Gutenberg's new lock/unlock APIs. Everything is implemented inside the @wordpress/block-editor package. The @wordpress/edit-site just needs to unlock BlockRemovalWarningModal and render it in its Editor component.

Another thought I had was should the check for whether to show the prompt be part of the removeBlocks action instead? Yes, this works inside the removeBlocks call. We've introduced some dedicated logic, introduced a private action named privateRemoveBlocks, and this new logic now comes with an extra argument, forceRemove, that is enabled when the user confirms deletion via the modal.

Testing Instructions

A) In the site editor, go to a template that has either Post Content or Query (both considered critical blocks) and try the following:

  1. Select the critical block and press backspace.
  2. Select the critical block, navigate to its toolbar options dropdown and click "Delete".
  3. Try both actions above with more than one block selected, including the critical block.
  4. In the list view, try deleting the block with backspace, and through the options dropdown.
  5. Also in the list view, try the above with more than one block selected, including the critical block.
  6. In all these situations, the modal should pop up asking for confirmation of critical block removal.
    7. For bonus points: check if I missed any block removal methods 😄

B) Make sure that there is no change in behaviour in other editors. For instance:

  1. Open a new post in the Post Editor.
  2. Insert one of the targeted blocks, e.g. by pasting in <!-- wp:query /-->
  3. Make sure you are able to delete it without seeing the modal.

Testing Instructions for Keyboard

Screenshots or screencast

51145-deleting-single 51145-deleting-multiple

@github-actions
Copy link

github-actions bot commented Jun 1, 2023

Size Change: +7.67 kB (+1%)

Total Size: 1.42 MB

Filename Size Change
build/api-fetch/index.min.js 2.28 kB -7 B (0%)
build/block-editor/index.min.js 198 kB +739 B (0%)
build/block-editor/style-rtl.css 14.9 kB +1 B (0%)
build/block-editor/style.css 14.9 kB +1 B (0%)
build/block-library/blocks/navigation/editor-rtl.css 2.26 kB -96 B (-4%)
build/block-library/blocks/navigation/editor.css 2.26 kB -95 B (-4%)
build/block-library/editor-rtl.css 12.1 kB -77 B (-1%)
build/block-library/editor.css 12.1 kB -79 B (-1%)
build/block-library/index.min.js 199 kB +12 B (0%)
build/components/index.min.js 240 kB -27 B (0%)
build/core-commands/index.min.js 2.12 kB +4 B (0%)
build/edit-post/index.min.js 33.9 kB -7 B (0%)
build/edit-site/index.min.js 80.1 kB +6.66 kB (+9%) 🔍
build/edit-site/style-rtl.css 12.2 kB +320 B (+3%)
build/edit-site/style.css 12.2 kB +324 B (+3%)
build/edit-widgets/index.min.js 16.8 kB -2 B (0%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 955 B
build/annotations/index.min.js 2.69 kB
build/autop/index.min.js 2.1 kB
build/blob/index.min.js 451 B
build/block-directory/index.min.js 6.99 kB
build/block-directory/style-rtl.css 1.02 kB
build/block-directory/style.css 1.02 kB
build/block-editor/content-rtl.css 4.22 kB
build/block-editor/content.css 4.22 kB
build/block-editor/default-editor-styles-rtl.css 381 B
build/block-editor/default-editor-styles.css 381 B
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/block/editor-rtl.css 305 B
build/block-library/blocks/block/editor.css 305 B
build/block-library/blocks/button/editor-rtl.css 584 B
build/block-library/blocks/button/editor.css 582 B
build/block-library/blocks/button/style-rtl.css 624 B
build/block-library/blocks/button/style.css 623 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 409 B
build/block-library/blocks/columns/style.css 409 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 647 B
build/block-library/blocks/cover/editor.css 650 B
build/block-library/blocks/cover/style-rtl.css 1.61 kB
build/block-library/blocks/cover/style.css 1.6 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 159 B
build/block-library/blocks/details/style.css 159 B
build/block-library/blocks/embed/editor-rtl.css 293 B
build/block-library/blocks/embed/editor.css 293 B
build/block-library/blocks/embed/style-rtl.css 410 B
build/block-library/blocks/embed/style.css 410 B
build/block-library/blocks/embed/theme-rtl.css 126 B
build/block-library/blocks/embed/theme.css 126 B
build/block-library/blocks/file/editor-rtl.css 316 B
build/block-library/blocks/file/editor.css 316 B
build/block-library/blocks/file/interactivity.min.js 395 B
build/block-library/blocks/file/style-rtl.css 269 B
build/block-library/blocks/file/style.css 270 B
build/block-library/blocks/file/view.min.js 375 B
build/block-library/blocks/freeform/editor-rtl.css 2.58 kB
build/block-library/blocks/freeform/editor.css 2.58 kB
build/block-library/blocks/gallery/editor-rtl.css 947 B
build/block-library/blocks/gallery/editor.css 952 B
build/block-library/blocks/gallery/style-rtl.css 1.53 kB
build/block-library/blocks/gallery/style.css 1.53 kB
build/block-library/blocks/gallery/theme-rtl.css 108 B
build/block-library/blocks/gallery/theme.css 108 B
build/block-library/blocks/group/editor-rtl.css 654 B
build/block-library/blocks/group/editor.css 654 B
build/block-library/blocks/group/style-rtl.css 57 B
build/block-library/blocks/group/style.css 57 B
build/block-library/blocks/group/theme-rtl.css 78 B
build/block-library/blocks/group/theme.css 78 B
build/block-library/blocks/heading/style-rtl.css 76 B
build/block-library/blocks/heading/style.css 76 B
build/block-library/blocks/html/editor-rtl.css 336 B
build/block-library/blocks/html/editor.css 337 B
build/block-library/blocks/image/editor-rtl.css 834 B
build/block-library/blocks/image/editor.css 833 B
build/block-library/blocks/image/interactivity.min.js 1.34 kB
build/block-library/blocks/image/style-rtl.css 1.34 kB
build/block-library/blocks/image/style.css 1.34 kB
build/block-library/blocks/image/theme-rtl.css 126 B
build/block-library/blocks/image/theme.css 126 B
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 213 B
build/block-library/blocks/latest-posts/editor.css 212 B
build/block-library/blocks/latest-posts/style-rtl.css 478 B
build/block-library/blocks/latest-posts/style.css 478 B
build/block-library/blocks/list/style-rtl.css 88 B
build/block-library/blocks/list/style.css 88 B
build/block-library/blocks/media-text/editor-rtl.css 266 B
build/block-library/blocks/media-text/editor.css 263 B
build/block-library/blocks/media-text/style-rtl.css 507 B
build/block-library/blocks/media-text/style.css 505 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 712 B
build/block-library/blocks/navigation-link/editor.css 711 B
build/block-library/blocks/navigation-link/style-rtl.css 115 B
build/block-library/blocks/navigation-link/style.css 115 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 296 B
build/block-library/blocks/navigation-submenu/editor.css 295 B
build/block-library/blocks/navigation/interactivity.min.js 978 B
build/block-library/blocks/navigation/style-rtl.css 2.21 kB
build/block-library/blocks/navigation/style.css 2.2 kB
build/block-library/blocks/navigation/view-modal.min.js 2.78 kB
build/block-library/blocks/navigation/view.min.js 438 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 401 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 174 B
build/block-library/blocks/paragraph/editor.css 174 B
build/block-library/blocks/paragraph/style-rtl.css 279 B
build/block-library/blocks/paragraph/style.css 281 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-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 588 B
build/block-library/blocks/post-featured-image/editor.css 586 B
build/block-library/blocks/post-featured-image/style-rtl.css 319 B
build/block-library/blocks/post-featured-image/style.css 319 B
build/block-library/blocks/post-navigation-link/style-rtl.css 153 B
build/block-library/blocks/post-navigation-link/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 314 B
build/block-library/blocks/post-template/style.css 314 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 103 B
build/block-library/blocks/preformatted/style.css 103 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 335 B
build/block-library/blocks/pullquote/style.css 335 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 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 450 B
build/block-library/blocks/query/editor.css 449 B
build/block-library/blocks/quote/style-rtl.css 222 B
build/block-library/blocks/quote/style.css 222 B
build/block-library/blocks/quote/theme-rtl.css 223 B
build/block-library/blocks/quote/theme.css 226 B
build/block-library/blocks/read-more/style-rtl.css 132 B
build/block-library/blocks/read-more/style.css 132 B
build/block-library/blocks/rss/editor-rtl.css 149 B
build/block-library/blocks/rss/editor.css 149 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 178 B
build/block-library/blocks/search/editor.css 178 B
build/block-library/blocks/search/style-rtl.css 587 B
build/block-library/blocks/search/style.css 584 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 531 B
build/block-library/blocks/separator/editor-rtl.css 146 B
build/block-library/blocks/separator/editor.css 146 B
build/block-library/blocks/separator/style-rtl.css 234 B
build/block-library/blocks/separator/style.css 234 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 323 B
build/block-library/blocks/shortcode/editor.css 323 B
build/block-library/blocks/site-logo/editor-rtl.css 754 B
build/block-library/blocks/site-logo/editor.css 754 B
build/block-library/blocks/site-logo/style-rtl.css 203 B
build/block-library/blocks/site-logo/style.css 203 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 116 B
build/block-library/blocks/site-title/editor.css 116 B
build/block-library/blocks/site-title/style-rtl.css 57 B
build/block-library/blocks/site-title/style.css 57 B
build/block-library/blocks/social-link/editor-rtl.css 184 B
build/block-library/blocks/social-link/editor.css 184 B
build/block-library/blocks/social-links/editor-rtl.css 674 B
build/block-library/blocks/social-links/editor.css 673 B
build/block-library/blocks/social-links/style-rtl.css 1.43 kB
build/block-library/blocks/social-links/style.css 1.42 kB
build/block-library/blocks/spacer/editor-rtl.css 348 B
build/block-library/blocks/spacer/editor.css 348 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 433 B
build/block-library/blocks/table/editor.css 433 B
build/block-library/blocks/table/style-rtl.css 645 B
build/block-library/blocks/table/style.css 644 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 251 B
build/block-library/blocks/tag-cloud/style.css 253 B
build/block-library/blocks/template-part/editor-rtl.css 403 B
build/block-library/blocks/template-part/editor.css 403 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/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 174 B
build/block-library/blocks/video/style.css 174 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.1 kB
build/block-library/common.css 1.1 kB
build/block-library/editor-elements-rtl.css 75 B
build/block-library/editor-elements.css 75 B
build/block-library/elements-rtl.css 54 B
build/block-library/elements.css 54 B
build/block-library/interactivity/runtime.min.js 2.69 kB
build/block-library/interactivity/vendors.min.js 8.2 kB
build/block-library/reset-rtl.css 478 B
build/block-library/reset.css 478 B
build/block-library/style-rtl.css 13.5 kB
build/block-library/style.css 13.5 kB
build/block-library/theme-rtl.css 686 B
build/block-library/theme.css 691 B
build/block-serialization-default-parser/index.min.js 1.12 kB
build/block-serialization-spec-parser/index.min.js 2.87 kB
build/blocks/index.min.js 50.9 kB
build/commands/index.min.js 14.9 kB
build/commands/style-rtl.css 827 B
build/commands/style.css 827 B
build/components/style-rtl.css 11.8 kB
build/components/style.css 11.8 kB
build/compose/index.min.js 12 kB
build/core-data/index.min.js 15.7 kB
build/customize-widgets/index.min.js 11.9 kB
build/customize-widgets/style-rtl.css 1.46 kB
build/customize-widgets/style.css 1.45 kB
build/data-controls/index.min.js 640 B
build/data/index.min.js 8.25 kB
build/date/index.min.js 40.4 kB
build/deprecated/index.min.js 451 B
build/dom-ready/index.min.js 324 B
build/dom/index.min.js 4.63 kB
build/edit-post/classic-rtl.css 544 B
build/edit-post/classic.css 545 B
build/edit-post/style-rtl.css 7.58 kB
build/edit-post/style.css 7.57 kB
build/edit-widgets/style-rtl.css 4.53 kB
build/edit-widgets/style.css 4.53 kB
build/editor/index.min.js 44.6 kB
build/editor/style-rtl.css 3.58 kB
build/editor/style.css 3.58 kB
build/element/index.min.js 4.8 kB
build/escape-html/index.min.js 537 B
build/format-library/index.min.js 7.63 kB
build/format-library/style-rtl.css 554 B
build/format-library/style.css 553 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/is-shallow-equal/index.min.js 527 B
build/keyboard-shortcuts/index.min.js 1.64 kB
build/keycodes/index.min.js 1.84 kB
build/list-reusable-blocks/index.min.js 2.13 kB
build/list-reusable-blocks/style-rtl.css 836 B
build/list-reusable-blocks/style.css 836 B
build/media-utils/index.min.js 2.9 kB
build/notices/index.min.js 948 B
build/plugins/index.min.js 1.77 kB
build/preferences-persistence/index.min.js 1.84 kB
build/preferences/index.min.js 1.24 kB
build/primitives/index.min.js 943 B
build/priority-queue/index.min.js 1.52 kB
build/private-apis/index.min.js 939 B
build/react-i18n/index.min.js 615 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.7 kB
build/reusable-blocks/index.min.js 2.38 kB
build/reusable-blocks/style-rtl.css 243 B
build/reusable-blocks/style.css 243 B
build/rich-text/index.min.js 10.7 kB
build/router/index.min.js 1.77 kB
build/server-side-render/index.min.js 1.94 kB
build/shortcode/index.min.js 1.39 kB
build/style-engine/index.min.js 1.42 kB
build/token-list/index.min.js 582 B
build/url/index.min.js 3.57 kB
build/vendors/inert-polyfill.min.js 2.48 kB
build/vendors/react-dom.min.js 41.8 kB
build/vendors/react.min.js 4.02 kB
build/viewport/index.min.js 958 B
build/warning/index.min.js 268 B
build/widgets/index.min.js 7.16 kB
build/widgets/style-rtl.css 1.15 kB
build/widgets/style.css 1.16 kB
build/wordcount/index.min.js 1.02 kB

compressed-size-action

@github-actions
Copy link

github-actions bot commented Jun 1, 2023

Flaky tests detected in 6a0a806.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/5348150109
📝 Reported issues:

Copy link
Member

@ramonjd ramonjd 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 working on this! I haven't tested manually yet, just left some flyby comments 😄

@@ -74,7 +77,21 @@ export default function BlockActions( {
return duplicateBlocks( clientIds, updateSelection );
},
onRemove() {
return removeBlocks( clientIds, updateSelection );
const shouldDisplayRemovalPrompt = clientIds
Copy link
Member

Choose a reason for hiding this comment

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

Is this a possible candidate for memoization? I'm not sure, I guess clientIds would be constantly changing. It might be more optimal assuming a user is trying to remove the same block from the same block tree 😄

@@ -105,7 +108,22 @@ function ListViewBlockSelectButton(
// fallback to focus the parent block.
firstBlockRootClientId;

removeBlocks( blocksToDelete, false );
const shouldDisplayRemovalPrompt = blocksToDelete
Copy link
Member

Choose a reason for hiding this comment

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

Similar thought to the shouldDisplayRemovalPrompt above.

const message =
blockName === 'core/post-content'
? __(
'This block displays the content of a post or page. Removing it it is not advised.'
Copy link
Member

Choose a reason for hiding this comment

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

Should we indicate why it's not advised as mentioned in the issue?

I'm leaning towards "yes", however I suppose that opens the can of worms that we'd have to customize the message for every warning depending on the block. 🤔

@@ -66,7 +69,24 @@ export default function useInput() {
node.contentEditable = false;
event.preventDefault();
if ( __unstableIsFullySelected() ) {
removeBlocks( getSelectedBlockClientIds() );
const shouldDisplayRemovalPrompt =
Copy link
Member

Choose a reason for hiding this comment

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

Maybe this should be something like blockNamesWithWarnings or something way better. should* makes me thing the return values is a bool

} from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';

export function showBlockRemovalWarning( blockName ) {
Copy link
Member

@ramonjd ramonjd Jun 1, 2023

Choose a reason for hiding this comment

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

Hmm, I'm wondering about the mixed type return value. The function name, at least in my mind, implies a bool, but I see blockName is handy to have to display the removal prompt content.

Could we rather pass down a list of IDs and return those that required removal. That means we could memoize in one spot too. E.g.,

getBlockNamesWithRemovalWarnings( clientIds ) // => [ 'core/query' ]

/**
 * 
 * @param {Array} clientIds 
 * @returns {Array}
 */
const getBlockNamesWithRemovalWarnings = ( clientIds ) =>
    clientIds
        .map( getBlockName )
        .filter( ( blockName ) => {
             return blockName === 'core/query' || blockName === 'core/post-content';
        } );

Would that work?

@tellthemachines
Copy link
Contributor Author

Thanks for the feedback @ramonjd ! Those are all great points, but I'd love to have more thoughts on the approach right now. Details can be ironed out once we're sure this is the way to go 😅

Copy link
Contributor

@andrewserong andrewserong left a comment

Choose a reason for hiding this comment

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

Nice work, it's testing pretty well!

One thing I noticed is that this applies in the post editor, too, so if you're editing a post or page that contains a Latest Posts section and go to remove the Query block, the warning will also display. Is that intended? Would it be worth further limiting the warning so that it only displays when editing a template or template part?

This is a first approach: it works, but the code is messy.

In terms of the logic, I actually quite like it, and that it's explicit about when it's being executed, rather than on every call of removeBlocks. The main thing I was wondering from looking over the code is whether we can abstract the logic into a hook? That way, if it's only an extra couple of lines in each place it's used, I'm wondering if that might make it feel less messy when it has to be manually introduced in each place where we need it? 🤔

should this initial version be extensible?

Personally, I quite like the hard-coded idea to begin with, since the idea of the feature is quite tied to the idea of what is required in a template in order for it to function. But I could imagine potentially having a property in block.json or something like that in the future, if folks wanted to have a flag to say "warn on removal", like you mention.

title={ sprintf(
/* translators: %s: the name of a menu to delete */
__( 'Remove %s?' ),
blockName
Copy link
Contributor

@andrewserong andrewserong Jun 1, 2023

Choose a reason for hiding this comment

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

Can we grab the human friendly (display) name here? Maybe via useBlockDisplayInformation, since currently it shows up as core/query:

image

@andrewserong
Copy link
Contributor

Details can be ironed out once we're sure this is the way to go 😅

Sounds good — apologies if I went too detailed, too!

@andrewserong
Copy link
Contributor

Just another thought for extensibility — I wonder if it'd be helpful storing the list of critical blocks to warn on removal in state somewhere? For example when core blocks are registered, setGroupingBlockName is called with the group block's name (here), so potentially that could be a similar place to register a canonical list of critical blocks to warn on removal? For the moment, I'd probably lean toward sticking with a hard-coded list, as I imagine it'd be easier to make it extensible in a follow-up, than the other way around? 🤔

@ramonjd
Copy link
Member

ramonjd commented Jun 1, 2023

I have questions regarding implementation, mainly: should this initial version be extensible?

Agree with @andrewserong, I think it'd be great to keep a smaller footprint and even make the store methods private for now until things are stabilized.

Another thought I had was should the check for whether to show the prompt be part of the removeBlocks action instead?

I agree with your instinct. Until there are more blocks on the warning list, I'm assuming it might be more optimal the way you've done it (?)

Details can be ironed out once we're sure this is the way to go

You bet!

@tellthemachines tellthemachines self-assigned this Jun 1, 2023
@ndiego
Copy link
Member

ndiego commented Jun 1, 2023

This is looking great, but I do want to call out the need for extensibility in the long run. I can imagine people wanting to add additional blocks to this "critical" list. I'm thinking about those that are building custom blocks for clients or perhaps e-commerce blocks.

There also needs to be a way to turn these warnings off. I think this is probably more important than extensibility right now. While helpful to the average user, I can see these warnings becoming a bit annoying for power users or theme builders who are in the process of building out a block theme.

@tellthemachines tellthemachines added the [Feature] Site Editor Related to the overarching Site Editor (formerly "full site editing") label Jun 2, 2023
@ramonjd
Copy link
Member

ramonjd commented Jun 2, 2023

While helpful to the average user, I can see these warnings becoming a bit annoying for power users or theme builders who are in the process of building out a block theme.

Good point 😄 I think most admins would turn it off immediately, or soon after they become familiar with blocks.

I suppose the warning should furthermore only arrive on the scene for already-published blocks, or blocks with changes that have been saved once? Otherwise we see something like this:

Screenshot 2023-06-02 at 10 44 28 am

I'm also wondering whether a warning for the query block (for example) should be shown at all in the post editor 🤷 I guess making it extensible would allow site owners to build their own rules.

Especially given that the premise behind the feature, that there are some blocks that one shouldn't remove, in my mind at least, is situational and could be unique to every site.

Less intrusive than a popup could be a notification that appears after the deletion of target blocks and offers and "Undo" step 🤷

Screenshot 2023-06-02 at 10 47 18 am

@tellthemachines
Copy link
Contributor Author

Thanks for all the thoughts folks ❤️

One thing I noticed is that this applies in the post editor, too, so if you're editing a post or page that contains a Latest Posts section and go to remove the Query block, the warning will also display. Is that intended?

I didn't really consider it! It doesn't seem as necessary on posts or pages, because it's doubtful Query will be the main content there. I'll look into limiting the prompts to templates for now. That's also something we'll need to consider whether to make customisable or not.

The main thing I was wondering from looking over the code is whether we can abstract the logic into a hook?

Something like shouldDisplayRemovalPrompt(blocksToRemove, removalFunction), which takes care of looking up the block names and calling displayRemovalPrompt? That could work! (Function names tbd; the naming in this PR is all over the place, as @ramonjd observed already 😂 )

apologies if I went too detailed, too!

No need to apologise! I just needed to work out the high-level shape of things before starting to address the finer points 😄

For the moment, I'd probably lean toward sticking with a hard-coded list, as I imagine it'd be easier to make it extensible in a follow-up, than the other way around?

Yeah, I think the extensibility bit will need some more thought. We'll likely need extenders to provide a custom message for the removal warning too.

Until there are more blocks on the warning list, I'm assuming it might be more optimal the way you've done it (?)

👍 It might not even be necessary to add the removal prompt logic anywhere else; that's probably something that can be worked out once there's a public API and we start seeing what kind of real world usage it gets.

There also needs to be a way to turn these warnings off. I think this is probably more important than extensibility right now.

Good point! Let's get some testing feedback on the initial version and see how best to approach configurability in the UI. I'm thinking it could be something like a "Don't show again" checkbox in the modal itself.

@tellthemachines
Copy link
Contributor Author

I suppose the warning should furthermore only arrive on the scene for already-published blocks, or blocks with changes that have been saved once?

The problem is we don't want to accidentally remove empty Post Content blocks - that's probably a more common occurrence than removing Query blocks. I think this is one of those things that we'll have to road test and smooth out iteratively.

@ntsekouras
Copy link
Contributor

Thanks for the PR @tellthemachines!

But I could imagine potentially having a property in block.json or something like that in the future, if folks wanted to have a flag to say "warn on removal", like you mention.

I definitely agree with @andrewserong about this. This seems like it should be a part of Blocks API and not hardcode some block names and messages in block editor package. This API in my mind would need to be aware of context(site editor, post editor, template, etc..) and provide different messages conditionally. Being a Block API means it will be also filterable with the existing filters for block settings.

@tellthemachines
Copy link
Contributor Author

This seems like it should be a part of Blocks API and not hardcode some block names and messages in block editor package.

Sure, the consensus is that there should be an API, but for the initial experiment it's better to just hardcode the block names so we can quickly get something out there to be tested, and get some feedback to help inform what kind of API is needed.

This API in my mind would need to be aware of context(site editor, post editor, template, etc..) and provide different messages conditionally.

I've been thinking about how best to provide this context. If there is to be an API that allows extenders to define third party blocks as "critical" and provide a message for the prompt, it would make sense that extenders can also configure which editors the prompt displays in. The other thing to consider is if this API should also work with non-WP block editors, in which case we shouldn't hardcode editor context either.

@tellthemachines
Copy link
Contributor Author

I've now put all the logic that determines whether to show the prompt in a hook which returns a function that can be called instead of replaceBlocks with the same arguments.

Regarding the problem of only displaying the prompts in the site editor, I tried moving the modal into the site editor itself, around here but it's not working correctly. I'm not sure if there's a better place to put it in the site editor, or if there's another reason it doesn't work. Will continue looking into it tomorrow, but any ideas welcome in the meantime!

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.

Hi! I left a mishmash of inline comments that reflect my thought process as I reviewed. That means that the comments at the very end (chronologically) are probably the most important ones. I hope that the most important questions are apparent, but if it's confusing please let me know and I can try to rephrase or sum up my thoughts. :)

Comment on lines 24 to 30
const { isSelected } = useSelect(
( select ) => {
return {
isSelected:
select( blockEditorStore ).isBlockSelected( clientId ),
};
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Why this change?

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, I should revert that! I had a couple more selectors in there but removed them with the latest commit.

Comment on lines 55 to 57
removalFunction: () => {
removeBlocks( clientIds, selectPrevious );
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the removal function ever anything other than removeBlocks( clientIds, selectPrevious )? Looking at the branch, it doesn't seem so. Seems like one piece of state that could be removed from isRemovalPromptDisplayed and one inch of API surface reduced.

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 always removeBlocks, the only difference is whether selectPrevious is true or false. I guess we could just pass selectPrevious instead of the whole function.

};
}

export function BlockRemovalWarningModal( {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should probably be under ../components

closeModal,
removalFunction,
} ) {
const message =
Copy link
Contributor

Choose a reason for hiding this comment

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

The messages should sit next to the corresponding block types. Something like:

const blockTypePromptMessages = new Map([
    [ 'core/query', __( 'This block ... ' ) ],
    [ 'core/post-content', __( 'This block ... ' ) ],
]);

to be shared by both hook and component.

(I know others have suggested making this pluggable, but I personally wouldn't rush it.)

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, no hurry to create a public API for this.

Comment on lines 94 to 99
<Button variant="tertiary" onClick={ () => closeModal() }>
{ __( 'Cancel' ) }
</Button>
<Button variant="primary" onClick={ () => onConfirmRemoval() }>
{ __( 'Confirm' ) }
</Button>
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
<Button variant="tertiary" onClick={ () => closeModal() }>
{ __( 'Cancel' ) }
</Button>
<Button variant="primary" onClick={ () => onConfirmRemoval() }>
{ __( 'Confirm' ) }
</Button>
<Button variant="tertiary" onClick={ closeModal }>
{ __( 'Cancel' ) }
</Button>
<Button variant="primary" onClick={ onConfirmRemoval }>
{ __( 'Confirm' ) }
</Button>

if ( innerBlocks.length ) {
return findFirstCriticalBlock( innerBlocks );
}
return false;
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor point, but following find convention, this could/should be undefined.

Copy link
Contributor

Choose a reason for hiding this comment

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

Renamed to get* in 5059a67

*/
import { store as blockEditorStore } from '../store';

function isBlockCritical( blockName ) {
Copy link
Contributor

Choose a reason for hiding this comment

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

See comment about blockTypePromptMessages

Copy link
Contributor

Choose a reason for hiding this comment

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

Removed in 82839d2

Comment on lines 76 to 78
displayPrompt: _displayPrompt,
removalFunction: _removalFunction,
blockToRemove: blockName,
Copy link
Contributor

Choose a reason for hiding this comment

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

See my other comment about the need for a removal function. If we could distill the data needs here, I think we could reduce this to a nicer:

Suggested change
displayPrompt: _displayPrompt,
removalFunction: _removalFunction,
blockToRemove: blockName,
shouldDisplayBlockRemovalPrompt: !! blockToBeRemoved(),

Where select( 'core/block-editor' ).blockToBeRemoved is the successor to isRemovalPromptDisplayed and only returns the block name if there is one, or null/undefined.

Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at this again, I wonder if this state should even be read/handled by BlockList at all. Shouldn't BlockRemovalWarningModal just handle everything internally? We have many such components:

		<>
			<elementContext.Provider value={ element }>
				<IntersectionObserver.Provider value={ intersectionObserver }>
					<div { ...innerBlocksProps } />
				</IntersectionObserver.Provider>
			</elementContext.Provider>
			<BlockRemovalWarningModal />
		</>

where BlockRemovalWarningModal might become BlockRemovalPromptListener or anything that better conveys its conditional nature.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah good point, it'll be cleaner if the modal itself gets what it needs from state. As it stands, the modal needs to know both the name of the block to be removed, and the function to call if the user confirms the action.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Shouldn't BlockRemovalWarningModal just handle everything internally?

I've been playing with this a bit but can't quite get it to work, I think because if there's no state change in the block list when a block deletion is attempted, there's no reason to re-render the component so the modal never gets triggered when it's needed. Plus it renders a bunch of times when the block list first loads which is kind of annoying 😅

Copy link
Contributor

Choose a reason for hiding this comment

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

I gave this a go in f0247fe. This works, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes! I was pretty much trying to do that, so not sure what went wrong 😕 probably had a typo somewhere. Thanks!

Comment on lines 1249 to 1221
export function displayRemovalPrompt( displayPrompt, options = {} ) {
const { removalFunction, blockName } = options;
return {
type: 'PROMPT_REMOVAL',
displayPrompt,
removalFunction,
blockName,
};
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we can / want to fold this logic into the removeBlocks action, since that's the central code path for removal and it already is the site for checks and conditional dispatching:

  • checks for canRemoveBlocks before proceeding
  • dispatches selectPreviousBlock alongside the removal action if requested
  • ensures correct focus by dispatching ensureDefaultBlock at the very end

I'm not sure of this idea, so I'd like to be challenged, but what about something like:

export const removeBlocks =
-	( clientIds, selectPrevious = true ) =>
+	( clientIds, selectPrevious = true, forceRemove = false ) =>
	( { select, dispatch } ) => {
		...
		if ( ! canRemoveBlocks ) {
			return;
		}

+		if ( ! forceRemove && needsConsentForRemoval( blockIds ) ) {
+			dispatch( { type: 'PROMPT_FOR_BLOCK_REMOVAL', blockIds } );
+			return;
+		}

		...
		dispatch( { type: 'REMOVE_BLOCKS', clientIds } );
+		dispatch( { type: 'PROMPT_FOR_BLOCK_REMOVAL', null } );
		...
	};

then the modal would dispatch a new remove action with forceRemove: true

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I thought about that (see my little monologue in the PR description 😄) but hesitate to do it because removeBlocks gets called in a lot of places (itself or via removeBlock) where the prompt isn't relevant, and that would mean going through the logic to check if there's a critical block in the list of blocks to remove every time.

It does feel conceptually tidier to have everything together in removeBlocks though.

I'm also happy to hear more thoughts about this!

Comment on lines 1480 to 1491
export function isRemovalPromptDisplayed( state = false, action ) {
switch ( action.type ) {
case 'PROMPT_REMOVAL':
return {
displayPrompt: action.displayPrompt,
removalFunction: action.removalFunction,
blockName: action.blockName,
};
}

return state;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe this could come down to something like

case 'PROMPT_FOR_BLOCK_REMOVAL':
  return action.blockIds;

with the corresponding name change.

return (
<Modal
title={ sprintf(
/* translators: %s: the name of a menu to delete */
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
/* translators: %s: the name of a menu to delete */
/* translators: %s: the title of a block to delete, e.g. "Post Content" */

@tellthemachines
Copy link
Contributor Author

tellthemachines commented Jun 7, 2023

Going back to the problem of getting the prompt to only display in the site editor, I just pushed an update that moves the modal into the site editor, and dispatches and action from inside the modal, flagging that the modal is available. Then the hook that checks if the prompt needs to be displayed also checks if the modal is available, and if it isn't it just goes ahead and removes the blocks.

This is so that the prompt doesn't display when a Query block is removed from the post editor, and it was the best way I could find of doing it without requiring the block editor package to be aware of its context (post or site editor). Thoughts for improvement or other ideas welcome!

Still to do:

  • Try passing just the selectPrevious parameter for removeBlocks instead of passing the whole removal function to the modal;
  • Move modal into components folder; maybe useBlockRemovalWarning should go into hooks too;
  • Revisit the naming of all these things 😅
  • Should these new actions and selectors be made private for now?
  • Add tests;
  • Decide on definitive modal text.

@jameskoster jameskoster requested a review from a team June 7, 2023 09:20
Comment on lines +19 to +30
// In certain editing contexts, we'd like to prevent accidental removal of
// important blocks. For example, in the site editor, the Query Loop block is
// deemed important. In such cases, we'll ask the user for confirmation that
// they intended to remove such block(s).
//
// @see https://github.com/WordPress/gutenberg/pull/51145
export const blockTypePromptMessages = {
'core/query': __( 'Query Loop displays a list of posts or pages.' ),
'core/post-content': __(
'Post Content displays the content of a post or page.'
),
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Right now this entangles block-editor with edit-site, since these are rules that only apply to the site editor. I don't want to hold up this PR any longer, but a future improvement could be to let the consumer (the component rendering BlockRemovalWarningModal) provide their own rules. Maybe:

<BlockRemovalWarningModal rules={ myPromptMessages } />

As you can tell, I'm undecided on the terminology: are these messages? Rules? Warnings?

Copy link
Contributor

Choose a reason for hiding this comment

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

-> #51841

@mcsf
Copy link
Contributor

mcsf commented Jun 22, 2023

Tests are failing across Gutenberg, potentially solved by #51790. In the meantime, I'll just merge this one. Thanks, everyone!

(Edit: E2E tests were fine when run locally.)

@mcsf mcsf merged commit 50a754d into trunk Jun 22, 2023
@mcsf mcsf deleted the add/block-removal-warning branch June 22, 2023 21:16
@github-actions github-actions bot added this to the Gutenberg 16.1 milestone Jun 22, 2023
Comment on lines +163 to +172
// FIXME: Without this existence check, the unit tests for
// `__experimentalDeleteReusableBlock` in
// `packages/reusable-blocks/src/store/test/actions.js` fail due to
// the fact that the `registry` object passed to the thunk actions
// doesn't include this private action. This needs to be
// investigated to understand whether it's a real smell or if it's
// because not all store code has been updated to accommodate
// private selectors.
select.isRemovalPromptSupported &&
select.isRemovalPromptSupported()
Copy link
Contributor

Choose a reason for hiding this comment

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

@jsnajdr: I quickly chatted with Adam about this. Is it possible that we are missing something in our Jest setup to accommodate private selectors?

Copy link
Member

Choose a reason for hiding this comment

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

It's not because of Jest, but because of the cumbersome way how we register the block-editor store, with the deprecated registerStore function, forced to use this way by the persist plugin.

export const store = createReduxStore( STORE_NAME, {
	...storeConfig,
	persist: [ 'preferences' ],
} );

// We will be able to use the `register` function once we switch
// the "preferences" persistence to use the new preferences package.
const registeredStore = registerStore( STORE_NAME, {
	...storeConfig,
	persist: [ 'preferences' ],
} );
unlock( registeredStore ).registerPrivateActions( privateActions );
unlock( registeredStore ).registerPrivateSelectors( privateSelectors );

This code:

  1. creates a store store descriptor for the block-editor store.
  2. instantiates and registers the block-editor store, but using a different descriptor, the one created internally inside registerStore.
  3. registers private actions and selectors to that one instance (!) that was just created.

The unit test that is failing is creating a new registry, and registers (instantiates) a block-editor store in this registry, using the store descriptor.

But nobody ever added the private actions and selectors to this descriptor!

The short-term solution is to register the private actions/selectors also to store. The long-term solution is to finish and merge #39632 (by @talldan), stop using the persist plugin, and stop using registerStore for block-editor.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks very much for the explanation! I agree that merging #39632 should be the way forward. In the meantime, if you like, I opened #52088 for the short-term solution.

mcsf added a commit that referenced this pull request Jun 29, 2023
As a workaround, until #39632 is merged, make sure that private actions
and selectors can be unlocked from the original store descriptor (the
one created by `createReduxStore`) and not just the one registered in
the default registry (created by `registerStore`).

Without this workaround, specific setups will unexpectedly fail, such as
the action tests in the `reusable-blocks` package, due to the way that
those tests create their own registries in which they register stores
like `block-editor`.

Context: #51145 (comment)

Props jsnajdr
mcsf added a commit that referenced this pull request Jun 29, 2023
…52088)

As a workaround, until #39632 is merged, make sure that private actions
and selectors can be unlocked from the original store descriptor (the
one created by `createReduxStore`) and not just the one registered in
the default registry (created by `registerStore`).

Without this workaround, specific setups will unexpectedly fail, such as
the action tests in the `reusable-blocks` package, due to the way that
those tests create their own registries in which they register stores
like `block-editor`.

Context: #51145 (comment)

Props jsnajdr
mcsf added a commit that referenced this pull request Jun 29, 2023
Following up on #51145, this untangles `edit-site` from `block-editor`
by removing the hard-coded set of rules `blockTypePromptMessages` from
the generic `BlockRemovalWarningModal` component. Rules are now to be
passed to that component by whichever block editor is using it.

Names and comments have been updated accordingly and improved.
mcsf added a commit that referenced this pull request Jun 30, 2023
* Block removal prompt: let consumers pass their own rules

Following up on #51145, this untangles `edit-site` from `block-editor`
by removing the hard-coded set of rules `blockTypePromptMessages` from
the generic `BlockRemovalWarningModal` component. Rules are now to be
passed to that component by whichever block editor is using it.

Names and comments have been updated accordingly and improved.

* Site editor: Add e2e test for block removal prompt
sethrubenstein pushed a commit to pewresearch/gutenberg that referenced this pull request Jul 13, 2023
* Show warning on critical block removal

* Extract prompt display logic into a hook

* Revert formatting change.

* Prompt for removal of all the blocks

* Move prompt state handling out of BlockList and into self

* findCriticalBlocks: don't dismiss children of matching node

* Refactor by embracing `flatMap` semantics

* When `isBlockCritical( blockName )`, don't immediately return: there
  could be other matching block types within that node's inner blocks.

* findCriticalBlocks -> getBlocksToPromptFor

The intention here was to drop the "find" prefix, which can mislead the
render into believing the function is meant to return a single match or
none.

* Drop isBlockCritical()

* Redesign removal modal

This is an attempt to reconcile a desire for clarity in the UI as to
what is about to be deleted (and why that matters) and the i18n
constraints that apply:

* Remove `__( 'Remove %s?' )` string, which -- although manageable --
  places an added burden on translators of more inflected languages.

* Trim the strings in `blockTypePromptMessages`. This allows us to stack
  all matching blocks' custom messages and finish off with a single
  "Removing it is not advised." paragraph.

* Move prompt into site editor.

* Reset removalPromptExists upon listener unmount

* Let action removeBlocks handle prompts and confirmations

* Add private action `privateRemoveBlocks` to hide extended interface

* Fix unit tests

* Try: Dispatch setRemovalPromptStatus in edit-site init

On one hand, `initializeEditor` is where we would typically set this
sort of setting, which is why this is worth trying out.

On the other hand, interfering with the `REMOVE_BLOCKS` action is a big
deal, so we should be absolutely sure that the prompt status in the
reducer is always accurate. The `useEffect` approach has the advantage
of keeping everything in one place: by rendering a single component like
BlockRemovalWarningModal, we guarantee that both the setting is correct
and the editor will render removal prompts.

* Revert "Try: Dispatch setRemovalPromptStatus in edit-site init"

This reverts commit a5cce0a.

I'll repeat my reasoning from the parent commit:

"[...] interfering with the `REMOVE_BLOCKS` action is a big
deal, so we should be absolutely sure that the prompt status in the
reducer is always accurate. The `useEffect` approach has the advantage
of keeping everything in one place: by rendering a single component like
BlockRemovalWarningModal, we guarantee that both the setting is correct
and the editor will render removal prompts."

* Make all actions & selectors private. Rename things.

* Addresses pull request feedback.

* See `FIXME` notes.

* Make BlockRemovalWarningModal private

* Cleanup: Remove BlockList changes from branch

* Tweak removal message for Query. Tweak comments.

* Split action into displayRemovalPrompt & clearRemovalPrompt

The action types have also been renamed: DISPLAY_REMOVAL_PROMPT,
CLEAR_REMOVAL_PROMPT.

Simplifies the function signatures and hopefully makes everything a
little bit clearer.

* Rename setRemovalPromptStatus to toggleRemovalPromptSupport

Also: rename the action type, then sprinkle with comments.

* Rename isRemovalPromptDisplayed to getRemovalPromptData

New reducer: removalPromptData
New selector: getRemovalPromptData

* Add missing @return to displayRemovalPrompt

* Tweak modal copy per feedback

* Turns out private selectors are attached to the thunk proxy!

Well that was easy. :)

* Don't export the new reducers

* Fix tests

---------

Co-authored-by: Miguel Fonseca <[email protected]>
sethrubenstein pushed a commit to pewresearch/gutenberg that referenced this pull request Jul 13, 2023
…ordPress#52088)

As a workaround, until WordPress#39632 is merged, make sure that private actions
and selectors can be unlocked from the original store descriptor (the
one created by `createReduxStore`) and not just the one registered in
the default registry (created by `registerStore`).

Without this workaround, specific setups will unexpectedly fail, such as
the action tests in the `reusable-blocks` package, due to the way that
those tests create their own registries in which they register stores
like `block-editor`.

Context: WordPress#51145 (comment)

Props jsnajdr
tellthemachines pushed a commit that referenced this pull request Jul 17, 2023
…52088)

As a workaround, until #39632 is merged, make sure that private actions
and selectors can be unlocked from the original store descriptor (the
one created by `createReduxStore`) and not just the one registered in
the default registry (created by `registerStore`).

Without this workaround, specific setups will unexpectedly fail, such as
the action tests in the `reusable-blocks` package, due to the way that
those tests create their own registries in which they register stores
like `block-editor`.

Context: #51145 (comment)

Props jsnajdr
tellthemachines pushed a commit that referenced this pull request Jul 17, 2023
* Block removal prompt: let consumers pass their own rules

Following up on #51145, this untangles `edit-site` from `block-editor`
by removing the hard-coded set of rules `blockTypePromptMessages` from
the generic `BlockRemovalWarningModal` component. Rules are now to be
passed to that component by whichever block editor is using it.

Names and comments have been updated accordingly and improved.

* Site editor: Add e2e test for block removal prompt
ramonjd added a commit that referenced this pull request Jul 18, 2023
* Try restoring the site editor animation (#51956)

* Try restoring the site editor animation

* fix header animation

* Remove accidental addition of layout prop

* tidy up formatting

* fix animate presence issue

* Fix animation between sidebar view and distraction free edit view

* Leave sidebar present and maintain canvas to
sidebar animation

The sidebar is necessary for routing on mobile so we have to maintain its presence in the DOM. Just hiding it isn't enough though, as it is still able to be reached with keyboard tabs and screen readers. Using the relatively new `inert` property disables the element from user interaction, so we add that when we don't want the sidebar to be shown.

* Fix mobile view for pattern library

On Mobile, the canvas mode wasn't being set to edit when using the pattern library. This updates it to use the showSidbar value instead, keeping it in sync with the inert setting.

---------

Co-authored-by: Saxon Fletcher <[email protected]>
Co-authored-by: Jerry Jones <[email protected]>

* Change password input to type text so contents are visible. (#52622)

* Iframe: Silence style compat warnings when in a BlockPreview (#52627)

* Do not autofocus page title field in the Create new page modal dialog. (#52603)

* Use lowercase p in "Manage Patterns" (#52617)

* Remove theme patterns title (#52570)

* Block editor store: also attach private APIs to old store descriptor (#52088)

As a workaround, until #39632 is merged, make sure that private actions
and selectors can be unlocked from the original store descriptor (the
one created by `createReduxStore`) and not just the one registered in
the default registry (created by `registerStore`).

Without this workaround, specific setups will unexpectedly fail, such as
the action tests in the `reusable-blocks` package, due to the way that
those tests create their own registries in which they register stores
like `block-editor`.

Context: #51145 (comment)

Props jsnajdr

* Block removal prompt: let consumers pass their own rules (#51841)

* Block removal prompt: let consumers pass their own rules

Following up on #51145, this untangles `edit-site` from `block-editor`
by removing the hard-coded set of rules `blockTypePromptMessages` from
the generic `BlockRemovalWarningModal` component. Rules are now to be
passed to that component by whichever block editor is using it.

Names and comments have been updated accordingly and improved.

* Site editor: Add e2e test for block removal prompt

* Fix Shift+Tab to Block Toolbar (#52613)

* Fix Shift+Tab to Block Toolbar

* Add changelog entry

* Show warning on removal of Post Template block in the site editor. (#52666)

* Avoid copying global style presets via the styles compatibility hook (#52640)

* i18n: Make the tab labels of `ColorGradientSettingsDropdown` component translatable (#52669)

* Rich Text/Footnotes: fix getRichTextValues for useInnerBlocksProps.save (#52682)

* Rich Text/Footnotes: fix getRichTextValues for useInnerBlocksProps.save

* Address feedback

* Patterns: Remove `reusable` text from menu once rename hint has been dismissed (#52664)

* Show uncategorized patterns on the Editor > Patterns page (#52633)

* Patterns: fix bug with Create Patterns menu not showing in site editor page editing (#52671)

* Pass the root client id into the reusable blocks menu

* Check that clientIds array is defined

* Make check for array item more specific

* Search block: Enqueue view script through block.json (#52552)

* Search block: Enqueue view script through block.json

* Remove extra space

* Use `_get_block_template_file` function and set $area variable. (#52708)

* Use `_get_block_template_file` function and set $area variable.

* Update packages/block-library/src/template-part/index.php

Co-authored-by: Felix Arntz <[email protected]>

---------

Co-authored-by: Felix Arntz <[email protected]>

* Site Editor: Don't allow creating template part on the Patterns page for non-block themes (#52656)

* Don't allow template part to be created on the Patterns page for non-block themes

* Remove unnecessary theme directory name in E2E test

* Change Delete page menu item to Move to trash. (#52641)

* Use relative path internally to include packages in dependencies (#52712)

* Spacing Sizes: Fix zero size (#52711)

* DimensionsPanel: Fix unexpected value decoding/encoding (#52661)

---------

Co-authored-by: Daniel Richards <[email protected]>
Co-authored-by: Saxon Fletcher <[email protected]>
Co-authored-by: Jerry Jones <[email protected]>
Co-authored-by: Robert Anderson <[email protected]>
Co-authored-by: Andrea Fercia <[email protected]>
Co-authored-by: Rich Tabor <[email protected]>
Co-authored-by: James Koster <[email protected]>
Co-authored-by: Miguel Fonseca <[email protected]>
Co-authored-by: Haz <[email protected]>
Co-authored-by: George Mamadashvili <[email protected]>
Co-authored-by: Aki Hamano <[email protected]>
Co-authored-by: Ella <[email protected]>
Co-authored-by: Glen Davies <[email protected]>
Co-authored-by: Carolina Nymark <[email protected]>
Co-authored-by: Petter Walbø Johnsgård <[email protected]>
Co-authored-by: Jonny Harris <[email protected]>
Co-authored-by: Felix Arntz <[email protected]>
Co-authored-by: Ramon <[email protected]>
Co-authored-by: Andrew Serong <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Site Editor Related to the overarching Site Editor (formerly "full site editing")
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

Warn users prior to removal of critical blocks in blog templates
10 participants