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:
parent
91d6f09fc3
commit
dd9cb723d4
@ -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({});
|
||||||
|
@ -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`,
|
||||||
);
|
);
|
||||||
|
@ -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>
|
||||||
|
@ -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}}\\"
|
|
||||||
}"
|
|
||||||
`);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -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}" })}}`,
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user