add keyboard navigation help text to CodeEditor

This commit is contained in:
Keith J. Grant
2021-02-16 16:57:36 -08:00
parent 070c67ffe8
commit 19f4de0d05

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useRef, useCallback } from 'react'; import React, { useEffect, useRef, useCallback, useState } from 'react';
import { oneOf, bool, number, string, func } from 'prop-types'; import { oneOf, bool, number, string, func } from 'prop-types';
import ReactAce from 'react-ace'; import ReactAce from 'react-ace';
import 'ace-builds/src-noconflict/mode-json'; import 'ace-builds/src-noconflict/mode-json';
@@ -6,11 +6,23 @@ import 'ace-builds/src-noconflict/mode-javascript';
import 'ace-builds/src-noconflict/mode-yaml'; import 'ace-builds/src-noconflict/mode-yaml';
import 'ace-builds/src-noconflict/mode-django'; import 'ace-builds/src-noconflict/mode-django';
import 'ace-builds/src-noconflict/theme-github'; import 'ace-builds/src-noconflict/theme-github';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
import styled from 'styled-components'; import styled from 'styled-components';
const LINE_HEIGHT = 24; const LINE_HEIGHT = 24;
const PADDING = 12; const PADDING = 12;
const FocusWrapper = styled.div`
&& + .pf-c-form__helper-text {
display: none;
}
&:focus-within + .pf-c-form__helper-text {
display: block;
}
`;
const AceEditor = styled(ReactAce)` const AceEditor = styled(ReactAce)`
font-family: var(--pf-global--FontFamily--monospace); font-family: var(--pf-global--FontFamily--monospace);
max-height: 90vh; max-height: 90vh;
@@ -45,16 +57,21 @@ function CodeEditor({
rows, rows,
fullHeight, fullHeight,
className, className,
i18n,
}) { }) {
const [isKeyboardFocused, setIsKeyboardFocused] = useState(false);
const wrapper = useRef(null); const wrapper = useRef(null);
const editor = useRef(null); const editor = useRef(null);
useEffect(function removeTextareaTabIndex() { useEffect(
const editorInput = editor.current.refEditor?.querySelector('textarea'); function removeTextareaTabIndex() {
if (editorInput) { const editorInput = editor.current.refEditor?.querySelector('textarea');
editorInput.tabIndex = -1; if (editorInput && !readOnly) {
} editorInput.tabIndex = -1;
}, []); }
},
[readOnly]
);
const listen = useCallback(event => { const listen = useCallback(event => {
if ( if (
@@ -88,43 +105,60 @@ function CodeEditor({
const numRows = fullHeight ? value.split('\n').length : rows; const numRows = fullHeight ? value.split('\n').length : rows;
return ( return (
/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */ <>
<div ref={wrapper} tabIndex={0}> <FocusWrapper
<AceEditor ref={wrapper}
mode={aceModes[mode] || 'text'} tabIndex={readOnly ? -1 : 0}
className={`pf-c-form-control ${className}`} onFocus={e => {
theme="github" if (e.target === e.currentTarget) {
onChange={onChange} setIsKeyboardFocused(true);
value={value} }
name={id || 'code-editor'} if (e.target.className.includes('ace_scrollbar')) {
editorProps={{ $blockScrolling: true }} setIsKeyboardFocused(false);
fontSize={16} }
width="100%"
height={`${numRows * LINE_HEIGHT + PADDING}px`}
hasErrors={hasErrors}
setOptions={{
readOnly,
useWorker: false,
}} }}
commands={[ >
{ <AceEditor
name: 'escape', mode={aceModes[mode] || 'text'}
bindKey: { win: 'Esc', mac: 'Esc' }, className={`pf-c-form-control ${className}`}
exec: () => { theme="github"
wrapper.current.focus(); onChange={onChange}
value={value}
name={id || 'code-editor'}
editorProps={{ $blockScrolling: true }}
fontSize={16}
width="100%"
height={`${numRows * LINE_HEIGHT + PADDING}px`}
hasErrors={hasErrors}
setOptions={{
readOnly,
useWorker: false,
}}
commands={[
{
name: 'escape',
bindKey: { win: 'Esc', mac: 'Esc' },
exec: () => {
wrapper.current.focus();
},
}, },
}, {
{ name: 'tab escape',
name: 'tab escape', bindKey: { win: 'Shift-Tab', mac: 'Shift-Tab' },
bindKey: { win: 'Shift-Tab', mac: 'Shift-Tab' }, exec: () => {
exec: () => { wrapper.current.focus();
wrapper.current.focus(); },
}, },
}, ]}
]} ref={editor}
ref={editor} />
/> </FocusWrapper>
</div> {isKeyboardFocused && (
<div className="pf-c-form__helper-text" aria-live="polite">
{i18n._(t`Press Enter to edit. Press ESC to stop editing.`)}
</div>
)}
</>
); );
} }
CodeEditor.propTypes = { CodeEditor.propTypes = {
@@ -146,4 +180,4 @@ CodeEditor.defaultProps = {
className: '', className: '',
}; };
export default CodeEditor; export default withI18n()(CodeEditor);