Skip to content
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 composite pattern to improve keyboard navigation on the inserter #23610

Merged
merged 5 commits into from
Jul 15, 2020

Conversation

youknowriad
Copy link
Contributor

The idea is to tab directly between categories and use arrow keys to navigate inside a given category. We could consider "Grid" navigation but I think that's already an improvement over master.

That said, I don't what I'm doing with reakit :) mostly trying to follow the docs, let me know what you think :)

@youknowriad youknowriad added [Type] Enhancement A suggestion for improvement. [Feature] Inserter The main way to insert blocks using the + button in the editing interface Needs Accessibility Feedback Need input from accessibility labels Jul 1, 2020
@youknowriad youknowriad requested review from gziolo and diegohaz July 1, 2020 11:25
@youknowriad youknowriad requested a review from ellatrix as a code owner July 1, 2020 11:25
@youknowriad youknowriad self-assigned this Jul 1, 2020
@youknowriad youknowriad requested review from ItsJonQ and mtias July 1, 2020 11:25
@gziolo gziolo requested a review from talldan July 1, 2020 11:27
@youknowriad youknowriad force-pushed the update/use-composite-in-inserter branch from afc0fad to e5bc4ec Compare July 1, 2020 11:31
@github-actions
Copy link

github-actions bot commented Jul 1, 2020

Size Change: +9.05 kB (0%)

Total Size: 1.15 MB

Filename Size Change
build/annotations/index.js 3.67 kB -1 B
build/api-fetch/index.js 3.39 kB +1 B
build/block-directory/index.js 7.68 kB +4 B (0%)
build/block-editor/index.js 124 kB +9.14 kB (7%) 🔍
build/block-editor/style-rtl.css 10.8 kB +22 B (0%)
build/block-editor/style.css 10.8 kB +24 B (0%)
build/block-library/index.js 132 kB -29 B (0%)
build/block-serialization-default-parser/index.js 1.88 kB -1 B
build/blocks/index.js 48.3 kB +3 B (0%)
build/components/index.js 198 kB -542 B (0%)
build/compose/index.js 9.67 kB +2 B (0%)
build/core-data/index.js 11.5 kB +13 B (0%)
build/data/index.js 8.45 kB -3 B (0%)
build/dom-ready/index.js 568 B -1 B
build/dom/index.js 3.23 kB +1 B
build/edit-navigation/index.js 10.8 kB +16 B (0%)
build/edit-post/index.js 304 kB +80 B (0%)
build/edit-post/style-rtl.css 5.61 kB +20 B (0%)
build/edit-post/style.css 5.61 kB +20 B (0%)
build/edit-site/index.js 16.8 kB +196 B (1%)
build/edit-site/style-rtl.css 3.04 kB +11 B (0%)
build/edit-site/style.css 3.04 kB +12 B (0%)
build/edit-widgets/index.js 9.35 kB -4 B (0%)
build/editor/index.js 45.1 kB +44 B (0%)
build/element/index.js 4.65 kB -1 B
build/hooks/index.js 2.13 kB -1 B
build/html-entities/index.js 621 B -1 B
build/i18n/index.js 3.56 kB +1 B
build/is-shallow-equal/index.js 711 B +2 B (0%)
build/keyboard-shortcuts/index.js 2.51 kB -1 B
build/list-reusable-blocks/index.js 3.12 kB -1 B
build/media-utils/index.js 5.32 kB +3 B (0%)
build/notices/index.js 1.79 kB +1 B
build/nux/index.js 3.4 kB -1 B
build/plugins/index.js 2.56 kB +2 B (0%)
build/primitives/index.js 1.4 kB -1 B
build/redux-routine/index.js 2.85 kB +2 B (0%)
build/rich-text/index.js 13.9 kB +6 B (0%)
build/token-list/index.js 1.27 kB -2 B (0%)
build/url/index.js 4.06 kB +2 B (0%)
build/warning/index.js 1.14 kB +4 B (0%)
ℹ️ View Unchanged
Filename Size Change
build/a11y/index.js 1.14 kB 0 B
build/autop/index.js 2.82 kB 0 B
build/blob/index.js 620 B 0 B
build/block-directory/style-rtl.css 944 B 0 B
build/block-directory/style.css 945 B 0 B
build/block-library/editor-rtl.css 7.6 kB 0 B
build/block-library/editor.css 7.59 kB 0 B
build/block-library/style-rtl.css 7.77 kB 0 B
build/block-library/style.css 7.77 kB 0 B
build/block-library/theme-rtl.css 728 B 0 B
build/block-library/theme.css 729 B 0 B
build/block-serialization-spec-parser/index.js 3.1 kB 0 B
build/components/style-rtl.css 15.9 kB 0 B
build/components/style.css 15.8 kB 0 B
build/data-controls/index.js 1.29 kB 0 B
build/date/index.js 5.38 kB 0 B
build/deprecated/index.js 772 B 0 B
build/edit-navigation/style-rtl.css 1.08 kB 0 B
build/edit-navigation/style.css 1.08 kB 0 B
build/edit-widgets/style-rtl.css 2.45 kB 0 B
build/edit-widgets/style.css 2.45 kB 0 B
build/editor/editor-styles-rtl.css 537 B 0 B
build/editor/editor-styles.css 539 B 0 B
build/editor/style-rtl.css 3.78 kB 0 B
build/editor/style.css 3.78 kB 0 B
build/escape-html/index.js 733 B 0 B
build/format-library/index.js 7.72 kB 0 B
build/format-library/style-rtl.css 547 B 0 B
build/format-library/style.css 548 B 0 B
build/keycodes/index.js 1.94 kB 0 B
build/list-reusable-blocks/style-rtl.css 476 B 0 B
build/list-reusable-blocks/style.css 476 B 0 B
build/nux/style-rtl.css 671 B 0 B
build/nux/style.css 668 B 0 B
build/priority-queue/index.js 789 B 0 B
build/server-side-render/index.js 2.71 kB 0 B
build/shortcode/index.js 1.7 kB 0 B
build/viewport/index.js 1.85 kB 0 B
build/wordcount/index.js 1.17 kB 0 B

compressed-size-action

Copy link
Member

@diegohaz diegohaz left a comment

Choose a reason for hiding this comment

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

Thanks @youknowriad! :)

Made some suggestions (that will probably fix CI as well). The code looks good and https://gutenberg.run/23610 is working great! As you said, it should be better with the grid pattern, but that would require more work, and I agree that a listbox is already an improvement over what we have on master.

<ul role="list" className="block-editor-block-types-list">
<Composite
as="ul"
role="list"
Copy link
Member

Choose a reason for hiding this comment

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

list is not a valid composite role. Screen reader users won't have a hint that they should use arrow keys to navigate through the items, so this would be probably less accessible for some of them. But we can use listbox here.

Suggested change
role="list"
role="listbox"

Also, this components requires either aria-label or aria-labelledby prop. Since the category title element is already in the document, the best option is to pass its ID to the aria-labelledby prop here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've used an aria-label as the alternative would have required a bigger refactor (separate components)

@diegohaz
Copy link
Member

diegohaz commented Jul 2, 2020

Found a bug:

  1. Add some blocks to the post using the block inserter panel. It's going to change their order in the list.
  2. Focus the list again and try to move focus with arrow keys.
  3. The DOM re-order will mess up with their "virtual" order.

This is better explained here. Re-mounting (updating the key prop) the item that just got re-ordered should fix this.

There's another solution here, but it's still unstable, and for some reason it's not working well on Firefox yet.

@youknowriad
Copy link
Contributor Author

Thanks for the feedback @diegohaz I pushed some tweaks, let me know what you think.

/**
* External dependencies
*/
import { Composite, useCompositeState } from 'reakit';
Copy link
Member

Choose a reason for hiding this comment

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

It adds nearly 10kB to the module.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, I was wondering about that. the solution could be to make it part of the @wordpress/components API but then it becomes something we need to ensure BC for. Always a struggle to decide what's best in these situations.

Copy link
Member

@gziolo gziolo Jul 2, 2020

Choose a reason for hiding this comment

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

Let's improve the experience first and see how the performance might get improved later then.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm hoping we can balance these kbits loss by refactoring some of our components to rely on reakit like NavigableToolbar/NavigableContainer...

Copy link
Member

Choose a reason for hiding this comment

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

Yes, it's essentially what Composite does 👍


return (
/*
* Disable reason: The `list` ARIA role is redundant but
* Safari+VoiceOver won't announce the list otherwise.
*/
/* eslint-disable jsx-a11y/no-redundant-roles */
<ul role="list" className="block-editor-block-types-list">
<Composite
Copy link
Member

Choose a reason for hiding this comment

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

Do you think we should expose Composite or maybe create a Listbox component in @wordpress/components? We can live with 10kB extra for now, but I'm sure there is going to be more cases where Reakit can be useful :)

@youknowriad
Copy link
Contributor Author

So the e2e tests are failing here and here's what I see:

  • Basically, when you insert a block (list) using the appender (canvas inserter), the popover is not closed while we expect it to close since the "focus" moves to the canvas.

For whatever reason, the changes on this PR prevent the popover from closing. That said, this is the behavior I see when doing -- --puppeteer-interactive but when I try manually, the popover closes itself properly.

@diegohaz Do you have any idea what might cause this? Do we prevent event bubbling anywhere in composite components (blur events specifically). Thanks.

@diegohaz
Copy link
Member

diegohaz commented Jul 4, 2020

@diegohaz Do you have any idea what might cause this? Do we prevent event bubbling anywhere in composite components (blur events specifically). Thanks.

It shouldn't. I'll take a look into that.

@youknowriad youknowriad force-pushed the update/use-composite-in-inserter branch from a0ae203 to c682202 Compare July 13, 2020 15:30
Copy link
Member

@diegohaz diegohaz left a comment

Choose a reason for hiding this comment

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

After some research, I learned that, according to the WAI-ARIA spec, interactive roles like button aren't allowed inside option elements:

the interaction model conveyed by the listbox role to assistive technologies does not support interacting with elements inside of an option

https://w3c.github.io/aria-practices/#Listbox

Having a button inside of an option doesn't seem to affect anything when using a screen reader though. In fact, the button role is just ignored and only its text content is announced. But in order to have a valid accessibility tree, we would have to change the structure to something like this:

<Composite role="listbox"> {/* not a ul */}
  <div className="block-editor-block-types-list__list-item"> {/* not an li, no role="option" */}
    <CompositeItem as={Button} role="option" />
  </div>
</Composite>

Alternatively, as the spec suggests, we could use the grid pattern, but this would require additional work with styling here.

Regarding the e2e test failure, it seems that it's happening because of the key={ orderId }. But it looks like the list of blocks aren't being re-ordered anymore. Is this correct? If so, key isn't needed anymore.

@youknowriad
Copy link
Contributor Author

But it looks like the list of blocks aren't being re-ordered anymore. Is this correct? If so, key isn't needed anymore.

They are if you enable "most used blocks" in the editor options.

@diegohaz
Copy link
Member

Just pushed 506de60. I've added the HTML structural changes that I'd proposed, and also a different approach to re-order items using the composite.unstable_sort() method that I mentioned here. It's gonna be stable in Reakit v1.2.0.

Tried to run e2e tests locally, but got some errors. I think it might be some configuration here, but let's see. Feel free to revert it if it doesn't work out.

@youknowriad
Copy link
Contributor Author

Oh great! Thanks for the update Diego, It looks great. It seems that one of the end2end tests is fixed. I'll be looking at the last failure a bit later.

@youknowriad youknowriad requested a review from ajitbohra as a code owner July 15, 2020 09:20
@youknowriad youknowriad requested review from nerrad and ntwb as code owners July 15, 2020 09:20
@youknowriad
Copy link
Contributor Author

The tests should be fixed now 🎉

@youknowriad youknowriad merged commit d8c7146 into master Jul 15, 2020
@youknowriad youknowriad deleted the update/use-composite-in-inserter branch July 15, 2020 16:44
@youknowriad youknowriad added the Backport to WP 6.7 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta label Jul 15, 2020
@github-actions github-actions bot added this to the Gutenberg 8.6 milestone Jul 15, 2020
@afercia
Copy link
Contributor

afercia commented Jul 15, 2020

I'm not sure listbox is an appropriate pattern.
The interaction described in the ARIA Authoring Practices is pretty different.
For example, the element with role-listbox should be focusable and allow to select the options while it is focused. It's really a different pattern that doesn't fit very well with this use case.

Re: the grid/gridcell pattern, as far as I can tell actual support is very scarce.

@diegohaz
Copy link
Member

@afercia Is a group or list element with roving tabindex an option? This would require some kind of instruction that could be announced with wp.speak() in conjunction with a visual description for keyboard-only users.

@afercia
Copy link
Contributor

afercia commented Jul 15, 2020

I don't know. This needs some more thinking and research.

Aside: I'd really like the grid/gridcell pattern was better supported. It is possible that things have changed with recent progress of assistive technologies. I'd greatly appreciate some help in testing the grid/gridcell pattern across different combinations of browsers and screen readers.

The grid/gridcell pattern would greatly help (if it worked) also in the Media views "grid", where we're looking for a decent, accessible, solution since a few years...

@diegohaz
Copy link
Member

While I'm working on this Grid component, I've been testing the grid pattern a lot, and it seems to be well supported across the most common screen reader and browser combinations. I haven't tested it very deeply with Narrator and on mobile yet though.

I think that the inserter would benefit from the grid pattern as I suggested on #21080 (comment), but I've also seen some pushback in the community on using the grid pattern on things that aren't tabular data. Most notoriously, this recent article (ARIA Grid As an Anti-Pattern), where the author arguments against the use of the layout grid pattern present in the Authoring Practices Guide.

In the comments, we've discussed about how we would communicate to screen reader users this kind of composite interaction without using a composite role like grid.

I still think that the grid pattern plus a visual instruction for keyboard-only users is the safest option here based on the design we currently have.

But, ultimately, this whole discussion seems very opinion based and lacks real user feedback. I wonder if we couldn't make it easier for users to give feedback on accessibility directly from the Gutenberg interface.

@afercia
Copy link
Contributor

afercia commented Jul 19, 2020

@diegohaz thanks for pointing me to yet another interesting article from Adrian Roselli and the conversation in the comments there.

I see it's a case about data-grids vs. layout-grids To me, the definition in the ARIA spec is a bit confusing in the first place, to start with. While it states the grid role "describes relationships among elements" (thus suggesting it's similar to a data table), it then states "It may be used for purposes as simple as grouping a collection of checkboxes or navigation links".

Specifically, I'm not sure I understand how a collection of checkboxes may represent "information whose structure is characterized by a two-dimensional relationship". A collection of checkboxes is just a group of logically related items. Not sure how it could fit in a two-dimensional relationship model. That said, I'm not that interested in a high-level academic discussion and I'm more interested in problem solving 🙂

I'd argue that Adrian recommends to use other mechanisms instead of grid such as:

Disclosure widgets can collapse large swathes of content that are tab-stop minefields. Accordions and tab panels can do the same.

Which is exactly was what removed from the Inserter 🙂 It used to have accordions. Now they're gone. Now there are tons of tab stops to go through the groups of blocks. I'd argue that also for mouse users, having to scroll through a list of more than 70 blocks isn't ideal. And plugins can add more block types to the default ones, making the list even longer. In my opinion, this part of the design change isn't a great improvement, to be fair.

Moving on, I'd like we make an effort to consider the introduction of new patterns in WordPress more holistically. WordPress is not just the block editor. When introducing a new pattern, we should make sure it's good also for other existing use cases in the admin. I think we all agree it's best to provide users with consistent, predictable, patterns instead of forcing them to learn a new, different, pattern each time.

In WordPress there's at least one more case where we still have to solve the two-dimensional navigation issue. I'd call it "spatial navigation" for the sake of brevity and because I liked the old Opera browser versions, where spatial navigation was a remarkable feature.

So how do we enable keyboard and screen reader users to use spatial navigation in the block editor inserter and in the Media library "grid"? Screenshot for reference:

Screenshot 2020-07-19 at 18 54 04

The Media grid does have a "spatial navigation" mechanism but it has some issues. It always worked this way and we haven't been able to fix it in the last 7 (maybe 8?) years.

  • arrow keys allow spatial navigation in the grid
  • the tab key still navigates the media items sequentially, which doesn't solve the "too many tab stops" issue
  • the media grid uses infinite scrolling 😬 See https://core.trac.wordpress.org/ticket/50105

However, the spatial navigation does not work for screen reader users. At least, doesn't work by default and isn't communciated in any way. Worth reminding that Windows screen readers have to main "modes" (their names but that's not relevant). In "browse mode", key presses are not passed to the browser. Spatial navigation won't work. In "focus mode" key presses are passed to the browser. There are also some ARIA roles that trigger a sort of focus mode that would make spatial navigation work.

Same applies to the Inserter: spatial navigation won't work by default for Windows screen reader users.

Users can manually switch between modes but they would need to know they have to do that to be able to navigate spatially. Ideally, the switch should be automatic, as screen readers do when they encounter forms or some specific ARIA roles.

In short: for both the Inserter and the Media grid we need an ARIA role to trigger the switch and to make screen readers pass key presses to the browser.

If the grid/gridcell is not appropriate, then I'm totally fine with not using it 🙂 However, given we can't change the design, we still need an appropriate ARIA role, excluding application which does more harm than good.

I don't know what other ARIA role would be appropriate in our two cases. I'm also wondering if this is a case where a "layout-grid" would be acceptable. Pinging @aardrian to bring him into the conversation 👋 (when you have a chance!)

There's also one more thing that concerns me: excessive use of ARIA. We should really strive to keep things simple instead of inventing complex UIs and then try to remediate with ARIA to make them accessible.

Also, and not a minor concern, we need to remind ourselves that some ARIA patterns aren't well known. Many screen reader users don't get trained in using uncommon ARIA widgets, and some of these patterns are rarely taught.

Aside: in my testing:
Chrome exposes the grid role as grid
Firefox exposes the grid role as table
Is that a known issue?

@ellatrix ellatrix mentioned this pull request Jul 20, 2020
6 tasks
@youknowriad youknowriad removed the Backport to WP 6.7 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta label Jul 22, 2020
@aardrian
Copy link

Coming in way late (too late)...

From @afercia:

I'd argue that Adrian recommends to use other mechanisms instead of grid such as:

Disclosure widgets can collapse large swathes of content that are tab-stop minefields. Accordions and tab panels can do the same.

I was.

I don't know what other ARIA role would be appropriate in our two cases. I'm also wondering if this is a case where a "layout-grid" would be acceptable. Pinging @aardrian to bring him into the conversation 👋 (when you have a chance!)

This would be a custom widget, likely a combination of other patterns (for example, the emoji picker referenced in comments on my post) such as modals, accordions, tabs, etc.

I encourage you to read Sarah Higley's post Grids Part 1: To grid or not to grid and then test with real users.

@diegohaz
Copy link
Member

diegohaz commented Jul 29, 2020

As I understand, the biggest issue with not using a composite role such as grid here is that screen readers will not automatically change to focus mode. Given the current design, I think it's still worth giving grid a try (with a visual hint for keyboard-only users) and testing with real users.

@aardrian
Copy link

As I understand, the biggest issue with not using a composite role such as grid here is that screen readers will not automatically change to focus mode.

Changing modes is one problem. Since grids are by default interactive you need to make a read-only grid (if I understand your use case correctly), and then you as the author need to handle all keyboard interaction. A challenge is when screen readers do not behave as expected by users nor consistently across pairings.

Given the current design, I think it's still worth giving grid a try (with a visual hint for keyboard-only users) and testing with real users.

By all means, give it a shot. Test it with real users (real AT users), but don't deploy anything until you do.

Also, to conform to 3.3.2 Labels or Instructions your visual hint cannot just be for sighted keyboard users. Touch, voice, and even SR users will need those instructions. Until the pattern is established (outside of Wordpress, not just in this UI given the constant onboarding of new authors) I recommend it is always visible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Inserter The main way to insert blocks using the + button in the editing interface Needs Accessibility Feedback Need input from accessibility [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants