From 2852443f4877115c1c629d462cd83dae0baa7109 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 1 Jul 2022 10:33:06 -0700 Subject: [PATCH] rustdoc: use
tag for the source code sidebar This fixes the extremely poor accessibility of the old system, making it possible to navigate the sidebar by keyboard, and also implicitly gives the sidebar items the correct ARIA roles. --- src/librustdoc/html/static/css/rustdoc.css | 37 ++++------------ src/librustdoc/html/static/css/themes/ayu.css | 3 +- .../html/static/css/themes/dark.css | 3 +- .../html/static/css/themes/light.css | 3 +- .../html/static/js/source-script.js | 29 +++++-------- .../sidebar-source-code-display.goml | 42 +++++++++---------- src/test/rustdoc-gui/source-code-page.goml | 20 ++++----- 7 files changed, 54 insertions(+), 83 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 532b98d9bb9..7d5318a194c 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -1528,37 +1528,18 @@ kbd { margin-bottom: 1em; } -div.children { - padding-left: 27px; - display: none; -} -div.name { +details.dir-entry > summary { + margin: 0 0 0 13px; + list-style-position: outside; cursor: pointer; - position: relative; - margin-left: 16px; } -div.files > a { + +details.dir-entry div.folders, details.dir-entry div.files { + padding-left: 27px; +} + +details.dir-entry a { display: block; - padding: 0 3px; -} -div.files > a:hover, div.name:hover { - background-color: #a14b4b; -} -div.name.expand + .children { - display: block; -} -div.name::before { - content: "\25B6"; - padding-left: 4px; - font-size: 0.625rem; - position: absolute; - left: -16px; - top: 4px; -} -div.name.expand::before { - transform: rotate(90deg); - left: -15px; - top: 2px; } /* The hideme class is used on summary tags that contain a span with diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index b7d0db1f002..a78ac5da749 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -630,7 +630,8 @@ kbd { color: #fff; border-bottom-color: #5c6773; } -#source-sidebar div.files > a:hover, div.name:hover { +#source-sidebar div.files > a:hover, details.dir-entry summary:hover, +#source-sidebar div.files > a:focus, details.dir-entry summary:focus { background-color: #14191f; color: #ffb44c; } diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index eb64ef3e771..a2abd53fa02 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -500,7 +500,8 @@ kbd { #source-sidebar > .title { border-bottom-color: #ccc; } -#source-sidebar div.files > a:hover, div.name:hover { +#source-sidebar div.files > a:hover, details.dir-entry summary:hover, +#source-sidebar div.files > a:focus, details.dir-entry summary:focus { background-color: #444; } #source-sidebar div.files > .selected { diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index 00cdf835897..c4510ad6ae6 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -484,7 +484,8 @@ kbd { #source-sidebar > .title { border-bottom-color: #ccc; } -#source-sidebar div.files > a:hover, div.name:hover { +#source-sidebar div.files > a:hover, details.dir-entry summary:hover, +#source-sidebar div.files > a:focus, details.dir-entry summary:focus { background-color: #E0E0E0; } #source-sidebar div.files > .selected { diff --git a/src/librustdoc/html/static/js/source-script.js b/src/librustdoc/html/static/js/source-script.js index acb1d8d7b5c..21c36092790 100644 --- a/src/librustdoc/html/static/js/source-script.js +++ b/src/librustdoc/html/static/js/source-script.js @@ -13,33 +13,27 @@ const rootPath = document.getElementById("rustdoc-vars").attributes["data-root-p let oldScrollPosition = 0; function createDirEntry(elem, parent, fullPath, hasFoundFile) { - const name = document.createElement("div"); - name.className = "name"; + const dirEntry = document.createElement("details"); + const summary = document.createElement("summary"); + + dirEntry.className = "dir-entry"; fullPath += elem["name"] + "/"; - name.onclick = ev => { - if (hasClass(ev.target, "expand")) { - removeClass(ev.target, "expand"); - } else { - addClass(ev.target, "expand"); - } - }; - name.innerText = elem["name"]; + summary.innerText = elem["name"]; + dirEntry.appendChild(summary); - const children = document.createElement("div"); - children.className = "children"; const folders = document.createElement("div"); folders.className = "folders"; if (elem.dirs) { for (const dir of elem.dirs) { if (createDirEntry(dir, folders, fullPath, hasFoundFile)) { - addClass(name, "expand"); + dirEntry.open = true; hasFoundFile = true; } } } - children.appendChild(folders); + dirEntry.appendChild(folders); const files = document.createElement("div"); files.className = "files"; @@ -51,15 +45,14 @@ function createDirEntry(elem, parent, fullPath, hasFoundFile) { const w = window.location.href.split("#")[0]; if (!hasFoundFile && w === file.href) { file.className = "selected"; - addClass(name, "expand"); + dirEntry.open = true; hasFoundFile = true; } files.appendChild(file); } } - children.appendChild(files); - parent.appendChild(name); - parent.appendChild(children); + dirEntry.appendChild(files); + parent.appendChild(dirEntry); return hasFoundFile; } diff --git a/src/test/rustdoc-gui/sidebar-source-code-display.goml b/src/test/rustdoc-gui/sidebar-source-code-display.goml index c441f84a821..1d8cc22100c 100644 --- a/src/test/rustdoc-gui/sidebar-source-code-display.goml +++ b/src/test/rustdoc-gui/sidebar-source-code-display.goml @@ -27,29 +27,29 @@ reload: // Waiting for the sidebar to be displayed... wait-for-css: ("#sidebar-toggle", {"visibility": "visible", "opacity": 1}) assert-css: ( - "#source-sidebar .expand + .children a.selected", + "#source-sidebar details[open] > .files a.selected", {"color": "rgb(0, 0, 0)", "background-color": "rgb(255, 255, 255)"}, ) // Without hover. assert-css: ( - "#source-sidebar .expand + .children > .files a:not(.selected)", + "#source-sidebar details[open] > .files a:not(.selected)", {"color": "rgb(0, 0, 0)", "background-color": "rgba(0, 0, 0, 0)"}, ) // With hover. -move-cursor-to: "#source-sidebar .expand + .children > .files a:not(.selected)" +move-cursor-to: "#source-sidebar details[open] > .files a:not(.selected)" assert-css: ( - "#source-sidebar .expand + .children > .files a:not(.selected)", + "#source-sidebar details[open] > .files a:not(.selected)", {"color": "rgb(0, 0, 0)", "background-color": "rgb(224, 224, 224)"}, ) // Without hover. assert-css: ( - "#source-sidebar .expand + .children .folders .name", + "#source-sidebar details[open] > .folders > details > summary", {"color": "rgb(0, 0, 0)", "background-color": "rgba(0, 0, 0, 0)"}, ) // With hover. -move-cursor-to: "#source-sidebar .expand + .children .folders .name" +move-cursor-to: "#source-sidebar details[open] > .folders > details > summary" assert-css: ( - "#source-sidebar .expand + .children .folders .name", + "#source-sidebar details[open] > .folders > details > summary", {"color": "rgb(0, 0, 0)", "background-color": "rgb(224, 224, 224)"}, ) @@ -59,29 +59,29 @@ reload: // Waiting for the sidebar to be displayed... wait-for-css: ("#sidebar-toggle", {"visibility": "visible", "opacity": 1}) assert-css: ( - "#source-sidebar .expand + .children a.selected", + "#source-sidebar details[open] > .files > a.selected", {"color": "rgb(221, 221, 221)", "background-color": "rgb(51, 51, 51)"}, ) // Without hover. assert-css: ( - "#source-sidebar .expand + .children > .files a:not(.selected)", + "#source-sidebar details[open] > .files > a:not(.selected)", {"color": "rgb(221, 221, 221)", "background-color": "rgba(0, 0, 0, 0)"}, ) // With hover. -move-cursor-to: "#source-sidebar .expand + .children > .files a:not(.selected)" +move-cursor-to: "#source-sidebar details[open] > .files a:not(.selected)" assert-css: ( - "#source-sidebar .expand + .children > .files a:not(.selected)", + "#source-sidebar details[open] > .files a:not(.selected)", {"color": "rgb(221, 221, 221)", "background-color": "rgb(68, 68, 68)"}, ) // Without hover. assert-css: ( - "#source-sidebar .expand + .children .folders .name", + "#source-sidebar details[open] > .folders > details > summary", {"color": "rgb(221, 221, 221)", "background-color": "rgba(0, 0, 0, 0)"}, ) // With hover. -move-cursor-to: "#source-sidebar .expand + .children .folders .name" +move-cursor-to: "#source-sidebar details[open] > .folders > details > summary" assert-css: ( - "#source-sidebar .expand + .children .folders .name", + "#source-sidebar details[open] > .folders > details > summary", {"color": "rgb(221, 221, 221)", "background-color": "rgb(68, 68, 68)"}, ) @@ -91,29 +91,29 @@ reload: // Waiting for the sidebar to be displayed... wait-for-css: ("#sidebar-toggle", {"visibility": "visible", "opacity": 1}) assert-css: ( - "#source-sidebar .expand + .children a.selected", + "#source-sidebar details[open] > .files a.selected", {"color": "rgb(255, 180, 76)", "background-color": "rgb(20, 25, 31)"}, ) // Without hover. assert-css: ( - "#source-sidebar .expand + .children > .files a:not(.selected)", + "#source-sidebar details[open] > .files a:not(.selected)", {"color": "rgb(197, 197, 197)", "background-color": "rgba(0, 0, 0, 0)"}, ) // With hover. -move-cursor-to: "#source-sidebar .expand + .children > .files a:not(.selected)" +move-cursor-to: "#source-sidebar details[open] > .files a:not(.selected)" assert-css: ( - "#source-sidebar .expand + .children > .files a:not(.selected)", + "#source-sidebar details[open] > .files a:not(.selected)", {"color": "rgb(255, 180, 76)", "background-color": "rgb(20, 25, 31)"}, ) // Without hover. assert-css: ( - "#source-sidebar .expand + .children .folders .name", + "#source-sidebar details[open] > .folders > details > summary", {"color": "rgb(197, 197, 197)", "background-color": "rgba(0, 0, 0, 0)"}, ) // With hover. -move-cursor-to: "#source-sidebar .expand + .children .folders .name" +move-cursor-to: "#source-sidebar details[open] > .folders > details > summary" assert-css: ( - "#source-sidebar .expand + .children .folders .name", + "#source-sidebar details[open] > .folders > details > summary", {"color": "rgb(255, 180, 76)", "background-color": "rgb(20, 25, 31)"}, ) diff --git a/src/test/rustdoc-gui/source-code-page.goml b/src/test/rustdoc-gui/source-code-page.goml index b45512601f2..05b0e809bca 100644 --- a/src/test/rustdoc-gui/source-code-page.goml +++ b/src/test/rustdoc-gui/source-code-page.goml @@ -34,19 +34,13 @@ assert-document-property: ({"URL": "/lib.rs.html"}, ENDS_WITH) click: "#sidebar-toggle" assert: ".source-sidebar-expanded" -// We check that the first entry of the sidebar is collapsed (which, for whatever reason, -// is number 2 and not 1...). -assert-attribute: ("#source-sidebar .name:nth-child(2)", {"class": "name"}) -assert-text: ("#source-sidebar .name:nth-child(2)", "implementors") -// We also check its children are hidden too. -assert-css: ("#source-sidebar .name:nth-child(2) + .children", {"display": "none"}) +// We check that the first entry of the sidebar is collapsed +assert-property: ("#source-sidebar details:first-of-type", {"open": "false"}) +assert-text: ("#source-sidebar details:first-of-type > summary", "implementors") // We now click on it. -click: "#source-sidebar .name:nth-child(2)" -assert-attribute: ("#source-sidebar .name:nth-child(2)", {"class": "name expand"}) -// Checking that its children are displayed as well. -assert-css: ("#source-sidebar .name:nth-child(2) + .children", {"display": "block"}) +click: "#source-sidebar details:first-of-type > summary" +assert-property: ("#source-sidebar details:first-of-type", {"open": "true"}) // And now we collapse it again. -click: "#source-sidebar .name:nth-child(2)" -assert-attribute: ("#source-sidebar .name:nth-child(2)", {"class": "name"}) -assert-css: ("#source-sidebar .name:nth-child(2) + .children", {"display": "none"}) +click: "#source-sidebar details:first-of-type > summary" +assert-property: ("#source-sidebar details:first-of-type", {"open": "false"})