@nocobase/client-v2 2.1.33 → 2.1.35
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/flow/models/actions/UpdateRecordActionUtils.d.ts +8 -2
- package/es/flow/models/blocks/table/JSColumnModel.d.ts +2 -0
- package/es/index.mjs +63 -63
- package/lib/index.js +69 -69
- package/package.json +7 -7
- package/src/flow/models/actions/UpdateRecordActionModel.tsx +18 -1
- package/src/flow/models/actions/UpdateRecordActionUtils.ts +18 -6
- package/src/flow/models/actions/__tests__/UpdateRecordActionModel.test.ts +76 -4
- package/src/flow/models/base/CollectionBlockModel.tsx +9 -0
- package/src/flow/models/base/__tests__/CollectionBlockModel.initialBeforeRenderRefresh.test.ts +60 -0
- package/src/flow/models/blocks/table/JSColumnModel.tsx +10 -1
- package/src/flow/models/blocks/table/__tests__/JSColumnModel.test.tsx +52 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/client-v2",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.35",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "es/index.mjs",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"@formily/antd-v5": "1.2.3",
|
|
28
28
|
"@formily/react": "^2.2.27",
|
|
29
29
|
"@formily/shared": "^2.2.27",
|
|
30
|
-
"@nocobase/evaluators": "2.1.
|
|
31
|
-
"@nocobase/flow-engine": "2.1.
|
|
32
|
-
"@nocobase/sdk": "2.1.
|
|
33
|
-
"@nocobase/shared": "2.1.
|
|
34
|
-
"@nocobase/utils": "2.1.
|
|
30
|
+
"@nocobase/evaluators": "2.1.35",
|
|
31
|
+
"@nocobase/flow-engine": "2.1.35",
|
|
32
|
+
"@nocobase/sdk": "2.1.35",
|
|
33
|
+
"@nocobase/shared": "2.1.35",
|
|
34
|
+
"@nocobase/utils": "2.1.35",
|
|
35
35
|
"ahooks": "^3.7.2",
|
|
36
36
|
"antd": "5.24.2",
|
|
37
37
|
"antd-style": "3.7.1",
|
|
@@ -48,5 +48,5 @@
|
|
|
48
48
|
"react-i18next": "^11.15.1",
|
|
49
49
|
"react-router-dom": "^6.30.1"
|
|
50
50
|
},
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "d844133f9bd61a3506a3be1af25a1cb06dafbc6f"
|
|
52
52
|
}
|
|
@@ -22,6 +22,11 @@ import { applyUpdateRecordAction } from './UpdateRecordActionUtils';
|
|
|
22
22
|
// import { RemoteFlowModelRenderer } from '../../FlowPage';
|
|
23
23
|
|
|
24
24
|
const SETTINGS_FLOW_KEY = 'assignSettings';
|
|
25
|
+
const AFTER_SUCCESS_DEFAULT_PARAMS = {
|
|
26
|
+
successMessage: tExpr('Saved successfully'),
|
|
27
|
+
manualClose: false,
|
|
28
|
+
actionAfterSuccess: 'stay',
|
|
29
|
+
};
|
|
25
30
|
|
|
26
31
|
function Info() {
|
|
27
32
|
const ctx = useFlowSettingsContext();
|
|
@@ -101,6 +106,10 @@ UpdateRecordActionModel.registerFlow({
|
|
|
101
106
|
tipComponent: Info,
|
|
102
107
|
validateBeforeSave: true,
|
|
103
108
|
}),
|
|
109
|
+
afterSuccess: {
|
|
110
|
+
use: 'afterSuccess',
|
|
111
|
+
defaultParams: AFTER_SUCCESS_DEFAULT_PARAMS,
|
|
112
|
+
},
|
|
104
113
|
},
|
|
105
114
|
});
|
|
106
115
|
|
|
@@ -113,7 +122,15 @@ UpdateRecordActionModel.registerFlow({
|
|
|
113
122
|
return getAssignFieldValuesDefaultParams(ctx, SETTINGS_FLOW_KEY);
|
|
114
123
|
},
|
|
115
124
|
async handler(ctx, params) {
|
|
116
|
-
await applyUpdateRecordAction(ctx, params, { settingsFlowKey: SETTINGS_FLOW_KEY });
|
|
125
|
+
const updated = await applyUpdateRecordAction(ctx, params, { settingsFlowKey: SETTINGS_FLOW_KEY });
|
|
126
|
+
if (!updated) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const savedAfterSuccess = ctx.model.getStepParams(SETTINGS_FLOW_KEY, 'afterSuccess') || {};
|
|
130
|
+
await ctx.runAction('afterSuccess', {
|
|
131
|
+
...AFTER_SUCCESS_DEFAULT_PARAMS,
|
|
132
|
+
...savedAfterSuccess,
|
|
133
|
+
});
|
|
117
134
|
},
|
|
118
135
|
},
|
|
119
136
|
},
|
|
@@ -8,9 +8,15 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { MultiRecordResource, SingleRecordResource } from '@nocobase/flow-engine';
|
|
11
|
+
import type { AxiosRequestConfig } from 'axios';
|
|
11
12
|
import { dispatchEventDeep } from '../../utils';
|
|
12
13
|
import { resolveAssignFieldValues } from '../blocks/assign-form/assignFieldValuesFlow';
|
|
13
14
|
|
|
15
|
+
type UpdateRecordActionParams = {
|
|
16
|
+
assignedValues?: unknown;
|
|
17
|
+
requestConfig?: AxiosRequestConfig;
|
|
18
|
+
};
|
|
19
|
+
|
|
14
20
|
export async function refreshLinkageRulesAfterUpdate(ctx: any) {
|
|
15
21
|
const blockModel = ctx?.blockModel || ctx?.model?.context?.blockModel || ctx?.model;
|
|
16
22
|
const actionModel = ctx?.model;
|
|
@@ -48,11 +54,11 @@ export async function refreshLinkageRulesAfterUpdate(ctx: any) {
|
|
|
48
54
|
|
|
49
55
|
export async function applyUpdateRecordAction(
|
|
50
56
|
ctx: any,
|
|
51
|
-
params:
|
|
57
|
+
params: UpdateRecordActionParams,
|
|
52
58
|
options?: {
|
|
53
59
|
settingsFlowKey?: string;
|
|
54
60
|
},
|
|
55
|
-
) {
|
|
61
|
+
): Promise<boolean> {
|
|
56
62
|
const settingsFlowKey = options?.settingsFlowKey || 'assignSettings';
|
|
57
63
|
|
|
58
64
|
// 统一接入二次确认:如果启用则弹窗;未配置时默认不启用
|
|
@@ -62,25 +68,31 @@ export async function applyUpdateRecordAction(
|
|
|
62
68
|
|
|
63
69
|
const assignedValues = await resolveAssignFieldValues(ctx, params?.assignedValues, 'UpdateRecordAction');
|
|
64
70
|
if (!assignedValues) {
|
|
65
|
-
return;
|
|
71
|
+
return false;
|
|
66
72
|
}
|
|
67
73
|
|
|
68
74
|
if (!assignedValues || typeof assignedValues !== 'object' || !Object.keys(assignedValues).length) {
|
|
69
75
|
ctx.message.warning(ctx.t('No assigned fields configured'));
|
|
70
|
-
return;
|
|
76
|
+
return false;
|
|
71
77
|
}
|
|
72
78
|
const collection = ctx.collection?.name;
|
|
73
79
|
const filterByTk = ctx.collection?.getFilterByTK?.(ctx.record);
|
|
74
80
|
if (!collection || typeof filterByTk === 'undefined' || filterByTk === null) {
|
|
75
81
|
ctx.message.error(ctx.t('Record is required to perform this action'));
|
|
76
|
-
return;
|
|
82
|
+
return false;
|
|
77
83
|
}
|
|
84
|
+
let updated = false;
|
|
78
85
|
if (ctx.resource instanceof SingleRecordResource) {
|
|
79
86
|
await ctx.resource.save(assignedValues, params.requestConfig);
|
|
87
|
+
updated = true;
|
|
80
88
|
} else if (ctx.resource instanceof MultiRecordResource) {
|
|
81
89
|
await ctx.resource.update(filterByTk, assignedValues, params.requestConfig);
|
|
90
|
+
updated = true;
|
|
91
|
+
}
|
|
92
|
+
if (!updated) {
|
|
93
|
+
return false;
|
|
82
94
|
}
|
|
83
95
|
|
|
84
96
|
await refreshLinkageRulesAfterUpdate(ctx);
|
|
85
|
-
|
|
97
|
+
return true;
|
|
86
98
|
}
|
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
11
|
-
import { MultiRecordResource } from '@nocobase/flow-engine';
|
|
11
|
+
import { FlowEngine, MultiRecordResource } from '@nocobase/flow-engine';
|
|
12
12
|
import { applyUpdateRecordAction } from '../UpdateRecordActionUtils';
|
|
13
|
+
import { UpdateRecordActionModel } from '../UpdateRecordActionModel';
|
|
13
14
|
import { dispatchEventDeep } from '../../../utils';
|
|
14
15
|
|
|
15
16
|
vi.mock('../../../utils', () => ({
|
|
@@ -21,7 +22,7 @@ describe('UpdateRecordActionModel apply action', () => {
|
|
|
21
22
|
vi.clearAllMocks();
|
|
22
23
|
});
|
|
23
24
|
|
|
24
|
-
it('dispatches paginationChange
|
|
25
|
+
it('dispatches paginationChange and returns success after updating the record', async () => {
|
|
25
26
|
const resource: any = Object.create(MultiRecordResource.prototype);
|
|
26
27
|
resource.update = vi.fn(async () => ({}));
|
|
27
28
|
resource.refresh = vi.fn(async () => {});
|
|
@@ -52,12 +53,13 @@ describe('UpdateRecordActionModel apply action', () => {
|
|
|
52
53
|
t: (value: string) => value,
|
|
53
54
|
};
|
|
54
55
|
|
|
55
|
-
await applyUpdateRecordAction(ctx, {
|
|
56
|
+
const updated = await applyUpdateRecordAction(ctx, {
|
|
56
57
|
assignedValues: {
|
|
57
58
|
marital_status: '已婚',
|
|
58
59
|
},
|
|
59
60
|
});
|
|
60
61
|
|
|
62
|
+
expect(updated).toBe(true);
|
|
61
63
|
expect(resource.update).toHaveBeenCalledWith(1, { marital_status: '已婚' }, undefined);
|
|
62
64
|
expect(resource.refresh).not.toHaveBeenCalled();
|
|
63
65
|
|
|
@@ -67,6 +69,76 @@ describe('UpdateRecordActionModel apply action', () => {
|
|
|
67
69
|
expect(paginationCalls.length).toBeGreaterThan(0);
|
|
68
70
|
expect(paginationCalls.some(([model]: [any]) => model === ctx.model)).toBe(true);
|
|
69
71
|
expect(paginationCalls.some(([model]: [any]) => model === blockModel)).toBe(true);
|
|
70
|
-
expect(ctx.message.success).
|
|
72
|
+
expect(ctx.message.success).not.toHaveBeenCalled();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('runs the configured after-success action only after a successful update', async () => {
|
|
76
|
+
const engine = new FlowEngine();
|
|
77
|
+
const action = new UpdateRecordActionModel({ uid: 'update-record-action', flowEngine: engine } as any);
|
|
78
|
+
action.setStepParams('assignSettings', 'confirm', { enable: false });
|
|
79
|
+
action.setStepParams('assignSettings', 'afterSuccess', {
|
|
80
|
+
successMessage: 'Record updated',
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const resource: any = Object.create(MultiRecordResource.prototype);
|
|
84
|
+
resource.update = vi.fn(async () => ({}));
|
|
85
|
+
const blockModel: any = { uid: 'details-block' };
|
|
86
|
+
const runAction = vi.fn(async () => {});
|
|
87
|
+
const ctx: any = {
|
|
88
|
+
model: action,
|
|
89
|
+
blockModel,
|
|
90
|
+
runAction,
|
|
91
|
+
collection: {
|
|
92
|
+
name: 'users',
|
|
93
|
+
getFilterByTK: vi.fn(() => 1),
|
|
94
|
+
},
|
|
95
|
+
record: { id: 1 },
|
|
96
|
+
resource,
|
|
97
|
+
message: {
|
|
98
|
+
success: vi.fn(),
|
|
99
|
+
warning: vi.fn(),
|
|
100
|
+
error: vi.fn(),
|
|
101
|
+
},
|
|
102
|
+
t: (value: string) => value,
|
|
103
|
+
};
|
|
104
|
+
const handler = action.getFlow('apply')?.getStep('apply')?.serialize().handler;
|
|
105
|
+
|
|
106
|
+
await handler(ctx, { assignedValues: { status: 'active' } });
|
|
107
|
+
|
|
108
|
+
expect(runAction).toHaveBeenNthCalledWith(1, 'confirm', { enable: false });
|
|
109
|
+
expect(runAction).toHaveBeenNthCalledWith(2, 'afterSuccess', {
|
|
110
|
+
successMessage: 'Record updated',
|
|
111
|
+
manualClose: false,
|
|
112
|
+
actionAfterSuccess: 'stay',
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('does not run the after-success action when no fields are assigned', async () => {
|
|
117
|
+
const engine = new FlowEngine();
|
|
118
|
+
const action = new UpdateRecordActionModel({ uid: 'update-record-action-empty', flowEngine: engine } as any);
|
|
119
|
+
const runAction = vi.fn(async () => {});
|
|
120
|
+
const ctx: any = {
|
|
121
|
+
model: action,
|
|
122
|
+
runAction,
|
|
123
|
+
collection: {
|
|
124
|
+
name: 'users',
|
|
125
|
+
getFilterByTK: vi.fn(() => 1),
|
|
126
|
+
},
|
|
127
|
+
record: { id: 1 },
|
|
128
|
+
resource: Object.create(MultiRecordResource.prototype),
|
|
129
|
+
message: {
|
|
130
|
+
success: vi.fn(),
|
|
131
|
+
warning: vi.fn(),
|
|
132
|
+
error: vi.fn(),
|
|
133
|
+
},
|
|
134
|
+
t: (value: string) => value,
|
|
135
|
+
};
|
|
136
|
+
const handler = action.getFlow('apply')?.getStep('apply')?.serialize().handler;
|
|
137
|
+
|
|
138
|
+
await handler(ctx, { assignedValues: {} });
|
|
139
|
+
|
|
140
|
+
expect(runAction).toHaveBeenCalledTimes(1);
|
|
141
|
+
expect(runAction).toHaveBeenCalledWith('confirm', { enable: false });
|
|
142
|
+
expect(ctx.message.warning).toHaveBeenCalledWith('No assigned fields configured');
|
|
71
143
|
});
|
|
72
144
|
});
|
|
@@ -82,6 +82,15 @@ export class CollectionBlockModel<T = DefaultStructure> extends DataBlockModel<T
|
|
|
82
82
|
const engine = this.context.engine as FlowEngine;
|
|
83
83
|
const currentVersion = this.getDirtyTrackingVersion(engine, dataSourceKey, resource, params);
|
|
84
84
|
|
|
85
|
+
if (resource instanceof MultiRecordResource && this.getDataLoadingMode() === 'manual' && !this.hasActiveFilters()) {
|
|
86
|
+
resource.setData([]);
|
|
87
|
+
resource.setMeta({ count: 0, hasNext: false });
|
|
88
|
+
resource.setPage(1);
|
|
89
|
+
resource.loading = false;
|
|
90
|
+
this.lastSeenDirtyVersion = currentVersion;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
85
94
|
if (forceRefresh) {
|
|
86
95
|
if (this.dirtyRefreshing) return;
|
|
87
96
|
this.dirtyRefreshing = true;
|
package/src/flow/models/base/__tests__/CollectionBlockModel.initialBeforeRenderRefresh.test.ts
CHANGED
|
@@ -128,4 +128,64 @@ describe('CollectionBlockModel initial beforeRender refresh', () => {
|
|
|
128
128
|
expect(resource.getData()).toEqual([]);
|
|
129
129
|
expect(resource.getMeta('count')).toBe(0);
|
|
130
130
|
});
|
|
131
|
+
|
|
132
|
+
it('skips forced active refresh in manual mode when filters are empty', async () => {
|
|
133
|
+
const { model, resource } = setupModelWithManualMode();
|
|
134
|
+
const refreshSpy = vi.spyOn(resource, 'refresh');
|
|
135
|
+
|
|
136
|
+
resource.setData([{ id: 2, name: 'Stale' }]);
|
|
137
|
+
resource.setMeta({ count: 1, hasNext: true });
|
|
138
|
+
resource.setPage(2);
|
|
139
|
+
resource.loading = true;
|
|
140
|
+
|
|
141
|
+
model.onActive(true);
|
|
142
|
+
await Promise.resolve();
|
|
143
|
+
await Promise.resolve();
|
|
144
|
+
|
|
145
|
+
expect(refreshSpy).not.toHaveBeenCalled();
|
|
146
|
+
expect(resource.getData()).toEqual([]);
|
|
147
|
+
expect(resource.getMeta('count')).toBe(0);
|
|
148
|
+
expect(resource.getMeta('hasNext')).toBe(false);
|
|
149
|
+
expect(resource.getMeta('page')).toBe(1);
|
|
150
|
+
expect(resource.getRequestParameter('page')).toBe(1);
|
|
151
|
+
expect(resource.loading).toBe(false);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('keeps forced active refresh in manual mode when filters are active', async () => {
|
|
155
|
+
const { model, resource } = setupModelWithManualMode();
|
|
156
|
+
const refreshSpy = vi.spyOn(resource, 'refresh').mockResolvedValue();
|
|
157
|
+
|
|
158
|
+
resource.setData([{ id: 2, name: 'Stale' }]);
|
|
159
|
+
resource.setMeta({ count: 1, hasNext: true, page: 2 });
|
|
160
|
+
model.setFilterActive('filter-form-item', true);
|
|
161
|
+
|
|
162
|
+
model.onActive(true);
|
|
163
|
+
await Promise.resolve();
|
|
164
|
+
|
|
165
|
+
expect(refreshSpy).toHaveBeenCalledTimes(1);
|
|
166
|
+
expect(resource.getData()).toEqual([{ id: 2, name: 'Stale' }]);
|
|
167
|
+
expect(resource.getMeta('count')).toBe(1);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('skips forced active refresh in manual mode when only resource filters are active', async () => {
|
|
171
|
+
const { model, resource } = setupModelWithManualMode();
|
|
172
|
+
const refreshSpy = vi.spyOn(resource, 'refresh');
|
|
173
|
+
|
|
174
|
+
resource.setData([{ id: 2, name: 'Stale' }]);
|
|
175
|
+
resource.setMeta({ count: 1, hasNext: true });
|
|
176
|
+
resource.setPage(2);
|
|
177
|
+
resource.loading = true;
|
|
178
|
+
resource.addFilterGroup('data-scope', { status: { $eq: 'active' } });
|
|
179
|
+
|
|
180
|
+
model.onActive(true);
|
|
181
|
+
await Promise.resolve();
|
|
182
|
+
|
|
183
|
+
expect(refreshSpy).not.toHaveBeenCalled();
|
|
184
|
+
expect(resource.getData()).toEqual([]);
|
|
185
|
+
expect(resource.getMeta('count')).toBe(0);
|
|
186
|
+
expect(resource.getMeta('hasNext')).toBe(false);
|
|
187
|
+
expect(resource.getMeta('page')).toBe(1);
|
|
188
|
+
expect(resource.getRequestParameter('page')).toBe(1);
|
|
189
|
+
expect(resource.loading).toBe(false);
|
|
190
|
+
});
|
|
131
191
|
});
|
|
@@ -9,13 +9,14 @@
|
|
|
9
9
|
|
|
10
10
|
import { LockOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
|
11
11
|
import { css } from '@emotion/css';
|
|
12
|
-
import type { PropertyMetaFactory } from '@nocobase/flow-engine';
|
|
12
|
+
import type { FlowModelOptions, PropertyMetaFactory } from '@nocobase/flow-engine';
|
|
13
13
|
import {
|
|
14
14
|
Droppable,
|
|
15
15
|
tExpr,
|
|
16
16
|
FlowsFloatContextMenu,
|
|
17
17
|
DragHandler,
|
|
18
18
|
MemoFlowModelRenderer,
|
|
19
|
+
createCurrentRecordMetaFactory,
|
|
19
20
|
createRecordMetaFactory,
|
|
20
21
|
createRecordResolveOnServerWithLocal,
|
|
21
22
|
ElementProxy,
|
|
@@ -54,6 +55,14 @@ function getRecordRenderSignature(record: any) {
|
|
|
54
55
|
export class JSColumnModel extends TableCustomColumnModel {
|
|
55
56
|
// Stable per‑instance render component to avoid remounts across re-renders
|
|
56
57
|
private _RenderComponent?: React.ComponentType;
|
|
58
|
+
|
|
59
|
+
onInit(options: FlowModelOptions): void {
|
|
60
|
+
super.onInit(options);
|
|
61
|
+
this.context.defineProperty('record', {
|
|
62
|
+
meta: createCurrentRecordMetaFactory(this.context, () => this.context.collection),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
57
66
|
renderHiddenInConfig() {
|
|
58
67
|
return (
|
|
59
68
|
<Tooltip
|
|
@@ -11,17 +11,62 @@ import { FlowEngine } from '@nocobase/flow-engine';
|
|
|
11
11
|
import { describe, expect, it, vi } from 'vitest';
|
|
12
12
|
import { JSColumnModel } from '../JSColumnModel';
|
|
13
13
|
|
|
14
|
-
describe('JSColumnModel
|
|
14
|
+
describe('JSColumnModel', () => {
|
|
15
|
+
it('declares current record metadata before rendering any row', async () => {
|
|
16
|
+
const engine = new FlowEngine();
|
|
17
|
+
engine.registerModels({ JSColumnModel });
|
|
18
|
+
const model = engine.createModel<JSColumnModel>({
|
|
19
|
+
use: 'JSColumnModel',
|
|
20
|
+
uid: 'js-column-record-meta',
|
|
21
|
+
props: {
|
|
22
|
+
width: 200,
|
|
23
|
+
title: 'JS column',
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
engine.context.dataSourceManager.getDataSource('main').addCollection({
|
|
28
|
+
name: 'users',
|
|
29
|
+
filterTargetKey: 'id',
|
|
30
|
+
fields: [
|
|
31
|
+
{ name: 'id', type: 'integer', interface: 'number' },
|
|
32
|
+
{ name: 'age', type: 'integer', interface: 'integer' },
|
|
33
|
+
],
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const collection = engine.context.dataSourceManager.getCollection('main', 'users');
|
|
37
|
+
model.context.defineProperty('collection', {
|
|
38
|
+
value: collection,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const recordNode = model.context.getPropertyMetaTree().find((node) => node.name === 'record');
|
|
42
|
+
const recordFields =
|
|
43
|
+
typeof recordNode?.children === 'function' ? await recordNode.children() : recordNode?.children || [];
|
|
44
|
+
|
|
45
|
+
expect(model.context.record).toBeUndefined();
|
|
46
|
+
expect(recordNode).toMatchObject({
|
|
47
|
+
name: 'record',
|
|
48
|
+
title: 'Current record',
|
|
49
|
+
paths: ['record'],
|
|
50
|
+
});
|
|
51
|
+
expect(recordFields).toEqual(
|
|
52
|
+
expect.arrayContaining([
|
|
53
|
+
expect.objectContaining({ name: 'id', paths: ['record', 'id'] }),
|
|
54
|
+
expect.objectContaining({ name: 'age', paths: ['record', 'age'] }),
|
|
55
|
+
]),
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
15
59
|
it('changes renderer key and invalidates beforeRender cache when row content changes', () => {
|
|
16
60
|
const engine = new FlowEngine();
|
|
17
|
-
|
|
61
|
+
engine.registerModels({ JSColumnModel });
|
|
62
|
+
const model = engine.createModel<JSColumnModel>({
|
|
63
|
+
use: 'JSColumnModel',
|
|
18
64
|
uid: 'js-column-row-refresh',
|
|
19
|
-
flowEngine: engine,
|
|
20
65
|
props: {
|
|
21
66
|
width: 200,
|
|
22
67
|
title: 'JS column',
|
|
23
68
|
},
|
|
24
|
-
}
|
|
69
|
+
});
|
|
25
70
|
|
|
26
71
|
engine.context.dataSourceManager.getDataSource('main').addCollection({
|
|
27
72
|
name: 'users',
|
|
@@ -39,12 +84,14 @@ describe('JSColumnModel row refresh', () => {
|
|
|
39
84
|
});
|
|
40
85
|
|
|
41
86
|
const column = model.getColumnProps();
|
|
42
|
-
const
|
|
87
|
+
const firstRecord = { id: 1, age: 3, workyears: 39.2 };
|
|
88
|
+
const first = column.render(null, firstRecord, 0) as any;
|
|
43
89
|
const fork = model.getFork('1') as any;
|
|
44
90
|
const invalidateFlowCache = vi.fn();
|
|
45
91
|
fork.invalidateFlowCache = invalidateFlowCache;
|
|
46
92
|
const second = column.render(null, { id: 1, age: 37, workyears: 39.2 }, 0) as any;
|
|
47
93
|
|
|
94
|
+
expect(fork.context.record).toEqual({ id: 1, age: 37, workyears: 39.2 });
|
|
48
95
|
expect(first.key).not.toBe(second.key);
|
|
49
96
|
expect(invalidateFlowCache).toHaveBeenCalledWith('beforeRender');
|
|
50
97
|
});
|