Closed chrisdholt closed 4 years ago
I think providing guidance for focusable disabled controls. Seems that both examples they gave is valid and leaving it up to the app author. Do we provide a way currently for our controls to focusable when disabled?
Not all elements in a toolbar are interactive. There could be labels for specific groups or informational elements like status messages.
Do we provide a way currently for our controls to focusable when disabled?
I think we would only do this if given affordance by the spec.
Options in a Listbox
Menu items in a Menu or menu bar
Tab elements in a set of Tabs
Tree items in a Tree View
It is allowable here; should we enforce or allow it to be opt in?
@scomea is going to work on getting this up for review.
Is an "allowFocusOnDisabledChildren" prop a reasonable way to give authors flexibility on the focus disabled question? And what should it default to?
There is a PR out but there is not currently consensus on the mechanism by which the toolbar identifies children that should have focus. There is a desire for a toolbar to accommodate non-focusable items in addition to whatever widgets the author wants to include. The current approach looks at the declared roles of children and compares them to a configurable list of "valid roles" and only adds those to the focus queue. An obvious shortfall is that this does not include semantic types without a formal role attribute (ie. ). So is the right approach to include a base set of html types (button, input, etc..) as also being focusable children? Or something else altogether? Input appreciated.
https://github.com/microsoft/fast-dna/pull/2671
Another option - by default toolbar assumes all children are focusable widgets and provides an "isItemFocusable(itemNode, itemIndex)" callback for authors who wish to include non-focusable children?
Another option - by default toolbar assumes all children are focusable widgets and provides an "isItemFocusable(itemNode, itemIndex)" callback for authors who wish to include non-focusable children?
After looking at #2671 this seems to be a better approach. I am having trouble coming up with suggestions for other approaches.
Another option - by default toolbar assumes all children are focusable widgets and provides an "isItemFocusable(itemNode, itemIndex)" callback for authors who wish to include non-focusable children?
If toolbar assumes all children are focusable will we be applying a tabIndex to all children like we do in ContextMenu? I think the idea of having a way to opt certain children out. So assume they are all focusable and then give a way for authors to opt that particular child out.
I propose that we take a stance on this to not support the secondary navigation methods mentioned above in order to not to complicate or convolute the API from the start.
I agree. In fact, I think the spec doesn't provide for a very consistent implementation, and uses examples that help support the case and carefully avoids examples that conflict. For instance, if we're considering that keyboard interaction is for screen readers, and potentially non-sighted users, then whether it's horizontal or vertical doesn't matter at all. Adjusting whether you use up / down or left / right implies that the visual layout is more important than the function of the toolbar.
Regarding the examples, how to we now handle a vertical toolbar with a number spinner or a horizontal toolbar with a horizontal slider? Are those combinations off limits? In a sense this becomes grid navigation and someone would need to press [space] to activate the control. Not proposing that, but it does seem like we have some cases that won't work without some sort of alternative.
(RE: disabled focus) should we enforce or allow it to be opt in?
I'm in favor of opt-in. Similarly to my previous reply about keyboard direction, as far as the aria spec is concerned, I don't believe discoverability of a feature changes somehow when a control is put into a menu or toolbar or other exception listed within. Also, from a visual perspective, disabled controls are not required to meet any sort of contrast requirements, so allowing them as stops in the tab sequence actually provides more information to a screen reader. Also, keyboard nav is useful for everyone and therefore I would lean toward needing fewer keystrokes to get to actionable options.
This follows the idea of progressive display as well, which is not always favorable, but something that can be determined as part of the app design. For instance, if a toggle turns off a whole section of a settings page by hiding it instead of disabling it, does any potential use of the page (sighted, low vision, AT, etc.) need different information than another?
by default toolbar assumes all children are focusable widgets and provides an "isItemFocusable(itemNode, itemIndex)" callback
How about by default it's focusable if it's enabled, and the callback can allow the page author to change that either way. That is, an enabled element could be determined to be non-focusable and a disabled element could be focusable.
by default toolbar assumes all children are focusable widgets and provides an "isItemFocusable(itemNode, itemIndex)" callback
How about by default it's focusable if it's enabled, and the callback can allow the page author to change that either way. That is, an enabled element could be determined to be non-focusable and a disabled element could be focusable.
I think it would be problematic as well to have to define that text nodes or labels are not focusable. They are valid children here but it seems overly complex if I want to include one that I have to immediately opt it out. It shouldn't be in the tabindex either way, but here I need to do something extra to ensure it doesn't get included.
text nodes or labels are not focusable
I suppose text nodes or labels are considered enabled (as a label for a disabled input would also be "disabled" or at least look like it), but to be more accurate I should have said something like "if it would normally be part of the tab stop". Essentially smart defaults that tab stop on normally stoppable elements as if they weren't in a toolbar, but provide the opportunity to adjust it since they are in the toolbar.
Might be really convenient if that callback provided an easy way to determine other "classes" that might influence the override, like if the item in question is disabled, and you want to allow tab stop on all disabled elements you simply return true.
In spirit, looking for the lightest touch way to get sensible defaults, given my previous comment about not treating elements differently because they are in a toolbar.
I think the first question to answer is this - are we going to get into the business of sniffing roles and types to determine whether an item is focusable or not? The advantage of the callback approach is that we avoid doing that and give developers control. If we are deciding how to treat text nodes we saying that we are going do that sniffing. Note that if we did go with the callback approach we could provide samples of callbacks that actually do role/type sniffing to decide focusability without baking that logic into the component itself.
Needing to support nested elements is a new wrinkle.
I'm thinking we support nested items with a "toolbarItemGroup" component. It would provide developers with a container than can be styled and its children would participate in the toolbar widget navigation queue.
are we going to get into the business of sniffing roles and types to determine whether an item is focusable or not?
If there's no way for the browser to do this for us, and if there's no way to pretend like someone hit tab or shift+tab instead of an arrow key (or send that key press, etc.) I think we have to. I don't think it would be good to give no default behavior and force the consumer to determine what is focusable or not.
I'm not sure why the browser doesn't do more to help with the aria guidance actually. Maybe that's something we could bubble up, but it pretty much guarantees no one is doing it. At least being able to get the role seems required.
support nested elements
Do we need to? This is another reason the browser should handle it, because it's really a dom issue at that point. It seems less useful that someone would create a component specifically for a toolbar as something like a toolbarItemGroup instead of a normal component. It still doesn't full support the composability story because there's no way to put an existing composited component in unless it's also a toolbar group somehow.
If it's just a stylistic separation, a
support nested elements
Do we need to? This is another reason the browser should handle it, because it's really a dom issue at that point. It seems less useful that someone would create a component specifically for a toolbar as something like a toolbarItemGroup instead of a normal component. It still doesn't full support the composability story because there's no way to put an existing composited component in unless it's also a toolbar group somehow.
If it's just a stylistic separation, a should be fine.
I can't really think of how the browser would handle all the variations here and I'm not sure that this it necessarily needs to. I'll add context to the "nested" requirement. Components are complex and the interactive portion of some of our components are nested within UI which exists to style them. An additional example here would be a "Split button" where the component is two buttons within a parent div which exists solely to style them. There is not a split button role - and there is not a reason to bring focus to the wrapper when each individual button should receive focus (a button and a menu button respectively). Additionally, if I want to add additional markup for reflow or styling, etc - I should be able to do that without losing the functionality here. Without being able to do that, I think there's little value to us producing something which requires a flat structure of elements
It seems acceptable to:
I think this is a complex problem that likely requires somewhat of a complex solution, but I think if we can't have nested UI and we need to declare all the things focusable or not, we aren't really providing any value to folks leveraging our library.
"Split button"
Good call. I know we've talked about that one before but I wasn't thinking of it now. I wonder if that's the primary example and if it could be addressed with two separate styled buttons or something. Someone could do anything they wanted, but I can't think of other examples I've seen like this.
Agreed that it's complex and there's probably no generic solution to handle it. Could we do something like traverse a custom components hierarchy and use arrow key navigation for it instead of tab, or perhaps treat a complex component as a separate tab stop? Firefox handles address bar like that, as do some portions of something more complicated like the Office ribbon.
I suppose if the group component helps with that and a consumer can opt-in to it by building something that way there's no harm in it.
"Split button"
Good call. I know we've talked about that one before but I wasn't thinking of it now. I wonder if that's the primary example and if it could be addressed with two separate styled buttons or something. Someone could do anything they wanted, but I can't think of other examples I've seen like this.
Agreed that it's complex and there's probably no generic solution to handle it. Could we do something like traverse a custom components hierarchy and use arrow key navigation for it instead of tab, or perhaps treat a complex component as a separate tab stop? Firefox handles address bar like that, as do some portions of something more complicated like the Office ribbon.
I suppose if the group component helps with that and a consumer can opt-in to it by building something that way there's no harm in it.
I honestly think it's just one example of what could be possible or necessary from a nesting perspective. Arrow key navigation is the defacto here for anything within the tab order, so I don't mean a literally "tab". I more or less mean that we have a ton of components which have inputs nested within divs, etc and I think expecting a flat structure here is just a non-starter. The keyboarding is defined above; I wouldn't expect it to change here necessarily.
To elaborate on my intent above:
In any given webpage, without any special keyboard interaction, a button would be "tabbed to". It is just in the tabindex. In the toolbar, we'll arrow to it and manage it's place in the tabindex (per the spec). If I want to recreate the edit UI from the GH response thing I'm typing into right now, I may want to style that as groups, or for reflow purposes, or...maybe I just love divs and want 2 around my buttons :) - I should be able to drop it into the toolbar and have it work. Likewise, if I include a span or heading element, I shouldn't have to mark it as "disabled" or "ignored" as it's counterintuitive; those are never in the tabindex, so I don't expect to have to opt it out. Hopefully that clarifies the intent.
I think the tappable lib which we already use for modal dialog can resolve the "what is a focusable widget" question adequately.
A draft version of the updated PR is here: https://github.com/microsoft/fast-dna/pull/2690
Closing this issue in favor of tracking via the official WC spec: #3008
Toolbar should be added as a new components in fast-components-react-base.
As defined by the W3C:
Keyboard Interaction: This component should follow the standard set forth by the WAI-ARIA practices specification: https://w3c.github.io/aria-practices/#keyboard-interaction-21
The W3C specification gives the following affordance to toolbar keyboard interactions:
I propose that we take a stance on this to not support the secondary navigation methods mentioned above in order to not to complicate or convolute the API from the start. As we gain feedback on the control, we can look at how we may want to support the above if/when requested.
Implementation: Ideally I think the toolbar control implementation would follow that of Tree View, where the control is able to take children and use a roving tabindex pattern to move between valid elements.
Questions:
Examples