Bug 1574082 - Improve the labelling of the table and radio buttons, r=mtigley
authorMarco Zehe <mzehe@mozilla.com>
Thu, 15 Aug 2019 14:29:48 +0000
changeset 488245 5104e61fa2b99ffd00c676a2af080763f2e319c6
parent 488244 10439459ebd638a97a6b2caffe4325a51efc36a0
child 488246 d8d687ca13f8a8f4b5820e31aa6a6c02413f608a
push id113904
push userncsoregi@mozilla.com
push dateThu, 15 Aug 2019 19:41:00 +0000
treeherdermozilla-inbound@b283a7ef186c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmtigley
bugs1574082, 1573197
milestone70.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1574082 - Improve the labelling of the table and radio buttons, r=mtigley This adds some polish to the existing markup, making it all a bit more intuitive. 1. Associate the graph legend to the fake table so it becomes kind of the caption for that table for screen reader users. Screen reader users will then hear something like "Table showing a graph of .." plus the table information they already get since bug 1573197 landed. 2. Actually combine the number and tab's title into a spoken label for screen readers on each radio button, and for the description, use the explanatory paragraph's content. That way, screen reader users can just tab and arrow to each item in focus mode and hear all the relevant information at once without having to skip back and forth between the elements. Differential Revision: https://phabricator.services.mozilla.com/D42082
browser/components/protections/content/protections.html
browser/components/protections/test/browser/browser_protections_report_ui.js
--- a/browser/components/protections/content/protections.html
+++ b/browser/components/protections/content/protections.html
@@ -31,62 +31,62 @@
             <p class="content" data-l10n-id="etp-card-content"></p>
             <p id="protection-details" role="link" tabindex="0" data-l10n-title="go-to-privacy-settings"></p>
           </div>
         </div>
         <div class="card-body">
           <div class="body-wrapper">
             <p id="graph-week-summary"></p>
             <div id="graph-wrapper">
-              <div id="graph" role="table"></div>
+              <div id="graph" role="table" aria-labelledby="graphLegendDescription"></div>
               <div id="legend">
                 <label id="graphLegendDescription" data-l10n-id="graph-legend-description"></label>
-                <input id="tab-social" data-type="social" type="radio" name="tabs" aria-describedby="socialTitle" checked>
-                <label for="tab-social" data-type="social"></label>
+                <input id="tab-social" data-type="social" type="radio" name="tabs" aria-labelledby="socialLabel socialTitle" aria-describedby="socialContent" checked>
+                <label id="socialLabel" for="tab-social" data-type="social"></label>
 
-                <input id="tab-cookie" data-type="cookie" type="radio" name="tabs" aria-describedby="cookieTitle">
-                <label for="tab-cookie" data-type="cookie"></label>
+                <input id="tab-cookie" data-type="cookie" type="radio" name="tabs" aria-labelledby="cookieLabel cookieTitle" aria-describedby="cookieContent">
+                <label id="cookieLabel" for="tab-cookie" data-type="cookie"></label>
 
-                <input id="tab-tracker" data-type="tracker" type="radio" name="tabs" aria-describedby="trackerTitle">
-                <label for="tab-tracker" data-type="tracker"></label>
+                <input id="tab-tracker" data-type="tracker" type="radio" name="tabs" aria-labelledby="trackerLabel trackerTitle" aria-describedby="trackerContent">
+                <label id="trackerLabel" for="tab-tracker" data-type="tracker"></label>
 
-                <input id="tab-fingerprinter" data-type="fingerprinter" type="radio" name="tabs" aria-describedby="fingerprinterTitle">
-                <label for="tab-fingerprinter" data-type="fingerprinter"></label>
+                <input id="tab-fingerprinter" data-type="fingerprinter" type="radio" name="tabs" aria-labelledby="fingerprinterLabel fingerprinterTitle" aria-describedby="fingerprinterContent">
+                <label id="fingerprinterLabel" for="tab-fingerprinter" data-type="fingerprinter"></label>
 
-                <input id="tab-cryptominer" data-type="cryptominer" type="radio" name="tabs" aria-describedby="cryptominerTitle">
-                <label for="tab-cryptominer" data-type="cryptominer"></label>
+                <input id="tab-cryptominer" data-type="cryptominer" type="radio" name="tabs" aria-labelledby="cryptominerLabel cryptominerTitle" aria-describedby="cryptominerContent">
+                <label id="cryptominerLabel" for="tab-cryptominer" data-type="cryptominer"></label>
                 <div id=highlight></div>
                 <div id=highlight-hover></div>
                 <div id="social" class="tab-content">
                   <p id="socialTitle" class="content-title" data-l10n-id="social-tab-title"></p>
-                  <p data-l10n-id="social-tab-contant">
+                  <p id="socialContent" data-l10n-id="social-tab-contant">
                     <a target="_blank" id="social-link" data-l10n-name="learn-more-link"></a>
                   </p>
                 </div>
                 <div id="cookie" class="tab-content">
                   <p id="cookieTitle" class="content-title" data-l10n-id="cookie-tab-title"></p>
-                  <p data-l10n-id="cookie-tab-content">
+                  <p id="cookieContent" data-l10n-id="cookie-tab-content">
                     <a target="_blank" id="cookie-link" data-l10n-name="learn-more-link"></a>
                   </p>
                 </div>
                 <div id="tracker" class="tab-content">
                   <p id="trackerTitle" class="content-title" data-l10n-id="tracker-tab-title"></p>
-                  <p data-l10n-id="tracker-tab-content">
+                  <p id="trackerContent" data-l10n-id="tracker-tab-content">
                     <a target="_blank" id="tracker-link" data-l10n-name="learn-more-link"></a>
                   </p>
                 </div>
                 <div id="fingerprinter" class="tab-content">
                   <p id="fingerprinterTitle" class="content-title" data-l10n-id="fingerprinter-tab-title"></p>
-                  <p data-l10n-id="fingerprinter-tab-content">
+                  <p id="fingerprinterContent" data-l10n-id="fingerprinter-tab-content">
                     <a target="_blank" id="fingerprinter-link" data-l10n-name="learn-more-link"></a>
                   </p>
                 </div>
                 <div id="cryptominer" class="tab-content">
                   <p id="cryptominerTitle" class="content-title" data-l10n-id="cryptominer-tab-title"></p>
-                  <p data-l10n-id="cryptominer-tab-content">
+                  <p id="cryptominerContent" data-l10n-id="cryptominer-tab-content">
                     <a target="_blank" id="cryptominer-link" data-l10n-name="learn-more-link"></a>
                   </p>
                 </div>
               </div>
             </div>
             <div id="graph-total-summary"></div>
           </div>
         </div>
--- a/browser/components/protections/test/browser/browser_protections_report_ui.js
+++ b/browser/components/protections/test/browser/browser_protections_report_ui.js
@@ -200,16 +200,21 @@ add_task(async function test_graph_displ
       "table",
       "Graph is an accessible table"
     );
     is(
       content.document.getElementById("graph").getAttribute("aria-colcount"),
       DATA_TYPES.length + 2,
       "Table has the right number of columns"
     );
+    is(
+      content.document.getElementById("graph").getAttribute("aria-labelledby"),
+      "graphLegendDescription",
+      "Table has an accessible label"
+    );
 
     // today has each type
     // yesterday will have no tracking cookies
     // 2 days ago will have no fingerprinters
     // 3 days ago will have no cryptominers
     // 4 days ago will have no trackers
     // 5 days ago will have no social (when we add social)
     // 6 days ago will be empty
@@ -386,53 +391,79 @@ add_task(async function test_graph_displ
     );
     ok(allBars[0].classList.contains("empty"), "6 days ago is an empty bar");
     is(
       allBars[0].getAttribute("aria-owns"),
       "day6 ",
       "Row has the columns in the right order"
     );
 
-    // Check that each tab has the correct aria-describedby value. This helps screen readers
-    // know what type of tracker the reported tab number is referencing.
+    // Check that each tab has the correct aria-labelledby and aria-describedby
+    // values. This helps screen readers know what type of tracker the reported
+    // tab number is referencing.
     const socialTab = content.document.getElementById("tab-social");
     is(
+      socialTab.getAttribute("aria-labelledby"),
+      "socialLabel socialTitle",
+      "aria-labelledby attribute is socialLabel socialTitle"
+    );
+    is(
       socialTab.getAttribute("aria-describedby"),
-      "socialTitle",
-      "aria-describedby attribute is socialTitle"
+      "socialContent",
+      "aria-describedby attribute is socialContent"
     );
 
     const cookieTab = content.document.getElementById("tab-cookie");
     is(
+      cookieTab.getAttribute("aria-labelledby"),
+      "cookieLabel cookieTitle",
+      "aria-labelledby attribute is cookieLabel cookieTitle"
+    );
+    is(
       cookieTab.getAttribute("aria-describedby"),
-      "cookieTitle",
-      "aria-describedby attribute is cookieTitle"
+      "cookieContent",
+      "aria-describedby attribute is cookieContent"
     );
 
     const trackerTab = content.document.getElementById("tab-tracker");
     is(
+      trackerTab.getAttribute("aria-labelledby"),
+      "trackerLabel trackerTitle",
+      "aria-labelledby attribute is trackerLabel trackerTitle"
+    );
+    is(
       trackerTab.getAttribute("aria-describedby"),
-      "trackerTitle",
-      "aria-describedby attribute is trackerTitle"
+      "trackerContent",
+      "aria-describedby attribute is trackerContent"
     );
 
     const fingerprinterTab = content.document.getElementById(
       "tab-fingerprinter"
     );
     is(
+      fingerprinterTab.getAttribute("aria-labelledby"),
+      "fingerprinterLabel fingerprinterTitle",
+      "aria-labelledby attribute is fingerprinterLabel fingerprinterTitle"
+    );
+    is(
       fingerprinterTab.getAttribute("aria-describedby"),
-      "fingerprinterTitle",
-      "aria-describedby attribute is fingerprinterTitle"
+      "fingerprinterContent",
+      "aria-describedby attribute is fingerprinterContent"
     );
 
     const cryptominerTab = content.document.getElementById("tab-cryptominer");
     is(
+      cryptominerTab.getAttribute("aria-labelledby"),
+      "cryptominerLabel cryptominerTitle",
+      "aria-labelledby attribute is cryptominerLabel cryptominerTitle"
+    );
+    is(
       cryptominerTab.getAttribute("aria-describedby"),
-      "cryptominerTitle",
-      "aria-describedby attribute is cryptominerTitle"
+      "cryptominerContent",
+      "aria-describedby attribute is cryptominerContent"
     );
   });
 
   // Use the TrackingDBService API to delete the data.
   await TrackingDBService.clearAll();
   // Make sure the data was deleted.
   let rows = await db.execute(SQL.selectAll);
   is(rows.length, 0, "length is 0");