Merge pull request #9186 from lurunze1226/fix-condition-builder-error

fix: ConditionBuilder使用公式编辑器切换类型异常问题
This commit is contained in:
wutong 2023-12-20 19:13:40 +08:00 committed by GitHub
commit 2b812344b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 444 additions and 26 deletions

View File

@ -96,7 +96,9 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
@autobind
handleOperatorChange(op: OperatorType) {
const {fields, value, index, onChange} = this.props;
const {fields, value, index, onChange, formula} = this.props;
const useFormulaInput =
formula?.mode === 'input-group' && formula?.inputSettings;
const leftFieldSchema: FieldSimple = findTree(
fields,
(i: FieldSimple) => i.name === (value?.left as ExpressionField)?.field
@ -104,7 +106,10 @@ export class ConditionItem extends React.Component<ConditionItemProps> {
const result = {
...value,
op: op,
right: value.right ?? leftFieldSchema?.defaultValue
/** 使用公式编辑器模式时因为不同条件下值格式不一致比如select类型下包含和等于对应的multiple会变化所以变化条件时需要清空right值 */
right: useFormulaInput
? leftFieldSchema?.defaultValue
: value.right ?? leftFieldSchema?.defaultValue
};
onChange(result, index);

View File

@ -191,6 +191,15 @@ export class FormulaEditor extends React.Component<
return;
}
if (typeof value !== 'string') {
try {
value = JSON.stringify(value);
} catch (error) {
console.error('[amis][formula] given value is not a string');
value = '';
}
}
const varMap: {
[propname: string]: string;
} = {};

View File

@ -9,7 +9,8 @@ import {
LocaleProps,
uncontrollable,
findTree,
isExpression
isExpression,
isObject
} from 'amis-core';
import {FormulaEditor, VariableItem} from './Editor';
@ -96,9 +97,35 @@ const FormulaInput: React.FC<FormulaInputProps> = props => {
if (schemaType === 'boolean') {
result = origin.value;
} else if (schemaType === 'select') {
result = Array.isArray(origin)
? origin.map(item => item.value)
: origin.value;
const {
joinValues,
extractValue,
delimiter,
multiple,
valueField = 'value'
} = inputSettings;
if (joinValues) {
if (multiple) {
result = Array.isArray(origin)
? (origin.map(item => item[valueField]).join(delimiter) as string)
: origin
? origin[valueField]
: '';
} else {
result = origin ? origin[valueField] : '';
}
} else if (extractValue) {
if (multiple) {
result = Array.isArray(origin)
? origin.map(item => item[valueField])
: origin
? [origin[valueField || 'value']]
: [];
} else {
result = origin ? origin[valueField] : '';
}
}
}
onChange?.(result);
},
@ -123,26 +150,58 @@ const FormulaInput: React.FC<FormulaInputProps> = props => {
: cmptValue === item?.value;
})
: null;
let useVariable = !!(isExpression(cmptValue) || targetVariable);
if (
isExpression(cmptValue) ||
targetVariable ||
(schemaType === 'number' &&
cmptValue != null &&
typeof cmptValue !== 'number') ||
(['date', 'time', 'datetime'].includes(schemaType) &&
!moment(cmptValue).isValid()) ||
(schemaType === 'select' &&
cmptValue != null &&
!(inputSettings?.options ?? []).some(
(item: any) => item?.value === cmptValue
)) ||
(schemaType === 'boolean' &&
cmptValue != null &&
typeof cmptValue !== 'boolean')
) {
/** 判断value是否为变量如果是变量使用ResultBox渲染 */
if (!useVariable) {
if (schemaType === 'number') {
useVariable = cmptValue != null && typeof cmptValue !== 'number';
} else if (['date', 'time', 'datetime'].includes(schemaType)) {
useVariable = !moment(cmptValue).isValid();
} else if (schemaType === 'select') {
const {
options,
joinValues,
extractValue,
delimiter,
multiple,
valueField = 'value'
} = inputSettings;
let selctedValue: any[] = [];
if (multiple) {
if (joinValues) {
selctedValue =
typeof cmptValue === 'string' ? cmptValue.split(delimiter) : [];
} else {
selctedValue = Array.isArray(cmptValue)
? extractValue
? cmptValue
: cmptValue.map(i => i?.[valueField])
: [];
}
} else {
if (joinValues) {
selctedValue = typeof cmptValue === 'string' ? [cmptValue] : [];
} else {
selctedValue = isObject(cmptValue) ? [cmptValue?.[valueField]] : [];
}
}
/** 选项类型清空后是空字符串, */
useVariable =
cmptValue &&
!(options ?? []).some((item: any) =>
selctedValue.includes(item?.value)
);
} else if (schemaType === 'boolean') {
useVariable = cmptValue != null && typeof cmptValue !== 'boolean';
}
}
if (useVariable) {
const varName =
cmptValue && mixedMode
typeof cmptValue === 'string' && cmptValue && mixedMode
? cmptValue.replace(/^\$\{/, '').replace(/\}$/, '')
: cmptValue;
const resultValue = targetVariable?.value ?? varName;

View File

@ -14,10 +14,17 @@
*/
import React from 'react';
import {fireEvent, render, screen, waitFor} from '@testing-library/react';
import {fireEvent, render, screen, cleanup, waitFor} from '@testing-library/react';
import '../../../src';
import {render as amisRender} from '../../../src';
import {render as amisRender, clearStoresCache} from '../../../src';
import {makeEnv, replaceReactAriaIds, wait} from '../../helper';
import { Select } from 'packages/amis-ui/lib/components/Select';
afterEach(() => {
cleanup();
clearStoresCache();
jest.useRealTimers();
});
const testSchema = {
type: 'page',
@ -801,3 +808,341 @@ test('Renderer:condition-builder with not embed', async () => {
baseElement.querySelector('.cxd-Modal .cxd-CBGroup')
).toBeInTheDocument();
});
/**
* 使
* 1. 7
* 2. select -> multiple不一样
* 3. 使select类型
*/
describe.only('Renderer: condition-builder with formula', () => {
const onSubmit = jest.fn();
test('condition-builder with different fields', async () => {
const {container} = render(amisRender({
"type": "form",
"data": {
"conditions": {
"id": "68bddc1495e9",
"conjunction": "and",
"children": [
{
"id": "b9cc34dae93a",
"left": {
"type": "field",
"field": "text"
},
"op": "equal"
},
{
"id": "4c718986c321",
"left": {
"type": "field",
"field": "number"
},
"op": "equal"
},
{
"id": "7ee79c416422",
"left": {
"type": "field",
"field": "boolean"
},
"op": "equal"
},
{
"id": "9cd76d8a6522",
"left": {
"type": "field",
"field": "select"
},
"op": "select_equals"
},
{
"id": "20a65e9df546",
"left": {
"type": "field",
"field": "date"
},
"op": "equal"
},
{
"id": "e729b32ea9e8",
"left": {
"type": "field",
"field": "time"
},
"op": "equal"
},
{
"id": "a5f48e000557",
"left": {
"type": "field",
"field": "datetime"
},
"op": "equal"
}
]
}
},
"body": [
{
"type": "condition-builder",
"label": "条件组件",
"name": "conditions",
"searchable": true,
"formula": {
"mode": "input-group",
"inputSettings": {},
"allowInput": true,
"mixedMode": true,
"variables": []
},
"fields": [
{
"label": "文本",
"type": "text",
"name": "text"
},
{
"label": "数字",
"type": "number",
"name": "number"
},
{
"label": "布尔",
"type": "boolean",
"name": "boolean"
},
{
"label": "选项",
"type": "select",
"name": "select",
"options": [
{
"label": "A",
"value": "a"
},
{
"label": "B",
"value": "b"
},
{
"label": "C",
"value": "c"
}
]
},
{
"label": "日期",
"children": [
{
"label": "日期",
"type": "date",
"name": "date"
},
{
"label": "时间",
"type": "time",
"name": "time"
},
{
"label": "日期时间",
"type": "datetime",
"name": "datetime"
}
]
}
]
}
]
}, {onSubmit}, makeEnv({})));
replaceReactAriaIds(container);
// 7种类型都存在
expect(container.querySelectorAll('.cxd-FormulaPicker-input')?.length).toEqual(7);
expect(container.querySelector('.cxd-FormulaPicker--text')).toBeInTheDocument();
expect(container.querySelector('.cxd-FormulaPicker-input-number')).toBeInTheDocument();
expect(container.querySelector('.cxd-FormulaPicker-input-boolean')).toBeInTheDocument();
expect(container.querySelector('.cxd-FormulaPicker-input-select')).toBeInTheDocument();
expect(container.querySelector('.cxd-FormulaPicker-input-date')).toBeInTheDocument();
expect(container.querySelector('.cxd-FormulaPicker-input-time')).toBeInTheDocument();
expect(container.querySelector('.cxd-FormulaPicker-input-datetime')).toBeInTheDocument();
});
test('condition-builder with select field and change operator', async () => {
const {container, findByText} = render(amisRender({
"type": "form",
"data": {
"conditions": {
"id": "68bddc1495e9",
"conjunction": "and",
"children": [
{
"id": "9cd76d8a6522",
"left": {
"type": "field",
"field": "select"
},
"op": "select_equals"
}
]
}
},
"body": [
{
"type": "condition-builder",
"label": "条件组件",
"name": "conditions",
"searchable": true,
"formula": {
"mode": "input-group",
"inputSettings": {},
"allowInput": true,
"mixedMode": true,
"variables": []
},
"fields": [
{
"label": "选项",
"type": "select",
"name": "select",
"options": [
{
"label": "A",
"value": "a"
},
{
"label": "B",
"value": "b"
},
{
"label": "C",
"value": "c"
}
]
}
]
}
]
}, {}, makeEnv({})));
replaceReactAriaIds(container);
// 选中第一个选项Form中默认值是等于操作
let fieldValueControl = container.querySelector('.cxd-FormulaPicker-input-select')!;
fireEvent.click(fieldValueControl);
await wait(100);
fireEvent.click(await findByText('A'));
expect(container.querySelector('.cxd-Tag-text')?.innerHTML).toEqual('A');
// 切换操作符,字段值清空,需要重新选择,且下拉选项变成多选
const opControl = container.querySelector('.cxd-CBGroup-operatorInput')!;
fireEvent.click(opControl);
await wait(100);
fireEvent.click(await findByText('包含'));
await wait(100);
expect(container.querySelector('.cxd-Select-placeholder')).toBeInTheDocument();
fieldValueControl = container.querySelector('.cxd-FormulaPicker-input-select')!;
fireEvent.click(fieldValueControl);
await wait(100);
expect(container.querySelectorAll('.cxd-Select-option-checkbox').length).toEqual(3);
});
test('condition-builder with field type change', async () => {
const onSubmit = jest.fn();
const {container, findByText, findByPlaceholderText} = render(amisRender({
"type": "form",
"data": {
"conditions": {
"id": "68bddc1495e9",
"conjunction": "and",
"children": [
{
"id": "b9cc34dae93a",
"left": {
"type": "field",
"field": "text"
},
"op": "equal"
}
]
}
},
"body": [
{
"type": "condition-builder",
"label": "条件组件",
"name": "conditions",
"searchable": true,
"formula": {
"mode": "input-group",
"inputSettings": {},
"allowInput": true,
"mixedMode": true,
"variables": []
},
"fields": [
{
"label": "文本",
"type": "text",
"name": "text"
},
{
"label": "选项",
"type": "select",
"name": "select",
"options": [
{
"label": "A",
"value": "a"
},
{
"label": "B",
"value": "b"
},
{
"label": "C",
"value": "c"
}
]
}
]
}
]
}, {onSubmit}, makeEnv({})));
replaceReactAriaIds(container);
// 切换字段类型,对应字段值控件更新
const fieldControl = container.querySelector('.cxd-DropDownSelection-input')!;
fireEvent.click(fieldControl);
await wait(100);
fireEvent.click(await findByText('选项'));
await wait(100);
let selectValueControl = container.querySelector('.cxd-FormulaPicker-input-select')!;
expect(selectValueControl).toBeInTheDocument();
// 切换操作符,下拉选项变成多选
const opControl = container.querySelector('.cxd-CBGroup-operatorInput')!;
fireEvent.click(opControl);
await wait(100);
fireEvent.click(await findByText('包含'));
await wait(100);
expect(container.querySelector('.cxd-Select-placeholder')).toBeInTheDocument();
selectValueControl = container.querySelector('.cxd-FormulaPicker-input-select')!;
fireEvent.click(selectValueControl);
await wait(100);
expect(container.querySelectorAll('.cxd-Select-option-checkbox').length).toEqual(3);
// 选择2个选项绑定值变化
fireEvent.click(await findByText('A'));
fireEvent.click(await findByText('C'));
const selectedValues = [];
const nodes = container.querySelectorAll('.cxd-Select-valueLabel');
for (const el of nodes.values()) {
selectedValues.push(el?.innerHTML);
}
expect(selectedValues.length).toEqual(2);
expect(selectedValues.join(',')).toEqual('A,C');
});
})