@nocobase/client-v2 2.2.0-beta.5 → 2.2.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/PluginSettingsManager.d.ts +33 -0
- package/es/RouteRepository.d.ts +21 -6
- package/es/components/form/JsonTextArea.d.ts +2 -1
- package/es/components/form/VariableJsonTextArea.d.ts +19 -0
- package/es/components/form/index.d.ts +1 -0
- package/es/components/index.d.ts +1 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.d.ts +2 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.d.ts +2 -1
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +2 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.d.ts +3 -2
- package/es/flow/admin-shell/admin-layout/AdminLayoutSlotModels.d.ts +4 -0
- package/es/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.d.ts +6 -0
- package/es/flow/components/FieldAssignRulesEditor.d.ts +3 -1
- package/es/flow/models/base/GridModel.d.ts +1 -1
- package/es/flow/models/blocks/filter-form/FilterFormBlockModel.d.ts +5 -0
- package/es/flow/models/blocks/form/FormBlockModel.d.ts +4 -0
- package/es/flow/models/blocks/form/value-runtime/rules.d.ts +21 -0
- package/es/flow/models/blocks/form/value-runtime/runtime.d.ts +21 -0
- package/es/flow/models/blocks/form/value-runtime/types.d.ts +2 -2
- package/es/flow-compat/FieldValidation.d.ts +2 -1
- package/es/index.d.ts +2 -1
- package/es/index.mjs +145 -119
- package/lib/index.js +148 -122
- package/package.json +7 -7
- package/src/PluginSettingsManager.ts +53 -0
- package/src/RouteRepository.ts +126 -24
- package/src/__tests__/PluginSettingsManager.test.ts +13 -0
- package/src/__tests__/RouteRepository.test.ts +216 -0
- package/src/__tests__/exports.test.ts +16 -0
- package/src/__tests__/plugin-manager.test.tsx +44 -2
- package/src/__tests__/settings-center.test.tsx +40 -1
- package/src/components/form/JsonTextArea.tsx +21 -11
- package/src/components/form/VariableJsonTextArea.tsx +175 -0
- package/src/components/form/__tests__/JsonTextArea.test.tsx +43 -0
- package/src/components/form/__tests__/VariableJsonTextArea.test.tsx +86 -0
- package/src/components/form/index.tsx +1 -0
- package/src/components/index.ts +1 -0
- package/src/flow/__tests__/FlowRoute.test.tsx +443 -5
- package/src/flow/actions/__tests__/dataScopeFilter.test.ts +58 -0
- package/src/flow/actions/__tests__/fieldLinkageRules.scopeDepth.test.ts +150 -1
- package/src/flow/actions/__tests__/linkageAssignField.legacy.test.ts +135 -0
- package/src/flow/actions/__tests__/linkageRules.hiddenSync.restore.test.ts +19 -5
- package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +92 -0
- package/src/flow/actions/customVariable.tsx +1 -2
- package/src/flow/actions/dataScopeFilter.ts +10 -0
- package/src/flow/actions/linkageRules.tsx +122 -34
- package/src/flow/actions/runjs.tsx +2 -14
- package/src/flow/actions/validation.tsx +62 -30
- package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +33 -10
- package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.test.tsx +310 -0
- package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +44 -8
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.ts +5 -3
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +61 -9
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +71 -8
- package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +1 -1
- package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +21 -8
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +188 -0
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +99 -0
- package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +166 -2
- package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.test.ts +20 -0
- package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.ts +33 -5
- package/src/flow/components/DefaultValue.tsx +1 -2
- package/src/flow/components/FieldAssignRulesEditor.tsx +128 -21
- package/src/flow/components/FlowRoute.tsx +56 -11
- package/src/flow/components/__tests__/FieldAssignRulesEditor.test.tsx +508 -4
- package/src/flow/components/__tests__/fieldAssignOptions.test.ts +151 -3
- package/src/flow/components/code-editor/__tests__/useCodeRunner.test.tsx +2 -17
- package/src/flow/components/code-editor/hooks/useCodeRunner.ts +2 -14
- package/src/flow/components/code-editor/runjsDiagnostics.ts +8 -45
- package/src/flow/components/fieldAssignOptions.ts +155 -27
- package/src/flow/models/actions/JSActionModel.tsx +2 -7
- package/src/flow/models/actions/JSCollectionActionModel.tsx +2 -7
- package/src/flow/models/actions/JSItemActionModel.tsx +2 -7
- package/src/flow/models/actions/JSRecordActionModel.tsx +2 -7
- package/src/flow/models/base/ActionModel.tsx +12 -1
- package/src/flow/models/base/GridModel.tsx +38 -7
- package/src/flow/models/base/PageModel/PageModel.tsx +4 -1
- package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +28 -4
- package/src/flow/models/base/__tests__/ActionModel.test.ts +83 -0
- package/src/flow/models/base/__tests__/GridModel.dragSnapshotContainer.test.ts +239 -1
- package/src/flow/models/base/__tests__/transformRowsToSingleColumn.test.ts +48 -0
- package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +31 -3
- package/src/flow/models/blocks/filter-form/FilterFormJSActionModel.tsx +2 -7
- package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +77 -0
- package/src/flow/models/blocks/form/EditFormModel.tsx +1 -0
- package/src/flow/models/blocks/form/FormBlockModel.tsx +7 -0
- package/src/flow/models/blocks/form/JSFormActionModel.tsx +2 -7
- package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +17 -0
- package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +880 -102
- package/src/flow/models/blocks/form/value-runtime/rules.ts +445 -13
- package/src/flow/models/blocks/form/value-runtime/runtime.ts +257 -13
- package/src/flow/models/blocks/form/value-runtime/types.ts +2 -2
- package/src/flow/models/blocks/js-block/JSBlock.tsx +2 -7
- package/src/flow/models/blocks/table/JSColumnModel.tsx +1 -9
- package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +1 -1
- package/src/flow/models/blocks/table/TableBlockModel.tsx +13 -3
- package/src/flow/models/blocks/table/__tests__/TableActionsColumnModel.test.tsx +54 -0
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.mobileSettingsButtons.test.tsx +78 -0
- package/src/flow/models/fields/AssociationFieldModel/AssociationFieldModel.tsx +1 -1
- package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +29 -6
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +13 -1
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableFieldModel.reset.test.ts +180 -0
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +6 -4
- package/src/flow/models/fields/AssociationFieldModel/__tests__/AssociationFieldModel.updateAssociation.test.ts +72 -0
- package/src/flow/models/fields/InputFieldModel.tsx +14 -22
- package/src/flow/models/fields/JSEditableFieldModel.tsx +2 -11
- package/src/flow/models/fields/JSFieldModel.tsx +2 -7
- package/src/flow/models/fields/JSItemModel.tsx +2 -14
- package/src/flow/models/fields/__tests__/InputFieldModel.test.tsx +17 -0
- package/src/flow/models/topbar/TopbarActionModel.tsx +93 -10
- package/src/flow-compat/FieldValidation.tsx +122 -60
- package/src/flow-compat/Popover.tsx +43 -4
- package/src/flow-compat/__tests__/Popover.test.tsx +34 -0
- package/src/index.ts +8 -1
- package/src/layout-manager/__tests__/LayoutRoute.test.tsx +41 -1
- package/src/nocobase-buildin-plugin/index.tsx +0 -2
- package/src/settings-center/AdminSettingsLayout.tsx +3 -0
- package/src/settings-center/SystemSettingsPage.tsx +0 -1
- package/src/settings-center/plugin-manager/PluginCard.tsx +2 -2
- package/src/settings-center/plugin-manager/index.tsx +3 -0
- package/src/settings-center/utils.tsx +0 -6
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { describe, expect, it, vi } from 'vitest';
|
|
11
11
|
import { FlowEngine, FlowModel } from '@nocobase/flow-engine';
|
|
12
|
-
import { fieldLinkageRules } from '../linkageRules';
|
|
12
|
+
import { fieldLinkageRules, linkageAssignField } from '../linkageRules';
|
|
13
13
|
|
|
14
14
|
describe('fieldLinkageRules action - linkage scope metadata', () => {
|
|
15
15
|
const targetCollection: any = {
|
|
@@ -868,6 +868,155 @@ describe('fieldLinkageRules action - linkage scope metadata', () => {
|
|
|
868
868
|
expect(setFormValues).toHaveBeenCalledTimes(2);
|
|
869
869
|
});
|
|
870
870
|
|
|
871
|
+
it('keeps row-scoped override patches editable after the target is user edited', async () => {
|
|
872
|
+
const store = {
|
|
873
|
+
roles: [{ uid: 'role-uid-1', name: 'existing' }],
|
|
874
|
+
};
|
|
875
|
+
const getAt = (path: Array<string | number>) => path.reduce((acc: any, seg) => acc?.[seg], store as any);
|
|
876
|
+
const setAt = (path: Array<string | number>, value: any) => {
|
|
877
|
+
let cur: any = store;
|
|
878
|
+
for (const seg of path.slice(0, -1)) {
|
|
879
|
+
cur = cur[seg];
|
|
880
|
+
}
|
|
881
|
+
cur[path[path.length - 1]] = value;
|
|
882
|
+
};
|
|
883
|
+
const editedKeys = new Set<string>();
|
|
884
|
+
const toKey = (path: Array<string | number>) => JSON.stringify(path);
|
|
885
|
+
const form = {
|
|
886
|
+
getFieldValue: (path: Array<string | number>) => getAt(path),
|
|
887
|
+
};
|
|
888
|
+
const formValueRuntime = {
|
|
889
|
+
canApplyOverrideValuePatch: vi.fn((path: Array<string | number>) => !editedKeys.has(toKey(path))),
|
|
890
|
+
};
|
|
891
|
+
const setFormValues = vi.fn(async (patches: Array<{ path: Array<string | number>; value: any }>) => {
|
|
892
|
+
for (const patch of patches) {
|
|
893
|
+
setAt(patch.path, patch.value);
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
const engine = new FlowEngine();
|
|
898
|
+
const rowFork = new FlowModel({ uid: 'role-override-row-fork', flowEngine: engine }) as any;
|
|
899
|
+
rowFork.context.defineProperty('fieldIndex', {
|
|
900
|
+
value: ['roles:0'],
|
|
901
|
+
});
|
|
902
|
+
rowFork.context.defineProperty('form', {
|
|
903
|
+
value: form,
|
|
904
|
+
});
|
|
905
|
+
rowFork.context.defineProperty('setFormValues', {
|
|
906
|
+
value: setFormValues,
|
|
907
|
+
});
|
|
908
|
+
rowFork.context.defineProperty('app', {
|
|
909
|
+
value: {
|
|
910
|
+
jsonLogic: {
|
|
911
|
+
apply: () => true,
|
|
912
|
+
},
|
|
913
|
+
},
|
|
914
|
+
});
|
|
915
|
+
rowFork.subModels = { items: [] };
|
|
916
|
+
rowFork.getAction = vi.fn((name: string) => {
|
|
917
|
+
if (name === 'linkageAssignField') {
|
|
918
|
+
return linkageAssignField;
|
|
919
|
+
}
|
|
920
|
+
});
|
|
921
|
+
|
|
922
|
+
const masterModel: any = {
|
|
923
|
+
uid: 'master-role-override-grid',
|
|
924
|
+
forks: new Set([rowFork]),
|
|
925
|
+
};
|
|
926
|
+
const rolesField: any = {
|
|
927
|
+
type: 'hasMany',
|
|
928
|
+
isAssociationField: () => true,
|
|
929
|
+
targetCollection: {
|
|
930
|
+
getField: (name: string) => ({ name, isAssociationField: () => false }),
|
|
931
|
+
},
|
|
932
|
+
};
|
|
933
|
+
const blockModel: any = {
|
|
934
|
+
collection: {
|
|
935
|
+
getField: (name: string) => (name === 'roles' ? rolesField : null),
|
|
936
|
+
},
|
|
937
|
+
formValueRuntime,
|
|
938
|
+
};
|
|
939
|
+
rowFork.context.defineProperty('blockModel', {
|
|
940
|
+
value: blockModel,
|
|
941
|
+
});
|
|
942
|
+
const gridModel: any = {
|
|
943
|
+
uid: 'grid-model-role-override-row',
|
|
944
|
+
context: {
|
|
945
|
+
blockModel,
|
|
946
|
+
},
|
|
947
|
+
getAction: vi.fn((name: string) => {
|
|
948
|
+
if (name === 'linkageAssignField') {
|
|
949
|
+
return linkageAssignField;
|
|
950
|
+
}
|
|
951
|
+
}),
|
|
952
|
+
__allModels: [],
|
|
953
|
+
};
|
|
954
|
+
const ctx: any = {
|
|
955
|
+
model: gridModel,
|
|
956
|
+
engine: {
|
|
957
|
+
forEachModel: (cb: (m: any) => void) => {
|
|
958
|
+
cb(masterModel);
|
|
959
|
+
},
|
|
960
|
+
},
|
|
961
|
+
flowKey: 'eventSettings',
|
|
962
|
+
inputArgs: {
|
|
963
|
+
source: 'user',
|
|
964
|
+
txId: 'tx-role-override-row',
|
|
965
|
+
changedPaths: [['roles', 0, 'uid']],
|
|
966
|
+
},
|
|
967
|
+
app: {
|
|
968
|
+
jsonLogic: {
|
|
969
|
+
apply: () => true,
|
|
970
|
+
},
|
|
971
|
+
},
|
|
972
|
+
resolveJsonTemplate: async (v: any) => v,
|
|
973
|
+
};
|
|
974
|
+
const makeParams = (value: string) => ({
|
|
975
|
+
value: [
|
|
976
|
+
{
|
|
977
|
+
key: `rule-${value}`,
|
|
978
|
+
title: `rule-${value}`,
|
|
979
|
+
enable: true,
|
|
980
|
+
condition: { logic: '$and', items: [] },
|
|
981
|
+
actions: [
|
|
982
|
+
{
|
|
983
|
+
name: 'linkageAssignField',
|
|
984
|
+
params: {
|
|
985
|
+
value: [
|
|
986
|
+
{
|
|
987
|
+
key: `r-${value}`,
|
|
988
|
+
enable: true,
|
|
989
|
+
targetPath: 'roles.name',
|
|
990
|
+
mode: 'override',
|
|
991
|
+
value,
|
|
992
|
+
condition: { logic: '$and', items: [] },
|
|
993
|
+
},
|
|
994
|
+
],
|
|
995
|
+
},
|
|
996
|
+
},
|
|
997
|
+
],
|
|
998
|
+
},
|
|
999
|
+
],
|
|
1000
|
+
});
|
|
1001
|
+
|
|
1002
|
+
await fieldLinkageRules.handler(ctx, makeParams('role-uid-1'));
|
|
1003
|
+
expect(store.roles[0].name).toBe('role-uid-1');
|
|
1004
|
+
|
|
1005
|
+
store.roles[0].uid = 'role-uid-2';
|
|
1006
|
+
await fieldLinkageRules.handler(ctx, makeParams('role-uid-2'));
|
|
1007
|
+
expect(store.roles[0].name).toBe('role-uid-2');
|
|
1008
|
+
expect(setFormValues).toHaveBeenCalledTimes(2);
|
|
1009
|
+
|
|
1010
|
+
store.roles[0].name = 'manual';
|
|
1011
|
+
store.roles[0].uid = 'role-uid-3';
|
|
1012
|
+
editedKeys.add(toKey(['roles', 0, 'name']));
|
|
1013
|
+
await fieldLinkageRules.handler(ctx, makeParams('role-uid-3'));
|
|
1014
|
+
|
|
1015
|
+
expect(store.roles[0].name).toBe('manual');
|
|
1016
|
+
expect(setFormValues).toHaveBeenCalledTimes(2);
|
|
1017
|
+
expect(formValueRuntime.canApplyOverrideValuePatch).toHaveBeenCalledWith(['roles', 0, 'name']);
|
|
1018
|
+
});
|
|
1019
|
+
|
|
871
1020
|
it('skips stale subtable row forks after the row has been removed', async () => {
|
|
872
1021
|
const setFormValues = vi.fn(async () => undefined);
|
|
873
1022
|
const defaultHandler = vi.fn((actionCtx: any, { addFormValuePatch }: any) => {
|
|
@@ -69,6 +69,72 @@ describe('linkage assign actions - legacy params', () => {
|
|
|
69
69
|
expect(setProps).not.toHaveBeenCalledWith(fieldModel, { value: 'default-in-update' });
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
+
it('linkageAssignField should mark override mode for field targets', async () => {
|
|
73
|
+
const fieldModel: any = { uid: 'f-override', fieldPath: 'a.b' };
|
|
74
|
+
const ctx: any = {
|
|
75
|
+
model: {
|
|
76
|
+
subModels: { grid: { subModels: { items: [fieldModel] } } },
|
|
77
|
+
},
|
|
78
|
+
engine: { getModel: vi.fn(() => ({ fieldPath: 'a.b' })) },
|
|
79
|
+
app: { jsonLogic: { apply: vi.fn() } },
|
|
80
|
+
};
|
|
81
|
+
const setProps = vi.fn();
|
|
82
|
+
|
|
83
|
+
await linkageAssignField.handler(ctx, {
|
|
84
|
+
value: [
|
|
85
|
+
{
|
|
86
|
+
key: 'r-override',
|
|
87
|
+
enable: true,
|
|
88
|
+
targetPath: 'a.b',
|
|
89
|
+
mode: 'override',
|
|
90
|
+
condition: { logic: '$and', items: [] },
|
|
91
|
+
value: 'override-value',
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
setProps,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
expect(setProps).toHaveBeenCalledTimes(1);
|
|
98
|
+
const [, props] = setProps.mock.calls[0];
|
|
99
|
+
expect(props).toMatchObject({
|
|
100
|
+
value: 'override-value',
|
|
101
|
+
__linkageAssignMode: 'override',
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('linkageAssignField should emit override patches for targets without form item models', async () => {
|
|
106
|
+
const ctx: any = {
|
|
107
|
+
model: { subModels: { grid: { subModels: { items: [] } } } },
|
|
108
|
+
engine: { getModel: vi.fn(() => null) },
|
|
109
|
+
app: { jsonLogic: { apply: vi.fn() } },
|
|
110
|
+
};
|
|
111
|
+
const setProps = vi.fn();
|
|
112
|
+
const addFormValuePatch = vi.fn();
|
|
113
|
+
|
|
114
|
+
await linkageAssignField.handler(ctx, {
|
|
115
|
+
value: [
|
|
116
|
+
{
|
|
117
|
+
key: 'r-override-direct',
|
|
118
|
+
enable: true,
|
|
119
|
+
targetPath: 'user.name',
|
|
120
|
+
mode: 'override',
|
|
121
|
+
condition: { logic: '$and', items: [] },
|
|
122
|
+
value: 'override-direct',
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
setProps,
|
|
126
|
+
addFormValuePatch,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
expect(setProps).not.toHaveBeenCalled();
|
|
130
|
+
expect(addFormValuePatch).toHaveBeenCalledWith({
|
|
131
|
+
path: 'user.name',
|
|
132
|
+
value: 'override-direct',
|
|
133
|
+
whenEmpty: false,
|
|
134
|
+
mode: 'override',
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
72
138
|
it('subFormLinkageAssignField should apply legacy object params at runtime', () => {
|
|
73
139
|
const forkModel: any = {
|
|
74
140
|
uid: 'f1',
|
|
@@ -381,6 +447,75 @@ describe('linkage assign actions - legacy params', () => {
|
|
|
381
447
|
expect(addFormValuePatch).toHaveBeenCalledTimes(1);
|
|
382
448
|
});
|
|
383
449
|
|
|
450
|
+
it('subFormLinkageAssignField should skip override patch after user edits the target path', () => {
|
|
451
|
+
const ctx: any = {
|
|
452
|
+
model: {
|
|
453
|
+
uid: 'sub-form-override-path',
|
|
454
|
+
getAclActionName: vi.fn(() => 'create'),
|
|
455
|
+
context: {},
|
|
456
|
+
parent: {
|
|
457
|
+
getStepParams: (flowKey: string, stepKey: string) => {
|
|
458
|
+
if (flowKey === 'fieldSettings' && stepKey === 'init') {
|
|
459
|
+
return { fieldPath: 'M2M.M2M' };
|
|
460
|
+
}
|
|
461
|
+
return {};
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
subModels: { grid: { subModels: { items: [] } } },
|
|
465
|
+
},
|
|
466
|
+
engine: {
|
|
467
|
+
getModel: vi.fn(() => null),
|
|
468
|
+
},
|
|
469
|
+
app: { jsonLogic: { apply: vi.fn() } },
|
|
470
|
+
};
|
|
471
|
+
const addFormValuePatch = vi.fn();
|
|
472
|
+
|
|
473
|
+
subFormLinkageAssignField.handler(ctx, {
|
|
474
|
+
value: [
|
|
475
|
+
{
|
|
476
|
+
key: 'r-override-path-1',
|
|
477
|
+
enable: true,
|
|
478
|
+
targetPath: 'Name',
|
|
479
|
+
mode: 'override',
|
|
480
|
+
condition: { logic: '$and', items: [] },
|
|
481
|
+
value: 'override-1',
|
|
482
|
+
},
|
|
483
|
+
],
|
|
484
|
+
addFormValuePatch,
|
|
485
|
+
setProps: vi.fn(),
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
expect(addFormValuePatch).toHaveBeenCalledTimes(1);
|
|
489
|
+
expect(addFormValuePatch).toHaveBeenNthCalledWith(1, {
|
|
490
|
+
path: 'M2M.M2M.Name',
|
|
491
|
+
value: 'override-1',
|
|
492
|
+
whenEmpty: false,
|
|
493
|
+
mode: 'override',
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
ctx.inputArgs = {
|
|
497
|
+
source: 'user',
|
|
498
|
+
changedPaths: [['M2M', 'M2M', 'Name']],
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
subFormLinkageAssignField.handler(ctx, {
|
|
502
|
+
value: [
|
|
503
|
+
{
|
|
504
|
+
key: 'r-override-path-2',
|
|
505
|
+
enable: true,
|
|
506
|
+
targetPath: 'Name',
|
|
507
|
+
mode: 'override',
|
|
508
|
+
condition: { logic: '$and', items: [] },
|
|
509
|
+
value: 'override-2',
|
|
510
|
+
},
|
|
511
|
+
],
|
|
512
|
+
addFormValuePatch,
|
|
513
|
+
setProps: vi.fn(),
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
expect(addFormValuePatch).toHaveBeenCalledTimes(1);
|
|
517
|
+
});
|
|
518
|
+
|
|
384
519
|
it('linkageAssignField should evaluate RunJS value before assign', async () => {
|
|
385
520
|
const fieldModel: any = { uid: 'f-runjs-assign', fieldPath: 'a.b' };
|
|
386
521
|
const runjs = vi.fn(async () => ({ success: true, value: 'from-runjs' }));
|
|
@@ -27,12 +27,12 @@ describe('linkageRules hidden state propagation', () => {
|
|
|
27
27
|
const hostModel: any = {
|
|
28
28
|
uid: 'host',
|
|
29
29
|
hidden: false,
|
|
30
|
-
context: { blockModel },
|
|
30
|
+
context: { blockModel, fieldPathArray: ['children'] },
|
|
31
31
|
__allModels: [],
|
|
32
32
|
setProps: vi.fn(),
|
|
33
33
|
getFlow: vi.fn(() => ({})),
|
|
34
34
|
getStepParams: vi.fn((_flowKey: string, stepKey: string) => {
|
|
35
|
-
if (stepKey === 'init') return { fieldPath: '
|
|
35
|
+
if (stepKey === 'init') return { fieldPath: 'children' };
|
|
36
36
|
return undefined;
|
|
37
37
|
}),
|
|
38
38
|
translate: (s: string) => s,
|
|
@@ -41,10 +41,22 @@ describe('linkageRules hidden state propagation', () => {
|
|
|
41
41
|
const sameTargetModel: any = {
|
|
42
42
|
uid: 'same-target',
|
|
43
43
|
hidden: false,
|
|
44
|
-
context: { blockModel },
|
|
44
|
+
context: { blockModel, fieldPathArray: ['children'] },
|
|
45
45
|
setProps: vi.fn(),
|
|
46
46
|
getStepParams: vi.fn((_flowKey: string, stepKey: string) => {
|
|
47
|
-
if (stepKey === 'init') return { fieldPath: '
|
|
47
|
+
if (stepKey === 'init') return { fieldPath: 'children' };
|
|
48
|
+
return undefined;
|
|
49
|
+
}),
|
|
50
|
+
translate: (s: string) => s,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const nestedColumnModel: any = {
|
|
54
|
+
uid: 'nested-column',
|
|
55
|
+
hidden: true,
|
|
56
|
+
context: { blockModel, fieldPathArray: ['children'] },
|
|
57
|
+
setProps: vi.fn(),
|
|
58
|
+
getStepParams: vi.fn((_flowKey: string, stepKey: string) => {
|
|
59
|
+
if (stepKey === 'init') return { fieldPath: 'children.children' };
|
|
48
60
|
return undefined;
|
|
49
61
|
}),
|
|
50
62
|
translate: (s: string) => s,
|
|
@@ -52,7 +64,7 @@ describe('linkageRules hidden state propagation', () => {
|
|
|
52
64
|
|
|
53
65
|
const engine = {
|
|
54
66
|
forEachModel: (visitor: (m: any) => void) => {
|
|
55
|
-
[hostModel, sameTargetModel].forEach(visitor);
|
|
67
|
+
[hostModel, sameTargetModel, nestedColumnModel].forEach(visitor);
|
|
56
68
|
},
|
|
57
69
|
};
|
|
58
70
|
|
|
@@ -92,9 +104,11 @@ describe('linkageRules hidden state propagation', () => {
|
|
|
92
104
|
});
|
|
93
105
|
expect(hostModel.hidden).toBe(true);
|
|
94
106
|
expect(sameTargetModel.hidden).toBe(true);
|
|
107
|
+
expect(nestedColumnModel.hidden).toBe(true);
|
|
95
108
|
|
|
96
109
|
await actionLinkageRules.handler(ctx, { value: [] });
|
|
97
110
|
expect(hostModel.hidden).toBe(false);
|
|
98
111
|
expect(sameTargetModel.hidden).toBe(false);
|
|
112
|
+
expect(nestedColumnModel.hidden).toBe(true);
|
|
99
113
|
});
|
|
100
114
|
});
|
|
@@ -728,6 +728,98 @@ describe('linkageSetFieldProps action', () => {
|
|
|
728
728
|
expect(form.setFieldValue).not.toHaveBeenCalled();
|
|
729
729
|
});
|
|
730
730
|
|
|
731
|
+
it('should not clear the parent field when a nested same-name field inherits the parent path', async () => {
|
|
732
|
+
const setFormValues = vi.fn(async () => undefined);
|
|
733
|
+
const form = {
|
|
734
|
+
getFieldValue: vi.fn((path: Array<string | number>) => {
|
|
735
|
+
if (JSON.stringify(path) === JSON.stringify(['children', 'children'])) {
|
|
736
|
+
return [{ title: 'nested' }];
|
|
737
|
+
}
|
|
738
|
+
if (JSON.stringify(path) === JSON.stringify(['children'])) {
|
|
739
|
+
return [{ title: 'parent' }];
|
|
740
|
+
}
|
|
741
|
+
return undefined;
|
|
742
|
+
}),
|
|
743
|
+
setFieldValue: vi.fn(),
|
|
744
|
+
};
|
|
745
|
+
const fieldModel: any = {
|
|
746
|
+
uid: 'nested-children-field',
|
|
747
|
+
hidden: false,
|
|
748
|
+
context: {
|
|
749
|
+
form,
|
|
750
|
+
fieldPathArray: ['children'],
|
|
751
|
+
},
|
|
752
|
+
props: {
|
|
753
|
+
label: 'Children',
|
|
754
|
+
},
|
|
755
|
+
getStepParams: vi.fn((flowKey: string, stepKey: string) => {
|
|
756
|
+
if (flowKey === 'fieldSettings' && stepKey === 'init') {
|
|
757
|
+
return { fieldPath: 'children.children' };
|
|
758
|
+
}
|
|
759
|
+
}),
|
|
760
|
+
setProps(key: any, value?: any) {
|
|
761
|
+
if (typeof key === 'string') {
|
|
762
|
+
this.props[key] = value;
|
|
763
|
+
} else {
|
|
764
|
+
this.props = { ...this.props, ...key };
|
|
765
|
+
}
|
|
766
|
+
},
|
|
767
|
+
};
|
|
768
|
+
const ctx: any = {
|
|
769
|
+
app: {
|
|
770
|
+
jsonLogic: {
|
|
771
|
+
apply: vi.fn(() => true),
|
|
772
|
+
},
|
|
773
|
+
},
|
|
774
|
+
model: {
|
|
775
|
+
context: { form },
|
|
776
|
+
subModels: {
|
|
777
|
+
grid: {
|
|
778
|
+
subModels: {
|
|
779
|
+
items: [fieldModel],
|
|
780
|
+
},
|
|
781
|
+
},
|
|
782
|
+
},
|
|
783
|
+
},
|
|
784
|
+
setFormValues,
|
|
785
|
+
getAction: (name: string) => (name === 'linkageSetFieldProps' ? linkageSetFieldProps : null),
|
|
786
|
+
resolveJsonTemplate: vi.fn(async (value) => value),
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
await fieldLinkageRules.handler(ctx, {
|
|
790
|
+
value: [
|
|
791
|
+
{
|
|
792
|
+
key: 'rule-1',
|
|
793
|
+
enable: true,
|
|
794
|
+
condition: { logic: '$and', items: [] },
|
|
795
|
+
actions: [
|
|
796
|
+
{
|
|
797
|
+
key: 'action-1',
|
|
798
|
+
name: 'linkageSetFieldProps',
|
|
799
|
+
params: {
|
|
800
|
+
value: {
|
|
801
|
+
fields: ['nested-children-field'],
|
|
802
|
+
state: 'hidden',
|
|
803
|
+
},
|
|
804
|
+
},
|
|
805
|
+
},
|
|
806
|
+
],
|
|
807
|
+
},
|
|
808
|
+
],
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
expect(fieldModel.hidden).toBe(true);
|
|
812
|
+
expect(setFormValues).toHaveBeenCalledWith(
|
|
813
|
+
[{ path: ['children', 'children'], value: undefined }],
|
|
814
|
+
expect.objectContaining({ source: 'linkage' }),
|
|
815
|
+
);
|
|
816
|
+
expect(setFormValues).not.toHaveBeenCalledWith(
|
|
817
|
+
[{ path: ['children'], value: undefined }],
|
|
818
|
+
expect.objectContaining({ source: 'linkage' }),
|
|
819
|
+
);
|
|
820
|
+
expect(form.setFieldValue).not.toHaveBeenCalled();
|
|
821
|
+
});
|
|
822
|
+
|
|
731
823
|
it('should keep hidden clear after same-round assignment for the same field', async () => {
|
|
732
824
|
const formValues: { name?: string } = {};
|
|
733
825
|
const setFormValues = vi.fn(async (patches: Array<{ path: Array<string | number>; value: string | undefined }>) => {
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
defineAction,
|
|
13
13
|
isRunJSValue,
|
|
14
14
|
normalizeRunJSValue,
|
|
15
|
-
runjsWithSafeGlobals,
|
|
16
15
|
tExpr,
|
|
17
16
|
type RunJSValue,
|
|
18
17
|
useFlowContext,
|
|
@@ -43,7 +42,7 @@ export const customVariable = defineAction({
|
|
|
43
42
|
if (variable.type === 'runjs') {
|
|
44
43
|
const getFunction = async () => {
|
|
45
44
|
const { code, version } = normalizeRunJSValue(variable.runjs);
|
|
46
|
-
return
|
|
45
|
+
return ctx.runjs(code, undefined, { version });
|
|
47
46
|
};
|
|
48
47
|
const metaFunction = () => ({
|
|
49
48
|
title: variable.title,
|
|
@@ -12,6 +12,12 @@ import { transformFilter } from '@nocobase/utils/client';
|
|
|
12
12
|
import _ from 'lodash';
|
|
13
13
|
|
|
14
14
|
const PRESERVE_NULL = { __nocobaseDataScopeNull__: true };
|
|
15
|
+
const SERVER_CURRENT_ROLE_VARIABLE = '{{$nRole}}';
|
|
16
|
+
const CURRENT_ROLE_EXPRESSION_RE = /^\s*\{\{\s*ctx\.role\s*\}\}\s*$/;
|
|
17
|
+
|
|
18
|
+
function isCurrentRoleExpression(value: unknown) {
|
|
19
|
+
return typeof value === 'string' && CURRENT_ROLE_EXPRESSION_RE.test(value);
|
|
20
|
+
}
|
|
15
21
|
|
|
16
22
|
function isPreserveNull(value: any) {
|
|
17
23
|
return (
|
|
@@ -45,6 +51,10 @@ function markEmptyVariableValues(rawNode: any, resolvedNode: any) {
|
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
if ('path' in rawNode && 'operator' in rawNode) {
|
|
54
|
+
if (isCurrentRoleExpression(rawNode.value)) {
|
|
55
|
+
resolvedNode.value = SERVER_CURRENT_ROLE_VARIABLE;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
48
58
|
if (
|
|
49
59
|
isVariableExpression(rawNode.value) &&
|
|
50
60
|
(resolvedNode.value === undefined || resolvedNode.value === null || resolvedNode.value === '')
|