First draft

This commit is contained in:
CocoByte 2025-02-17 14:31:40 -07:00
parent 92c66b7eca
commit 1b255366af
No known key found for this signature in database
GPG key ID: BBFAA2526384C97F
4 changed files with 78 additions and 2 deletions

View file

@ -1,4 +1,4 @@
import { hideElement, showElement, addOrRemoveSessionBoolean } from './helpers-admin.js';
import { hideElement, showElement, addOrRemoveSessionBoolea, announceForScreenReaders } from './helpers-admin.js';
import { handlePortfolioSelection } from './helpers-portfolio-dynamic-fields.js';
function displayModalOnDropdownClick(linkClickedDisplaysModal, statusDropdown, actionButton, valueToCheck){
@ -684,3 +684,42 @@ export function initDynamicDomainRequestFields(){
handleSuborgFieldsAndButtons();
}
}
export function initFilterFocusListeners() {
document.addEventListener("DOMContentLoaded", function() {
let filters = document.querySelectorAll("#changelist-filter li a"); // Get list of all filter links
let clickedFilter = false; // Used to determine if we are truly navigating away or not
// Restore focus from localStorage if it exists
let lastClickedFilter = localStorage.getItem("admin_filter_focus");
if (lastClickedFilter) {
let focusedElement = document.querySelector(`#changelist-filter li a[href='${lastClickedFilter}']`);
if (focusedElement) {
// Focus the element
focusedElement.setAttribute("tabindex", "-1");
focusedElement.focus({ preventScroll: true });
// Announce focus change for screen readers
announceForScreenReaders("Filter refocused on " + focusedElement.textContent);
}
}
// Setup listeners. When a filter is clicked, save it as the filter to focus after page refresh
filters.forEach(filter => {
filter.addEventListener("click", function() {
// Save this filter in local storage so we can focus it after page refresh
localStorage.setItem("admin_filter_focus", this.getAttribute("href"));
// Mark that a filter was clicked so that our beforeunload listener doesn't clear the local storage
clickedFilter = true;
});
});
// Clear focus selection in local storage if user is truly leaving the page
window.addEventListener("beforeunload", function(event) {
if (!clickedFilter) {
localStorage.removeItem("admin_filter_focus");
}
});
});
}

View file

@ -22,3 +22,23 @@ export function addOrRemoveSessionBoolean(name, add){
sessionStorage.removeItem(name);
}
}
/**
* Creates a temporary live region to announce messages for screen readers.
*/
export function announceForScreenReaders(message) {
let liveRegion = document.createElement("div");
liveRegion.setAttribute("aria-live", "assertive");
liveRegion.setAttribute("role", "alert");
liveRegion.style.position = "absolute";
liveRegion.style.left = "-9999px";
document.body.appendChild(liveRegion);
// Delay the update slightly to ensure it's recognized
setTimeout(() => {
liveRegion.textContent = message;
setTimeout(() => {
document.body.removeChild(liveRegion);
}, 1000);
}, 100);
}

View file

@ -10,7 +10,8 @@ import {
initRejectedEmail,
initApprovedDomain,
initCopyRequestSummary,
initDynamicDomainRequestFields } from './domain-request-form.js';
initDynamicDomainRequestFields,
initFilterFocusListeners } from './domain-request-form.js';
import { initDomainFormTargetBlankButtons } from './domain-form.js';
import { initDynamicPortfolioFields } from './portfolio-form.js';
import { initDynamicDomainInformationFields } from './domain-information-form.js';
@ -31,6 +32,7 @@ initRejectedEmail();
initApprovedDomain();
initCopyRequestSummary();
initDynamicDomainRequestFields();
initFilterFocusListeners();
// Domain
initDomainFormTargetBlankButtons();

View file

@ -39,6 +39,21 @@
</h2>
{% endblock %}
{% for spec in cl.filter_specs %}
<div id="changelist-filter">
<h3>{{ spec.title }}</h3>
<ul>
{% for choice in spec.choices %}
<li>
<a id="filter-{{ forloop.counter }}" href="{{ choice.query_string|iriencode }}">
{{ choice.display }}
</a>
</li>
{% endfor %}
</ul>
</div>
{% endfor %}
{% comment %} Replace the Django ul markup with a div. We'll replace the li with a p in change_list_object_tools {% endcomment %}
{% block object-tools %}
<div class="object-tools">