Skip to content

Commit

Permalink
fix(a11y): scope keyux events to the uploader-related DOM
Browse files Browse the repository at this point in the history
  • Loading branch information
nd0ut committed Jun 23, 2024
1 parent c3f4cc7 commit c4538c4
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 3 deletions.
6 changes: 6 additions & 0 deletions abstract/Block.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,11 @@ export class Block extends BaseComponent {
return this.has('*localeManager') ? this.$['*localeManager'] : null;
}

/** @returns {A11y | null} */
get a11y() {
return this.has('*a11y') ? this.$['*a11y'] : null;
}

destroyCallback() {
/** @type {Set<Block>} */
let blocksRegistry = this.$['*blocksRegistry'];
Expand Down Expand Up @@ -225,6 +230,7 @@ export class Block extends BaseComponent {
Data.deleteCtx(this.ctxName);

this.localeManager?.destroy();
this.a11y?.destroy();
}

/**
Expand Down
80 changes: 77 additions & 3 deletions abstract/a11y.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,85 @@
// @ts-check
import { startKeyUX, hiddenKeyUX, jumpKeyUX, focusGroupKeyUX, pressKeyUX } from 'keyux';

/**
* MinimalWindow interface is not exported by keyux, so we import it here using tricky way.
*
* @typedef {Parameters<import('keyux').KeyUXModule>[0]} MinimalWindow
*/

/**
* This is global window wrapper that allows to scope event listeners to a specific part of the DOM.
*
* It is used to scope the key UX to the widget.
*
* @implements {MinimalWindow}
*/
class ScopedMinimalWindow {
/** @private */
_listeners = new Map();

/**
* @private
* @type {() => NodeListOf<Node>}
*/
_selectScope;

/** @param {() => NodeListOf<Node>} selectScope */
constructor(selectScope) {
this._selectScope = selectScope;
}

/**
* @param {'keydown' | 'keyup'} type
* @param {(event: Event) => void} listener
*/
addEventListener(type, listener) {
/** @param {Event} e */
const wrappedListener = (e) => {
const target = e.target;
if (!target) return;
const scope = [...this._selectScope()];
if (scope.some((el) => el === e.target || el.contains(/** @type {Node} */ (target)))) {
listener(e);
}
};
this._listeners.set(listener, wrappedListener);
window.addEventListener(type, wrappedListener);
}

/**
* @param {'keydown' | 'keyup'} type
* @param {(event: {}) => void} listener
*/
removeEventListener(type, listener) {
const wrappedListener = this._listeners.get(listener);
if (wrappedListener) {
window.removeEventListener(type, wrappedListener);
}
}

get document() {
return window.document;
}

get navigator() {
return window.navigator;
}
}

export class A11y {
/**
* @private
* @type {() => void}
*/
_destroy;

constructor() {
this.init();
const scopedWindow = new ScopedMinimalWindow(() => document.querySelectorAll('[lr-wgt-common]'));
this._destroy = startKeyUX(scopedWindow, [focusGroupKeyUX(), pressKeyUX('is-pressed'), jumpKeyUX(), hiddenKeyUX()]);
}

init() {
startKeyUX(window, [focusGroupKeyUX(), pressKeyUX('is-pressed'), jumpKeyUX(), hiddenKeyUX()]);
destroy() {
this._destroy();
}
}

0 comments on commit c4538c4

Please sign in to comment.