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

Table of Contents: Try storing in post-meta #54224

Closed
wants to merge 16 commits into from

Conversation

Mamaduka
Copy link
Member

@Mamaduka Mamaduka commented Sep 6, 2023

What?

Part of #42229.
Resolves #41031.
Resolves #43595.

PR changes the storing mechanism for ToC block headings from attribute to post meta. This opens up the possibility of rendering the ToC block outside of post content (see #41173) and fixes a block's "undo quirk" issue.

The dynamic rendering allows us to skip storing permalinks in DB, which resolves #43595 and should make migrating content with the ToC block easier. You don't need to replace domains in serialized content.

Note

I've intentionally omitted post-meta revision handling from this PR. I hope that WP#4859 will also land in WP 6.4.

Why?

  • Undo quirk - The table of contents uses a derived state from the block editor and shouldn't create an undo step. Marking attribute changes as non-persistent conflicted with attribute edit coalescing and breaking the user experience.
  • Permalinks - The page permalinks are only generated after publishing; storing them resulted in stale data.

How?

  • Now that the block uses post meta for storing the derived data, we can easily skip creating the undo step by using the undoIgnore option flag.
  • Permalinks for heading entries are dynamic.

Testing Instructions

  1. Open a page.
  2. Add a Table of Contents blocks.
  3. Add headings of various levels.
  4. Publish the page.
  5. Confirm that ToC links work when viewing the page (See Table of contents block – links don't work on initial page save #43595).
  6. Reload the page in the editor.
  7. Make changes to a Heading block.
  8. Confirm edit coalescing works again for Heading blocks. It doesn't undo changes letter by letter.

Screenshots or screencast

Before

CleanShot.2023-09-06.at.21.45.29.mp4

After

CleanShot.2023-09-06.at.21.46.56.mp4

@Mamaduka Mamaduka added [Type] Bug An existing feature does not function as intended [Priority] High Used to indicate top priority items that need quick attention [Block] Table of contents (experimental) Affects the Table of contents Block labels Sep 6, 2023
@Mamaduka Mamaduka self-assigned this Sep 6, 2023
@github-actions
Copy link

github-actions bot commented Sep 6, 2023

This pull request has changed or added PHP files. Please confirm whether these changes need to be synced to WordPress Core, and therefore featured in the next release of WordPress.

If so, it is recommended to create a new Trac ticket and submit a pull request to the WordPress Core Github repository soon after this pull request is merged.

If you're unsure, you can always ask for help in the #core-editor channel in WordPress Slack.

Thank you! ❤️

View changed files
❔ phpunit/blocks/render-block-table-of-contents-test.php
❔ lib/blocks.php

@github-actions
Copy link

github-actions bot commented Sep 6, 2023

Size Change: +63 B (0%)

Total Size: 1.52 MB

Filename Size Change
build/block-library/index.min.js 204 kB +63 B (0%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 955 B
build/annotations/index.min.js 2.69 kB
build/api-fetch/index.min.js 2.28 kB
build/autop/index.min.js 2.1 kB
build/blob/index.min.js 451 B
build/block-directory/index.min.js 7.01 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.25 kB
build/block-editor/content.css 4.24 kB
build/block-editor/default-editor-styles-rtl.css 381 B
build/block-editor/default-editor-styles.css 381 B
build/block-editor/index.min.js 216 kB
build/block-editor/style-rtl.css 15.1 kB
build/block-editor/style.css 15 kB
build/block-library/blocks/archives/editor-rtl.css 61 B
build/block-library/blocks/archives/editor.css 60 B
build/block-library/blocks/archives/style-rtl.css 90 B
build/block-library/blocks/archives/style.css 90 B
build/block-library/blocks/audio/editor-rtl.css 150 B
build/block-library/blocks/audio/editor.css 150 B
build/block-library/blocks/audio/style-rtl.css 122 B
build/block-library/blocks/audio/style.css 122 B
build/block-library/blocks/audio/theme-rtl.css 126 B
build/block-library/blocks/audio/theme.css 126 B
build/block-library/blocks/avatar/editor-rtl.css 116 B
build/block-library/blocks/avatar/editor.css 116 B
build/block-library/blocks/avatar/style-rtl.css 104 B
build/block-library/blocks/avatar/style.css 104 B
build/block-library/blocks/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 629 B
build/block-library/blocks/button/style.css 628 B
build/block-library/blocks/buttons/editor-rtl.css 337 B
build/block-library/blocks/buttons/editor.css 337 B
build/block-library/blocks/buttons/style-rtl.css 332 B
build/block-library/blocks/buttons/style.css 332 B
build/block-library/blocks/calendar/style-rtl.css 239 B
build/block-library/blocks/calendar/style.css 239 B
build/block-library/blocks/categories/editor-rtl.css 113 B
build/block-library/blocks/categories/editor.css 112 B
build/block-library/blocks/categories/style-rtl.css 124 B
build/block-library/blocks/categories/style.css 124 B
build/block-library/blocks/code/editor-rtl.css 53 B
build/block-library/blocks/code/editor.css 53 B
build/block-library/blocks/code/style-rtl.css 121 B
build/block-library/blocks/code/style.css 121 B
build/block-library/blocks/code/theme-rtl.css 124 B
build/block-library/blocks/code/theme.css 124 B
build/block-library/blocks/columns/editor-rtl.css 108 B
build/block-library/blocks/columns/editor.css 108 B
build/block-library/blocks/columns/style-rtl.css 421 B
build/block-library/blocks/columns/style.css 421 B
build/block-library/blocks/comment-author-avatar/editor-rtl.css 125 B
build/block-library/blocks/comment-author-avatar/editor.css 125 B
build/block-library/blocks/comment-content/style-rtl.css 92 B
build/block-library/blocks/comment-content/style.css 92 B
build/block-library/blocks/comment-template/style-rtl.css 199 B
build/block-library/blocks/comment-template/style.css 198 B
build/block-library/blocks/comments-pagination-numbers/editor-rtl.css 123 B
build/block-library/blocks/comments-pagination-numbers/editor.css 121 B
build/block-library/blocks/comments-pagination/editor-rtl.css 222 B
build/block-library/blocks/comments-pagination/editor.css 209 B
build/block-library/blocks/comments-pagination/style-rtl.css 235 B
build/block-library/blocks/comments-pagination/style.css 231 B
build/block-library/blocks/comments-title/editor-rtl.css 75 B
build/block-library/blocks/comments-title/editor.css 75 B
build/block-library/blocks/comments/editor-rtl.css 840 B
build/block-library/blocks/comments/editor.css 839 B
build/block-library/blocks/comments/style-rtl.css 637 B
build/block-library/blocks/comments/style.css 636 B
build/block-library/blocks/cover/editor-rtl.css 647 B
build/block-library/blocks/cover/editor.css 650 B
build/block-library/blocks/cover/style-rtl.css 1.69 kB
build/block-library/blocks/cover/style.css 1.68 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 98 B
build/block-library/blocks/details/style.css 98 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/style-rtl.css 311 B
build/block-library/blocks/file/style.css 312 B
build/block-library/blocks/file/view.min.js 318 B
build/block-library/blocks/footnotes/style-rtl.css 201 B
build/block-library/blocks/footnotes/style.css 199 B
build/block-library/blocks/freeform/editor-rtl.css 2.61 kB
build/block-library/blocks/freeform/editor.css 2.61 kB
build/block-library/blocks/gallery/editor-rtl.css 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/style-rtl.css 1.42 kB
build/block-library/blocks/image/style.css 1.41 kB
build/block-library/blocks/image/theme-rtl.css 126 B
build/block-library/blocks/image/theme.css 126 B
build/block-library/blocks/image/view-interactivity.min.js 1.83 kB
build/block-library/blocks/latest-comments/style-rtl.css 357 B
build/block-library/blocks/latest-comments/style.css 357 B
build/block-library/blocks/latest-posts/editor-rtl.css 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 505 B
build/block-library/blocks/media-text/style.css 503 B
build/block-library/blocks/more/editor-rtl.css 431 B
build/block-library/blocks/more/editor.css 431 B
build/block-library/blocks/navigation-link/editor-rtl.css 668 B
build/block-library/blocks/navigation-link/editor.css 669 B
build/block-library/blocks/navigation-link/style-rtl.css 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/editor-rtl.css 2.26 kB
build/block-library/blocks/navigation/editor.css 2.26 kB
build/block-library/blocks/navigation/style-rtl.css 2.23 kB
build/block-library/blocks/navigation/style.css 2.22 kB
build/block-library/blocks/navigation/view.min.js 984 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 235 B
build/block-library/blocks/paragraph/editor.css 235 B
build/block-library/blocks/paragraph/style-rtl.css 335 B
build/block-library/blocks/paragraph/style.css 335 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 215 B
build/block-library/blocks/post-navigation-link/style.css 214 B
build/block-library/blocks/post-template/editor-rtl.css 99 B
build/block-library/blocks/post-template/editor.css 98 B
build/block-library/blocks/post-template/style-rtl.css 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 125 B
build/block-library/blocks/preformatted/style.css 125 B
build/block-library/blocks/pullquote/editor-rtl.css 135 B
build/block-library/blocks/pullquote/editor.css 135 B
build/block-library/blocks/pullquote/style-rtl.css 335 B
build/block-library/blocks/pullquote/style.css 335 B
build/block-library/blocks/pullquote/theme-rtl.css 168 B
build/block-library/blocks/pullquote/theme.css 168 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 122 B
build/block-library/blocks/query-pagination-numbers/editor.css 121 B
build/block-library/blocks/query-pagination/editor-rtl.css 221 B
build/block-library/blocks/query-pagination/editor.css 211 B
build/block-library/blocks/query-pagination/style-rtl.css 302 B
build/block-library/blocks/query-pagination/style.css 299 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/query/style-rtl.css 370 B
build/block-library/blocks/query/style.css 368 B
build/block-library/blocks/query/view.min.js 555 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 607 B
build/block-library/blocks/search/style.css 607 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 468 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 204 B
build/block-library/blocks/site-logo/style.css 204 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 682 B
build/block-library/blocks/social-links/editor.css 681 B
build/block-library/blocks/social-links/style-rtl.css 1.44 kB
build/block-library/blocks/social-links/style.css 1.43 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 432 B
build/block-library/blocks/table/editor.css 432 B
build/block-library/blocks/table/style-rtl.css 639 B
build/block-library/blocks/table/style.css 639 B
build/block-library/blocks/table/theme-rtl.css 146 B
build/block-library/blocks/table/theme.css 146 B
build/block-library/blocks/tag-cloud/style-rtl.css 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 185 B
build/block-library/blocks/video/style.css 185 B
build/block-library/blocks/video/theme-rtl.css 126 B
build/block-library/blocks/video/theme.css 126 B
build/block-library/classic-rtl.css 179 B
build/block-library/classic.css 179 B
build/block-library/common-rtl.css 1.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/editor-rtl.css 12.1 kB
build/block-library/editor.css 12.1 kB
build/block-library/elements-rtl.css 54 B
build/block-library/elements.css 54 B
build/block-library/reset-rtl.css 472 B
build/block-library/reset.css 472 B
build/block-library/style-rtl.css 13.9 kB
build/block-library/style.css 13.9 kB
build/block-library/theme-rtl.css 688 B
build/block-library/theme.css 693 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 51.4 kB
build/commands/index.min.js 15.5 kB
build/commands/style-rtl.css 921 B
build/commands/style.css 918 B
build/components/index.min.js 255 kB
build/components/style-rtl.css 11.7 kB
build/components/style.css 11.7 kB
build/compose/index.min.js 12.1 kB
build/core-commands/index.min.js 2.6 kB
build/core-data/index.min.js 16.8 kB
build/customize-widgets/index.min.js 12 kB
build/customize-widgets/style-rtl.css 1.48 kB
build/customize-widgets/style.css 1.48 kB
build/data-controls/index.min.js 640 B
build/data/index.min.js 8.84 kB
build/date/index.min.js 17.8 kB
build/deprecated/index.min.js 451 B
build/dom-ready/index.min.js 324 B
build/dom/index.min.js 4.64 kB
build/edit-post/classic-rtl.css 544 B
build/edit-post/classic.css 545 B
build/edit-post/index.min.js 35.5 kB
build/edit-post/style-rtl.css 7.84 kB
build/edit-post/style.css 7.83 kB
build/edit-site/index.min.js 91.8 kB
build/edit-site/style-rtl.css 13.5 kB
build/edit-site/style.css 13.5 kB
build/edit-widgets/index.min.js 16.9 kB
build/edit-widgets/style-rtl.css 4.8 kB
build/edit-widgets/style.css 4.79 kB
build/editor/index.min.js 45.5 kB
build/editor/style-rtl.css 3.53 kB
build/editor/style.css 3.52 kB
build/element/index.min.js 4.82 kB
build/escape-html/index.min.js 537 B
build/format-library/index.min.js 7.71 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/interactivity/index.min.js 11.3 kB
build/is-shallow-equal/index.min.js 527 B
build/keyboard-shortcuts/index.min.js 1.72 kB
build/keycodes/index.min.js 1.87 kB
build/list-reusable-blocks/index.min.js 2.2 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/nux/index.min.js 1.99 kB
build/nux/style-rtl.css 735 B
build/nux/style.css 732 B
build/patterns/index.min.js 2.7 kB
build/patterns/style-rtl.css 240 B
build/patterns/style.css 240 B
build/plugins/index.min.js 1.79 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 958 B
build/react-i18n/index.min.js 615 B
build/react-refresh-entry/index.min.js 9.47 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.7 kB
build/reusable-blocks/style-rtl.css 243 B
build/reusable-blocks/style.css 243 B
build/rich-text/index.min.js 10.2 kB
build/router/index.min.js 1.78 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.97 kB
build/sync/index.min.js 53.8 kB
build/token-list/index.min.js 582 B
build/url/index.min.js 3.73 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 249 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

@Mamaduka Mamaduka force-pushed the try/toc-store-headings-in-meta branch from a0f7014 to 21f711f Compare September 9, 2023 10:41
@Mamaduka
Copy link
Member Author

Mamaduka commented Sep 9, 2023

cc @WordPress/gutenberg-core

Copy link
Member

@tyxla tyxla 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 this work 👍 Looks solid at a first glance!

My biggest concern is around the alternative scenarios for editing a post's content, and whether in those scenarios we'll maintain an up-to-date ToC post meta with the most recent headings in the content. WDYT?

packages/block-library/src/table-of-contents/index.php Outdated Show resolved Hide resolved
packages/block-library/src/table-of-contents/index.php Outdated Show resolved Hide resolved
packages/block-library/src/table-of-contents/index.php Outdated Show resolved Hide resolved
packages/block-library/src/table-of-contents/index.php Outdated Show resolved Hide resolved

if ( ! empty( $heading['link'] ) ) {
$pagelink = ! empty( $heading['page'] ) ? add_query_arg( 'page', $heading['page'], $permalink ) : $permalink;
$content = '<a class="wp-block-table-of-contents__entry" href="' . esc_url( $pagelink . $heading['link'] ) . '">' . esc_html( $heading['content'] ) . '</a>';
Copy link
Member

Choose a reason for hiding this comment

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

The way we're constructing the link $pagelink . $heading['link'] seems a bit fragile. Should we add some validation that $heading['link'] is correct just in case (for example, has no leading/trailing space, starts with # and contains at least one alphanumeric character afterwards)?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's an autogenerated value and has validation on the client side. Usually, blocks don't revalidate passed values, only checking if a value exists.

packages/block-library/src/table-of-contents/deprecated.js Outdated Show resolved Hide resolved
packages/block-library/src/table-of-contents/hooks.js Outdated Show resolved Hide resolved
get_block_wrapper_attributes(),
block_core_table_of_contents_build_list( $tree )
);
}
Copy link
Member

Choose a reason for hiding this comment

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

There are various ways to edit the content of a post through the API, like using the REST API for example (POST to a /wp/v2/posts/<id>), or a raw wp_update_post() call from a plugin. There the user can provide content (or post_content) with changes to the headings, and from what I understand, these changes won't be reflected in the relevant post meta field. Is that something that's being considered?

Copy link
Member Author

Choose a reason for hiding this comment

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

The same argument could be made for the Footnotes block. It's hard to do anything for similar updates when observers are on the client side.

Copy link
Member

Choose a reason for hiding this comment

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

I definitely don't want to block this PR, however, to be fair, this isn't a good reason IMHO. This only means that we need to consider the same scenarios for the Footnotes block.

Updating a post through:

  • REST API
  • WP CLI
  • save_post hook and wp_update_post() and family
  • SQL query

is a valid post-updating scenario and we should not be skipping any of them. Otherwise, we'll the metadata can end up being obsolete, and I'd bet we don't want that.

Copy link
Member

Choose a reason for hiding this comment

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

My preferences has been saving it in the post content, however @youknowriad and @mtias pointed out that would make it much harder to render them outside of the post content in a template. That was the reason for moving them to post meta. Honestly, if you want to save a post with footnotes through another editor, it will have to know how to handle, insert, order footnotes anyway. ToC block may be different since it’s purely a derivative of the content.

Copy link
Member

@ellatrix ellatrix Sep 14, 2023

Choose a reason for hiding this comment

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

ToC takes post content as input only? Why does it even need post meta? Is that just for caching? Maybe invalidate the cache on save?

Copy link
Member

Choose a reason for hiding this comment

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

What if there are other blocks with headings? Maybe use the tag processor to just get all the headings?

Copy link
Member Author

Choose a reason for hiding this comment

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

What if there are other blocks with headings? Maybe use the tag processor to just get all the headings?

I don't have a historical context on this one, but I think it was a safer assumption that Heading blocks will always be a part of the content structure while other headings that are part of blocks aren't. cc @ZebulanStanphill

Copy link
Member

Choose a reason for hiding this comment

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

that would make it much harder to render them outside of the post content in a template

why is this? if the table of contents is a block it seems like we could quickly grab it with parse_blocks() and handle it elsewhere. that should be faster than making a DB call.

would the [HTML] API be suitable for this use case?

yes and no! I'd love to explore this together. it depends on what we're wanting to do, specifically. if it's simply to discover every H1 through H6 and link to them in order, that should be easy. if it's looking more in depth to determine what should be a header, who knows. let's figure it out.

the HTML Processor is not ready yet for it because it's currently lacking support for list elements and other common tags, and it bails when it encounters unsupported elements, but the Tag Processor can help.

the Tag Processor could even build a map of all id values that are linked within the page with a corresponding <a href="#{id}"> if it mattered.

Copy link
Member

Choose a reason for hiding this comment

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

What if there are other blocks with headings? Maybe use the tag processor to just get all the headings?

I don't have a historical context on this one, but I think it was a safer assumption that Heading blocks will always be a part of the content structure while other headings that are part of blocks aren't. cc @ZebulanStanphill

Actually, here is the reasoning that led to the current implementation lacking support for headings outside of Heading blocks: #29560

I think the idea was that eventually we'd add a dedicated API for handling document heading structure, which would be abstracted from the actual blocks/content used. Combined with the complexities of parsing HTML (my implementation back then relied on libxml), it seemed easier to just support Heading blocks at the time.

Of course, the abstract headings API remains an unimplemented concept, and now we have the HTML Tag Processor to make parsing HTML easier.

With regard to "what should count as a heading", I think it's fairly simple, conceptually: any h2-h6 tag inside the post content is a heading, unless it is inside a nested <article> or <aside> . This would align the most with actual HTML semantics. (As a side note, an <h1> in the content would be ignored since that should only ever be used for the post title, which would be excluded from the table of contents anyway.)

Perhaps a slightly more difficult question would be "how do we determine where the post content is". Presumably, we could just look for the parent <article> to determine the boundaries of where to look for heading elements, or just look at the entire rendered document if on an old theme that doesn't use HTML5.

Copy link
Member

Choose a reason for hiding this comment

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

The Tag Processor should be able to find all H1-H6 elements, but it's not technically capable of knowing if they are inside a nested article or aside. it can be used to approximate that though in the meantime until the HTML Processor is ready for that.

places where it could get off track are not only when we have missing closing tags, but also when we encounter boundaries like the TEMPLATE element, tables, and others, which implicitly close tags that might still be open.

@fabiankaegy

This comment was marked as resolved.

@draganescu
Copy link
Contributor

draganescu commented Sep 11, 2023

I was suspicious of this move but then reviewing how it works and noting we've done the same with footnotes it's 🎉 Most of my comments would be the same with @tyxla in terms of naming etc.

This opens up the possibility of rendering the ToC block outside of post content

One would think given the blocks architecture a rest endpoint would generate the structure on the fly.

Nevertheless it is interesting, though, that one would not expect that the full blown JS app we're runniing in the browser, benefitting of the whole blocks architecture, cannot handle the structure of the currently edited document, and for a measly undo issue we have to move to the serverside. The question is wether we're not crucially sidestepping something solveable (really just asking didn't explore solutions) by building yet another core block that depends on WordPress to work.

Stale permalinks are a pervasive problem across how WordPress operates its block editing. Again, instead of figuring out the way to handle dynamic content we move to make blocks dynamic - which solves the problem but in a very WordPress-y way, not a Gutenberg-y one. Which is fine, but we should really start splitting that block library package in WP Dependent vs WP Agnostic blocks.

@Mamaduka Mamaduka force-pushed the try/toc-store-headings-in-meta branch from 2aa5d26 to c79c8c3 Compare September 12, 2023 14:47
@Mamaduka
Copy link
Member Author

Thanks for the concern, @fabiankaegy! The post and page were just stubs with a @todo comment. The block is now supported by all post types that match the criteria - public, support REST API, and the editor.

Those are good questions, @draganescu. Thanks for voicing them!

We need a better way to work with derived values in the editor. The __unstableMarkNextChangeAsNotPersistent works for most cases, but maybe skipping the history record should be built into the updateBlockAttributes hook.

@github-actions
Copy link

github-actions bot commented Sep 12, 2023

Flaky tests detected in 825578e.
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/6169745903
📝 Reported issues:

@Mamaduka
Copy link
Member Author

Thanks for the detailed review, @tyxla!

My biggest concern is around the alternative scenarios for editing a post's content, and whether in those scenarios we'll maintain an up-to-date ToC post meta with the most recent headings in the content. WDYT?

Are there any other blocks besides this issue? The block is still experimental and won't automatically ship with the following WP release. I could do a follow-up once there's a clear requirement for the ToC/Footnotes block and syncing values when content is programmatically updated.

@tyxla
Copy link
Member

tyxla commented Sep 14, 2023

Are there any other blocks besides this issue? The block is still experimental and won't automatically ship with the following WP release. I could do a follow-up once there's a clear requirement for the ToC/Footnotes block and syncing values when content is programmatically updated.

Although it's a big one, I can't think of any other blocker, and I'd be happy to have it addressed in a follow-up, as long as this block remains experimental.

Before moving forward though, let's take a step back and evaluate all other alternatives. I understand that we can't parse the blocks on the server side (is that confirmed, could you share a bit more details about it?), but there might be other opportunities to grab all the headings from the content when needed. The idea of using the HTML API could be one alternative to explore. I just think we should try out everything before creating a second source of truth that we'll need extra work to maintain and keep in sync.

@kevin940726
Copy link
Member

@aaronrobertshaw @glendaviesnz Just to note that this is conceptually very similar to what we want to achieve for partially synced patterns in #53705. We both want to propagate/derive updates from other blocks to another block's attributes. Perhaps we can draw some inspirations from here when we go back to work on it.

@priethor
Copy link
Contributor

Are there any other blocks besides this issue? The block is still experimental and won't automatically ship with the following WP release

Hey @Mamaduka, what do you mean by not automatically ship? Will it be opt-in in 6.4?

@Mamaduka
Copy link
Member Author

@priethor, blocks marked as experiments aren't included in WP core builds. The ToC will stay experimental until we resolve the main issues.

@Mamaduka Mamaduka added [Status] Blocked Used to indicate that a current effort isn't able to move forward and removed [Priority] High Used to indicate top priority items that need quick attention labels Sep 15, 2023
@burnuser
Copy link

Maybe a look in already working TOC plugins will lead to some inspiration:
SimpleTOC: https://wordpress.org/plugins/simpletoc/
GutenTOC: https://wordpress.org/plugins/gutentoc-advance-table-of-content/

Or generating TOC on the fly when building the webpage:
Easy Table of Contents: https://wordpress.org/plugins/easy-table-of-contents/
Table of Contents Plus: https://wordpress.org/plugins/table-of-contents-plus/

@Mamaduka Mamaduka closed this Oct 9, 2023
@Mamaduka Mamaduka deleted the try/toc-store-headings-in-meta branch October 9, 2023 10:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Block] Table of contents (experimental) Affects the Table of contents Block [Status] Blocked Used to indicate that a current effort isn't able to move forward [Type] Bug An existing feature does not function as intended
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Table of contents block – links don't work on initial page save Table of Contents: fix undo history quirk
10 participants