Markup optimisations (#258)
* feature: Cleanup markup for accessibility * feature: Cleanup markup for accessibility * feature: Cleanup markup for accessibility * feature: Cleanup markup for accessibility * bugfix: Broken test * feature: Cleanup markup for accessibility
This commit is contained in:
parent
ce670cf58c
commit
7dd1d0a7fc
|
|
@ -7,7 +7,7 @@ tmp_dir = "tmp"
|
|||
bin = "./OliveTin"
|
||||
cmd = "go build -o OliveTin github.com/OliveTin/OliveTin/cmd/OliveTin"
|
||||
delay = 1
|
||||
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
|
||||
exclude_dir = ["assets", "tmp", "vendor", "testdata", "webui.dev", "webui"]
|
||||
exclude_file = []
|
||||
exclude_regex = ["_test.go"]
|
||||
exclude_unchanged = false
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ describe('config: hiddenNav', function () {
|
|||
it('nav is hidden', async () => {
|
||||
await webdriver.get(runner.baseUrl())
|
||||
|
||||
const toggler = await webdriver.findElement(By.id('sidebar-toggle-wrapper'))
|
||||
const toggler = await webdriver.findElement(By.tagName('header'))
|
||||
|
||||
expect(await toggler.isDisplayed()).to.be.false
|
||||
})
|
||||
|
|
|
|||
|
|
@ -16,25 +16,27 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
<main title = "main content">
|
||||
<div id = "perma-widget">
|
||||
<div id = "sidebar-toggle-wrapper">
|
||||
<label for = "hide-sidebar-checkbox" id = "sidebar-toggler-button" title = "Toggle sidebar" tabindex = "0">☰</label>
|
||||
</div>
|
||||
<h1 id = "page-title">OliveTin</h1>
|
||||
</div>
|
||||
<header>
|
||||
<button id = "sidebar-toggler-button" aria-label = "Open sidebar navigation" aria-pressed = "false" aria-haspopup = "menu">☰</button>
|
||||
|
||||
<input type = "checkbox" id = "hide-sidebar-checkbox" hidden checked />
|
||||
<nav hidden>
|
||||
<h1 id = "page-title">OliveTin</h1>
|
||||
|
||||
<nav>
|
||||
<ul id = "navigation-links">
|
||||
<li title = "Actions"><a id = "showActions">Actions</a></li>
|
||||
<li title = "Actions">
|
||||
<a id = "showActions" href = "#actions">Actions</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul id = "supplemental-links">
|
||||
<li title = "Logs"><a id = "showLogs">Logs</a></li>
|
||||
<li title = "Logs">
|
||||
<a id = "showLogs" href = "#logs">Logs</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main title = "main content">
|
||||
<section id = "contentLogs" title = "Logs" hidden>
|
||||
<div class = "toolbar">
|
||||
<input placeholder = "Search for action name" id = "logSearchBox" />
|
||||
|
|
@ -79,6 +81,10 @@
|
|||
</p>
|
||||
</footer>
|
||||
|
||||
<dialog title = "Big Error Message" id = "big-error" class = "error">
|
||||
|
||||
</dialog>
|
||||
|
||||
<dialog title = "Execution Results" id = "execution-results-popup">
|
||||
<div class = "action-header">
|
||||
<span id = "execution-dialog-icon" class = "icon" role = "img"></span>
|
||||
|
|
@ -107,16 +113,12 @@
|
|||
<div id = "execution-dialog-output">
|
||||
<details>
|
||||
<summary>Standard Output</summary>
|
||||
<pre id = "execution-dialog-stdout">
|
||||
?
|
||||
</pre>
|
||||
<pre id = "execution-dialog-stdout">?</pre>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Standard Error</summary>
|
||||
<pre id = "execution-dialog-stderr">
|
||||
?
|
||||
</pre>
|
||||
<pre id = "execution-dialog-stderr">?</pre>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
|
|
@ -150,7 +152,7 @@
|
|||
<template id = "tplActionButton">
|
||||
<button>
|
||||
<span title = "action button icon" class = "icon">💩</span>
|
||||
<span class = "title">Untitled Button</span>
|
||||
<span class = "title" aria-live = "polite">Untitled Button</span>
|
||||
</button>
|
||||
|
||||
<div class = "action-button-footer" hidden></div>
|
||||
|
|
@ -165,16 +167,12 @@
|
|||
|
||||
<details>
|
||||
<summary>stdout</summary>
|
||||
<pre class = "stdout">
|
||||
?
|
||||
</pre>
|
||||
<pre class = "stdout">?</pre>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>stderr</summary>
|
||||
<pre class = "stderr">
|
||||
?
|
||||
</pre>
|
||||
<pre class = "stderr">?</pre>
|
||||
</details>
|
||||
|
||||
<div class = "tags"></div>
|
||||
|
|
@ -184,34 +182,28 @@
|
|||
</template>
|
||||
|
||||
<script type = "text/javascript">
|
||||
const bigErrorDialog = document.getElementById('big-error')
|
||||
|
||||
/**
|
||||
This is the bootstrap code, which relies on very simple, old javascript
|
||||
to at least display a helpful error message if we can't use OliveTin.
|
||||
*/
|
||||
window.showBigError = function (type, friendlyType, message) {
|
||||
clearBigErrors(type)
|
||||
window.showBigError = function (type, friendlyType, message, isFatal) {
|
||||
bigErrorDialog.innerHTML = '<h1>Error ' + friendlyType + '</h1><p>' + message + "</p><p><a href = 'http://docs.olivetin.app/err-" + type + ".html' target = 'blank'/>" + type + " error in OliveTin Documentation</a></p>"
|
||||
|
||||
if (isFatal) {
|
||||
bigErrorDialog.innerHTML += '<p>You will need to refresh your browser to clear this message.</p>'
|
||||
} else {
|
||||
bigErrorDialog.innerHTML += '<p>This error message will go away automatically if the problem is solved.</p>'
|
||||
}
|
||||
|
||||
bigErrorDialog.showModal()
|
||||
|
||||
console.error('Error ' + type + ': ', message)
|
||||
|
||||
const domErr = document.createElement('div')
|
||||
domErr.classList.add('error')
|
||||
domErr.classList.add(type)
|
||||
domErr.innerHTML = '<h1>Error ' + friendlyType + '</h1><p>' + message + "</p><p><a href = 'http://docs.olivetin.app/err-" + type + ".html' target = 'blank'/>" + type + " error in OliveTin Documentation</a></p>"
|
||||
|
||||
document.body.prepend(domErr)
|
||||
}
|
||||
|
||||
window.clearBigErrors = function (additionalClass) {
|
||||
let selector = 'div.error'
|
||||
|
||||
if (additionalClass != null) {
|
||||
selector += '.' + additionalClass
|
||||
}
|
||||
|
||||
for (const oldError of document.querySelectorAll(selector).values()) {
|
||||
window.old = oldError;
|
||||
oldError.remove();
|
||||
}
|
||||
window.clearBigErrors = function () {
|
||||
bigErrorDialog.close()
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -99,33 +99,53 @@ function showSection (title) {
|
|||
}
|
||||
}
|
||||
|
||||
for (const otherName of ['Actions', 'Logs']) {
|
||||
document.getElementById('show' + otherName).classList.remove('activeSection')
|
||||
document.getElementById('content' + otherName).hidden = true
|
||||
}
|
||||
|
||||
// document.getElementById('show' + name).classList.add('activeSection')
|
||||
// document.getElementById('content' + name).hidden = false
|
||||
|
||||
document.getElementById('hide-sidebar-checkbox').checked = true
|
||||
setSectionNavigationVisible(false)
|
||||
|
||||
changeDirectory(null)
|
||||
}
|
||||
|
||||
function setSectionNavigationVisible (visible) {
|
||||
const nav = document.querySelector('nav')
|
||||
const btn = document.getElementById('sidebar-toggler-button')
|
||||
|
||||
if (document.body.classList.contains('has-sidebar')) {
|
||||
if (visible) {
|
||||
nav.classList.add('shown')
|
||||
btn.setAttribute('aria-pressed', false)
|
||||
btn.setAttribute('aria-label', 'Open sidebar navigation')
|
||||
btn.innerHTML = '«'
|
||||
} else {
|
||||
nav.classList.remove('shown')
|
||||
btn.setAttribute('aria-pressed', true)
|
||||
btn.setAttribute('aria-label', 'Close sidebar navigation')
|
||||
btn.innerHTML = '☰'
|
||||
}
|
||||
} else {
|
||||
btn.disabled = true
|
||||
}
|
||||
}
|
||||
|
||||
export function setupSectionNavigation (style) {
|
||||
const nav = document.querySelector('nav')
|
||||
const btn = document.getElementById('sidebar-toggler-button')
|
||||
|
||||
if (style === 'sidebar') {
|
||||
nav.classList += 'sidebar'
|
||||
nav.classList.add('sidebar')
|
||||
|
||||
document.body.classList += 'has-sidebar'
|
||||
document.body.classList.add('has-sidebar')
|
||||
|
||||
btn.onclick = () => {
|
||||
if (nav.classList.contains('shown')) {
|
||||
setSectionNavigationVisible(false)
|
||||
} else {
|
||||
nav.classList += 'topbar'
|
||||
|
||||
document.body.classList += 'has-topbar'
|
||||
setSectionNavigationVisible(true)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nav.classList.add('topbar')
|
||||
|
||||
nav.hidden = false
|
||||
document.body.classList.add('has-topbar')
|
||||
}
|
||||
|
||||
document.getElementById('showActions').onclick = () => { showSection('Actions') }
|
||||
document.getElementById('showLogs').onclick = () => { showSection('Logs') }
|
||||
|
|
@ -159,6 +179,7 @@ function marshalDashboardStructureToHtml (json) {
|
|||
const navigationA = document.createElement('a')
|
||||
navigationA.title = dashboard.title
|
||||
navigationA.innerText = dashboard.title
|
||||
navigationA.setAttribute('href', '#' + dashboard.title)
|
||||
navigationA.onclick = () => {
|
||||
showSection(dashboard.title)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
'use strict'
|
||||
|
||||
import { initMarshaller, setupSectionNavigation, marshalDashboardComponentsJsonToHtml, marshalLogsJsonToHtml } from './js/marshaller.js'
|
||||
import {
|
||||
initMarshaller,
|
||||
setupSectionNavigation,
|
||||
marshalDashboardComponentsJsonToHtml,
|
||||
marshalLogsJsonToHtml
|
||||
} from './js/marshaller.js'
|
||||
import { checkWebsocketConnection } from './js/websocket.js'
|
||||
|
||||
function searchLogs (e) {
|
||||
|
|
@ -68,7 +73,7 @@ function fetchGetDashboardComponents () {
|
|||
return res.json()
|
||||
}).then(res => {
|
||||
if (!window.restAvailable) {
|
||||
window.clearBigErrors('fetch-buttons')
|
||||
window.clearBigErrors()
|
||||
}
|
||||
|
||||
window.restAvailable = true
|
||||
|
|
@ -77,7 +82,7 @@ function fetchGetDashboardComponents () {
|
|||
refreshServerConnectionLabel() // in-case it changed, update the label quicker
|
||||
}).catch((err) => { // err is 1st arg
|
||||
window.restAvailable = false
|
||||
window.showBigError('fetch-buttons', 'getting buttons', err, 'blat')
|
||||
window.showBigError('fetch-buttons', 'getting buttons', err, false)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -89,11 +94,13 @@ function fetchGetLogs () {
|
|||
}).then(res => {
|
||||
marshalLogsJsonToHtml(res)
|
||||
}).catch(err => {
|
||||
window.showBigError('fetch-buttons', 'getting buttons', err, 'blat')
|
||||
window.showBigError('fetch-buttons', 'getting buttons', err, false)
|
||||
})
|
||||
}
|
||||
|
||||
function processWebuiSettingsJson (settings) {
|
||||
setupSectionNavigation(settings.SectionNavigationStyle)
|
||||
|
||||
window.restBaseUrl = settings.Rest
|
||||
|
||||
if (settings.ThemeName) {
|
||||
|
|
@ -112,8 +119,13 @@ function processWebuiSettingsJson (settings) {
|
|||
document.querySelector('#available-version').hidden = false
|
||||
}
|
||||
|
||||
document.querySelector('#perma-widget').hidden = !settings.ShowNavigation
|
||||
document.querySelector('footer[title="footer"]').hidden = !settings.ShowFooter
|
||||
if (!settings.ShowNavigation) {
|
||||
document.querySelector('header').style.display = 'none'
|
||||
}
|
||||
|
||||
if (!settings.ShowFooter) {
|
||||
document.querySelector('footer[title="footer"]').style.display = 'none'
|
||||
}
|
||||
|
||||
window.pageTitle = 'OliveTin'
|
||||
|
||||
|
|
@ -130,7 +142,6 @@ function processWebuiSettingsJson (settings) {
|
|||
function main () {
|
||||
initMarshaller()
|
||||
setupLogSearchBox()
|
||||
setupSectionNavigation('sidebar')
|
||||
|
||||
window.fetch('webUiSettings.json').then(res => {
|
||||
return res.json()
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ body {
|
|||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
dialog {
|
||||
|
|
@ -33,53 +35,78 @@ fieldset {
|
|||
border: 0;
|
||||
}
|
||||
|
||||
#sidebar-toggle-wrapper {
|
||||
display: inline;
|
||||
footer {
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
footer,
|
||||
footer a {
|
||||
footer span {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
a {
|
||||
color: black;
|
||||
}
|
||||
|
||||
#sidebar-toggler-button {
|
||||
top: 1em;
|
||||
left: 0.5em;
|
||||
height: 2em;
|
||||
width: 2em;
|
||||
background-color: white;
|
||||
text-align: center;
|
||||
display: inline-grid;
|
||||
place-items: center;
|
||||
padding: .2em;
|
||||
box-shadow: 0 0 5px 0 #444;
|
||||
color: #666;
|
||||
nav ul li a {
|
||||
display: block;
|
||||
padding: 0.5em 1em;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#sidebar-toggler-button:hover,
|
||||
#sidebar-toggler-button:focus {
|
||||
a:focus {
|
||||
background-color: black;
|
||||
color: white;
|
||||
outline: 1px solid black;
|
||||
}
|
||||
|
||||
nav ul li a:hover {
|
||||
background-color: #efefef;
|
||||
color: black;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#sidebar-toggler-button {
|
||||
height: 2em;
|
||||
width: 2em;
|
||||
text-align: center;
|
||||
display: inline-grid;
|
||||
place-items: center;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
nav {
|
||||
background-color: white;
|
||||
display: none;
|
||||
left: -250px;
|
||||
}
|
||||
|
||||
nav.topbar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
place-content: end;
|
||||
border-radius: .5em;
|
||||
padding-right: 1em;
|
||||
padding-left: 1em;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
nav.sidebar {
|
||||
background-color: white;
|
||||
position: fixed;
|
||||
width: 180px;
|
||||
height: 100dvh;
|
||||
left: 0;
|
||||
top: 0;
|
||||
transition: left 0.5s ease;
|
||||
box-shadow: 0 0 10px 0 #444;
|
||||
z-index: 3;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#navigation-links {
|
||||
nav.shown {
|
||||
display: flex;
|
||||
z-index: -1;
|
||||
flex-direction: column;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.sidebar #navigation-links {
|
||||
padding-top: 4em;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
|
@ -88,55 +115,34 @@ nav.sidebar {
|
|||
flex-grow: 0;
|
||||
}
|
||||
|
||||
input:checked ~ nav.sidebar {
|
||||
left: -250px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
display: inline;
|
||||
font-size: small;
|
||||
padding-left: .5em;
|
||||
}
|
||||
|
||||
h1 a {
|
||||
color: black;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
nav ul li {
|
||||
list-style: none;
|
||||
text-align: left;
|
||||
border-bottom: 1px inset black;
|
||||
}
|
||||
|
||||
nav ul li a {
|
||||
display: block;
|
||||
padding-left: 1em;
|
||||
padding-top: 0.5em;
|
||||
padding-bottom: 0.5em;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
h1 a:visited {
|
||||
color: black;
|
||||
}
|
||||
|
||||
nav ul li a:hover {
|
||||
color: black;
|
||||
background-color: #efefef;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
nav.topbar {
|
||||
background-color: white;
|
||||
nav.topbar ul {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
nav.topbar ul li {
|
||||
display: inline-block;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
nav.topbar ul li a {
|
||||
border-radius: .5em;
|
||||
}
|
||||
|
||||
nav.sidebar ul li {
|
||||
list-style: none;
|
||||
text-align: left;
|
||||
border-bottom: 1px inset black;
|
||||
}
|
||||
|
||||
table {
|
||||
|
|
@ -190,10 +196,6 @@ tr.log-row span.icon {
|
|||
background-color: salmon;
|
||||
}
|
||||
|
||||
div.error {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.title.temporary-status-message {
|
||||
color: gray;
|
||||
}
|
||||
|
|
@ -240,6 +242,7 @@ input[type="submit"]
|
|||
background-color: white;
|
||||
box-shadow: 0 0 6px 0 #aaa;
|
||||
user-select: none;
|
||||
transition: background-color 1s ease, color 1s ease;
|
||||
}
|
||||
|
||||
/* Action Buttons */
|
||||
|
|
@ -251,10 +254,14 @@ action-button {
|
|||
action-button button {
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
z-index: 1;
|
||||
border-radius: .7em;
|
||||
}
|
||||
|
||||
span.title {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
action-button details {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
|
@ -304,37 +311,29 @@ input[type="submit"]:focus {
|
|||
|
||||
button:disabled,
|
||||
input[type="submit"]:disabled {
|
||||
color: gray;
|
||||
background-color: #333;
|
||||
color: #3c3c3c;
|
||||
background-color: #cecece;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
|
||||
/* Button animations */
|
||||
|
||||
.action-failed {
|
||||
transition: 1s;
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
.action-success {
|
||||
transition: 1s;
|
||||
background-color: limegreen;
|
||||
}
|
||||
|
||||
.action-nonzero-exit {
|
||||
animation: kf-action-nonzero-exit 1s;
|
||||
transition: 1s;
|
||||
background-color: orange;
|
||||
}
|
||||
|
||||
.action-timeout {
|
||||
transition: 1s;
|
||||
background-color: cyan;
|
||||
}
|
||||
|
||||
.action-blocked {
|
||||
transition: 1s;
|
||||
background-color: purple;
|
||||
color: white;
|
||||
}
|
||||
|
|
@ -347,10 +346,7 @@ img.logo {
|
|||
|
||||
main {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
body.has-sidebar main {
|
||||
padding-top: 3em;
|
||||
padding-top: 3.5em;
|
||||
}
|
||||
|
||||
summary {
|
||||
|
|
@ -369,15 +365,18 @@ label {
|
|||
padding-right: 1em;
|
||||
}
|
||||
|
||||
#perma-widget {
|
||||
position: absolute;
|
||||
top: 1em;
|
||||
left: 1em;
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
#perma-widget label {
|
||||
display: inline;
|
||||
header {
|
||||
text-align: left;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
z-index: 3;
|
||||
align-items: center;
|
||||
padding-left: 1em;
|
||||
padding-bottom: .2em;
|
||||
padding-top: 1em;
|
||||
position: fixed;
|
||||
background-color: #dee3e7;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
input {
|
||||
|
|
@ -505,31 +504,33 @@ div.toolbar * {
|
|||
}
|
||||
|
||||
button,
|
||||
input[type="submit"],
|
||||
#sidebar-toggler-button {
|
||||
input[type="submit"] {
|
||||
border: 1px solid #666;
|
||||
background-color: #222;
|
||||
box-shadow: 0 0 6px 0 #444;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#sidebar-toggler-button:focus,
|
||||
#sidebar-toggler-button:hover
|
||||
{
|
||||
color: white;
|
||||
button:focus,
|
||||
input[type="submit"]:focus {
|
||||
outline: 2px solid #72B7F4;
|
||||
}
|
||||
|
||||
footer,
|
||||
footer a {
|
||||
color: gray;
|
||||
footer {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
|
||||
h1 a, h1 a:visited {
|
||||
color: white;
|
||||
a, a:visited {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
nav {
|
||||
a:focus {
|
||||
background-color: #72B7F4;
|
||||
color: black;
|
||||
outline: 1px solid #72B7F4;
|
||||
}
|
||||
|
||||
nav.sidebar {
|
||||
background-color: #111;
|
||||
color: white;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue