-
-
Notifications
You must be signed in to change notification settings - Fork 213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
a11y: use role=alert for messages from Django and JS #1641
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
[data-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; | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: flex-start; | ||
} | ||
[data-alert][data-level="error"] { | ||
background-color: #c60f13; | ||
} | ||
[data-alert] [data-close] { | ||
color: #fff; | ||
padding-right: 10px; | ||
width: 100px; | ||
line-height: 1; | ||
margin: .5rem; | ||
background-color: #202425; | ||
font-size: .7rem; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
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].filter((item) => item.tagName === 'P').pop() | ||
) | ||
almet marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
|
||
_display(alert) { | ||
const duration = alert.dataset?.duration || 3000 | ||
const level = alert.dataset?.level || 'info' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This repeats (in the code) the default behavior values. Maybe use a constant tied to the class instead? |
||
const wrapper = document.createElement('div') | ||
const alertHTML = alert.cloneNode(true).outerHTML | ||
wrapper.innerHTML = ` | ||
<div data-level="${level}" data-alert data-toclose> | ||
${alertHTML} | ||
<button class="umap-close-link" type="button" data-close> | ||
<i class="umap-close-icon"></i><span>${L._('Close')}</span> | ||
</button> | ||
</div> | ||
` | ||
const alertDiv = wrapper.firstElementChild | ||
this.alertNode.after(alertDiv) | ||
if (isFinite(duration)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Love using |
||
setTimeout(() => { | ||
alertDiv.remove() | ||
}, duration) | ||
} | ||
} | ||
|
||
add(message, level = 'info', duration = 3000) { | ||
this.alertNode.innerHTML = ` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this replacing all the whole node? How does this plays with multiple alerts being added? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's indeed a concern, I was wondering if that case actually happens 🤔 |
||
<p data-level="${level}" data-duration="${duration}"> | ||
${message} | ||
</p> | ||
` | ||
} | ||
} | ||
|
||
// TODISCUSS: this might be something we want somewhere else. | ||
document.addEventListener('click', (event) => { | ||
if (event.target.closest('[data-close]')) { | ||
event.target.closest('[data-toclose]').remove() | ||
} | ||
}) |
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, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Maybe
alertNode
could be a private property of the object?