web-components icon indicating copy to clipboard operation
web-components copied to clipboard

[select] item navigation controls suggested by VoiceOver don't work [0.5 day]

Open tomivirkki opened this issue 3 years ago • 5 comments

What is the problem?

With VoiceOver on

  1. open https://a11y-vaadin-playground.netlify.app/pages/v22/select.html
  2. open the first <vaadin-select> dropdown
  3. See the suggested controls

Screenshot 2021-10-26 at 12 25 59

However, Control-Option-Shift-Down Arrow does nothing

May be that the dropdown/list-box roles aren't correct so a wrong kind of an announcement is shown

Browsers

  • [X] Chrome
  • [ ] Firefox
  • [ ] Safari
  • [ ] Safari on iOS
  • [ ] Edge

Screen Readers

  • [ ] None
  • [ ] NVDA
  • [ ] JAWS
  • [X] VoiceOver on MacOS
  • [ ] VoiceOver on iOS

tomivirkki avatar Oct 26 '21 09:10 tomivirkki

We need to figure out if it is a problem in avatar-group and menu-bar.

yuriy-fix avatar Aug 02 '22 11:08 yuriy-fix

Tested this for other components:

vaadin-avatar-group

Chrome

The role is announced as "group". Also, the VoiceOver cursor does not move to item:

screenshot

chrome-avatar-group

Safari

The role is announced as "text element" (this is expected, as VoiceOver does not annonce role="option", unlike native <option> element). The VoiceOver cursor is moved to the item in the list-box:

screenshot

safari-avatar-group

vaadin-select

Chrome

Same as in vaadin-avatar-group: the VoiceOver cursor remains on the vaadin-select-value-button:

screenshot

chrome-select

Safari

Same as in vaadin-avatar-group: the VoiceOver cursor is moved to the item in the list-box:

screenshot

safari-select

vaadin-menu-bar

This component is not affected, most likely because it's using role="menuitem" which has different item navigation. Also, the VoiceOver cursor is correctly moved to the item in the overlay when using VoiceOver on Chrome.

web-padawan avatar Aug 02 '22 13:08 web-padawan

One more issue about item navigation in Safari: when moving to different item using Control + Shift + Arrow Down and pressing Enter, the wrong item is selected, as the keyboard focus is not in sync with the VoiceOver cursor 😕

https://user-images.githubusercontent.com/10589913/182384389-22051ff2-5930-45f4-bd38-6cb3facdd5c5.mp4

UPD: this problem also exists in the ARIA examples when using VoiceOver on Chrome 🤷‍♂️

web-padawan avatar Aug 02 '22 13:08 web-padawan

Regarding the original issue, I could not get VoiceOver outline working on Chrome. Here are some things that I tried:

  1. Postpone calling this._menuElement.focus() until vaadin-overlay-open event is fired,
  2. Disable animations for vaadin-select-overlay (the way it's done in our visual tests),
  3. Set aria-controls attribute on the vaadin-select-value-button to link it to list-box.

Regardless of those steps, the behavior of the VoiceOver cursor doesn't change:

  1. When pressing Enter, the VoiceOver cursor remains on vaadin-select-value-button
  2. When pressing Arrow Down, the VoiceOver cursor then moves to the whole list-box

https://user-images.githubusercontent.com/10589913/182564309-5e523647-a882-436d-9348-9fb885ad6c89.mp4

web-padawan avatar Aug 03 '22 09:08 web-padawan

Looks like something is wrong with either vaadin-list-box or vaadin-item. Using div with correct roles works. Also, using custom elements that set correct roles (without using tabindex) works fine, too:

menu-button

Code example
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Menu button</title>

    <script type="module">
      import { html, PolymerElement } from '@polymer/polymer';
      import { PositionMixin } from '@vaadin/vaadin-overlay/src/vaadin-overlay-position-mixin.js';
      import { OverlayElement } from '@vaadin/vaadin-overlay';

      customElements.define(
        'menu-button-overlay',
        class extends PositionMixin(OverlayElement) {
          static get is() {
            return 'menu-button-overlay';
          }
        },
      );

      customElements.define(
        'menu-button-list-box',
        class extends PolymerElement {
          static get is() {
            return 'menu-button-list-box';
          }

          static get template() {
            return html`
              <style>
                :host {
                  display: block;
                }
              </style>
              <slot></slot>
            `;
          }

          ready() {
            super.ready();

            this.setAttribute('role', 'listbox');
          }
        },
      );

      customElements.define(
        'menu-button-item',
        class extends PolymerElement {
          static get is() {
            return 'menu-button-item';
          }

          static get template() {
            return html`
              <style>
                :host {
                  display: block;
                }
              </style>
              <slot></slot>
            `;
          }

          ready() {
            super.ready();

            this.setAttribute('role', 'option');
          }
        },
      );

      customElements.define(
        'menu-button',
        class extends PolymerElement {
          static get template() {
            return html`
              <button id="button" on-click="_onClick">Open</button>
              <menu-button-overlay id="overlay" opened="{{opened}}" on-vaadin-overlay-open="_onOverlayOpen">
                <template>
                  <menu-button-list-box>
                    <menu-button-item aria-selected="false" tabindex="0" style="padding: 5px 10px;"
                      >Foo</menu-button-item
                    >
                    <menu-button-item role="option" aria-selected="false" tabindex="-1" style="padding: 5px 10px;"
                      >Bar</menu-button-item
                    >
                    <menu-button-item role="option" aria-selected="false" tabindex="-1" style="padding: 5px 10px;"
                      >Baz</menu-button-item
                    >
                  </menu-button-list-box>
                </template>
              </menu-button-overlay>
            `;
          }

          static get properties() {
            return {
              opened: Boolean,
            };
          }

          ready() {
            super.ready();

            this.$.overlay.positionTarget = this.$.button;
            // this.$.overlay.noVerticalOverlap = true;
          }

          _onClick() {
            this.opened = true;
          }

          _onOverlayOpen() {
            const item = this.$.overlay.content.querySelector('[tabindex="0"]');
            item.focus();
          }
        },
      );
    </script>
  </head>

  <body>
    <menu-button></menu-button>
  </body>
</html>

web-padawan avatar Aug 03 '22 11:08 web-padawan