summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIlmari Lauhakangas <ilmari.lauhakangas@libreoffice.org>2023-10-25 18:20:13 +0300
committerAdolfo Jayme Barrientos <fitojb@ubuntu.com>2023-10-25 18:05:54 +0200
commit4f4db70a67558885482ba33892910f8123f00c03 (patch)
treeef3cf15229add2315375267917a95af3befb35fc
parentc10e043fdaf6ed01727d99dfa3bcccd49ba9c527 (diff)
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.hallot@libreoffice.org> (cherry picked from commit 69f85cbf17c5acb8fb9b38772139c34eea96a772) Reviewed-on: https://gerrit.libreoffice.org/c/help/+/158436 Reviewed-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
-rw-r--r--help3xsl/a11y-toggle.js146
-rw-r--r--help3xsl/default.css6
-rw-r--r--help3xsl/help.js1
-rw-r--r--help3xsl/help2.js27
-rw-r--r--help3xsl/online_transform.xsl8
-rw-r--r--help3xsl/xap_templ_query.xsl4
6 files changed, 75 insertions, 117 deletions
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"/>