Fix Ace editor keyboard trap (#5451)

* bug: fix a11y and add sr notification

* refactor: improvements to sr notification
This commit is contained in:
Rafael Wendel
2021-04-07 09:50:54 -03:00
committed by GitHub
parent a61a25dd32
commit bb1f8cbcf5
2 changed files with 65 additions and 0 deletions

View File

@@ -2,6 +2,7 @@ import React, { useEffect, useMemo, useState, useCallback, useImperativeHandle }
import PropTypes from "prop-types";
import cx from "classnames";
import { AceEditor, snippetsModule, updateSchemaCompleter } from "./ace";
import { srNotify } from "@/lib/accessibility";
import { SchemaItemType } from "@/components/queries/SchemaBrowser";
import resizeObserver from "@/services/resizeObserver";
import QuerySnippet from "@/services/query-snippet";
@@ -89,6 +90,25 @@ const QueryEditor = React.forwardRef(function(
// Lineup only mac
editor.commands.bindKey({ win: null, mac: "Ctrl+P" }, "golineup");
// Esc for exiting
editor.commands.bindKey({ win: "Esc", mac: "Esc" }, () => {
editor.blur();
});
let notificationCleanup = null;
editor.on("focus", () => {
notificationCleanup = srNotify({
text: "You've entered the SQL editor. To exit press the ESC key.",
politeness: "assertive",
});
});
editor.on("blur", () => {
if (notificationCleanup) {
notificationCleanup();
}
});
// Reset Completer in case dot is pressed
editor.commands.on("afterExec", e => {
if (e.command.name === "insertstring" && e.args === "." && editor.completer) {

View File

@@ -0,0 +1,45 @@
import { HTMLAttributes } from "react";
interface SrNotifyProps {
text: string;
expiry: number;
container: HTMLElement;
politeness: HTMLAttributes<HTMLDivElement>["aria-live"];
}
export function srNotify({ text, expiry = 1000, container = document.body, politeness = "polite" }: SrNotifyProps) {
const element = document.createElement("div");
const id = `speak-${Date.now()}`;
element.id = id;
element.className = "sr-only";
element.textContent = text;
element.setAttribute("role", "alert");
element.setAttribute("aria-live", politeness);
container.appendChild(element);
let timer: null | number = null;
let isDone = false;
const cleanupFn = () => {
if (isDone) {
return;
}
isDone = true;
try {
container.removeChild(element);
} catch (e) {
console.error(e);
}
if (timer) {
window.clearTimeout(timer);
}
};
timer = window.setTimeout(cleanupFn, expiry);
return cleanupFn;
}