[Glitch] Patch over a11y issues caused by "simple" ruby gems
Port 5553448678a8a9f4ef970948b6fb7f013e9a4238 to glitch-soc Signed-off-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
parent
b0f609bfb7
commit
4eeb1b0118
@ -181,6 +181,8 @@ async function loaded() {
|
||||
|
||||
truncateRuleHints();
|
||||
|
||||
applyRailsA11yPatches();
|
||||
|
||||
const reactComponents = document.querySelectorAll('[data-component]');
|
||||
|
||||
if (reactComponents.length > 0) {
|
||||
@ -515,6 +517,88 @@ on('click', '.rules-list button', ({ target }) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Patch accessibility issues caused by Ruby Gems that
|
||||
* don't produce accessible markup (simple-forms & simple-navigation)
|
||||
*/
|
||||
function applyRailsA11yPatches() {
|
||||
/**
|
||||
* Mark current navigation item with aria-current
|
||||
*/
|
||||
const activeNavLink = document.querySelector(
|
||||
'.simple-navigation-active-leaf a.selected',
|
||||
);
|
||||
activeNavLink?.setAttribute('aria-current', 'page');
|
||||
|
||||
/**
|
||||
* Hides the asterisk added to labels of required form fields
|
||||
* from assistive tech. (Those fields already have the `required` attribute)
|
||||
*/
|
||||
document
|
||||
.querySelectorAll<HTMLElement>('.simple_form label.required abbr')
|
||||
.forEach((element) => {
|
||||
element.setAttribute('aria-hidden', 'true');
|
||||
});
|
||||
|
||||
/**
|
||||
* Associate form field hints with their inputs via aria-describedby
|
||||
*/
|
||||
document
|
||||
.querySelectorAll<HTMLDivElement>('.simple_form .field_with_hint')
|
||||
.forEach((field) => {
|
||||
const inputs = field.querySelectorAll<
|
||||
HTMLInputElement | HTMLTextAreaElement
|
||||
>("input[type='text'], input[type='checkbox'], textarea");
|
||||
|
||||
const hint = field.querySelector<HTMLDivElement>('.hint');
|
||||
|
||||
// Bail out if there are more than one input as
|
||||
// the association can't be safely made.
|
||||
if (inputs.length !== 1 || !inputs[0] || !hint) {
|
||||
return;
|
||||
}
|
||||
|
||||
const input = inputs[0];
|
||||
const inputId = input.getAttribute('id');
|
||||
const hintId = `${inputId}_hint`;
|
||||
|
||||
input.setAttribute('aria-describedby', hintId);
|
||||
hint.setAttribute('id', hintId);
|
||||
});
|
||||
|
||||
/**
|
||||
* Add fieldset-like group labels ("legends") to the date-of-birth selector
|
||||
* and groups of radio buttons
|
||||
*/
|
||||
const groups = document.querySelectorAll<HTMLDivElement>(
|
||||
'.simple_form .date_of_birth, .simple_form .input.with_label.radio_buttons',
|
||||
);
|
||||
groups.forEach((groupWrapper) => {
|
||||
// This is the element serving as the label of the group.
|
||||
const groupLabel = groupWrapper.querySelector<HTMLLabelElement>('label');
|
||||
const labelWithId =
|
||||
groupWrapper.querySelector<HTMLLabelElement>('label[for]');
|
||||
const groupHint = groupWrapper.querySelector<HTMLDivElement>('.hint');
|
||||
|
||||
// We need a unique ID to generate the aria associations. If `groupLabel`
|
||||
// doesn't have one, we just take the first label with a `for` attribute
|
||||
// that we can find, which is fine because we'll modify it before use.
|
||||
const inputId =
|
||||
groupLabel?.getAttribute('for') ?? labelWithId?.getAttribute('for');
|
||||
const labelId = `${inputId}_label`;
|
||||
const hintId = `${inputId}_hint`;
|
||||
|
||||
groupLabel?.setAttribute('id', labelId);
|
||||
groupHint?.setAttribute('id', hintId);
|
||||
|
||||
groupWrapper.setAttribute('role', 'group');
|
||||
groupWrapper.setAttribute('aria-labelledby', labelId);
|
||||
if (groupHint) {
|
||||
groupWrapper.setAttribute('aria-describedby', hintId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function main() {
|
||||
ready(loaded).catch((error: unknown) => {
|
||||
console.error(error);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user