-
Notifications
You must be signed in to change notification settings - Fork 2
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
Add Tab
and TabList
components
#813
Conversation
// Make the ::before occupy space within the button, but make it | ||
// invisible. This ensures that the tab button is wide enough to show | ||
// bolded text even if the visible text is not currently bold. See | ||
// https://css-tricks.com/bold-on-hover-without-the-layout-shift/ |
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.
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.
Clever! 😃
const contentFn = (Component, props = {}) => { | ||
return mount( | ||
<div role="tablist"> | ||
<Component {...props}>This is child content</Component> | ||
</div> | ||
); | ||
}; |
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.
Here and in the tests for TabList
: our automated common and accessibility tests are working well! Accessibility tests will fail if Tab
isn't inside of a tablist
, and TabList
tests will fail if the TabList
doesn't have any tab
children. Sweet.
Codecov Report
@@ Coverage Diff @@
## main #813 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 52 54 +2
Lines 633 649 +16
Branches 225 233 +8
=========================================
+ Hits 633 649 +16
📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
I've gone ahead and pinged both @robertknight and @acelaya for review on this one. Rob more specifically for the keyboard navigation and behavior aspects. |
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.
A couple of minor comments, but looks good 👍🏼
*/ | ||
textContent?: string; | ||
selected?: boolean; | ||
variant?: 'basic'; |
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 understand the intention is to have more variant
s eventually? Maybe I would suggest not adding this property until we have other variants?
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 was on the fence about this. Tell you what: with this UI prototyping, I'm exercising these components quite a lot (it led me to add icon support to Tab
and vertical
support to TabList
). I'm starting to lean toward the component itself doing no styling other than layout. I promise to address this in a follow-up PR.
// Make the ::before occupy space within the button, but make it | ||
// invisible. This ensures that the tab button is wide enough to show | ||
// bolded text even if the visible text is not currently bold. See | ||
// https://css-tricks.com/bold-on-hover-without-the-layout-shift/ |
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.
Clever! 😃
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 implementation fundamentals look good. From a design perspective I think in future we may want to reconsider the lack of any borders between tabs or between tabs and tab panels, as this makes the tab-iness of the control less obvious, at least in certain contexts.
useArrowKeyNavigation(tabListRef, { | ||
selector: 'button', | ||
horizontal: true, | ||
vertical: vertical, |
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.
vertical: vertical, | |
vertical, |
<code>aria-controls</code> | ||
</a>{' '} | ||
attribute to each <code>Tab</code>. This is not always feasible in | ||
our applications. |
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.
My initial reaction to "This is not always feasible in our applications" was "why?". Are we doing something unusual or is there an implementation problem? What are the problems caused by not using this?
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.
Great question! The WAI-ARIA authoring guidelines for a full tabby-widget-y interface assumes a structure like so:
<tablist>
<tab controls="panel1">One</tab>
<tab controls="panel2" aria-selected>Two</tab>
<tab controls="panel3">Three</tab>
</tablist>
<tabpanel id="panel1" style="visibility:hidden">Content for panel One</tabpanel>
<tabpanel id="panel2">Content for panel Two</tabpanel>
<tabpanel id="panel3" style="visibility:hidden">Content for panel Three</tabpanel>
To wit:
- A
tabpanel
for eachtab
- Non-active
tabpanel
s are visually hidden
However, in the sidebar, we have:
<tablist>
<tab>Annotations</tab>
<tab aria-selected>Page Notes</tab>
<tab>Orphans</tab>
</tablist>
<div>Page-note annotation threads</div>
To wit: All three tabs control the same "panel"; we don't have a discrete element "per panel". I don't think it would appropriate to assign the same aria-controls
value to each tab (i.e. the id
of the div
containing threads, which, for clarity, does not currently have an id
attribute). But I could be wrong.
I think this sort of situation is why WAI-ARIA guidelines indicate that there should be an aria-controls
on role="tab"
elements, but does not indicate that there must.
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.
To wit: All three tabs control the same "panel"; we don't have a discrete element "per panel".
Ah, OK. That should be an implementation detail that we are re-using the DOM elements though. From a user's perspective (screen reader or not) I think it would ideally "look" the same as if we were using separate panels. I wonder if there is a blessed way of doing this kind of virtualization. How does aria-controls
actually get used?
Annotations | ||
<span className="relative bottom-[3px] left-[2px] text-[10px]"> | ||
{52} | ||
</span> |
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.
Do you have plans to create a pattern library component for counts associated with a tab? I suspect this may be a common-enough pattern and have enough considerations around design/a11y that require thought to make it worthwhile.
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.
We could do that, sure. It would simplify the acrobatics I did in re textContent
. I'll give it a think.
* button will be sized to accommodate this string in bold text. This can | ||
* prevent tab jiggle. | ||
*/ | ||
textContent?: string; |
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 suppose we could try to simplify the API by determining the textContent
automatically, eg. in a useLayoutEffect
that reads textContent
after render, although that would have a limitation of not being able to account for expected text changes (eg. if counts were not available and become available later), and be a bit more expensive.
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.
Yeah, I'm going to continue to hem and haw on this some. I don't plan to immediately cut a release of the package, so something intelligent might occur to me in the next few days.
For sure. My goal in the "styling" part of this was simply to capture the current pattern as-is. |
This PR adds new
Tab
andTabList
components, and pattern-library documentation for same.Why now: I believe we may wish to use a tabbed interface in upcoming new UI in the client and before building out new tabs, I wanted to:
SelectionTabs
) in the client, and extraction helps to better encapsulate the design pattern vs. theSelectionsTab
-specific logic.TabList
adds arrow-key navigation per https://www.w3.org/WAI/ARIA/apg/patterns/tabpanel/Tab
component takes aim at alleviating it systematically (see documentation ontextContent
for that component).As always with these new-component PRs, the lion's share of the diff is the pattern-library documentation page component. The components themselves were refreshingly simple to put together: I was able to scaffold out and concentrate on the specific problems I wanted to solve (fixing the jiggle, applying keyboard navigation) and documentation. The toolbox is getting more robust and it's starting to pay dividends!
Testing
Check out this branch, run
make dev
and visit the Tabs page in the pattern library