Skip to content

Commit 10348cb

Browse files
committed
feat: adjust clear button to undoable reset to default prefill
Closes #76
1 parent e284b41 commit 10348cb

File tree

6 files changed

+158
-15
lines changed

6 files changed

+158
-15
lines changed

lib/components/Input/Input.jsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useRef } from 'react';
22

33
import { Link } from '@carbon/react';
44
import { Launch } from '@carbon/icons-react';
@@ -13,12 +13,19 @@ export default function Input({
1313
onSetInput,
1414
variablesForElement
1515
}) {
16-
const handleResetInput = () => {
16+
17+
const containerRef = /** @type {import('react').RefObject<HTMLDivElement | null>} */ (useRef(null));
18+
19+
const handleReset = () => {
1720
onResetInput();
21+
const cmContent = /** @type {HTMLElement | undefined} */ (
22+
containerRef.current?.querySelector('.cm-content')
23+
);
24+
cmContent?.focus();
1825
};
1926

2027
return (
21-
<div className="input">
28+
<div className="input" ref={ containerRef }>
2229
<div className="input__header">
2330
<div className="input__header--title">
2431
Input process variables
@@ -28,8 +35,8 @@ export default function Input({
2835
</Link>
2936
<Link
3037
className="input__header--button-reset"
31-
onClick={ handleResetInput }
32-
role="button">Clear</Link>
38+
onClick={ handleReset }
39+
role="button">Reset</Link>
3340
</div>
3441
<InputEditor
3542
allOutputs={ allOutputs }

lib/components/Input/InputEditor.jsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { useEffect, useMemo, useRef, useState } from 'react';
22
import { renderToStaticMarkup } from 'react-dom/server';
33

44
import { autocompletion, closeBrackets } from '@codemirror/autocomplete';
5-
import { defaultKeymap } from '@codemirror/commands';
5+
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
66
import { bracketMatching, indentOnInput } from '@codemirror/language';
77
import { Compartment, EditorState, Annotation } from '@codemirror/state';
88
import { EditorView, keymap, placeholder } from '@codemirror/view';
@@ -100,8 +100,10 @@ export default function InputEditor({
100100
closeBrackets(),
101101
bracketMatching(),
102102
indentOnInput(),
103+
history(),
103104
keymap.of([
104-
...defaultKeymap
105+
...defaultKeymap,
106+
...historyKeymap
105107
]),
106108
new Compartment().of(json()),
107109
new Compartment().of(EditorState.tabSize.of(2)),
@@ -249,4 +251,5 @@ function getDetail(value) {
249251
}
250252

251253
return type.charAt(0).toUpperCase() + type.slice(1);
252-
}
254+
}
255+

lib/utils/prefill.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { has, isObject, isString } from 'min-dash';
22

3-
export const DEFAULT_INPUT_CONFIG = '{}';
3+
export const DEFAULT_INPUT_CONFIG = '{\n \n}';
44

55

66
/**

test/ElementConfig.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ describe('ElementConfig', function() {
112112
// then
113113
const inputConfigForElement = elementConfig.getInputConfigForElement(element);
114114

115-
expect(inputConfigForElement).to.eql('{}');
115+
expect(inputConfigForElement).to.eql('{\n \n}');
116116

117117
expect(spy).to.have.been.calledOnce;
118118
}));
@@ -195,7 +195,7 @@ describe('ElementConfig', function() {
195195
const inputConfigForElement = elementConfig.getInputConfigForElement(element);
196196

197197
// then
198-
expect(inputConfigForElement).to.eql('{}');
198+
expect(inputConfigForElement).to.eql('{\n \n}');
199199
}));
200200

201201
});

test/components/Input/Input.spec.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ function renderWithProps(props) {
3838
const {
3939
element = elementRegistry.get('ServiceTask_1'),
4040
input = '{}',
41-
setInput = () => {},
42-
reset = () => {},
41+
onSetInput = () => {},
42+
onResetInput = () => {},
43+
onErrorChange = () => {},
4344
variablesForElement,
4445
output,
4546
onRunTask = () => {}
@@ -49,8 +50,9 @@ function renderWithProps(props) {
4950
<Input
5051
element={ element }
5152
input={ input }
52-
setInput={ setInput }
53-
reset={ reset }
53+
onSetInput={ onSetInput }
54+
onResetInput={ onResetInput }
55+
onErrorChange={ onErrorChange }
5456
variablesForElement={ variablesForElement }
5557
output={ output }
5658
onRunTask={ onRunTask }

test/components/Input/InputEditor.spec.js

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,137 @@ describe('InputEditor', function() {
312312

313313
});
314314

315+
316+
describe('undo/redo', function() {
317+
318+
const isMac = /Mac/.test(navigator.platform);
319+
const undoKeys = isMac ? '{Meta>}z{/Meta}' : '{Control>}z{/Control}';
320+
const redoKeys = isMac ? '{Meta>}{Shift>}z{/Shift}{/Meta}' : '{Control>}y{/Control}';
321+
322+
323+
it('should undo typing', async function() {
324+
325+
// given
326+
const onChangeSpy = sinon.spy();
327+
328+
const { getByRole } = renderWithProps({
329+
value: '{}',
330+
onChange: onChangeSpy
331+
});
332+
333+
const textbox = getByRole('textbox');
334+
await user.click(textbox);
335+
336+
// when - type something
337+
await user.keyboard('{ArrowRight}{Enter}"a": 1');
338+
339+
// assume - typing happened
340+
await waitFor(() => {
341+
expect(onChangeSpy).to.have.been.called;
342+
});
343+
344+
const valueAfterTyping = onChangeSpy.lastCall.args[0];
345+
expect(valueAfterTyping).to.contain('"a": 1');
346+
347+
onChangeSpy.resetHistory();
348+
349+
// when - undo
350+
await user.keyboard(undoKeys);
351+
352+
// then - onChange should fire with reverted content
353+
await waitFor(() => {
354+
expect(onChangeSpy).to.have.been.called;
355+
});
356+
});
357+
358+
359+
it('should undo reset', async function() {
360+
361+
// given
362+
const onChangeSpy = sinon.spy();
363+
const originalValue = '{\n "foo": "bar"\n}';
364+
const resetValue = '{}';
365+
366+
const { container, getByRole, rerender } = renderWithProps({
367+
value: originalValue,
368+
onChange: onChangeSpy
369+
});
370+
371+
// assume - editor shows original value
372+
expect(container.textContent).to.contain('"foo": "bar"');
373+
374+
// when - simulate reset by changing value prop
375+
rerender(
376+
<InputEditor
377+
value={ resetValue }
378+
onChange={ onChangeSpy }
379+
onErrorChange={ () => {} }
380+
/>
381+
);
382+
383+
await waitFor(() => {
384+
expect(container.textContent).to.not.contain('"foo": "bar"');
385+
});
386+
387+
// when - undo the reset
388+
const textbox = getByRole('textbox');
389+
await user.click(textbox);
390+
await user.keyboard(undoKeys);
391+
392+
// then - editor should revert to original value
393+
await waitFor(() => {
394+
expect(onChangeSpy).to.have.been.calledWith(originalValue);
395+
});
396+
});
397+
398+
399+
it('should redo after undo', async function() {
400+
401+
// given
402+
const onChangeSpy = sinon.spy();
403+
const originalValue = '{\n "foo": "bar"\n}';
404+
const resetValue = '{}';
405+
406+
const { container, getByRole, rerender } = renderWithProps({
407+
value: originalValue,
408+
onChange: onChangeSpy
409+
});
410+
411+
// reset
412+
rerender(
413+
<InputEditor
414+
value={ resetValue }
415+
onChange={ onChangeSpy }
416+
onErrorChange={ () => {} }
417+
/>
418+
);
419+
420+
await waitFor(() => {
421+
expect(container.textContent).to.not.contain('"foo": "bar"');
422+
});
423+
424+
// undo
425+
const textbox = getByRole('textbox');
426+
await user.click(textbox);
427+
await user.keyboard(undoKeys);
428+
429+
await waitFor(() => {
430+
expect(onChangeSpy).to.have.been.calledWith(originalValue);
431+
});
432+
433+
onChangeSpy.resetHistory();
434+
435+
// when - redo
436+
await user.keyboard(redoKeys);
437+
438+
// then - should go back to reset value
439+
await waitFor(() => {
440+
expect(onChangeSpy).to.have.been.calledWith(resetValue);
441+
});
442+
});
443+
444+
});
445+
315446
});
316447

317448
function renderWithProps(props = {}) {

0 commit comments

Comments
 (0)