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

New block/tabs #69256

Open
wants to merge 7 commits into
base: trunk
Choose a base branch
from
Open

Conversation

sethrubenstein
Copy link
Contributor

@sethrubenstein sethrubenstein commented Feb 19, 2025

What?

This is Pew Research Center's approach to Tabs in the block editor and would fulfill much of the needs requested in #34079.

Before getting started with testing, some additional work is needed that I'll be following up with in a few weeks but I wanted to get this PR started.

Additional tasks:

  • Finish the default styling.
  • Update the tab label block binding to the latest binding API (I started this months ago).
  • Implement minimal state management for the last active tab by creating a custom WordPress/data store for core/tabs. This will allow core/tab to recognize which block was last selected within its core/tabs parent when the user no longer has either in focus. Currently, when you exit your selection, we only display the first tab.
  • Address accessibility handlers in the editing interface and resolve styling inconsistencies in the editor.
  • Strengthen any accessibility concerns on the frontend.

This builds upon work started by @creativecoder in #63689 and expands on making this concept even more extensible for developers.

The major changes from this original concept are the following:

  • An overhaul of tab editing has been implemented. A slotfill (thanks @fabiankaegy for the idea) is now used to manage the tabs list and individual tab items. This enhances the user experience in two key ways:
      1. Selecting a tab now highlights the entire block, making it easier to start editing.
      1. Perhaps more importantly, we're not monitoring changes to innerblocks content to manage the tabs list, which caused significant performance issues when there were more than 10 tabs. Additionally, with the new slotfill selection method, we no longer render the innerblocks if the tab is not selected. This also improves the overall editing performance of the block.
  • Some attribute changes were made, notably the removal of tabIndex. We now add the tab index to each tab during render time. This change allows for greater programmatic usage of tabs and simplifies the process of copying and pasting new tab panes in the editor.
  • Structural changes to the core/tab block will allow it to degrade more gracefully by enabling the core/tabs block to control all Interactivity API adoption. If a core/tab block is displayed outside of core/tabs, it should simply show the tab content without any issues or modifications.
  • The addition of various tab state colors as attributes and color controls enhances the user interface. Now, there are color controls and simple styles to support tab hover, tab active, tab background, text, and more.
  • The addition of "deep linking" support allows for URL activation of a specific tab for content sharing. For example, adding the tab-hash found in the tab to the URL as ...?activeTabIndex=TXkgc2Vjb25kIHRhYl9fMQ== will open the second tab, as demonstrated in the screencast below.
  • Addition of core/paragraph and core/heading block binding variations to display the block label within each tab.

Why?

As the block editor and the concept of "full site editing" matures, I'm a strong believer that some basic ui concepts and paradigms should be available in the block library. Tabs is one such basic ui concept, dialog/modals are another.

How?

Testing Instructions

  1. Open a post
  2. Add a tabs block
  3. To start, two sample tabs are generated; click the plus icon in the tabs list to create a new tab
  4. Add content
  5. Preview and ensure tabs are selectable.

Testing Instructions for Keyboard

TK, working on keyboard handlers next.

Screenshots or screencast

In WP env:

CleanShot.2025-02-19.at.18.45.08.mp4

This is a screencast of the tabs block in action on the upcoming RLS site. Here you'll see Tabs that are generated dynamically on the server. The tab block is placed and styled in the editor, but it's individual tabs and contents are generated by logic on the server. Making Tabs as extensible as possible was our main goal:

CleanShot.2025-02-19.at.18.51.55.mp4

…finements in the editor interface and overall cleanup.
…ion/element-grouping. Will come back in later this week to add key handlers
…tore for remembering the last active tab when i get back from vacation
Copy link

github-actions bot commented Feb 19, 2025

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: sethrubenstein <[email protected]>
Co-authored-by: luisherranz <[email protected]>
Co-authored-by: fabiankaegy <[email protected]>
Co-authored-by: hanneslsm <[email protected]>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

*/
get isActiveTab() {
const context = getContext();
const { attributes } = getElement();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note to self: Now that I think about it, there’s really no reason to not give each tab iAPI context with its index value instead of relying on getElement.

Copy link
Member

@luisherranz luisherranz Feb 20, 2025

Choose a reason for hiding this comment

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

Either way is fine. The only advantage of using context is that the child elements of the element in question can also access the index/id, whereas with the element's attribute, you need to know if you are in the element or in one of the children.

// using context in both the element and their children
get isActiveTab() {
  const { activeTabIndex, tabIndex } = getContext();
  return activeTabIndex === tabIndex;
}

// using an attribute, scoped to the element
get isActiveTab() {
  const { activeTabIndex } = getContext();
  const { attributes } = getElement();
  return activeTabIndex === attributes['data-tab-index'];
}

// using an attribute, scoped to a child of the element
get isActiveTab() {
  const { activeTabIndex } = getContext();
  const { ref } = getElement();
  return activeTabIndex === ref?.closest('[data-tab-index]').dataset.tabIndex;
}

// or joining both
get isActiveTab() {
  const { activeTabIndex } = getContext();
  const { ref, attributes } = getElement();
  const tabIndex = attributes['data-tab-index'] || ref?.closest('[data-tab-index]').dataset.tabIndex
  return activeTabIndex === tabIndex;
}

Remember also that ref is null during the first hydration (same as in React/Preact), so it usually causes problems for this type of case.

@carolinan carolinan added the New Block Suggestion for a new block label Feb 20, 2025
@t-hamano t-hamano added the [Type] Enhancement A suggestion for improvement. label Feb 20, 2025
@sethrubenstein
Copy link
Contributor Author

Added controls for setting the default starting tab index. The attribute for this resides in core/tabs, but I'm surfacing the control in each tab so it can set it's own index as the attribute value in the parent.

CleanShot.2025-02-20.at.09.17.47.mp4

* Replace them with your own styles or remove the file completely.
*/

.wp-block-prc-block-tabs.wp-block {

Choose a reason for hiding this comment

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

the prc slug should probably be removed.

@hanneslsm
Copy link

hanneslsm commented Feb 20, 2025

Thank you for the amazing work! Looking forward for this new block!

Some feedback:

  1. In the details block we have Type / to add a hidden block as the inserter text. It could make sense to change the text also in the tabs to something like Type / to add a block in the tab.
  2. I'm not a big fan of the block styles and would vote for only adding a very limited amount and remove as many styling as possible. In my opinion this is theme territory and I know that I'd unregister probably all of them first. Especially pills, underlined pills and tabbed outline have very opinionated styles and it will lead to many frustrations when users won't be able to change the styles like border radius. tabbed and underline should be enough for a core block. I'd also rename tabbed to filled to be more descriptive and consistent with the button block (note .is-style-filled is already taken by the button). We could also think about adding a outline style, to be even more consistent with the button block
    3) If no title is set and content added, the block shouldn't be rendered in the front end (ignore, it could actually become very handy having a tab without a label, especially when it's set as default.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
New Block Suggestion for a new block [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants