Skip to content
This repository has been archived by the owner on Mar 4, 2020. It is now read-only.

feat(accessibility): add behaviours for tabs, tablists and toolbars #83

Merged
merged 32 commits into from
Sep 5, 2018

Conversation

silviuaavram
Copy link
Collaborator

@silviuaavram silviuaavram commented Aug 13, 2018

This PR adds accessibility behaviours for Menus styled as a Tablist or Toolbar, along with examples of use.
As a side task, I've added the possibility to handle props in the component from the accessibility behaviour. I did it to be able to add attributes like 'aria-label' to other parts of the component other than the default. For instance, in the Menu case, I want to add the 'aria-label' passed as a prop to the component into the <a> element instead of the <li>.

},
anchor: {
role: 'tab',
'aria-selected': !!props['active'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you won't need double exclamation mark here, it can be just set to props['active'] in string

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please test this with screen readers - double exclamation marks will turn anything into true/false. What is the behavior described by ARIA best practices? Should the screen reader always read "not selected" when you navigate to a tab that is not selected? or should it just read "selected" on the tab that is selected? if latter is the preferred way, I think it should be props['active'] ? true : undefined

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was tested with both double exclamation and without. It works in both cases, reason why I added is because of ambiguity. there is also a double exclamation for SelectableListItemBehavior, 'aria-selected': !!props['active'], . How should we go consistent?

Aria states we should have true and false as values. VoiceOver screen reader will not read 'not selected' if we have "false", it will just ignore it. ChromeVox, however, will read it as 'not selected'. Afaik we support VoiceOver only, but even with ChromeVox behaviour should be ok, it's just a different way to read it, and Aria accepts it too. https://www.w3.org/TR/wai-aria-practices/examples/tabs/tabs-1/tabs.html

root: {
role: 'toolbar',
'aria-label': props['aria-label'],
'aria-disabled': !!props['disabled'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here regarding exclamation marks

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar to tab behavior. wouldn't the screen reader with this read 'not disabled' each time user navigates to a button? note that different readers might read it differently.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will remove the double excl marks if we decide to be consistent with that approach.

attributes: {
root: {
role: 'tablist',
'aria-label': props['aria-label'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is not needed. It will come out of the box through the rest/unhandledProps.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will remove it, thanks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, this behavior (where rest or unhandledProps are passed down the components' tree) is not guaranteed by consumer of accessibility module. If we would like accesibility module being an independent unit, these lines should be here

attributes: {
root: {
role: 'toolbar',
'aria-label': props['aria-label'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is not needed. It will come out of the box through the rest/unhandledProps.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this one too

@@ -12,3 +12,6 @@ export { default as InputBehavior } from './Behaviors/Input/InputBehavior'
export { default as TreeBehavior } from './Behaviors/Tree/TreeBehavior'
export { default as TreeItemBehavior } from './Behaviors/Tree/TreeItemBehavior'
export { default as GroupBehavior } from './Behaviors/Tree/GroupBehavior'
export { default as TabBehavior } from './Behaviors/Tab/TabBehavior'
export { default as TabListBehavior } from './Behaviors/Tab/TabListBehavior'
export { default as ToolbarBehavior } from './Behaviors/Toolbar/ToolbarBehavior'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we also need a ToolbarButton behavior? Menu.Item renders a link with role='menuitem'. Can you please test with screen readers as well?

Copy link
Collaborator Author

@silviuaavram silviuaavram Aug 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aria does not specify any custom role for the menu items themselves when it comes to toolbar. only on the container itself.

However in the example https://www.w3.org/TR/wai-aria-practices/examples/toolbar/toolbar.html they use Buttons as menu items, and we use links by default. It is the reason I used component as child and it was fine, <li> then <button>.
But now, with latest master, I have an anchor tag that has a button as child. So it's <li> <a> <button> which means double focus. And it's kind of incorrect as far as semantics are concerned.
I've tried a trick, and replaced Buttons with Icons, and it works. Icons now are acting as anchors. Should we leave it like this?

Another way is to override the anchor tag from the rendering MenuItem altogether and replace it with the button. Don't know if it's possible right now. I can check. What do you think?

@@ -49,6 +49,16 @@ const Variations = () => (
description="A vertical menu may take the size of its container."
examplePath="components/Menu/Variations/MenuExampleVerticalFluid"
/>
<ComponentExample
title="Menu as a Tab List"
description="A menu that acts as a tablist."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's change the description to "A menu with TabList accessibility behavior" and the one below to "A menu with toolbar accessibility behavior"

@@ -46,6 +46,9 @@ class Menu extends AutoControlledComponent<any, any> {

/** Accessibility behavior if overridden by the user. */
accessibility: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),

/** Accessibility label that will be added by the accessibility behavior. */
'aria-label': PropTypes.string,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we still need this? and line 71

@codecov
Copy link

codecov bot commented Aug 15, 2018

Codecov Report

Merging #83 into master will not change coverage.
The diff coverage is 100%.

Impacted file tree graph

@@           Coverage Diff           @@
##           master      #83   +/-   ##
=======================================
  Coverage   67.82%   67.82%           
=======================================
  Files         101      101           
  Lines        1386     1386           
  Branches      275      265   -10     
=======================================
  Hits          940      940           
  Misses        443      443           
  Partials        3        3
Impacted Files Coverage Δ
src/components/Menu/MenuItem.tsx 92.3% <ø> (ø) ⬆️
src/index.ts 100% <100%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 57eb518...61bff62. Read the comment docs.

@kuzhelov
Copy link
Contributor

@silviuavram, may I, please, kindly ask you to update the description of this PR, so that it would be easier to onboard more reviewers :) Thanks!

@kuzhelov kuzhelov added needs author feedback Author's opinion is asked 🚧 WIP labels Aug 22, 2018
@silviuaavram silviuaavram force-pushed the siavram-tablist-behaviors branch 2 times, most recently from 7729e06 to efc818c Compare August 28, 2018 09:58
@@ -31,6 +31,9 @@ class MenuItem extends UIComponent<any, any> {
/** Shorthand for primary content. */
content: customPropTypes.contentShorthand,

/** A button can show it is currently unable to be interacted with. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

button -> menu item

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copy-paste done wrong. will fix it.


const disabledFlags = [true, false]
disabledFlags.forEach(disabledValue => {
test(`aria-disabled should be ${disabledValue} if button disabled prop is ${disabledValue}`, () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems that there is a error here, as we are setting disabled for Menu, not for Button

Copy link
Contributor

@kuzhelov kuzhelov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to address problem of coupling rendering function with accessibility - lets rethink originally taken 'handledProps' approach, see related comment

@kuzhelov kuzhelov added the needs author changes Author needs to implement changes before merge label Aug 30, 2018
'aria-labelledby': props['aria-labelledby'],
},
},
handledProps: ['aria-label', 'aria-labelledby'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aria-disabled should be declared as handled one as well, related change to anchor's attribute value should be introduced:

anchor: {
     ...
      'aria-disabled': props['disabled'] || props['aria-disabled'],
      ...
    },

'aria-controls': props['aria-controls'],
},
},
handledProps: ['aria-label', 'aria-labelledby', 'aria-controls'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aria-selected should be introduced as handled one, corresponding changes to attributes logic should be provided as well:

anchor: {
      ...
      'aria-selected': props['active'] || props['aria-selected'],
      ...
    },

},
},
handledProps: ['aria-label', 'aria-labelledby', 'aria-expanded'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove aria-expanded if you are removing it from anchor part

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed this, wanted at first to add the same override as in tab and toolbar but I found that submenuOpened is probably a leftover as it's not being used. will remove this as well

{ key: 'editorials', content: 'Editorials', accessibility: TabBehavior },
{ key: 'review', content: 'Reviews', accessibility: TabBehavior },
{ key: 'events', content: 'Upcoming Events', accessibility: TabBehavior },
]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the accessibility prop always be the same across all MenuItems inside one Menu? If this is the case, we can just apply the accessibility prop to the Menu and provide the same as defaultProperty to all MenuItems inside the Menu. Take a look into the renderItem method in the Menu and decide if we should do this in the similar way as the other common props are delegated to the MenuItem components.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, agree - we've discussed this moment, and agreed that it will be addressed by means of separate PR. For sure, there shouldn't be a mandatory requirement for consumer to specify this thing for menu items (especially given that those menu item objects could come as result of deserialising some HTTP response data, and at that moment there could be no context provided for them about which behavior Menu is using).

The way it could be achieved is by introducing behaviors property to Accessibility Behavior:

const behavior = {
  attributes: { .. },
  behaviors: { item: TabBehavior }
}

We should create and address RFC (issue) for that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That a good point! Just one thing to keep in mind, that we should leave the possibility to set different behaviors for Items (for e.g. case when one MenuItem has a submenu, others - don't).
So, in Menu, we can set default behaviors for items, unless a user has already set it.

Copy link
Contributor

@kuzhelov kuzhelov Sep 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes - but this option is always opened to us, as it is still possible to explicitly specify behavior for each individual menu item via accessibility prop:

<Menu items=[
   ...
   { key: '...', content: '...', accessibility: CustomBehavior }
]

Here we are touching just the fact that some reasonable default behavior should be applied to Menu/List items - without limiting the set of cases covered now.

@kuzhelov
Copy link
Contributor

kuzhelov commented Sep 4, 2018

it would be great if @levithomason would take a look on this PR as well

@kuzhelov kuzhelov changed the title Added behaviours for tabs, tablists and toolbars feat(accessibility): add behaviours for tabs, tablists and toolbars Sep 4, 2018
@kuzhelov kuzhelov added 🚀 ready for review and removed needs author changes Author needs to implement changes before merge labels Sep 4, 2018
@kuzhelov kuzhelov merged commit e56b62c into master Sep 5, 2018
@kuzhelov kuzhelov deleted the siavram-tablist-behaviors branch November 19, 2018 16:00
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants