WordPress / gutenberg

The Block Editor project for WordPress and beyond. Plugin is available from the official repository.
https://wordpress.org/gutenberg/
Other
10.52k stars 4.21k forks source link

Navigation with `TabPanel` #45594

Open tomalec opened 2 years ago

tomalec commented 2 years ago

What problem does this address?

What we have

Currently, the TabPanel component uses buttons, to manage dynamic tabbed content, without chaining the page (URL). This serves well for tiny widgets and toolboxes.

In many WooCommerce extensions, we would like to use the same UI element to navigate between pages and change URL.

So we would like to have an element, that looks the same, and behaves mostly the same, with a tiny difference it

The problem is TabPanel not only does not enable such a feature, it's also not extendable/composable enough to build such a feature on the top ourselves. (It gets a bit worse as we cannot even control/set the selected tab from the outside/as the consumers of the component: https://github.com/WordPress/gutenberg/issues/27371, https://github.com/WordPress/gutenberg/issues/45390)

What we're forced to do

Because of the above, we ended up introducing hacks & workarounds:

IMO, either workaround is fragile, bug-prone, and not sustainable. In one case, we waste a lot of effort to maintain a clone, test it, and eventually, it still may get diverged from the original impairing end-user experience. On the other, we not only have to spend time to introduce this hack, but it may at any point, unexpectedly stop working, as we are not using public API, or desired use-case, but we're tempering with the details of its implementation, which are only a "manifestation" of its state.

What is your proposed solution?

I think it may be improved by different means.

Simplest for the consumers - new mode/component

The easiest for us - plugin/extension developers, would be to get a property to just change modes, like <TabPanel navigation=true>, or another component for such case - <TabNavigation>. But I guess it may be quite an effort to introduce and maintain a new API from @wordpress/components perspective.

Mid-way - make TabPanel extendable

This idea requires further design. But I imagine <tabPanel> could be redesigned (from a software perspective) or split into more composable parts. In a way, I as a consumer could configure or assemble what I need myself.

For example to have an element with just a visual layout and styles, then have an element that decorates a given element as "tab", then another high-level element that takes just JSON tabs setup and assembles it together. This way to achieve <NavigationTabPanel>, we would replace/extend only that high-level element.

Export styles

If @wordpress/components.TabPanel would expose just its styles. We, plugin developers, would still need to "redo" the <NavigationTabPanel>, but this time, we would be able to use exact same styles, reducing the risk of inconsistent look & feel for end customers.

It could be exported as Constructible StyleSheet, plain text, CSS Module, or whatever is usable.


I'm open for any other solution to this problem, or a path to actually use TabPanel for navigation. //cc @ciampo

talldan commented 2 years ago

@tomalec It could be useful to include some screenshots or videos of your hacked solutions.


There have been a few other discussions about TabPanel recently in https://github.com/WordPress/gutenberg/pull/44788 and https://github.com/WordPress/gutenberg/pull/45005, and how it's not very flexible.

or split into more composable parts

This would be my preference, because I think it might allow multiple problems to be solved. Perhaps if the tabs prop is omitted, components can be used for the tab buttons. That would allow more customization of layout and what the buttons do.

There is also the experimental Navigation component (https://wordpress.github.io/gutenberg/?path=/docs/components-experimental-navigation--default), and maybe that's closer to what you're looking for.

aaronrobertshaw commented 2 years ago

Thanks for putting together the detailed issue @tomalec 👍

The easiest for us - plugin/extension developers, would be to get a property to just change modes

Personally, I think we should be wary of this approach. I don't believe a TabPanel should allow for navigation between URLs. Adding a simple prop to effectively toggle between two separate components also sounds like it could overcomplicate what should be a fairly straightforward component.

A dedicated component addressing the desired behaviour, e.g. TabNavigation, would be a better option in my opinion. In the course of achieving that, we still have the opportunity to break down the current TabPanel into some sub-components that could be re-used.

Some extra background on the TabPanel role can be found in these docs. Of particular note is:

Tabs do not act as anchor links to individual panels. While semantic HTML may be coded with the tabs being anchor links navigating to the tab's associated tabpanel, when JavaScript is used to progressively enhance the content to a tabbed interface, the link's default behavior should be prevented.

By that definition of a TabPanel, a navigation=true prop and navigating to different URLs means it wouldn't be a tab panel anymore.

I'm pretty sure I saw this same topic answered recently, and much more eloquently, by @ciampo but I can't put my finger on the issue where that happened. No doubt, he'll have some wisdom to share here 🙂

ciampo commented 2 years ago

Hey @tomalec , thank you for opening this very well-written issue!

I agree that it would be great to have a way to ensure visual consistency between different components — in this case, a tablist + tabpanel component and tab-looking navigation component.

As @talldan mentioned, there have indeed been recent talks about reworking TabPanel in order to add some requested features — my suggestion there is to rewrite the component using ariakit under the hood, which would:

However, as @aaronrobertshaw explained comprehensively above, when working on such components we need to focus on their semantics first. In that sense, a tab panel and a "navigation menu" (i.e. a list of links that cause the window.location to change when clicked) have very different semantics, and I don't think they make sense as variations of the same component.

A dedicated component addressing the desired behaviour, e.g. TabNavigation, would be a better option in my opinion. In the course of achieving that, we still have the opportunity to break down the current TabPanel into some sub-components that could be re-used.

This also feels to me like the best option. We could refactor tab stiles away and apply them to both TabPanel and a new component — that component could be as simple as a Tab (ie. a button with tab styles applied) or a TabNavigation component (in case there's more functionality needed). If we were to continue with this approach, we'd need to understand first the actual requirements for the component, though.

here is also the experimental Navigation component (https://wordpress.github.io/gutenberg/?path=/docs/components-experimental-navigation--default), and maybe that's closer to what you're looking for.

I don't think the Navigation component is a good match for this — it is quite opinionated for creating nested vertical menus (they kind that are found in sidebars) — plus, it's experimental and I don't think we're planning on iterating on it. We instead preferred a different approach to API design and we applied it in the newer Navigator component.

afercia commented 2 years ago

Personally, I think we should be wary of this approach. I don't believe a TabPanel should allow for navigation between URLs.

a tab panel and a "navigation menu" (i.e. a list of links that cause the window.location to change when clicked) have very different semantics, and I don't think they make sense as variations of the same component.

I'd totally agree. Tabs are meant to toggle visibility of content in the page (ie. in the same 'resource'). Navigation brings users to a new resource. It's not just semantics. More importantly, it's about the expected interaction behavior the semantics brings in.

Historically, WordPress hasn't made a great use of 'tab-looking navigation' UIs. Think for example at the WordPress About page. Users see a tab-looking design but what they actually get is a navigation menu. As a user, I would expect that activating one of those tabs would just reveal new content in the page. Instead, I'm unexpectedly brought to a new page. To me, a navigation UI should look like a navigation menu. I'd like to suggest to try to learn from the past and not replicate the same design inconsistency that is in core. Unfortunately, many plugins use to replicate this inconsistency (typically on their settings pages) and style their navigation menus like tabs. I'd tend to think that's not a great design, because form doesn't match function.

We could argue on what 'navigation' and 'new resource' actually mean. Maybe I'm a bit old-fashioned but to me a 'new resource' is a new page (or file). New content that appears within a part of the current page is not a 'new resource' that warrants navigation. Worth also noting that a tab-looking navigation component should also integrate with the browser history API, update the document title accordingly, and announce in some way to screen reader users that a navigation-like interaction occurred. That sounds definitely out of the scope of the TabPanel component.