cabloy 5.1.209 → 5.1.211
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/.cabloy-version +1 -1
- package/CHANGELOG.md +13 -0
- package/package.json +2 -1
- package/repo-agent-governance/managed-assets.json +6 -6
- package/repo-agent-governance/skills/cabloy-contract-loop/SKILL.md +14 -8
- package/repo-agent-governance/skills/cabloy-contract-loop/references/verification-checklist.md +4 -1
- package/repo-agent-governance/tests/governance.test.mjs +65 -0
- package/repo-docs/ai/playbook-contract-regeneration.md +2 -0
- package/repo-docs/frontend/openapi-sdk-guide.md +23 -4
- package/repo-docs/fullstack/contract-loop-playbook.md +8 -0
- package/repo-docs/fullstack/openapi-to-sdk.md +12 -6
- package/repo-docs/reference/repo-scripts.md +3 -0
- package/scripts/bootstrapAgentGovernance.mjs +18 -0
- package/scripts/generateOpenapiIsolated.test.ts +207 -0
- package/scripts/generateOpenapiIsolated.ts +91 -0
- package/scripts/runIsolatedTest.test.ts +90 -0
- package/scripts/runIsolatedTest.ts +157 -82
- package/scripts/upgrade.test.ts +113 -0
- package/scripts/upgrade.ts +32 -0
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/packages-vona/vona-core/package.json +1 -1
- package/vona/packages-vona/vona-core/src/lib/core/testClaim.test.ts +8 -1
- package/vona/packages-vona/vona-core/src/lib/core/testClaim.ts +2 -2
- package/vona/packages-vona/vona-mock/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/openapi.config.test.ts +29 -0
- package/zova/openapi.config.ts +13 -4
- package/zova/packages-cli/cli/package.json +2 -2
- package/zova/packages-cli/cli-set-front/cli/templates/openapi/config/boilerplate/project/openapi.config.ts +13 -4
- package/zova/packages-cli/cli-set-front/package.json +1 -1
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/src/suite/a-training/modules/training-student/src/.metadata/index.ts +7 -7
- package/zova/src/suite/a-training/modules/training-student/src/api/openapi/schemas.ts +12 -0
- package/zova/src/suite/a-training/modules/training-student/src/api/openapi/types.ts +173 -72
- package/zova/src/suite/a-training/modules/training-student/src/api/trainingStudent.ts +8 -1
- package/zova/src/suite/cabloy-basic/modules/basic-input/src/component/formFieldInput/controller.tsx +2 -1
- package/zova/src/suite-vendor/a-zova/modules/a-form/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/component/formField/controller.tsx +4 -5
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/lib/schemaRefresh.ts +21 -12
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/types/formField.ts +2 -0
- package/zova/src/suite-vendor/a-zova/modules/a-form/test/lib/schemaRefreshLifecycle.test.ts +242 -26
- package/zova/src/suite-vendor/a-zova/package.json +2 -2
|
@@ -82,6 +82,7 @@ export type ApiApiTrainingStudentdeleteForceResponseBody =
|
|
|
82
82
|
|
|
83
83
|
@Api()
|
|
84
84
|
export class ApiTrainingStudent extends BeanApiBase {
|
|
85
|
+
/** List Students */
|
|
85
86
|
select(
|
|
86
87
|
options?: {
|
|
87
88
|
query?: ApiApiTrainingStudentselectRequestQuery;
|
|
@@ -93,6 +94,7 @@ export class ApiTrainingStudent extends BeanApiBase {
|
|
|
93
94
|
);
|
|
94
95
|
}
|
|
95
96
|
|
|
97
|
+
/** Create Student */
|
|
96
98
|
create(body: ApiApiTrainingStudentcreateRequestBody, options?: IApiActionOptions) {
|
|
97
99
|
return this.$fetch.post<any, ApiApiTrainingStudentcreateResponseBody>(
|
|
98
100
|
ApiApiTrainingStudentcreatePath,
|
|
@@ -101,6 +103,7 @@ export class ApiTrainingStudent extends BeanApiBase {
|
|
|
101
103
|
);
|
|
102
104
|
}
|
|
103
105
|
|
|
106
|
+
/** View Student */
|
|
104
107
|
view(
|
|
105
108
|
options: {
|
|
106
109
|
params: ApiApiTrainingStudentviewRequestParams;
|
|
@@ -112,6 +115,7 @@ export class ApiTrainingStudent extends BeanApiBase {
|
|
|
112
115
|
);
|
|
113
116
|
}
|
|
114
117
|
|
|
118
|
+
/** Delete Student */
|
|
115
119
|
delete(
|
|
116
120
|
options: {
|
|
117
121
|
params: ApiApiTrainingStudentdeleteRequestParams;
|
|
@@ -123,6 +127,7 @@ export class ApiTrainingStudent extends BeanApiBase {
|
|
|
123
127
|
);
|
|
124
128
|
}
|
|
125
129
|
|
|
130
|
+
/** Update Student */
|
|
126
131
|
update(
|
|
127
132
|
body: ApiApiTrainingStudentupdateRequestBody,
|
|
128
133
|
options: {
|
|
@@ -136,6 +141,7 @@ export class ApiTrainingStudent extends BeanApiBase {
|
|
|
136
141
|
);
|
|
137
142
|
}
|
|
138
143
|
|
|
144
|
+
/** View Student Summary */
|
|
139
145
|
summary(
|
|
140
146
|
options: {
|
|
141
147
|
params: ApiApiTrainingStudentsummaryRequestParams;
|
|
@@ -147,7 +153,7 @@ export class ApiTrainingStudent extends BeanApiBase {
|
|
|
147
153
|
);
|
|
148
154
|
}
|
|
149
155
|
|
|
150
|
-
/** Bulk Delete */
|
|
156
|
+
/** Bulk Delete Students */
|
|
151
157
|
deleteBulk(body: ApiApiTrainingStudentdeleteBulkRequestBody, options?: IApiActionOptions) {
|
|
152
158
|
return this.$fetch.post<any, ApiApiTrainingStudentdeleteBulkResponseBody>(
|
|
153
159
|
ApiApiTrainingStudentdeleteBulkPath,
|
|
@@ -156,6 +162,7 @@ export class ApiTrainingStudent extends BeanApiBase {
|
|
|
156
162
|
);
|
|
157
163
|
}
|
|
158
164
|
|
|
165
|
+
/** Permanently Delete Student */
|
|
159
166
|
deleteForce(
|
|
160
167
|
options: {
|
|
161
168
|
params: ApiApiTrainingStudentdeleteForceRequestParams;
|
package/zova/src/suite/cabloy-basic/modules/basic-input/src/component/formFieldInput/controller.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import type { IResourceFormFieldOptionsBase } from 'zova-module-a-openapi';
|
|
|
5
5
|
import { classes } from 'typestyle';
|
|
6
6
|
import { BeanControllerBase } from 'zova';
|
|
7
7
|
import { Controller } from 'zova-module-a-bean';
|
|
8
|
-
import { ZFormField } from 'zova-module-a-form';
|
|
8
|
+
import { constFieldBindingSlot, ZFormField } from 'zova-module-a-form';
|
|
9
9
|
|
|
10
10
|
declare module 'zova-module-a-openapi' {
|
|
11
11
|
export interface IResourceFormFieldRecord {
|
|
@@ -37,6 +37,7 @@ export class ControllerFormFieldInput extends BeanControllerBase {
|
|
|
37
37
|
return (
|
|
38
38
|
<ZFormField
|
|
39
39
|
{...this.$props}
|
|
40
|
+
{...{ [constFieldBindingSlot]: true }}
|
|
40
41
|
slotDefault={({ propsBucket, props }, $$formField) => {
|
|
41
42
|
const className = !propsBucket.needHandleBorder
|
|
42
43
|
? props.class
|
|
@@ -12,6 +12,7 @@ import type { ControllerForm } from '../form/controller.jsx';
|
|
|
12
12
|
import { schemaRefreshFingerprint } from '../../lib/schemaRefresh.js';
|
|
13
13
|
import { TypeFormField } from '../../types/form.js';
|
|
14
14
|
import {
|
|
15
|
+
constFieldBindingSlot,
|
|
15
16
|
constFieldProps,
|
|
16
17
|
IFormFieldComponentOptions,
|
|
17
18
|
IFormFieldOptions,
|
|
@@ -105,6 +106,7 @@ export class ControllerFormField<TParentData extends {} = {}> extends BeanContro
|
|
|
105
106
|
props.behaviors,
|
|
106
107
|
!!props.onEffect,
|
|
107
108
|
!!props.slotDefault,
|
|
109
|
+
props[constFieldBindingSlot] === true,
|
|
108
110
|
]);
|
|
109
111
|
}
|
|
110
112
|
|
|
@@ -118,12 +120,9 @@ export class ControllerFormField<TParentData extends {} = {}> extends BeanContro
|
|
|
118
120
|
return;
|
|
119
121
|
}
|
|
120
122
|
const props = this.$props;
|
|
121
|
-
//
|
|
123
|
+
// Only explicitly renderer-owned binding slots qualify; custom slots remain opaque.
|
|
122
124
|
const inputBindingSlot =
|
|
123
|
-
props[constFieldProps] === true &&
|
|
124
|
-
(props.render
|
|
125
|
-
? this.$$form.getRenderProvider(props.render)
|
|
126
|
-
: this.formProvider.components?.Input) === 'basic-input:formFieldInput';
|
|
125
|
+
props[constFieldProps] === true && props[constFieldBindingSlot] === true;
|
|
127
126
|
const eligible =
|
|
128
127
|
!props.sys &&
|
|
129
128
|
!props.behaviors &&
|
|
@@ -238,18 +238,16 @@ function opaqueBehavior(value: unknown): boolean {
|
|
|
238
238
|
);
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
+
const pageEntryBlockOptions = ['blocks', 'resource', 'id', 'formScene', 'pageTitleKey', 'draft'];
|
|
241
242
|
const blockOptions: Record<string, string[]> = {
|
|
242
|
-
'basic-pageentry:blockPageEntry':
|
|
243
|
-
|
|
244
|
-
'resource',
|
|
245
|
-
'id',
|
|
246
|
-
'formScene',
|
|
247
|
-
'pageTitleKey',
|
|
248
|
-
'draft',
|
|
249
|
-
],
|
|
243
|
+
'basic-pageentry:blockPageEntry': pageEntryBlockOptions,
|
|
244
|
+
'start-pageentry:blockPageEntry': pageEntryBlockOptions,
|
|
250
245
|
'basic-pageentry:blockForm': ['blocks'],
|
|
246
|
+
'start-pageentry:blockForm': ['blocks'],
|
|
251
247
|
'basic-form:blockFormLayout': ['formLayout'],
|
|
248
|
+
'start-form:blockFormLayout': ['formLayout'],
|
|
252
249
|
'basic-pageentry:blockToolbarRow': ['actions'],
|
|
250
|
+
'start-pageentry:blockToolbarRow': ['actions'],
|
|
253
251
|
};
|
|
254
252
|
|
|
255
253
|
function safeLayoutNode(value: unknown): boolean {
|
|
@@ -317,7 +315,7 @@ function safeBlocks(value: unknown): boolean {
|
|
|
317
315
|
);
|
|
318
316
|
}
|
|
319
317
|
|
|
320
|
-
// Only Basic's declared layout grammar owns these title strings. Rest data/options,
|
|
318
|
+
// Only Basic's and Start's declared layout grammar owns these title strings. Rest data/options,
|
|
321
319
|
// action metadata, descriptions, field names, and unknown structures stay strict.
|
|
322
320
|
function normalizeLayoutNode(value: unknown): unknown {
|
|
323
321
|
if (!isRecord(value)) return value;
|
|
@@ -334,7 +332,12 @@ function normalizeBlocks(value: unknown): unknown {
|
|
|
334
332
|
if (!isRecord(block) || !isRecord(block.options)) return block;
|
|
335
333
|
const options = { ...block.options };
|
|
336
334
|
if (Object.hasOwn(options, 'blocks')) options.blocks = normalizeBlocks(options.blocks);
|
|
337
|
-
if (
|
|
335
|
+
if (
|
|
336
|
+
['basic-form:blockFormLayout', 'start-form:blockFormLayout'].includes(
|
|
337
|
+
block.render as string,
|
|
338
|
+
) &&
|
|
339
|
+
safeLayout(options.formLayout)
|
|
340
|
+
) {
|
|
338
341
|
const layout = options.formLayout as Record<string, unknown>;
|
|
339
342
|
options.formLayout = { children: (layout.children as unknown[]).map(normalizeLayoutNode) };
|
|
340
343
|
}
|
|
@@ -425,7 +428,13 @@ export function createSchemaRefreshContract(
|
|
|
425
428
|
const form = isRecord(rest.form) ? rest.form : undefined;
|
|
426
429
|
const render = rest.render ?? form?.render;
|
|
427
430
|
const renderType = isRecord(render) ? render.type : render;
|
|
428
|
-
if (
|
|
431
|
+
if (
|
|
432
|
+
!['basic-details:formFieldDetails', 'start-details:formFieldDetails'].includes(
|
|
433
|
+
String(renderType),
|
|
434
|
+
)
|
|
435
|
+
) {
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
429
438
|
for (const name of [
|
|
430
439
|
property.key,
|
|
431
440
|
property.schemaKey,
|
|
@@ -434,7 +443,7 @@ export function createSchemaRefreshContract(
|
|
|
434
443
|
if (typeof name === 'string') detailNames.add(name);
|
|
435
444
|
}
|
|
436
445
|
}
|
|
437
|
-
// Basic Details
|
|
446
|
+
// Basic and Start Details resolve row schemas through the exact hidden `_name` auxiliary.
|
|
438
447
|
// It belongs to that nested form, not to an unrelated parent field's behavior scope.
|
|
439
448
|
const auxiliaryDetails = new Set<string>();
|
|
440
449
|
for (const property of effective) {
|
|
@@ -28,6 +28,8 @@ export type HTMLInputElementType =
|
|
|
28
28
|
| 'tel'
|
|
29
29
|
| 'email';
|
|
30
30
|
export const constFieldProps = '$$FieldProps';
|
|
31
|
+
// Internal: the renderer supplies its own form-value binding slot.
|
|
32
|
+
export const constFieldBindingSlot = '$$FieldBindingSlot';
|
|
31
33
|
|
|
32
34
|
export interface IFormFieldScope<TParentData extends {} = {}> {
|
|
33
35
|
name: string;
|
|
@@ -22,7 +22,12 @@ function deferred<T = void>() {
|
|
|
22
22
|
|
|
23
23
|
// Like basic-pageentry's draftModel harness, execute the current source rather than a
|
|
24
24
|
// copied refresh algorithm. TSX requires transpileModule instead of native stripping.
|
|
25
|
-
function evaluateClass(
|
|
25
|
+
function evaluateClass(
|
|
26
|
+
path: string,
|
|
27
|
+
name: string,
|
|
28
|
+
bindings: Record<string, unknown>,
|
|
29
|
+
jsx = ts.JsxEmit.Preserve,
|
|
30
|
+
) {
|
|
26
31
|
const source = readFileSync(new URL(path, import.meta.url), 'utf8')
|
|
27
32
|
.replace(/^import[\s\S]*?;\n/gm, '')
|
|
28
33
|
.replace(/^\s*@(Controller|UseScope|Use)\([^\n]*\)\s*\n/gm, '');
|
|
@@ -32,7 +37,8 @@ function evaluateClass(path: string, name: string, bindings: Record<string, unkn
|
|
|
32
37
|
compilerOptions: {
|
|
33
38
|
target: ts.ScriptTarget.ES2022,
|
|
34
39
|
module: ts.ModuleKind.ESNext,
|
|
35
|
-
jsx
|
|
40
|
+
jsx,
|
|
41
|
+
jsxFactory: 'createElement',
|
|
36
42
|
},
|
|
37
43
|
});
|
|
38
44
|
assert.deepEqual(
|
|
@@ -85,7 +91,14 @@ class ControllerBase {
|
|
|
85
91
|
}
|
|
86
92
|
}
|
|
87
93
|
|
|
88
|
-
const
|
|
94
|
+
const fieldTypes = ts.transpileModule(
|
|
95
|
+
readFileSync(new URL('../../src/types/formField.ts', import.meta.url), 'utf8'),
|
|
96
|
+
{ compilerOptions: { module: ts.ModuleKind.CommonJS } },
|
|
97
|
+
).outputText;
|
|
98
|
+
const { constFieldProps, constFieldBindingSlot } = new Function(
|
|
99
|
+
'exports',
|
|
100
|
+
`${fieldTypes}\nreturn exports;`,
|
|
101
|
+
)({});
|
|
89
102
|
const commonBindings = {
|
|
90
103
|
markRaw: (value: unknown) => value,
|
|
91
104
|
useSelector: (store: any, selector: (state: any) => any) => ({
|
|
@@ -95,8 +108,32 @@ const commonBindings = {
|
|
|
95
108
|
isNil: (value: unknown) => value === undefined || value === null,
|
|
96
109
|
schemaRefreshFingerprint,
|
|
97
110
|
constFieldProps,
|
|
111
|
+
constFieldBindingSlot,
|
|
98
112
|
};
|
|
99
113
|
|
|
114
|
+
const ZFormField = {};
|
|
115
|
+
const ControllerFormFieldInput = evaluateClass(
|
|
116
|
+
'../../../../../../suite/cabloy-basic/modules/basic-input/src/component/formFieldInput/controller.tsx',
|
|
117
|
+
'ControllerFormFieldInput',
|
|
118
|
+
{
|
|
119
|
+
BeanControllerBase: ControllerBase,
|
|
120
|
+
constFieldBindingSlot,
|
|
121
|
+
ZFormField,
|
|
122
|
+
classes: (...values: unknown[]) => values.filter(Boolean).join(' '),
|
|
123
|
+
createElement: (type: unknown, props: any) => ({ type, props }),
|
|
124
|
+
},
|
|
125
|
+
ts.JsxEmit.React,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
function inputBindingProps(props: any = {}) {
|
|
129
|
+
const input = new ControllerFormFieldInput();
|
|
130
|
+
input.$props = props;
|
|
131
|
+
const rendered = input.render();
|
|
132
|
+
assert.equal(rendered.type, ZFormField);
|
|
133
|
+
assert.equal(rendered.props[constFieldBindingSlot], true);
|
|
134
|
+
return rendered.props;
|
|
135
|
+
}
|
|
136
|
+
|
|
100
137
|
const ControllerForm = evaluateClass('../../src/component/form/controller.tsx', 'ControllerForm', {
|
|
101
138
|
...commonBindings,
|
|
102
139
|
BeanControllerFormBase: ControllerBase,
|
|
@@ -387,6 +424,118 @@ test(
|
|
|
387
424
|
},
|
|
388
425
|
);
|
|
389
426
|
|
|
427
|
+
test(
|
|
428
|
+
'Start edit layout locale refresh preserves recovered input while localized custom options reset',
|
|
429
|
+
{ concurrency: false },
|
|
430
|
+
async () => {
|
|
431
|
+
const nestedEffect = () => {};
|
|
432
|
+
const studentSchema = (locale: string) => {
|
|
433
|
+
const title = locale === 'en-us' ? 'Training Records' : '培训记录';
|
|
434
|
+
const result: any = schema(
|
|
435
|
+
locale,
|
|
436
|
+
{},
|
|
437
|
+
{
|
|
438
|
+
rest: {
|
|
439
|
+
blocks: [
|
|
440
|
+
{
|
|
441
|
+
render: 'start-pageentry:blockPageEntry',
|
|
442
|
+
options: {
|
|
443
|
+
blocks: [
|
|
444
|
+
{
|
|
445
|
+
render: 'start-pageentry:blockForm',
|
|
446
|
+
options: {
|
|
447
|
+
blocks: [
|
|
448
|
+
{
|
|
449
|
+
render: 'start-form:blockFormLayout',
|
|
450
|
+
options: {
|
|
451
|
+
formLayout: {
|
|
452
|
+
children: [
|
|
453
|
+
{
|
|
454
|
+
type: 'tabs',
|
|
455
|
+
children: [
|
|
456
|
+
{
|
|
457
|
+
type: 'tab',
|
|
458
|
+
title,
|
|
459
|
+
children: [{ type: 'field', name: 'name' }],
|
|
460
|
+
},
|
|
461
|
+
],
|
|
462
|
+
},
|
|
463
|
+
],
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
},
|
|
467
|
+
],
|
|
468
|
+
},
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
render: 'start-pageentry:blockToolbarRow',
|
|
472
|
+
options: {
|
|
473
|
+
actions: [
|
|
474
|
+
{
|
|
475
|
+
render: 'start-form:actionSubmit',
|
|
476
|
+
options: { permission: { actionInherit: 'update' } },
|
|
477
|
+
},
|
|
478
|
+
],
|
|
479
|
+
},
|
|
480
|
+
},
|
|
481
|
+
],
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
],
|
|
485
|
+
},
|
|
486
|
+
},
|
|
487
|
+
);
|
|
488
|
+
result.properties.level = {
|
|
489
|
+
type: 'number',
|
|
490
|
+
rest: {
|
|
491
|
+
form: {
|
|
492
|
+
render: 'training-student:formFieldLevel',
|
|
493
|
+
options: { items: [{ value: 1, title }] },
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
};
|
|
497
|
+
result.properties.trainingRecords = {
|
|
498
|
+
type: 'array',
|
|
499
|
+
rest: { form: { render: 'start-details:formFieldDetails' } },
|
|
500
|
+
items: {
|
|
501
|
+
type: 'object',
|
|
502
|
+
properties: {
|
|
503
|
+
subject: { type: 'string', rest: { onEffect: nestedEffect } },
|
|
504
|
+
},
|
|
505
|
+
},
|
|
506
|
+
};
|
|
507
|
+
return result;
|
|
508
|
+
};
|
|
509
|
+
await withRuntime(
|
|
510
|
+
{ data: { name: 'baseline', level: 1 }, schema: studentSchema('en-us') },
|
|
511
|
+
async runtime => {
|
|
512
|
+
const name = await initializedField(runtime, inputBindingProps());
|
|
513
|
+
const level = await initializedField(runtime, {
|
|
514
|
+
name: 'level',
|
|
515
|
+
slotDefault: () => 'custom level options',
|
|
516
|
+
});
|
|
517
|
+
runtime.form.reset(
|
|
518
|
+
{ name: 'recovered unsaved name', level: 2 },
|
|
519
|
+
{ keepDefaultValues: true },
|
|
520
|
+
);
|
|
521
|
+
// Establish the post-recovery epoch before switching locales, like mounted fields do.
|
|
522
|
+
name.refresh();
|
|
523
|
+
level.refresh();
|
|
524
|
+
for (const locale of ['zh-cn', 'en-us']) {
|
|
525
|
+
runtime.form.form.setFieldValue('level', 2, { dontValidate: true });
|
|
526
|
+
runtime.events.length = 0;
|
|
527
|
+
runtime.select(locale, studentSchema(locale));
|
|
528
|
+
name.refresh();
|
|
529
|
+
level.refresh();
|
|
530
|
+
assert.equal(runtime.form.form.getFieldValue('name'), 'recovered unsaved name');
|
|
531
|
+
assert.equal(runtime.form.form.getFieldValue('level'), 1);
|
|
532
|
+
assert.deepEqual(runtime.changed, []);
|
|
533
|
+
}
|
|
534
|
+
},
|
|
535
|
+
);
|
|
536
|
+
},
|
|
537
|
+
);
|
|
538
|
+
|
|
390
539
|
test(
|
|
391
540
|
'locale replay preserves exact raw empty and falsy values',
|
|
392
541
|
{ concurrency: false },
|
|
@@ -732,6 +881,7 @@ test(
|
|
|
732
881
|
const before: unknown[] = [];
|
|
733
882
|
const after: unknown[] = [];
|
|
734
883
|
const owner = await initializedField(runtime, {
|
|
884
|
+
[constFieldBindingSlot]: true,
|
|
735
885
|
slotDefault: () => 'initial slot',
|
|
736
886
|
onFieldStateReady: (field: any) => before.push(field.form.getFieldValue(field.name)),
|
|
737
887
|
});
|
|
@@ -755,31 +905,30 @@ test(
|
|
|
755
905
|
);
|
|
756
906
|
|
|
757
907
|
test(
|
|
758
|
-
'
|
|
908
|
+
'explicit renderer-owned binding slots preserve live names independently of component selectors',
|
|
759
909
|
{ concurrency: false },
|
|
760
910
|
async () => {
|
|
761
|
-
for (const
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
runtime
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
render
|
|
773
|
-
|
|
911
|
+
for (const selector of ['basic-input:formFieldInput', 'start-input:formFieldInput']) {
|
|
912
|
+
for (const render of [
|
|
913
|
+
undefined,
|
|
914
|
+
'Input',
|
|
915
|
+
selector,
|
|
916
|
+
{ $$typeof: 'zova-jsx:component', type: selector },
|
|
917
|
+
]) {
|
|
918
|
+
await withRuntime({}, async runtime => {
|
|
919
|
+
runtime.form.$$scopeOpenapi.config.formProvider = {
|
|
920
|
+
components: { Input: selector },
|
|
921
|
+
};
|
|
922
|
+
const owner = await initializedField(runtime, inputBindingProps({ render }));
|
|
923
|
+
dirty(runtime, 'typed name');
|
|
924
|
+
runtime.select('zh-cn');
|
|
925
|
+
owner.refresh();
|
|
926
|
+
assert.deepEqual(lifecycle(runtime.events), ['update', 'reset', 'validate', 'ready']);
|
|
927
|
+
assert.equal(lastEvent(runtime.events).value, 'typed name');
|
|
928
|
+
assertResetMeta(lastEvent(runtime.events).meta);
|
|
929
|
+
assert.deepEqual(runtime.changed, []);
|
|
774
930
|
});
|
|
775
|
-
|
|
776
|
-
runtime.select('zh-cn');
|
|
777
|
-
owner.refresh();
|
|
778
|
-
assert.deepEqual(lifecycle(runtime.events), ['update', 'reset', 'validate', 'ready']);
|
|
779
|
-
assert.equal(lastEvent(runtime.events).value, 'typed name');
|
|
780
|
-
assertResetMeta(lastEvent(runtime.events).meta);
|
|
781
|
-
assert.deepEqual(runtime.changed, []);
|
|
782
|
-
});
|
|
931
|
+
}
|
|
783
932
|
}
|
|
784
933
|
},
|
|
785
934
|
);
|
|
@@ -789,9 +938,13 @@ test(
|
|
|
789
938
|
{ concurrency: false },
|
|
790
939
|
async () => {
|
|
791
940
|
for (const props of [
|
|
792
|
-
{ [constFieldProps]: false, render: 'basic-input:formFieldInput' },
|
|
941
|
+
inputBindingProps({ [constFieldProps]: false, render: 'basic-input:formFieldInput' }),
|
|
942
|
+
{ render: 'basic-input:formFieldInput' },
|
|
793
943
|
{ render: 'custom:formFieldInput' },
|
|
794
944
|
{ render: 'Input' },
|
|
945
|
+
{},
|
|
946
|
+
{ [constFieldBindingSlot]: false },
|
|
947
|
+
{ [constFieldBindingSlot]: 'true' },
|
|
795
948
|
]) {
|
|
796
949
|
await withRuntime({}, async runtime => {
|
|
797
950
|
runtime.form.$$scopeOpenapi.config.formProvider = {
|
|
@@ -812,6 +965,69 @@ test(
|
|
|
812
965
|
},
|
|
813
966
|
);
|
|
814
967
|
|
|
968
|
+
test('the input wrapper owns its marker and value/blur binding slot', () => {
|
|
969
|
+
const externalSlot = () => 'external slot';
|
|
970
|
+
const props = inputBindingProps({
|
|
971
|
+
name: 'name',
|
|
972
|
+
[constFieldBindingSlot]: false,
|
|
973
|
+
slotDefault: externalSlot,
|
|
974
|
+
});
|
|
975
|
+
assert.equal(props.name, 'name');
|
|
976
|
+
assert.notEqual(props.slotDefault, externalSlot);
|
|
977
|
+
const writes: unknown[] = [];
|
|
978
|
+
let blurred = false;
|
|
979
|
+
const input = props.slotDefault(
|
|
980
|
+
{ propsBucket: { value: 'current', disableNotifyChanged: true }, props: {} },
|
|
981
|
+
{
|
|
982
|
+
setValue: (...args: unknown[]) => writes.push(args),
|
|
983
|
+
handleBlur: () => {
|
|
984
|
+
blurred = true;
|
|
985
|
+
},
|
|
986
|
+
},
|
|
987
|
+
);
|
|
988
|
+
assert.equal(input.type, 'input');
|
|
989
|
+
assert.equal(input.props.value, 'current');
|
|
990
|
+
input.props.onInput({ target: { value: 'edited' } });
|
|
991
|
+
input.props.onBlur();
|
|
992
|
+
assert.deepEqual(writes, [['edited', true]]);
|
|
993
|
+
assert.equal(blurred, true);
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
test(
|
|
997
|
+
'a missing Input provider does not authorize an unmarked slot',
|
|
998
|
+
{ concurrency: false },
|
|
999
|
+
async () => {
|
|
1000
|
+
await withRuntime({}, async runtime => {
|
|
1001
|
+
const owner = await initializedField(runtime, { slotDefault: () => 'custom slot' });
|
|
1002
|
+
dirty(runtime, 'typed name');
|
|
1003
|
+
runtime.select('zh-cn');
|
|
1004
|
+
owner.refresh();
|
|
1005
|
+
assert.deepEqual(lifecycle(runtime.events), ['update', 'reset', 'ready']);
|
|
1006
|
+
assert.equal(lastEvent(runtime.events).value, 'baseline');
|
|
1007
|
+
});
|
|
1008
|
+
},
|
|
1009
|
+
);
|
|
1010
|
+
|
|
1011
|
+
test(
|
|
1012
|
+
'binding-slot ownership changes invalidate the prior refresh configuration',
|
|
1013
|
+
{ concurrency: false },
|
|
1014
|
+
async () => {
|
|
1015
|
+
for (const initiallyOwned of [false, true]) {
|
|
1016
|
+
await withRuntime({}, async runtime => {
|
|
1017
|
+
const owner = await initializedField(runtime, {
|
|
1018
|
+
[constFieldBindingSlot]: initiallyOwned,
|
|
1019
|
+
slotDefault: () => 'slot',
|
|
1020
|
+
});
|
|
1021
|
+
dirty(runtime, 'typed name');
|
|
1022
|
+
owner.field.$props[constFieldBindingSlot] = !initiallyOwned;
|
|
1023
|
+
owner.refresh();
|
|
1024
|
+
assert.deepEqual(lifecycle(runtime.events), ['update', 'reset', 'ready']);
|
|
1025
|
+
assert.equal(lastEvent(runtime.events).value, 'baseline');
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
},
|
|
1029
|
+
);
|
|
1030
|
+
|
|
815
1031
|
test(
|
|
816
1032
|
'disposal during awaited behavior initialization suppresses refresh and ready callbacks',
|
|
817
1033
|
{ concurrency: false },
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zova-suite-a-zova",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.169",
|
|
4
4
|
"gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
|
|
5
5
|
"description": "zova",
|
|
6
6
|
"license": "MIT",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"zova-module-a-behaviors": "^5.1.22",
|
|
16
16
|
"zova-module-a-command": "^5.1.35",
|
|
17
17
|
"zova-module-a-fetch": "^5.1.25",
|
|
18
|
-
"zova-module-a-form": "^5.1.
|
|
18
|
+
"zova-module-a-form": "^5.1.54",
|
|
19
19
|
"zova-module-a-icon": "^5.1.27",
|
|
20
20
|
"zova-module-a-interceptor": "^5.1.31",
|
|
21
21
|
"zova-module-a-logger": "^5.1.28",
|