Fix ace a11y listener (#60639)
Also move the hook use_ui_ace_keyboard_mode.tsx into es_ui_shared This was defined (and used) in both Console and SearchProfiler. Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
ef0935ff45
commit
b841526979
|
@ -22,6 +22,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { debounce } from 'lodash';
|
||||
import { parse } from 'query-string';
|
||||
import React, { CSSProperties, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useUIAceKeyboardMode } from '../../../../../../../es_ui_shared/public';
|
||||
// @ts-ignore
|
||||
import mappings from '../../../../../lib/mappings/mappings';
|
||||
import { ConsoleMenu } from '../../../../components';
|
||||
|
@ -34,7 +35,6 @@ import {
|
|||
import * as senseEditor from '../../../../models/sense_editor';
|
||||
import { autoIndent, getDocumentation } from '../console_menu_actions';
|
||||
import { subscribeResizeChecker } from '../subscribe_console_resize_checker';
|
||||
import { useUIAceKeyboardMode } from '../use_ui_ace_keyboard_mode';
|
||||
import { applyCurrentSettings } from './apply_editor_settings';
|
||||
import { registerCommands } from './keyboard_shortcuts';
|
||||
|
||||
|
|
|
@ -29,3 +29,5 @@ export {
|
|||
} from './request/np_ready_request';
|
||||
|
||||
export { indices } from './indices';
|
||||
|
||||
export { useUIAceKeyboardMode } from './use_ui_ace_keyboard_mode';
|
||||
|
|
|
@ -96,7 +96,7 @@ export function useUIAceKeyboardMode(aceTextAreaElement: HTMLTextAreaElement | n
|
|||
}
|
||||
return () => {
|
||||
if (aceTextAreaElement) {
|
||||
document.removeEventListener('keydown', documentKeyDownListener);
|
||||
document.removeEventListener('keydown', documentKeyDownListener, { capture: true });
|
||||
aceTextAreaElement.removeEventListener('keydown', aceKeydownListener);
|
||||
const textAreaContainer = aceTextAreaElement.parentElement;
|
||||
if (textAreaContainer && textAreaContainer.contains(overlayMountNode.current!)) {
|
|
@ -8,7 +8,7 @@ import React, { memo, useRef, useEffect, useState } from 'react';
|
|||
import { Editor as AceEditor } from 'brace';
|
||||
|
||||
import { initializeEditor } from './init_editor';
|
||||
import { useUIAceKeyboardMode } from './use_ui_ace_keyboard_mode';
|
||||
import { useUIAceKeyboardMode } from '../../../../../../src/plugins/es_ui_shared/public';
|
||||
|
||||
type EditorShim = ReturnType<typeof createEditorShim>;
|
||||
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Copied from Console plugin
|
||||
*/
|
||||
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { keyCodes, EuiText } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
const OverlayText = () => (
|
||||
// The point of this element is for accessibility purposes, so ignore eslint error
|
||||
// in this case
|
||||
//
|
||||
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
|
||||
<>
|
||||
<EuiText size="s">
|
||||
{i18n.translate('xpack.searchProfiler.aceAccessibilityOverlayInstructionEnter', {
|
||||
defaultMessage: 'Press Enter to start editing.',
|
||||
})}
|
||||
</EuiText>
|
||||
<EuiText size="s">
|
||||
{i18n.translate('xpack.searchProfiler.aceAccessibilityOverlayInstructionExit', {
|
||||
defaultMessage: `When you are done, press Escape to stop editing.`,
|
||||
})}
|
||||
</EuiText>
|
||||
</>
|
||||
);
|
||||
|
||||
export function useUIAceKeyboardMode(aceTextAreaElement: HTMLTextAreaElement | null) {
|
||||
const overlayMountNode = useRef<HTMLDivElement | null>(null);
|
||||
const autoCompleteVisibleRef = useRef<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
function onDismissOverlay(event: KeyboardEvent) {
|
||||
if (event.keyCode === keyCodes.ENTER) {
|
||||
event.preventDefault();
|
||||
aceTextAreaElement!.focus();
|
||||
}
|
||||
}
|
||||
|
||||
function enableOverlay() {
|
||||
if (overlayMountNode.current) {
|
||||
overlayMountNode.current.focus();
|
||||
}
|
||||
}
|
||||
|
||||
const isAutoCompleteVisible = () => {
|
||||
const autoCompleter = document.querySelector<HTMLDivElement>('.ace_autocomplete');
|
||||
if (!autoCompleter) {
|
||||
return false;
|
||||
}
|
||||
// The autoComplete is just hidden when it's closed, not removed from the DOM.
|
||||
return autoCompleter.style.display !== 'none';
|
||||
};
|
||||
|
||||
const documentKeyDownListener = () => {
|
||||
autoCompleteVisibleRef.current = isAutoCompleteVisible();
|
||||
};
|
||||
|
||||
const aceKeydownListener = (event: KeyboardEvent) => {
|
||||
if (event.keyCode === keyCodes.ESCAPE && !autoCompleteVisibleRef.current) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
enableOverlay();
|
||||
}
|
||||
};
|
||||
|
||||
if (aceTextAreaElement) {
|
||||
// We don't control HTML elements inside of ace so we imperatively create an element
|
||||
// that acts as a container and insert it just before ace's textarea element
|
||||
// so that the overlay lives at the correct spot in the DOM hierarchy.
|
||||
overlayMountNode.current = document.createElement('div');
|
||||
overlayMountNode.current.className = 'kbnUiAceKeyboardHint';
|
||||
overlayMountNode.current.setAttribute('role', 'application');
|
||||
overlayMountNode.current.tabIndex = 0;
|
||||
overlayMountNode.current.addEventListener('focus', enableOverlay);
|
||||
overlayMountNode.current.addEventListener('keydown', onDismissOverlay);
|
||||
|
||||
ReactDOM.render(<OverlayText />, overlayMountNode.current);
|
||||
|
||||
aceTextAreaElement.parentElement!.insertBefore(overlayMountNode.current, aceTextAreaElement);
|
||||
aceTextAreaElement.setAttribute('tabindex', '-1');
|
||||
|
||||
// Order of events:
|
||||
// 1. Document capture event fires first and we check whether an autocomplete menu is open on keydown
|
||||
// (not ideal because this is scoped to the entire document).
|
||||
// 2. Ace changes it's state (like hiding or showing autocomplete menu)
|
||||
// 3. We check what button was pressed and whether autocomplete was visible then determine
|
||||
// whether it should act like a dismiss or if we should display an overlay.
|
||||
document.addEventListener('keydown', documentKeyDownListener, { capture: true });
|
||||
aceTextAreaElement.addEventListener('keydown', aceKeydownListener);
|
||||
}
|
||||
return () => {
|
||||
if (aceTextAreaElement) {
|
||||
document.removeEventListener('keydown', documentKeyDownListener);
|
||||
aceTextAreaElement.removeEventListener('keydown', aceKeydownListener);
|
||||
const textAreaContainer = aceTextAreaElement.parentElement;
|
||||
if (textAreaContainer && textAreaContainer.contains(overlayMountNode.current!)) {
|
||||
textAreaContainer.removeChild(overlayMountNode.current!);
|
||||
}
|
||||
}
|
||||
};
|
||||
}, [aceTextAreaElement]);
|
||||
}
|
Loading…
Reference in a new issue