Skip to content

Tabs: consider simplifying edge case handling #66011

@ciampo

Description

@ciampo

The private Tabs component has a bunch of custom logic that handles a few edge cases:

  1. Initial tab selection:
    • if a defaultTabId is specified, Tabs will select an initial tab only if there is a tab matching defaultTabId. If there isn't a matching tab, Tabs won't have an initially selected tab. If a tab with a matching id is added lazily, Tabs will select it.
    • the initial tab is also used as a fallback when the currently selected tab can't be found anymore. If the initial tab also can't be found, the Tabs component won't have any selected tab;
    • This bunch of logic only applied in uncontrolled mode — the idea being that a consumer controlling the component is in charge of handling these edge cases;
  2. The currently selected tab becomes disabled:
    • the main assumption is that a disabled tab cannot stay as the selected tab;
    • In controlled mode, Tabs assumes that the consumer of the component is in charge of the situation, and therefore it un-selects the previously active, now disabled tab;
    • In uncontrolled more, if the currently selected tab becomes disabled, Tabs falls back to the defaultTabId if possible. Otherwise, it selects the first enabled tab (if there is one).
  3. In controlled mode, Tabs will reset the active tab id (ie. no tabs are active) if a tab associated to the currently selected ID can't be found
  4. If there is no active tab, fallback to place focus on the first enabled tab, so there is always an active element
  5. It keeps the active id in sync with the currently focused tabs

This custom logic was originally added after observing how the legacy TabPanel component worked, in an effort to cover all edge cases found when using the component in the editor.

As the ariakit library updates and we iterate on the component I am considering removing most (if not all) of this custom logic:

  • All of this custom logic is hard to maintain, and it can sometimes cause new unwanted edge cases or unexpected regressions (like during the latest ariakit update);
  • The difference between controlled vs uncontrolled behaviour is one more layer of complexity;
    -ariakit's latest changes should make sure that a composite widget (like tabs) is always somewhat tabbable, even if the active/selected ID doesn't match an existing DOM element;
  • In general, I think we should delegate the handling of such special cases to the consumer of the component, and keep Tabs focused on providing good tab widgets fundamentals;

My instinct is to remove as much of this code as possible (ideally all of it):

  • review whether the current way of handling these edge cases makes sense at all, or is instead a bad practice;
  • move edge case handling directly where Tabs is consumed, if necessary;

@WordPress/gutenberg-components , I'd like to hear your thoughts on this proposal

Metadata

Metadata

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions