#4989 Make user dashboard homepage accessible
Opened 2 months ago by appadeia. Modified a month ago
appadeia/pagure jan/a11y  into  master

file modified
+2 -2
@@ -50,12 +50,12 @@ 

              <div class="dropdown-menu dropdown-menu-right">

                {% if config.get('ENABLE_NEW_PROJECTS', True) and config.get('ENABLE_UI_NEW_PROJECTS', True) %}

                <a class="dropdown-item" href="{{ url_for('ui_ns.new_project') }}">

-               <span class="fa {{projecticon()}}"></span> &nbsp;New {{projectstring()}}

+               <span class="fa {{projecticon()}}" aria-hidden="true"></span> &nbsp;New {{projectstring()}}

Hm, I need some help understanding this:

https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-hidden_attribute says:

Adding aria-hidden="true" to an element removes that element and all of its children from the accessibility tree. This can improve the experience for assistive technology users by hiding:
    [...]
    offscreen or collapsed content, such as menus

which applies to this line here, but how does the user access the menu (and its buttons) then?

Reading the last "note" on https://www.w3.org/TR/using-aria/#4thrule since these entries are hidden by default (using display: none;, if I understand correctly we shouldn't need to add aria-hidden="true" here, no?

                </a>

                {% endif %}

                {% if config.get('ENABLE_GROUP_MNGT', False)  %}

                <a class="dropdown-item" href="{{ url_for('ui_ns.add_group') }}">

-               <span class="fa fa-users"></span> &nbsp;New Group

+               <span class="fa fa-users" aria-hidden="true"></span> &nbsp;New Group

                </a>

                {% endif %}

              </div>

@@ -14,8 +14,8 @@ 

  <div class="container">

    <div class="row">

      <div class="col userdash-tabs">

-       <ul class="nav nav-tabs nav-sidetabs flex-column border-bottom-0">

-         <li class="pl-3 pb-3 font-weight-bold">

+       <ul class="nav nav-tabs nav-sidetabs flex-column border-bottom-0" role="tablist">

+         <li class="pl-3 pb-3 font-weight-bold" aria-hidden="true" >

              {{ g.fas_user.username | avatar(20) | safe }} {{g.fas_user.username}}

          </li>

  
@@ -26,14 +26,17 @@ 

            </form>

          </li>

  

-         <li class="nav-item text-dark">

+         <li class="nav-item text-dark" >

            <a class="nav-link d-flex align-items-center {{'active' if select=='projects'}}"

-             href="{{ url_for('ui_ns.index')}}">

-             <span>

-               <i class="fa {{projecticon()}} fa-fw text-muted"></i>

+             href="{{ url_for('ui_ns.index')}}"

+             aria-label="{{ userdash_counts['repos_length'] }} projects"

+             role="tab"

+             aria-selected="{{'true' if select=='projects' else 'false'}}">

+             <span aria-hidden="true">

+               <i class="fa {{projecticon()}} fa-fw text-muted" ></i>

                <span class="d-none d-md-inline">{{projectstring(plural=True)}}&nbsp;</span>

              </span>

-             <div class="ml-auto">

+             <div class="ml-auto" aria-hidden="true">

                <span class="badge badge-secondary">

                  {{ userdash_counts['repos_length'] }}

                </span>
@@ -44,12 +47,15 @@ 

          {% if userdash_counts['forks_length'] > 0 %}

          <li class="nav-item text-dark">

            <a class="nav-link d-flex align-items-center {{'active' if select=='forks'}}"

-             href="{{ url_for('ui_ns.userdash_forks')}}">

-             <span>

+             href="{{ url_for('ui_ns.userdash_forks')}}"

+             aria-label="{{ userdash_counts['forks_length'] }} forks"

+             role="tab"

+             aria-selected="{{'true' if select=='forks' else 'false'}}">

+             <span aria-hidden="true">

                <i class="fa fa-fw text-muted fa-code-fork"></i>

                <span class="d-none d-md-inline">Forks&nbsp;</span>

              </span>

-             <div class="ml-auto">

+             <div class="ml-auto" aria-hidden="true">

                <span class="badge badge-secondary">

                  {{ userdash_counts['forks_length'] }}

                </span>
@@ -62,8 +68,11 @@ 

  

          <li class="nav-item text-dark">

            <a class="nav-link d-flex align-items-center {{'active' if select=='activity'}}"

-             href="{{ url_for('ui_ns.userdash_activity')}}">

-             <span>

+             href="{{ url_for('ui_ns.userdash_activity')}}"

+             aria-label="Activity"

+             role="tab"

+             aria-selected="{{'true' if select=='activity' else 'false'}}">

+             <span aria-hidden="true">

                <i class="fa fa-fw text-muted fa-users"></i>

                <span class="d-none d-md-inline">Activity&nbsp;</span>

              </span>
@@ -73,12 +82,15 @@ 

          {% if userdash_counts['groups_length'] > 0 %}

          <li class="nav-item text-dark">

            <a class="nav-link d-flex align-items-center {{'active' if select=='groups'}}"

-             href="{{ url_for('ui_ns.userdash_groups')}}">

-             <span>

+             href="{{ url_for('ui_ns.userdash_groups')}}"

+             aria-label="{{ userdash_counts['groups_length'] }} groups"

+             role="tab"

+             aria-selected="{{'true' if select=='groups' else 'false'}}">

+             <span aria-hidden="true">

                <i class="fa fa-fw text-muted fa-users"></i>

                <span class="d-none d-md-inline">Groups&nbsp;</span>

              </span>

-             <div class="ml-auto">

+             <div class="ml-auto" aria-hidden="true">

                <span class="badge badge-secondary">

                  {{ userdash_counts['groups_length'] }}

                </span>
@@ -89,12 +101,15 @@ 

  

          <li class="nav-item text-dark">

            <a class="nav-link d-flex align-items-center {{'active' if select=='watchlist'}}"

-             href="{{ url_for('ui_ns.userdash_watchlist')}}">

-             <span>

+             href="{{ url_for('ui_ns.userdash_watchlist')}}"

+             aria-label="watchlist: {{ userdash_counts['watchlist_length'] }} items"

+             role="tab"

+             aria-selected="{{'true' if select=='watchlist' else 'false'}}">

+             <span aria-hidden="true">

                <i class="fa fa-fw text-muted fa-eye"></i>

                <span class="d-none d-md-inline">Watchlist&nbsp;</span>

              </span>

-             <div class="ml-auto">

+             <div class="ml-auto" aria-hidden="true">

                <span class="badge badge-secondary">

                  {{ userdash_counts['watchlist_length'] }}

                </span>

@@ -30,7 +30,7 @@ 

            <div class="row">

              <div class="col">

                    <span class="btn btn-sm btn-outline-secondary border-0 font-weight-bold">

-                       <span class="fa fa-fw fa-exclamation-circle"></span> {{filtered_repos_count}} {{projectstring(plural=True)}}

+                       <span class="fa fa-fw fa-exclamation-circle" aria-hidden="true"></span> {{filtered_repos_count}} {{projectstring(plural=True)}}

                    </span>

  

                  <div class="btn-group">
@@ -81,7 +81,7 @@ 

                        <form id="filters_form" action="{{ url_for('ui_ns.userdash_projects') }}" method="GET">

  

                          <div class="form-group row mb-1" id="acls-filter-group">

-                           <label for="acls" class="col-auto align-self-center pl-1 pr-0"><i class="text-muted fa fa-fw fa-user-secret"></i></label>

+                           <label for="acls" class="col-auto align-self-center pl-1 pr-0"><i class="text-muted fa fa-fw fa-user-secret" aria-hidden="true"></i></label>

                            <div class="col pl-1">

                              <select name="acl" id="acls-selectize" placeholder="ACLs">

                                <option value=""></option>
@@ -92,19 +92,19 @@ 

                              </select>

                            </div>

                            <div class="col-auto pl-0 pr-1 pt-1">

-                             <i class="fa fa-times fa-fw text-muted" id="acls-selectize-reset"></i>

+                             <i class="fa fa-times fa-fw text-muted" id="acls-selectize-reset" aria-label="Reset access control list filter"></i>

                            </div>

                          </div>

  

                          <div class="form-group row mb-2">

-                           <label for="search_pattern" class="col-auto align-self-center pl-1 pr-0"><i class="text-muted fa fa-fw fa-search"></i></label>

+                           <label for="search_pattern" class="col-auto align-self-center pl-1 pr-0"><i class="text-muted fa fa-fw fa-search" aria-hidden="true"></i></label>

                            <div class="col pl-1">

                              <input type="text" class="form-control"

                                      name="search_pattern" placeholder="Search"

                                      value="{{ search_pattern or '' }}" />

                            </div>

                            <div class="col-auto pl-0 pr-1 pt-1">

-                             <i class="fa fa-times fa-fw text-muted" id="search_pattern-selectize-reset"></i>

+                             <i class="fa fa-times fa-fw text-muted" id="search_pattern-selectize-reset" aria-label="Reset search filter"></i>

                            </div>

                          </div>

                          {#
@@ -211,7 +211,7 @@ 

                    {% if repo.avatar_email %}

                      <img src="{{ repo.avatar_email | avatar_url }}" width=20 height=20 />&nbsp;

                    {% else %}

-                     <span class="fa fa-fw {{projecticon()}} mr-1 text-semimuted"></span>

+                     <span class="fa fa-fw {{projecticon()}} mr-1 text-semimuted" aria-hidden="true"></span>

                    {% endif %}

                    <a href="{{url_for(

                      'ui_ns.view_repo',
@@ -226,15 +226,16 @@ 

                    {% endif %}

                      {% if r['grouplist']%}

                      <span class="btn btn-outline-secondary border-0 opacity-100 font-size-09 disabled font-weight-bold">

-                         <span class="fa fa-users fa-fw"></span>

+                         <span class="fa fa-users fa-fw" aria-hidden="true"></span>

                        {% for group in r["grouplist"]%}

                          {{group["group_name"]}} ({{group["access"]}})

                        {% endfor %}

                      </span>

                      {% endif %}

                      {% if r["access"] %}

-                     <span class="btn btn-outline-secondary border-0 opacity-100 font-size-09 disabled font-weight-bold">

-                       <span class="fa fa-user-secret fa-fw"></span>{{r["access"] if r["access"] != "main admin" else "maintainer"}}

+                     <span class="btn btn-outline-secondary border-0 opacity-100 font-size-09 disabled font-weight-bold"

+                     aria-label="your role: {{r["access"] if r["access"] != "main admin" else "maintainer"}}">

+                       <span class="fa fa-user-secret fa-fw" aria-hidden="true"></span>{{r["access"] if r["access"] != "main admin" else "maintainer"}}

                      </span>

                      {% endif %}

                    {# <div class="btn-group mr-2">
@@ -256,8 +257,9 @@ 

                    {% if config.get('ENABLE_TICKETS', True) and repo.settings.get('issue_tracker', True) %}

                        <a class="btn btn-sm btn-outline-primary font-weight-bold" data-toggle="tooltip"

                          href="{{ url_for('ui_ns.view_issues', repo=repo.name, namespace=repo.namespace) }}"

-                         title="{{ repo.open_tickets_public }} open issues against {{repo.name}}">

-                         <span class="fa fa-fw fa-exclamation-circle"></span>

+                         title="{{ repo.open_tickets_public }} open issues against {{repo.name}}"

+                         aria-label="{{ repo.open_tickets_public }} open issues against {{repo.name}}">

+                         <span class="fa fa-fw fa-exclamation-circle" aria-hidden="true"></span>

                          {{- repo.open_tickets_public }}

                        </a>

                    {% endif %}
@@ -267,8 +269,9 @@ 

                        repo=repo.name,

                        username=repo.user.username if repo.is_fork else None,

                        namespace=repo.namespace) }}"

-                       title="{{repo.open_requests}} open pull requests in {{repo.name}}">

-                       <span class="fa fa-fw fa-arrow-circle-down"></span>

+                       title="{{repo.open_requests}} open pull requests in {{repo.name}}"

+                       aria-label="{{repo.open_requests}} open pull requests in {{repo.name}}">

+                       <span class="fa fa-fw fa-arrow-circle-down" aria-hidden="true"></span>

                        {{- repo.open_requests}}

                      </a>

                      </div>

@@ -44,8 +44,8 @@ 

              </p>

              <p>

                <a href="{{ url_for('ui_ns.ssh_hostkey') }}">SSH Hostkey/Fingerprint</a>

-               | <a href="https://docs.pagure.org/pagure/usage/index.html">Documentation</a>

-               | <a href="/about">About</a>

+               <span aria-hidden="true">|</span> <a href="https://docs.pagure.org/pagure/usage/index.html">Documentation</a>

+               <span aria-hidden="true">|</span> <a href="/about">About</a>

              </p>

              <p class="text-muted credit">&copy; Red Hat, Inc. and others.</p>

          </div>

This adds aria attributes to everything needed to make the landing page when you're logged in accessible, bar the form controls using selectize. Note that selectize is completely and utterly inaccessible and all usage of it will probably need to be replaced in its own pull request to improve a11y.

I like this but looks like we're going to have to fix some tests :/

Do you want to do it, or shall I give it a try?

Hm, I need some help understanding this:

https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-hidden_attribute says:

Adding aria-hidden="true" to an element removes that element and all of its children from the accessibility tree. This can improve the experience for assistive technology users by hiding:
    [...]
    offscreen or collapsed content, such as menus

which applies to this line here, but how does the user access the menu (and its buttons) then?

I like this but looks like we're going to have to fix some tests :/

Do you want to do it, or shall I give it a try?

I think an explanation of how the tests work for testing UI would be helpful for me to attempt to do it first.

Reading the last "note" on https://www.w3.org/TR/using-aria/#4thrule since these entries are hidden by default (using display: none;, if I understand correctly we shouldn't need to add aria-hidden="true" here, no?

I like this but looks like we're going to have to fix some tests :/

Do you want to do it, or shall I give it a try?

I think an explanation of how the tests work for testing UI would be helpful for me to attempt to do it first.

The test suite is practically closer to an integration test suite than unit-tests, so in the UI or API tests we test end to end, from the HTTP request to the DB and back. Therefore a number of tests check the HTML return to ensure the right page is returned or for the presence of key elements in that page.

Hm, I need some help understanding this:

https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-hidden_attribute says:
Adding aria-hidden="true" to an element removes that element and all of its children from the accessibility tree. This can improve the experience for assistive technology users by hiding: [...] offscreen or collapsed content, such as menus
which applies to this line here, but how does the user access the menu (and its buttons) then?

The aria-hidden here serves to make the screen reader skip trying to read an icon's ::before and skip straight to the content.

Bootstrap seems to automatically manage the menu's a11y state.

When I navigate through the page and hit on an entry that toggles a menu, my screen reader says the current state of the menu (open or closed). I can then interact to toggle the menu, which will show up in the a11y focus chain as I proceed to head forwards and backwards in it. When I go back to the entry and toggle again, the menu is removed from the a11y focus chain.

Adding aria-hidden="true" to an element removes that element and all of its children from the accessibility tree. This can improve the experience for assistive technology users by hiding:
[...]
offscreen or collapsed content, such as menus
````
which applies to this line here, but how does the user access the menu (and its buttons) then?

The aria-hidden here serves to make the screen reader skip trying to read an icon's ::before and skip straight to the content.

Bootstrap seems to automatically manage the menu's a11y state.

When I navigate through the page and hit on an entry that toggles a menu, my screen reader says the current state of the menu (open or closed). I can then interact to toggle the menu, which will show up in the a11y focus chain as I proceed to head forwards and backwards in it. When I go back to the entry and toggle again, the menu is removed from the a11y focus chain.

Many thanks for this explanation, helps me a lot :)