fix(client): fix json input component value handling (#2028)

* fix(client): fix json input component value handling

* fix(client): fix json input test case

* refactor(client): remove format button from json variable component

* refactor(client): move json input style to global

* test(client): remove unused test case
This commit is contained in:
Junyi 2023-06-11 14:33:52 +07:00 committed by GitHub
parent 91d6f09fc3
commit dd9cb723d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 40 additions and 39 deletions

View File

@ -2,19 +2,52 @@ import { Field } from '@formily/core';
import { useField } from '@formily/react'; import { useField } from '@formily/react';
import { Input } from 'antd'; import { Input } from 'antd';
import { TextAreaProps } from 'antd/es/input'; import { TextAreaProps } from 'antd/es/input';
import React, { Ref } from 'react'; import React, { useState, useEffect, Ref } from 'react';
import { cx, css } from '@emotion/css';
export type JSONTextAreaProps = TextAreaProps & { value?: string; space?: number }; export type JSONTextAreaProps = TextAreaProps & { value?: string; space?: number };
export const Json = React.forwardRef<typeof Input.TextArea, JSONTextAreaProps>( export const Json = React.forwardRef<typeof Input.TextArea, JSONTextAreaProps>(
({ value, onChange, space = 2, ...props }: JSONTextAreaProps, ref: Ref<any>) => { ({ value, onChange, space = 2, ...props }: JSONTextAreaProps, ref: Ref<any>) => {
const field = useField<Field>(); const field = useField<Field>();
const [text, setText] = useState('');
useEffect(() => {
try {
if (value != null) {
setText(JSON.stringify(value, null, space));
}
} catch (ex) {
//
}
}, [space, value]);
return ( return (
<Input.TextArea <Input.TextArea
{...props} {...props}
className={cx(
css`
font-size: 90%;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
`,
props.className,
)}
ref={ref} ref={ref}
defaultValue={value != null ? JSON.stringify(value, null, space) : ''} value={text}
onChange={(ev) => { onChange={(ev) => {
setText(ev.target.value);
try {
if (ev.target.value.trim() !== '') {
JSON.parse(ev.target.value);
}
field.setFeedback({});
} catch (err) {
field.setFeedback({
type: 'error',
code: 'JSONSyntaxError',
messages: [err.message],
});
}
}}
onBlur={(ev) => {
try { try {
const v = ev.target.value.trim() !== '' ? JSON.parse(ev.target.value) : null; const v = ev.target.value.trim() !== '' ? JSON.parse(ev.target.value) : null;
field.setFeedback({}); field.setFeedback({});

View File

@ -136,7 +136,8 @@ describe('Input.JSON', () => {
const textarea = container.querySelector('textarea') as HTMLTextAreaElement; const textarea = container.querySelector('textarea') as HTMLTextAreaElement;
const pre = container.querySelector('pre') as HTMLPreElement; const pre = container.querySelector('pre') as HTMLPreElement;
fireEvent.change(textarea, { target: { value: '{"name":"nocobase"}' } }); fireEvent.change(textarea, { target: { value: '{"name":"nocobase"}' } });
expect(textarea.value).toBe('{"name":"nocobase"}'); fireEvent.blur(textarea, { target: { value: '{"name":"nocobase"}' } });
expect(textarea.value).toBe('{\n "name": "nocobase"\n}');
expect(pre).toMatchInlineSnapshot(` expect(pre).toMatchInlineSnapshot(`
<pre <pre
class="ant-json css-4dta7v" class="ant-json css-4dta7v"
@ -153,6 +154,7 @@ describe('Input.JSON', () => {
const textarea = container.querySelector('textarea') as HTMLTextAreaElement; const textarea = container.querySelector('textarea') as HTMLTextAreaElement;
fireEvent.change(textarea, { target: { value: '{"name":nocobase}' } }); fireEvent.change(textarea, { target: { value: '{"name":nocobase}' } });
fireEvent.blur(textarea, { target: { value: '{"name":nocobase}' } });
expect(screen.getByText(`Unexpected token o in JSON at position 9`).innerHTML).toBe( expect(screen.getByText(`Unexpected token o in JSON at position 9`).innerHTML).toBe(
`Unexpected token o in JSON at position 9`, `Unexpected token o in JSON at position 9`,
); );

View File

@ -3,7 +3,6 @@ import { Button } from 'antd';
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import { Input } from '../input'; import { Input } from '../input';
import { useTranslation } from 'react-i18next';
import { VariableSelect } from './VariableSelect'; import { VariableSelect } from './VariableSelect';
// NOTE: https://stackoverflow.com/questions/23892547/what-is-the-best-way-to-trigger-onchange-event-in-react-js/46012210#46012210 // NOTE: https://stackoverflow.com/questions/23892547/what-is-the-best-way-to-trigger-onchange-event-in-react-js/46012210#46012210
@ -19,25 +18,9 @@ function setNativeInputValue(input, value) {
export function JSONInput(props) { export function JSONInput(props) {
const inputRef = useRef<any>(null); const inputRef = useRef<any>(null);
const { value, space = 2, scope } = props; const { scope } = props;
const { t } = useTranslation();
const options = typeof scope === 'function' ? scope() : scope ?? []; const options = typeof scope === 'function' ? scope() : scope ?? [];
function onFormat() {
if (!inputRef.current) {
return;
}
if (!value) {
return;
}
const { textArea } = inputRef.current.resizableTextArea;
const nextValue = JSON.stringify(value, null, space);
setNativeInputValue(textArea, nextValue);
textArea.setSelectionRange(nextValue.length, nextValue.length);
textArea.focus();
}
function onInsert(selected) { function onInsert(selected) {
if (!inputRef.current) { if (!inputRef.current) {
return; return;
@ -73,7 +56,6 @@ export function JSONInput(props) {
} }
`} `}
> >
<Button onClick={onFormat}>{t('Prettify')}</Button>
<VariableSelect options={options} onInsert={onInsert} /> <VariableSelect options={options} onInsert={onInsert} />
</Button.Group> </Button.Group>
</div> </div>

View File

@ -61,7 +61,6 @@ describe('Variable', () => {
const variableSelector = document.querySelector('.ant-select-selector') as HTMLElement; const variableSelector = document.querySelector('.ant-select-selector') as HTMLElement;
expect(input).toBeInTheDocument(); expect(input).toBeInTheDocument();
expect(variableSelector).toBeInTheDocument(); expect(variableSelector).toBeInTheDocument();
expect(screen.getByText('Prettify')).toBeInTheDocument();
// https://testing-library.com/docs/user-event/keyboard/ // https://testing-library.com/docs/user-event/keyboard/
await userEvent.type(input, '{{ "a": "'); await userEvent.type(input, '{{ "a": "');
@ -71,13 +70,5 @@ describe('Variable', () => {
await userEvent.type(input, '" }'); await userEvent.type(input, '" }');
expect(input.value).toMatchInlineSnapshot('"{ \\"a\\": \\"{{v1}}\\" }"'); expect(input.value).toMatchInlineSnapshot('"{ \\"a\\": \\"{{v1}}\\" }"');
// 格式化一下
await userEvent.click(screen.getByText('Prettify'));
expect(input.value).toMatchInlineSnapshot(`
"{
\\"a\\": \\"{{v1}}\\"
}"
`);
}); });
}); });

View File

@ -1,5 +1,4 @@
import { ArrayItems } from '@formily/antd'; import { ArrayItems } from '@formily/antd';
import { cx, css } from '@emotion/css';
import { NAMESPACE } from '../locale'; import { NAMESPACE } from '../locale';
import { useWorkflowVariableOptions } from '../variable'; import { useWorkflowVariableOptions } from '../variable';
@ -146,13 +145,7 @@ export default {
minRows: 10, minRows: 10,
}, },
placeholder: `{{t("Input request data", { ns: "${NAMESPACE}" })}}`, placeholder: `{{t("Input request data", { ns: "${NAMESPACE}" })}}`,
className: cx( className: 'full-width',
'full-width',
css`
font-size: 90%;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
`,
),
}, },
description: `{{t("Only support standard JSON data", { ns: "${NAMESPACE}" })}}`, description: `{{t("Only support standard JSON data", { ns: "${NAMESPACE}" })}}`,
}, },