Skip to content

Commit

Permalink
a11y: use role=alert for messages from Django and JS
Browse files Browse the repository at this point in the history
Also define a custom module+css for alerts (chore).
  • Loading branch information
davidbgk committed Feb 23, 2024
1 parent 76ed220 commit 6d47382
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 10 deletions.
28 changes: 28 additions & 0 deletions umap/static/umap/alerts.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#umap-alert {
box-sizing: border-box;
min-height: 46px;
line-height: 46px;
padding-left: 10px;
width: calc(100% - 500px);
position: absolute;
left: 250px; /* Keep save/cancel button accessible. */
right: 250px;
box-shadow: 0 1px 7px #999999;
background: none repeat scroll 0 0 rgba(20, 22, 23, 0.8);
font-weight: bold;
color: #fff;
font-size: 0.8em;
z-index: 1012;
border-radius: 2px;
visibility: visible;
top: 23px;
}
#umap-alert:empty {
top: -46px;
visibility: hidden;
}


#umap-alert.error {
background-color: #c60f13;
}
34 changes: 34 additions & 0 deletions umap/static/umap/js/modules/alerts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
export default class Alerts {
constructor() {
this.alertNode = document.querySelector('[role="alert"]')
const observer = new MutationObserver(this._callback.bind(this))
observer.observe(this.alertNode, { childList: true })
// On initial page load, we want to display messages from Django.
Array.from(this.alertNode.children).forEach(this._display.bind(this))
}

_callback(mutationList, observer) {
for (const mutation of mutationList) {
this._display(mutation.addedNodes[0])
}
}

_display(alert) {
const duration = alert.dataset.duration
const level = alert.dataset.level
const alertDiv = document.createElement('div')
alertDiv.id = 'umap-alert'
alertDiv.classList.add(level)
alertDiv.appendChild(alert.cloneNode(true))
this.alertNode.after(alertDiv)
if (duration) {
setTimeout(() => {
alertDiv.remove()
}, duration)
}
}

add(message, level, duration) {
this.alertNode.innerHTML = `<p data-level="${level}" data-duration="${duration}">${message}</p>`
}
}
12 changes: 11 additions & 1 deletion umap/static/umap/js/modules/global.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import * as L from '../../vendors/leaflet/leaflet-src.esm.js'
import URLs from './urls.js'
import Alerts from './alerts.js'
import Browser from './browser.js'
import { Request, ServerRequest, RequestError, HTTPError, NOKError } from './request.js'
// Import modules and export them to the global scope.
// For the not yet module-compatible JS out there.

// Copy the leaflet module, it's expected by leaflet plugins to be writeable.
window.L = { ...L }
window.U = { URLs, Request, ServerRequest, RequestError, HTTPError, NOKError, Browser }
window.U = {
Alerts,
URLs,
Request,
ServerRequest,
RequestError,
HTTPError,
NOKError,
Browser,
}
2 changes: 2 additions & 0 deletions umap/static/umap/js/umap.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ U.Map = L.Map.extend({
// After calling parent initialize, as we are doing initCenter our-selves
if (geojson.geometry) this.options.center = this.latLng(geojson.geometry)
this.urls = new U.URLs(this.options.urls)
this.alerts = new U.Alerts()

this.ui = new U.UI(this._container)
this.ui.on('dataloading', (e) => this.fire('dataloading', e))
Expand Down Expand Up @@ -1062,6 +1063,7 @@ U.Map = L.Map.extend({
if (!error) {
let duration = 3000,
alert = { content: L._('Map has been saved!'), level: 'info' }
this.alerts.add(L._('Map has been saved!'), 'info', duration)
if (!this.options.umap_id) {
alert.content = L._('Congratulations, your map has been created!')
this.options.umap_id = data.id
Expand Down
1 change: 1 addition & 0 deletions umap/static/umap/test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@

<link rel="stylesheet" href="../../umap/font.css" />
<link rel="stylesheet" href="../../umap/base.css" />
<link rel="stylesheet" href="../../umap/alerts.css" />
<link rel="stylesheet" href="../../umap/content.css" />
<link rel="stylesheet" href="../../umap/nav.css" />
<link rel="stylesheet" href="../../umap/map.css" />
Expand Down
1 change: 1 addition & 0 deletions umap/templates/umap/css.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
href="{% static 'umap/vendors/iconlayers/iconLayers.css' %}" />
<link rel="stylesheet" href="{% static 'umap/font.css' %}" />
<link rel="stylesheet" href="{% static 'umap/base.css' %}" />
<link rel="stylesheet" href="{% static 'umap/alerts.css' %}" />
<link rel="stylesheet" href="{% static 'umap/content.css' %}" />
<link rel="stylesheet" href="{% static 'umap/nav.css' %}" />
<link rel="stylesheet" href="{% static 'umap/map.css' %}" />
Expand Down
15 changes: 7 additions & 8 deletions umap/templates/umap/map_init.html
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
{% load umap_tags %}

<div role="alert">
{% for m in messages %}
{# We have just one, but we need to loop, as for messages API #}
<p data-level="{{ m.tags }}" data-duration="100000">{{ m }}</p>
{% endfor %}
</div>
<div id="map"></div>
<!-- djlint:off -->
<script defer type="text/javascript">
window.addEventListener('DOMContentLoaded', (event) => {
U.MAP = new U.Map("map", {{ map_settings|notag|safe }})
{% for m in messages %}
{# We have just one, but we need to loop, as for messages API #}
U.MAP.ui.alert({
content: "{{ m }}",
level: "{{ m.tags }}",
duration: 100000
})
{% endfor %}
})
</script>
<!-- djlint:on -->
2 changes: 1 addition & 1 deletion umap/templates/umap/messages.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="wrapper">
<div class="row">
<div class="row" role="alert">
{% if messages %}
<ul class="messages">
{% for message in messages %}
Expand Down

0 comments on commit 6d47382

Please sign in to comment.