help3xsl/a11y-toggle.js       |  146 ++++++++++++++----------------------------
 help3xsl/default.css          |    6 -
 help3xsl/help.js              |    1 
 help3xsl/help2.js             |   27 ++++---
 help3xsl/online_transform.xsl |    8 +-
 help3xsl/xap_templ_query.xsl  |    4 -
 6 files changed, 75 insertions(+), 117 deletions(-)

New commits:
commit 4f4db70a67558885482ba33892910f8123f00c03
Author:     Ilmari Lauhakangas <ilmari.lauhakan...@libreoffice.org>
AuthorDate: Wed Oct 25 18:20:13 2023 +0300
Commit:     Adolfo Jayme Barrientos <fit...@ubuntu.com>
CommitDate: Wed Oct 25 18:05:54 2023 +0200

    Improve accessibility of dropdown menus
    
    Now the opened menus can be closed with Esc key, so we conform
    to the dismissible success criterion in WCAG 2.1:
    https://www.w3.org/WAI/WCAG21/Understanding/content-on-hover-or-focus.html
    
    To simplify things, now the navigation lists are populated upon
    page load in all cases.
    
    Change-Id: I0a7daaea122d3e03de36c322ccb012c546b271e0
    Reviewed-on: https://gerrit.libreoffice.org/c/help/+/158429
    Tested-by: Jenkins
    Reviewed-by: Olivier Hallot <olivier.hal...@libreoffice.org>
    (cherry picked from commit 69f85cbf17c5acb8fb9b38772139c34eea96a772)
    Reviewed-on: https://gerrit.libreoffice.org/c/help/+/158436
    Reviewed-by: Adolfo Jayme Barrientos <fit...@ubuntu.com>

diff --git a/help3xsl/a11y-toggle.js b/help3xsl/a11y-toggle.js
index 821a8e0272..62e1032b02 100644
--- a/help3xsl/a11y-toggle.js
+++ b/help3xsl/a11y-toggle.js
@@ -1,100 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /*
-MIT License
-
-Copyright (c) 2016 Edenspiekermann
-
-*/
-
-(function () {
-  'use strict';
-
-  var internalId = 0;
-  var togglesMap = {};
-  var targetsMap = {};
-
-  function $ (selector, context) {
-    return Array.prototype.slice.call(
-      (context || document).querySelectorAll(selector)
-    );
-  }
-
-  function getClosestToggle (element) {
-    if (element.closest) {
-      return element.closest('[data-a11y-toggle]');
-    }
-
-    while (element) {
-      if (element.nodeType === 1 && element.hasAttribute('data-a11y-toggle')) {
-        return element;
-      }
-
-      element = element.parentNode;
-    }
-
-    return null;
-  }
-
-  function handleToggle (toggle) {
-    var target = toggle && targetsMap[toggle.getAttribute('aria-controls')];
-
-    if (!target) {
-      return false;
-    }
-
-    var toggles = togglesMap['#' + target.id];
-    var isExpanded = target.getAttribute('aria-hidden') === 'false';
-
-    target.setAttribute('aria-hidden', isExpanded);
-    toggles.forEach(function (toggle) {
-      toggle.setAttribute('aria-expanded', !isExpanded);
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+function hideNavs() {
+    let navs = document.querySelectorAll('[data-a11y-toggle] + nav');
+    navs.forEach((nav) => {
+        if (!nav.hasAttribute('hidden')) {
+            nav.previousElementSibling.setAttribute('aria-expanded', 'false');
+            nav.setAttribute('hidden', 'true');
+        }
     });
-  }
-
-  var initA11yToggle = function (context) {
-    togglesMap = $('[data-a11y-toggle]', context).reduce(function (acc, 
toggle) {
-      var selector = '#' + toggle.getAttribute('data-a11y-toggle');
-      acc[selector] = acc[selector] || [];
-      acc[selector].push(toggle);
-      return acc;
-    }, togglesMap);
-
-    var targets = Object.keys(togglesMap);
-    targets.length && $(targets).forEach(function (target) {
-      var toggles = togglesMap['#' + target.id];
-      var isExpanded = target.hasAttribute('data-a11y-toggle-open');
-      var labelledby = [];
-
-      toggles.forEach(function (toggle) {
-        toggle.id || toggle.setAttribute('id', 'a11y-toggle-' + internalId++);
-        toggle.setAttribute('aria-controls', target.id);
-        toggle.setAttribute('aria-expanded', isExpanded);
-        labelledby.push(toggle.id);
-      });
-
-      target.setAttribute('aria-hidden', !isExpanded);
-      target.hasAttribute('aria-labelledby') || 
target.setAttribute('aria-labelledby', labelledby.join(' '));
-
-      targetsMap[target.id] = target;
-    });
-  };
-
-  document.addEventListener('DOMContentLoaded', function () {
-    initA11yToggle();
-  });
-
-  document.addEventListener('click', function (event) {
-    var toggle = getClosestToggle(event.target);
-    handleToggle(toggle);
-  });
-
-  document.addEventListener('keyup', function (event) {
-    if (event.which === 13 || event.which === 32) {
-      var toggle = getClosestToggle(event.target);
-      if (toggle && toggle.getAttribute('role') === 'button') {
-        handleToggle(toggle);
-      }
+}
+const navToggle = document.querySelectorAll('[data-a11y-toggle]');
+navToggle.forEach((toggle) => {
+      let navList = toggle.nextElementSibling;
+      let navLinks = navList.querySelectorAll('a');
+      toggle.addEventListener('click', (event) => {
+          if (navList.hasAttribute('hidden')) {
+              toggle.setAttribute('aria-expanded', 'true');
+              navList.removeAttribute('hidden');
+              // Set focus on first link
+              // will be highlighted for keyboard users
+              navLinks[0].focus();
+          } else {
+              navList.setAttribute('hidden', 'true');
+              toggle.setAttribute('aria-expanded', 'false');
+          }
+          event.stopPropagation();
+    }, false);
+});
+document.addEventListener('keydown', (event) => {
+    // Ignore IME composition
+    if (event.isComposing || event.keyCode === 229) {
+        return;
     }
-  });
 
-  window && (window.a11yToggle = initA11yToggle);
-})();
+    // Close menu with ESC key
+    if (event.keyCode === 27) {
+        hideNavs();
+    }
+}, false);
+document.addEventListener('click', (event) => {
+    // close navigation menus when clicking anywhere (except when on mobile)
+    if (event.target.closest('[data-a11y-toggle] + nav') || 
Math.max(document.documentElement.clientWidth, window.innerWidth || 0) < 960) 
return
+    hideNavs();
+})
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/help3xsl/default.css b/help3xsl/default.css
index 7ce0da75f3..f01f567484 100644
--- a/help3xsl/default.css
+++ b/help3xsl/default.css
@@ -448,7 +448,7 @@ h6 {
   transition-duration: .35s;
 }
 
-#langs-nav:not([aria-hidden='true']), #modules-nav:not([aria-hidden='true']) {
+#langs-nav, #modules-nav {
     z-index: 100;
     /* line them up horizontally */
     display: flex;
@@ -525,7 +525,7 @@ aside input[type=checkbox]:checked ~ .contents-treeview {
     font-size: 15px;
     display: block;
 }
-.index .hidden {
+.index .hidden, #langs-nav[hidden], #modules-nav[hidden] {
     display: none;
 }
 #Bookmarks {
@@ -846,7 +846,7 @@ li.disabled a {
     }
 
     /* change the menu direction to stacked */
-    #langs-nav:not([aria-hidden='true']), 
#modules-nav:not([aria-hidden='true']) {
+    #langs-nav, #modules-nav {
         display: flex;
         flex-direction: column;
         overflow-y: auto;
diff --git a/help3xsl/help.js b/help3xsl/help.js
index 3e9c0fe110..8e0dc54206 100644
--- a/help3xsl/help.js
+++ b/help3xsl/help.js
@@ -258,4 +258,5 @@ function youtubeLoader(ytId, width, height) {
     placeholder.innerHTML = iframeMarkup;
     placeholder.removeAttribute("style");
 }
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/help3xsl/help2.js b/help3xsl/help2.js
index 0d2f0524a1..61efcecca5 100644
--- a/help3xsl/help2.js
+++ b/help3xsl/help2.js
@@ -172,7 +172,7 @@ function existingLang(lang) {
 function setupModules(lang) {
     var modulesNav = document.getElementById('modules-nav');
     if (!modulesNav.classList.contains('loaded')) {
-        var html =
+        let html =
             '<a href="' + lang + 
'/text/shared/05/new_help.html?DbPAR=SHARED"><div 
class="office-icon"></div>%PRODUCTNAME</a>' +
             '<a href="' + lang + 
'/text/swriter/main0000.html?DbPAR=WRITER"><div 
class="writer-icon"></div>Writer</a>' +
             '<a href="' + lang + '/text/scalc/main0000.html?DbPAR=CALC"><div 
class="calc-icon"></div>Calc</a>' +
@@ -187,10 +187,12 @@ function setupModules(lang) {
     }
 }
 
-function setupLanguages(page) {
-    var langNav = document.getElementById('langs-nav');
+function setupLanguages(url) {
+    let langNav = document.getElementById('langs-nav');
+    if (!langNav) return;
+    let page = url.substring(url.search('/text/'));
     if (!langNav.classList.contains('loaded')) {
-        var html = '';
+        let html = '';
         languagesSet.forEach(function(lang) {
             html += '<a href="' + lang + page + '">' + ((lang in 
languageNames)? languageNames[lang]: lang) + '</a>';
         });
@@ -237,18 +239,19 @@ if(missingElement != null){missingElement.innerHTML = 
helpID;}
 
 debugInfo(getParameterByName("Debug"));
 
-// Mobile devices need the modules and langs on page load
+// Mobile devices need the modules and langs displayed on page load
 if (Math.max(document.documentElement.clientWidth, window.innerWidth || 0) < 
960) {
-    let e = new Event('click');
-    let modulesBtn = document.getElementById('modules');
-    let langsBtn = document.getElementById('langs');
     let modules = document.getElementById('modules-nav');
     let langs = document.getElementById('langs-nav');
-    modules.setAttribute('data-a11y-toggle-open', '');
-    modulesBtn.dispatchEvent(e);
+    modules.removeAttribute('hidden');
     if (langs) {
-        langs.setAttribute('data-a11y-toggle-open', '');
-        langsBtn.dispatchEvent(e);
+        langs.removeAttribute('hidden');
     }
 }
+
+const href = window.location.href;
+const lang = existingLang(getParameterByName("Language", href) || 
navigator.language);
+setupModules(lang);
+setupLanguages(href);
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/help3xsl/online_transform.xsl b/help3xsl/online_transform.xsl
index aaa56175d8..51961c68b6 100644
--- a/help3xsl/online_transform.xsl
+++ b/help3xsl/online_transform.xsl
@@ -185,17 +185,17 @@
         </a>
         <div class="dropdowns">
             <div class="modules">
-                <button type="button" data-a11y-toggle="modules-nav" 
id="modules" onclick="setupModules('{$lang}');">
+                <button type="button" data-a11y-toggle="modules-nav" 
id="modules" aria-haspopup="true" aria-expanded="false" 
aria-controls="modules-nav">
                     <xsl:value-of select="$ui_module"/>
                 </button>
-            <nav id="modules-nav"/><!-- is filled in via setupModules() on 
demand -->
+                <nav id="modules-nav" hidden=""/><!-- is filled in via 
setupModules() -->
             </div>
             <xsl:if test="$online">
                 <div class="lang">
-                    <button type="button" data-a11y-toggle="langs-nav" 
id="langs" onclick="setupLanguages('{$htmlpage}');">
+                    <button type="button" data-a11y-toggle="langs-nav" 
id="langs" aria-haspopup="true" aria-expanded="false" 
aria-controls="modules-nav">
                         <xsl:value-of select="$ui_language"/>
                     </button>
-                    <nav id="langs-nav"/><!-- is filled in via 
setupLanguages() on demand -->
+                    <nav id="langs-nav" hidden=""/><!-- is filled in via 
setupLanguages() -->
                 </div>
             </xsl:if>
         </div>
diff --git a/help3xsl/xap_templ_query.xsl b/help3xsl/xap_templ_query.xsl
index 51f2fea3cf..3aedf2e1b3 100644
--- a/help3xsl/xap_templ_query.xsl
+++ b/help3xsl/xap_templ_query.xsl
@@ -118,9 +118,9 @@ document.write("<span title=\""+D+" 
"+T+"\">]]><xsl:apply-templates select="//va
     </header>
 </div>
 <div class="modules">
-    <button type="button" data-a11y-toggle="modules-nav" id="modules" 
onclick="setupModules(']]><xsl:value-of 
select="$lang"/><![CDATA[');">]]><xsl:value-of select="$ui_module"/><![CDATA[
+    <button type="button" data-a11y-toggle="modules-nav" id="modules" 
onclick="setupModules(']]><xsl:value-of select="$lang"/><![CDATA[');" 
aria-haspopup="true" aria-expanded="false" 
aria-controls="modules-nav">]]><xsl:value-of select="$ui_module"/><![CDATA[
     </button>
-    <nav id="modules-nav"/><!-- is filled in via setupModules() on demand -->
+    <nav id="modules-nav" hidden=""/><!-- is filled in via setupModules() on 
demand -->
 </div>
 <aside class="rightside">
     <input id="accordion-1" name="accordion-menu" type="checkbox"/>

Reply via email to