@nocobase/client-v2 2.1.0-beta.42 → 2.1.0-beta.43

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.
@@ -10,7 +10,7 @@
10
10
  import React from 'react';
11
11
  import { App, ConfigProvider } from 'antd';
12
12
  import { describe, expect, it } from 'vitest';
13
- import { render, screen, waitFor } from '@nocobase/test/client';
13
+ import { render, screen, userEvent, waitFor } from '@nocobase/test/client';
14
14
  import {
15
15
  FlowEngine,
16
16
  FlowEngineProvider,
@@ -24,6 +24,7 @@ import { AssignFormItemModel } from '../AssignFormItemModel';
24
24
  import { AssignFormModel } from '../AssignFormModel';
25
25
  import { createAssignFieldValuesStep } from '../assignFieldValuesFlow';
26
26
  import { InputFieldModel } from '../../../fields/InputFieldModel';
27
+ import { VariableFieldFormModel } from '../../../fields/VariableFieldFormModel';
27
28
 
28
29
  class MockFlowModelRepository implements IFlowModelRepository {
29
30
  async findOne(): Promise<Record<string, any> | null> {
@@ -49,7 +50,13 @@ describe('assignFieldValuesFlow (editor)', () => {
49
50
  it('repairs AssignFormModel resource init and clears cached collection', async () => {
50
51
  const engine = new FlowEngine();
51
52
  engine.setModelRepository(new MockFlowModelRepository());
52
- engine.registerModels({ AssignFormModel, AssignFormGridModel, AssignFormItemModel, InputFieldModel });
53
+ engine.registerModels({
54
+ AssignFormModel,
55
+ AssignFormGridModel,
56
+ AssignFormItemModel,
57
+ InputFieldModel,
58
+ VariableFieldFormModel,
59
+ });
53
60
  engine.context.defineProperty('location', { value: { search: '' } });
54
61
  engine.context.defineProperty('themeToken', { value: { marginLG: 24 } });
55
62
 
@@ -120,5 +127,90 @@ describe('assignFieldValuesFlow (editor)', () => {
120
127
  const form = engine.findModelByParentId<AssignFormModel>(action.uid, 'assignForm');
121
128
  expect(form?.subModels.grid.subModels.items.length).toBe(1);
122
129
  });
130
+
131
+ await waitFor(() => {
132
+ expect(screen.getByDisplayValue('1111')).toBeInTheDocument();
133
+ });
134
+
135
+ const input = screen.getByDisplayValue('1111') as HTMLInputElement;
136
+ await userEvent.clear(input);
137
+ await userEvent.type(input, '2222');
138
+
139
+ await waitFor(() => {
140
+ const form = engine.findModelByParentId<AssignFormModel>(action.uid, 'assignForm');
141
+ expect(form?.getAssignedValues()).toEqual({ nickname: '2222' });
142
+ });
143
+ });
144
+
145
+ it('does not clear delegated parent resource cache when repairing AssignFormModel init', async () => {
146
+ const engine = new FlowEngine();
147
+ engine.setModelRepository(new MockFlowModelRepository());
148
+ engine.registerModels({ AssignFormModel, AssignFormGridModel, AssignFormItemModel, InputFieldModel });
149
+ engine.context.defineProperty('location', { value: { search: '' } });
150
+ engine.context.defineProperty('themeToken', { value: { marginLG: 24 } });
151
+
152
+ const dsm = engine.context.dataSourceManager;
153
+ const main = dsm.getDataSource('main');
154
+ main.addCollection({
155
+ name: 'users',
156
+ fields: [{ name: 'nickname', type: 'string', interface: 'input' }],
157
+ });
158
+ const users = dsm.getCollection('main', 'users');
159
+
160
+ const action = engine.createModel({
161
+ use: 'FlowModel',
162
+ uid: 'act-assign-parent-resource',
163
+ });
164
+
165
+ let resourceCreateCount = 0;
166
+ action.context.defineProperty('resource', {
167
+ get: () => ({ id: ++resourceCreateCount }),
168
+ });
169
+ const cachedResource = action.context.resource;
170
+
171
+ engine.createModel<AssignFormModel>({
172
+ use: 'AssignFormModel',
173
+ uid: 'form-assign-parent-resource',
174
+ parentId: action.uid,
175
+ subKey: 'assignForm',
176
+ stepParams: {
177
+ resourceSettings: {
178
+ init: {
179
+ dataSourceKey: 'main',
180
+ collectionName: 'missing',
181
+ },
182
+ },
183
+ },
184
+ });
185
+
186
+ action.context.defineProperty('blockModel', { value: { collection: users } });
187
+
188
+ const step = createAssignFieldValuesStep({ settingsFlowKey: 'assignSettings' });
189
+ const schema = step.uiSchema();
190
+ const Editor = schema.editor?.['x-component'] as React.ComponentType;
191
+ const flowSettingsCtx = new FlowRuntimeContext(action as any, 'assignSettings', 'settings');
192
+
193
+ render(
194
+ <FlowEngineProvider engine={engine}>
195
+ <ConfigProvider>
196
+ <App>
197
+ <FlowSettingsContextProvider value={flowSettingsCtx}>
198
+ <Editor />
199
+ </FlowSettingsContextProvider>
200
+ </App>
201
+ </ConfigProvider>
202
+ </FlowEngineProvider>,
203
+ );
204
+
205
+ await waitFor(() => {
206
+ const form = engine.findModelByParentId<AssignFormModel>(action.uid, 'assignForm');
207
+ expect(form?.getStepParams('resourceSettings', 'init')).toEqual({
208
+ dataSourceKey: 'main',
209
+ collectionName: 'users',
210
+ });
211
+ });
212
+
213
+ expect(action.context.resource).toBe(cachedResource);
214
+ expect(resourceCreateCount).toBe(1);
123
215
  });
124
216
  });
@@ -38,6 +38,9 @@ type AssignFieldValuesModel = {
38
38
  uid: string;
39
39
  assignFormUid?: string;
40
40
  context?: AssignFieldValuesContext;
41
+ subModels?: {
42
+ assignForm?: AssignFormModel;
43
+ };
41
44
  getStepParams?: (flowKey: string, stepKey: string) => { assignedValues?: AssignedValues } | undefined;
42
45
  setStepParams?: (flowKey: string, stepKey: string, params: { assignedValues: AssignedValues }) => void;
43
46
  };
@@ -88,11 +91,53 @@ function isValidResourceInit(init: unknown): init is {
88
91
  );
89
92
  }
90
93
 
91
- function clearResourceContextCache(model: { context?: { removeCache?: (key: string) => void } } | undefined) {
92
- model?.context?.removeCache?.('dataSource');
93
- model?.context?.removeCache?.('collection');
94
- model?.context?.removeCache?.('resource');
95
- model?.context?.removeCache?.('association');
94
+ type LocalCacheContext = {
95
+ _observableCache?: Record<string, unknown>;
96
+ _cache?: Record<string, unknown>;
97
+ _pending?: Record<string, unknown>;
98
+ };
99
+
100
+ function toLocalCacheContext(context: unknown): LocalCacheContext | undefined {
101
+ return context && typeof context === 'object' ? (context as LocalCacheContext) : undefined;
102
+ }
103
+
104
+ function clearOwnContextCache(context: unknown, key: string) {
105
+ const localContext = toLocalCacheContext(context);
106
+ if (!localContext) {
107
+ return;
108
+ }
109
+ delete localContext._observableCache?.[key];
110
+ delete localContext._cache?.[key];
111
+ delete localContext._pending?.[key];
112
+ }
113
+
114
+ function clearResourceContextCache(model: { context?: unknown } | undefined) {
115
+ clearOwnContextCache(model?.context, 'dataSource');
116
+ clearOwnContextCache(model?.context, 'collection');
117
+ clearOwnContextCache(model?.context, 'resource');
118
+ clearOwnContextCache(model?.context, 'association');
119
+ }
120
+
121
+ function resolveAssignFormModel(ctx: {
122
+ model: AssignFieldValuesModel;
123
+ engine: {
124
+ getModel?: (uid: string, fromRoot?: boolean) => AssignFormModel | undefined;
125
+ findModelByParentId?: (parentId: string, subKey: string) => AssignFormModel | undefined | null;
126
+ };
127
+ }) {
128
+ if (ctx.model.assignFormUid) {
129
+ const form = ctx.engine.getModel?.(ctx.model.assignFormUid, true);
130
+ if (form) {
131
+ return form;
132
+ }
133
+ }
134
+
135
+ const localForm = ctx.model.subModels?.assignForm;
136
+ if (localForm) {
137
+ return localForm;
138
+ }
139
+
140
+ return ctx.engine.findModelByParentId?.(ctx.model.uid, 'assignForm') || undefined;
96
141
  }
97
142
 
98
143
  export function createAssignFormSubModelOptions(ctx: AssignFieldValuesContext) {
@@ -235,14 +280,23 @@ export function createAssignFieldValuesStep(options: AssignFieldValuesStepOption
235
280
  },
236
281
  };
237
282
  },
238
- async beforeParamsSave(ctx: {
239
- model: AssignFieldValuesModel;
240
- engine: {
241
- getModel?: (uid: string, fromRoot?: boolean) => AssignFormModel | undefined;
242
- };
243
- }) {
244
- const form = ctx.model.assignFormUid ? ctx.engine.getModel?.(ctx.model.assignFormUid, true) : undefined;
245
- if (!form) return;
283
+ async beforeParamsSave(
284
+ ctx: {
285
+ model: AssignFieldValuesModel;
286
+ engine: {
287
+ getModel?: (uid: string, fromRoot?: boolean) => AssignFormModel | undefined;
288
+ findModelByParentId?: (parentId: string, subKey: string) => AssignFormModel | undefined | null;
289
+ };
290
+ },
291
+ params?: { assignedValues?: AssignedValues },
292
+ previousParams?: { assignedValues?: AssignedValues },
293
+ ) {
294
+ const form = resolveAssignFormModel(ctx);
295
+ if (!form) {
296
+ const assignedValues = params?.assignedValues || previousParams?.assignedValues || {};
297
+ ctx.model.setStepParams?.(options.settingsFlowKey, ASSIGN_FIELD_VALUES_STEP_KEY, { assignedValues });
298
+ return;
299
+ }
246
300
  if (options.validateBeforeSave) {
247
301
  await form.form?.validateFields?.();
248
302
  }