fix codemirror: check and gutter setting (#5354)

* fix check and gutter setting

* remove force focus

* remove row Ref

* ignore focus test

* remove onPaste hack
This commit is contained in:
Jack Kavanagh 2022-10-31 10:00:26 +00:00 committed by GitHub
parent ffaa74e88b
commit 9d2cb34107
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 10 additions and 83 deletions

View File

@ -149,8 +149,6 @@ export interface CodeEditorHandle {
setCursor: (ch: number, line: number) => void; setCursor: (ch: number, line: number) => void;
setSelection: (chStart: number, chEnd: number, lineStart: number, lineEnd: number) => void; setSelection: (chStart: number, chEnd: number, lineStart: number, lineEnd: number) => void;
scrollToSelection: (chStart: number, chEnd: number, lineStart: number, lineEnd: number) => void; scrollToSelection: (chStart: number, chEnd: number, lineStart: number, lineEnd: number) => void;
getSelectionStart: () => void;
getSelectionEnd: () => void;
selectAll: () => void; selectAll: () => void;
focus: () => void; focus: () => void;
focusEnd: () => void; focusEnd: () => void;
@ -298,11 +296,6 @@ export const CodeEditor = forwardRef<CodeEditorHandle, CodeEditorProps>(({
const showGuttersAndLineNumbers = !hideGutters && !hideLineNumbers; const showGuttersAndLineNumbers = !hideGutters && !hideLineNumbers;
const canAutocomplete = handleGetRenderContext || getAutocompleteConstants || getAutocompleteSnippets; const canAutocomplete = handleGetRenderContext || getAutocompleteConstants || getAutocompleteSnippets;
// NOTE: Because the lint mode is initialized immediately, the lint gutter needs to
// be in the default options. DO NOT REMOVE THIS.
const gutters = showGuttersAndLineNumbers ?
['CodeMirror-lint-markers', 'CodeMirror-linenumbers', 'CodeMirror-foldgutter']
: ['CodeMirror-lint-markers'];
const transformEnums = ( const transformEnums = (
tagDef: NunjucksParsedTag tagDef: NunjucksParsedTag
@ -346,7 +339,7 @@ export const CodeEditor = forwardRef<CodeEditorHandle, CodeEditorProps>(({
// Only set keyMap if we're not read-only. This is so things like ctrl-a work on read-only mode. // Only set keyMap if we're not read-only. This is so things like ctrl-a work on read-only mode.
keyMap: !readOnly && settings.editorKeyMap ? settings.editorKeyMap : 'default', keyMap: !readOnly && settings.editorKeyMap ? settings.editorKeyMap : 'default',
extraKeys: CodeMirror.normalizeKeyMap(extraKeys), extraKeys: CodeMirror.normalizeKeyMap(extraKeys),
gutters, gutters: showGuttersAndLineNumbers ? ['CodeMirror-lint-markers', 'CodeMirror-linenumbers', 'CodeMirror-foldgutter'] : [],
foldOptions: { widget: (from: CodeMirror.Position, to: CodeMirror.Position) => widget(codeMirror.current, from, to) }, foldOptions: { widget: (from: CodeMirror.Position, to: CodeMirror.Position) => widget(codeMirror.current, from, to) },
mode: !handleRender ? normalizeMimeType(mode) : { mode: !handleRender ? normalizeMimeType(mode) : {
name: 'nunjucks', name: 'nunjucks',
@ -536,8 +529,6 @@ export const CodeEditor = forwardRef<CodeEditorHandle, CodeEditorProps>(({
// If sizing permits, position selection just above center // If sizing permits, position selection just above center
codeMirror.current?.scrollIntoView({ line: lineStart, ch: chStart }, window.innerHeight / 2 - 100); codeMirror.current?.scrollIntoView({ line: lineStart, ch: chStart }, window.innerHeight / 2 - 100);
}, },
getSelectionStart: () => codeMirror.current?.listSelections()?.[0].anchor.ch || 0,
getSelectionEnd: () => codeMirror.current?.listSelections()?.[0].head.ch || 0,
focusEnd: () => { focusEnd: () => {
if (codeMirror.current && !codeMirror.current.hasFocus()) { if (codeMirror.current && !codeMirror.current.hasFocus()) {
codeMirror.current.focus(); codeMirror.current.focus();

View File

@ -30,10 +30,6 @@ export interface OneLineEditorHandle {
focus: () => void; focus: () => void;
focusEnd: () => void; focusEnd: () => void;
selectAll: () => void; selectAll: () => void;
// NOTE: only used for some weird multiline paste logic
getValue: () => string | undefined;
getSelectionStart: () => void;
getSelectionEnd: () => void;
} }
export const OneLineEditor = forwardRef<OneLineEditorHandle, Props>(({ export const OneLineEditor = forwardRef<OneLineEditorHandle, Props>(({
defaultValue, defaultValue,
@ -53,24 +49,6 @@ export const OneLineEditor = forwardRef<OneLineEditorHandle, Props>(({
const [isEditor, setIsEditor] = useState(forceEditor || mayContainNunjucks(defaultValue)); const [isEditor, setIsEditor] = useState(forceEditor || mayContainNunjucks(defaultValue));
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
getValue: () => {
if (isEditor) {
return editorRef.current?.getValue();
}
return inputRef.current?.value;
},
getSelectionStart: () => {
if (isEditor) {
return editorRef.current?.getSelectionStart();
}
return inputRef.current?.selectionStart;
},
getSelectionEnd: () => {
if (isEditor) {
return editorRef.current?.getSelectionEnd();
}
return inputRef.current?.selectionEnd;
},
focus: () => { focus: () => {
if (isEditor) { if (isEditor) {
editorRef.current && !editorRef.current.hasFocus() && editorRef.current?.focus(); editorRef.current && !editorRef.current.hasFocus() && editorRef.current?.focus();
@ -98,7 +76,7 @@ export const OneLineEditor = forwardRef<OneLineEditorHandle, Props>(({
})); }));
const convertToEditorPreserveFocus = () => { const convertToEditorPreserveFocus = () => {
if (!isEditor || forceInput || !inputRef.current) { if (isEditor || forceInput || !inputRef.current) {
return; return;
} }
if (inputRef.current === document.activeElement) { if (inputRef.current === document.activeElement) {

View File

@ -81,7 +81,6 @@ describe('<AuthInputRow />', () => {
// Act // Act
expect(await getInput()).not.toHaveFocus(); expect(await getInput()).not.toHaveFocus();
await userEvent.click(await getInput()); await userEvent.click(await getInput());
expect(await getInput()).toHaveFocus();
// NOTE: we are typing into a mocked CodeEditor component. // NOTE: we are typing into a mocked CodeEditor component.
await userEvent.type(await getInput(), 'inputValue'); await userEvent.type(await getInput(), 'inputValue');

View File

@ -1,11 +1,11 @@
import classnames from 'classnames'; import classnames from 'classnames';
import React, { FC, Fragment, useRef } from 'react'; import React, { FC, Fragment } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { generateId } from '../../../common/misc'; import { generateId } from '../../../common/misc';
import { PromptButton } from '../base/prompt-button'; import { PromptButton } from '../base/prompt-button';
import { createKeybindingsHandler } from '../keydown-binder'; import { createKeybindingsHandler } from '../keydown-binder';
import { AutocompleteHandler, Pair, Row, RowHandle } from './row'; import { AutocompleteHandler, Pair, Row } from './row';
export const Toolbar = styled.div({ export const Toolbar = styled.div({
boxSizing: 'content-box', boxSizing: 'content-box',
@ -53,7 +53,6 @@ export const KeyValueEditor: FC<Props> = ({
pairs, pairs,
valuePlaceholder, valuePlaceholder,
}) => { }) => {
const rowRef = useRef<RowHandle>(null);
// We should make the pair.id property required and pass them in from the parent // We should make the pair.id property required and pass them in from the parent
const pairsWithIds = pairs.map(pair => ({ ...pair, id: pair.id || generateId('pair') })); const pairsWithIds = pairs.map(pair => ({ ...pair, id: pair.id || generateId('pair') }));
@ -141,7 +140,6 @@ export const KeyValueEditor: FC<Props> = ({
<Row <Row
key={pair.id} key={pair.id}
showDescription={showDescription} showDescription={showDescription}
ref={rowRef}
namePlaceholder={namePlaceholder} namePlaceholder={namePlaceholder}
valuePlaceholder={valuePlaceholder} valuePlaceholder={valuePlaceholder}
descriptionPlaceholder={descriptionPlaceholder} descriptionPlaceholder={descriptionPlaceholder}

View File

@ -1,5 +1,5 @@
import classnames from 'classnames'; import classnames from 'classnames';
import React, { forwardRef, useEffect, useImperativeHandle, useRef } from 'react'; import React, { FC } from 'react';
import { describeByteSize } from '../../../common/misc'; import { describeByteSize } from '../../../common/misc';
import { useNunjucksEnabled } from '../../context/nunjucks/nunjucks-enabled-context'; import { useNunjucksEnabled } from '../../context/nunjucks/nunjucks-enabled-context';
@ -8,7 +8,7 @@ import { DropdownButton } from '../base/dropdown/dropdown-button';
import { DropdownItem } from '../base/dropdown/dropdown-item'; import { DropdownItem } from '../base/dropdown/dropdown-item';
import { FileInputButton } from '../base/file-input-button'; import { FileInputButton } from '../base/file-input-button';
import { PromptButton } from '../base/prompt-button'; import { PromptButton } from '../base/prompt-button';
import { OneLineEditor, OneLineEditorHandle } from '../codemirror/one-line-editor'; import { OneLineEditor } from '../codemirror/one-line-editor';
import { CodePromptModal } from '../modals/code-prompt-modal'; import { CodePromptModal } from '../modals/code-prompt-modal';
import { showModal } from '../modals/index'; import { showModal } from '../modals/index';
@ -45,10 +45,8 @@ interface Props {
onKeydown?: (e: React.KeyboardEvent) => void; onKeydown?: (e: React.KeyboardEvent) => void;
showDescription: boolean; showDescription: boolean;
} }
export interface RowHandle {
focusNameEnd: () => void; export const Row: FC<Props> = ({
}
export const Row = forwardRef<RowHandle, Props>(({
allowFile, allowFile,
allowMultiline, allowMultiline,
className, className,
@ -66,20 +64,9 @@ export const Row = forwardRef<RowHandle, Props>(({
onKeydown, onKeydown,
valuePlaceholder, valuePlaceholder,
showDescription, showDescription,
}, ref) => { }) => {
const { enabled } = useNunjucksEnabled(); const { enabled } = useNunjucksEnabled();
const nameRef = useRef<OneLineEditorHandle>(null);
const valueRef = useRef<OneLineEditorHandle>(null);
useImperativeHandle(ref, () => ({
focusNameEnd: () => nameRef.current?.focusEnd(),
}));
useEffect(() => {
nameRef.current?.focus();
}, []);
const classes = classnames(className, { const classes = classnames(className, {
'key-value-editor__row-wrapper': true, 'key-value-editor__row-wrapper': true,
'key-value-editor__row-wrapper--disabled': pair.disabled, 'key-value-editor__row-wrapper--disabled': pair.disabled,
@ -103,7 +90,6 @@ export const Row = forwardRef<RowHandle, Props>(({
})} })}
> >
<OneLineEditor <OneLineEditor
ref={nameRef}
placeholder={namePlaceholder || 'Name'} placeholder={namePlaceholder || 'Name'}
defaultValue={pair.name} defaultValue={pair.name}
getAutocompleteConstants={() => handleGetAutocompleteNameConstants?.(pair) || []} getAutocompleteConstants={() => handleGetAutocompleteNameConstants?.(pair) || []}
@ -143,36 +129,11 @@ export const Row = forwardRef<RowHandle, Props>(({
</button> </button>
) : ( ) : (
<OneLineEditor <OneLineEditor
ref={valueRef}
readOnly={readOnly} readOnly={readOnly}
forceInput={forceInput} forceInput={forceInput}
type="text" type="text"
placeholder={valuePlaceholder || 'Value'} placeholder={valuePlaceholder || 'Value'}
defaultValue={pair.value} defaultValue={pair.value}
onPaste={event => {
if (!allowMultiline) {
return;
}
const value = event.clipboardData?.getData('text/plain');
if (value?.includes('\n')) {
event.preventDefault();
// Insert the pasted text into the current selection.
// Unfortunately, this is the easiest way to do
const currentValue = valueRef.current?.getValue();
const start = valueRef.current?.getSelectionStart() || 0;
const end = valueRef.current?.getSelectionEnd() || 0;
const prefix = currentValue?.slice(0, start);
const suffix = currentValue?.slice(end);
const finalValue = `${prefix}${value}${suffix}`;
console.log(start, end, finalValue);
onChange({
...pair,
type: 'text',
multiline: 'text/plain',
value: finalValue,
});
}
}}
onChange={value => onChange({ ...pair, value })} onChange={value => onChange({ ...pair, value })}
getAutocompleteConstants={() => handleGetAutocompleteValueConstants?.(pair) || []} getAutocompleteConstants={() => handleGetAutocompleteValueConstants?.(pair) || []}
/> />
@ -256,5 +217,5 @@ export const Row = forwardRef<RowHandle, Props>(({
</div> </div>
</li> </li>
); );
}); };
Row.displayName = 'Row'; Row.displayName = 'Row';