Add Table of Contents block (dynamic rendering + hooks version)#21234
Add Table of Contents block (dynamic rendering + hooks version)#21234ZebulanStanphill merged 6 commits intomasterfrom
Conversation
|
Size Change: +1.65 kB (0%) Total Size: 1.38 MB
ℹ️ View Unchanged
|
41a5891 to
16b9988
Compare
16b9988 to
b5a277b
Compare
4147d65 to
27ce31b
Compare
|
I have no idea what's causing the test failure. |
|
Looks like undo doesn't work. I set up a hierarchy of headers and then deleted one, saw the contents update, then pressed undo and it didn't come back again (it seems to quickly appear then get deleted again). Maybe the BTW your test failure may be related to this. |
b3f796a to
e7b7c84
Compare
|
@ZebulanStanphill Confirmed, that fixed it. There is some weird behavior with the editor overall selecting blocks that is making testing difficult. It is unrelated to this PR, so hard to know if you should merge before or just wait until the larger issue is resolved. I'll hold off on approving and see if it gets resolved quickly and retest again. |
|
There was an unrelated React Native test failure, so I rebased again. @mkaz Glad to hear the weird selection issue is finally fixed! 😄 The items in the table of contents will only be linked if the corresponding heading has an anchor id. It would be technically convoluted (and perhaps even un-block-like) for the Table of Contents block to modify other blocks on the page. For this reason, I think that the Heading block should be updated to create its own anchor id by default (rather than requiring a user to set one); but that is a task for another PR. With that issue out of the way, there's one more thing I'd like to settle just to be completely safe: Should this block be called "Table of Contents" or "Document Outline"? We already have a piece of UI in the block editor that displays a hierarchical list of headings... and it's called a "Document Outline". I also wonder if "Table of Contents" might be a confusing name if people confuse it with the Table block? But on the other hand, "Table of Contents" is probably a more common term than "Document Outline". Whatever we go with, I think we ought to use the same terminology everywhere. So if the block stays as-is, the "Document Outline" tool should be renamed. And if the tool stays as-is, the block should be renamed. What do you think? |
|
I just did some testing myself, and it seems the selection bug is still there, but it's a lot better now. The most reliable way I've found to reproduce it is to select the Table of Contents block, click outside the block, and then quickly click the Table of Contents block again. Interestingly, doing the same thing on a Heading block produced a console error, though the Heading block seemed to be selected without a problem: Still, I think the behavior is good enough to merge now. |
mkaz
left a comment
There was a problem hiding this comment.
Thanks for the update and explanations around links. I agree one block shouldn't alter the other and the next step to add default id for heading blocks. Is there a ticket already for this?
I prefer Table of Contents, I don't think it will be confused with Table block, and I think it is more common and what people will look for with this block. I'm indifferent on renaming the "Document Outline" part, makes sense for consistency and can also be done in another ticket.
So with all that... 🚢 🚀
Copy changes from pull request #15426 (#15426). Adds Table of Contents block to the editor. Code contributions in this commit entirely made by ashwin-pc, originally based on the "Guidepost" block by sorta brilliant (https://sortabrilliant.com/guidepost/). Apply polish suggestions from code review. Improve variable names. Add comment Get rid of autosync (users should now convert to list if they want to edit the contents) Add ability to transform into list; remove unused ListLevel props Update table-of-contents block test configuration Simplify expression Remove unused function Remove unused styles. Rename TOCEdit to TableOfContentsEdit Apply suggestions from code review Remove non-existent import Make imports explicit Remove unused function Change unsubscribe function to class property Change JSON.stringify comparison to Lodash's isEqual Turns out refresh() is required Remove unnecessary state setting Don't change state on save Change behaviour to only add links if there are anchors specified by the user Newline Replace anchor with explicit key in map since anchor can now sometimes be empty Update test data Update packages/block-library/src/table-of-contents/block.json Rename ListLevel to ListItem for clarity and polish. Co-authored-by: ashwin-pc <ashwinpc1993@gmail.com> Co-authored-by: Daniel Richards <daniel.p.richards@gmail.com> Co-authored-by: Zebulan Stanphill <zebulanstanphill@protonmail.com> Co-authored-by: JR Tashjian <jrtashjian@gmail.com>
…e of core blocks. DRY out usage of list item class name. Improve handling of heading attributes. Add proper placeholder state in editor. Fix mistakes, improve naming/description, and polish code. Add support for page breaks in a Classic block. Always ignore empty headings. Various performance improvements. Change List block conversion to be a button in the toolbar. (It can't use the transform API since it uses dynamic data.) Remove unused key from hierarchical heading list.
Co-authored-by: Joen A <1204802+jasmussen@users.noreply.github.com>
Yes, a pretty old one even: #6182. I decided to make 2 last-minute changes before merge. First, I changed one of the search keywords from "outline" to "document outline" to ensure that searching that full term will bring up this block. Second, I added @jrtashjian to the initial commit author list, since he worked on the original Guidepost plugin that served as the basis for @ashwin-pc's PR, which in turn served as the basis for @SeanDS's PR, which served as the basis for mine. And so, after two and a half years of development, the Gutenberg block editor finally has a core Table of Contents block. To everyone who helped make this possible: thank you. ❤️ |
|
Thank you to everyone involved! |
|
Two and half years of waiting! I'm looking forward to Gutenberg 10.2 🎉 |
|
I'm happy to see this get added into core and appreciate the kudos! Thanks all who were involved 🚀 |
|
Amazing work @ZebulanStanphill! I had no idea it would take so much effort to get this working, and there's no way I could have pulled it off the way you did here so I'm really happy you adopted it. Looking forward to this landing in core! |
|
I am using the YOAST TOC block. It is great to see it is arriving at the Core. I encountered an issue with the YOAST block that might affect your implementation as well. I thought I might mention it: Example: I guess that the best solution is: when the TOC block displays on an archive page - make its links absolute? |
|
Thanks @ZebulanStanphill ! Looking forward to the feature. |
|
thanks everyone |
|
It would be nice if it also supported numbered list, in addition to the bulleted list. :) |
|
When will this feature hit the mainline wordpress release? Do we need to wait for a new wordpress version or can we install 10.2 earlier? The milestone is 9 days overdue, so I expect a release soon? |
|
I've got a refactor PR for the Table of Contents block (#29739), but I'm running into some infinite-loop issues and I need some help to fix them. |
|
Has any further work been done on this? |
|
WordPress version 6.5.3 does not have a table of contents block, I recommend adding a Table of Contents block to WordPress 6.6 core to quickly create a table of contents based on all the headings on a page. From then on I won't need to install the Table of Contents Plugin anymore. |


Description
Closes #11047. Related: #22874.
This is a derivative of #21040 (which itself is a derivative of #15426) that has been massively refactored to support h1-h6 elements anywhere in the content (not just core Heading blocks), as well as paginated posts.
Big props to @SeanDS, @ashwin-pc, sorta brilliant, and everyone else who has helped make this block a reality! 😄
Screenshots
Implementation notes
There are some notable quirks about the implementation I should point out:
First, both the front-end and editor implementations creates temporary HTML documents (or unattached
<div>s in the case of the editor) in memory during the render process, just so they can be filled with the current post's content and parsed using DOM APIs to determine how manyh1-h6tags and<!--nextpage-->comments there are in the current post. This feels hacky and sounds like it could be a performance issue, but in practice I haven't noticed any serious performance impact, and I am not aware of a better solution.Second, when using the "Only include current page" option with a paginated post, the editor implementation is technically not able to determine which page a Table of Contents block will appear on with 100% accuracy. It only accounts for
<!--nextpage-->comments that occur in a Page Break or Classic block. The front-end implementation does not have this imperfection.To fix this using the currently available APIs would require searching the content of every block preceding the Table of Contents block every time the editor content changes. As it is, it currently only scans the content of Classic blocks, and the content of Page Break blocks don't have to be scanned since, well, just knowing they are a Page Break block already tells you all you need to know.
However, I doubt this slight inconsistency would be an issue in practice. I can't think of a situation where someone wouldn't just use the Page Break block when creating new content, and old content will all be in the Classic block. It would be trivial to add support for page breaks within Custom HTML blocks, but that seems like an anti-pattern to me.
It's also worth noting that I scan the Classic block's content for page breaks by reading its
contentattribute. It's not possible to support every block through this method since 3rd party blocks could use a differently-named attribute to store their text/html content, and I can't predict what the content attribute will be called. To support any given block would require getting the blocks' inner HTML... and I'm not aware of any API to do that from the editor.Technical feedback on any of this is very welcome. There has been some discussion in #22874 about a document-outline/headings API to provide a method of getting the required data without constantly scanning raw HTML for different elements/comments, but I think this PR in its current state may be good enough as-is.
Todo