-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Use TabPanel in edit settings sidebar header (#13587) #16663
Conversation
* Add styles to the component itself * Add aria-label prop to the TabButton * Replace markup with TabPanel component in edit settings sidebar header
Discovered two issues to address:
I ended up refactoring |
Fix style border bug
onSelect={ onSelect } | ||
controlledTabName={ selectedTabName } | ||
> | ||
{ () => { } } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jffng Hallooo! This PR looks great! I was a little confused at this one (at first glance).
What do you think about adjusted the original TabPanel
component's children()
render to do something like...
children && children(selectedTab)
That way, you won't have to pass it in a children
render function that returns null/empty.
P.S. I'm not sure what the current conventions are for handling this flow 😊. The flow I suggested feels little cleaner + less ambiguous
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @ItsJonQ! I agree. We could also set children
's default value to noop
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aww yiss! noop
it up! 🎉
Modify e2e tests to use arrow keys to switch between tabs and select using aria-label Allow settings header to control the active tab
b4542b8
to
bcf46a3
Compare
Thanks @jffng for a fantastic PR, and thanks @ItsJonQ for the follow-up code review. I just tested this and love jumping around with the arrow keys. I did notice some keyboard nav weirdness though. When I was in the tabPanel, and "Document" was focused, hitting I feel like that in-between tab position wants to rest on the "Block" tab, but isn't so it just leaves me hanging. Would love an accessibility review here too! |
@@ -133,9 +133,9 @@ The class to add to the active tab | |||
- Required: No | |||
- Default: `is-active` | |||
|
|||
#### initialTabName | |||
#### controlledTabName |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a breaking change and can't be landed as is. It needs to provide a way which ensures that this code is backward compatible with a solid deprecation strategy which informs developers that there is a new way to handle this prop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the review @gziolo, noted.
I added support for the initialTabName
for backwards compatibility, as well deprecation notes in the README. However, I lack context as to the timing / version to officially deprecate this prop.
This is also assuming that having a prop to control the active tab from a parent component is a good idea / not an anti-pattern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we don't want to keep this param, we should rather remove it from docs and ensure that when used it works as close as possible as the new one.
@@ -0,0 +1,46 @@ | |||
.components-tab-panel__tabs { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mapk are we fine with these styles to be the default look outside of the context of sidebar and WordPress in general?
No, nice catch @mapk. The component was rendering an empty container where the tab panel content would normally go (we're not actually using it in this case), and this empty container was receiving the focus in that in-between state. I pushed a fix that should resolve it, making the tab panel content optional.
Yes! |
} | ||
}; | ||
|
||
useEffect( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you consider adding onSelect
to the objects passed to tabs
instead? It might simplify the solution as you could leave the whole logic which reasons which tab is selected to TabPanel
component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had considered it, but avoided that route as I was originally trying to minimize changes to TabPanel
. I agree adding an onSelect
method to the tab objects would simplify the solution. (We would still need to support the onSelect
component handler for backwards compatibility)
The issue that led me to refactor TabPanel
was the need to control the selected tab from the parent component without triggering a re-render of the entire component, causing the active tab to lose focus.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue that led me to refactor
TabPanel
was the need to control the selected tab from the parent component without triggering a re-render of the entire component, causing the active tab to lose focus.
Shouldn't the component ensure that there is never focus loss? 3rd party developers shouldn't care about it, it should be baked in into such component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will think about that, thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe I was able to simplify the logic, please let me know if these changes address your feedback @gziolo, thanks.
Adjust test to three tab presses to navigate the block settings instead of four
796cb66
to
8733d7d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jffng, the changes applied to PR look great. This is exactly what I would see as someone who consumes this component. Everything is handled behind the scenes and you don't need to worry about all the logic which handles navigation between tabs.
</ul> | ||
<TabPanel | ||
tabs={ tabs } | ||
controlledTabName={ sidebarName + '__panel-tab' } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it need to be explicitly set from outside or is it possible to append __panel-tab
part internally in the TabPanel
implementation?
// translators: ARIA label for the Settings Sidebar tab, selected. | ||
[ __( 'Block (selected)' ), 'is-active' ] : | ||
__( 'Block (selected)' ) : |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe, the part which informs about the selected tab could be constructed inside the TabPanel
from now on. It seems like we only append (selected)
when a tab is selected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice suggestion, this simplifies the settings header logic even further.
packages/edit-post/src/components/sidebar/settings-header/index.js
Outdated
Show resolved
Hide resolved
15ca57e
to
84ab486
Compare
@drw158 - how do you feel about the changes proposed in the context of the audit that you performed and documented in #16710? As far as I see, |
@gziolo I'm leaning towards either structure as shown in Reakit or Reach UI, especially if we rename our current Panel component. <TabList {...tab} aria-label="My tabs">
<Tab {...tab} stopId="tab1">
Tab 1
</Tab>
<Tab {...tab} stopId="tab2" disabled>
Tab 2
</Tab>
<Tab {...tab} stopId="tab3">
Tab 3
</Tab>
</TabList>
<TabPanel {...tab} stopId="tab1">
Tab 1
</TabPanel>
<TabPanel {...tab} stopId="tab2">
Tab 2
</TabPanel>
<TabPanel {...tab} stopId="tab3">
Tab 3
</TabPanel> |
Description
This PR replaces the sidebar settings header with
TabPanel
from the components package.No visual change, but a slight behavioral change in the keyboard navigation: only the active tab is placed in the tab order, and the user can navigate between tabs using the arrows.
This follows the WAI-ARIA recommendation on keyboard interaction for tab panel components:
How has this been tested?
Tested in the playground and editor on:
macOS 10.14.5
Chrome 75.0.3770.100
Safari 12.1.1
Firefox 67.0.4
Types of changes
Addresses #13587
Checklist: