Skip to content

Commit 0632025

Browse files
committed
review: hand in as much context as possible to validate function
1 parent 7754139 commit 0632025

File tree

3 files changed

+57
-9
lines changed

3 files changed

+57
-9
lines changed

packages/core/src/models/uischema.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,18 @@ export interface ValidateFunctionCondition extends BaseCondition, Scoped {
157157
*
158158
* @param data The data as resolved via the scope.
159159
* @returns `true` if the condition is fulfilled */
160-
validate: (data: unknown) => boolean;
160+
validate: (context: ValidateFunctionContext) => boolean;
161+
}
162+
163+
export interface ValidateFunctionContext {
164+
/** The resolved data scoped to the `ValidateFunctionCondition`'s scope. */
165+
data: unknown;
166+
/** The full data of the form. */
167+
fullData: unknown;
168+
/** Optional instance path. Necessary when the actual data path can not be inferred via the scope alone as it is the case with nested controls. */
169+
path: string | undefined;
170+
/** The `UISchemaElement` containing the rule that uses the ValidateFunctionCondition, e.g. a `ControlElement` */
171+
uischemaElement: UISchemaElement;
161172
}
162173

163174
/**

packages/core/src/util/runtime.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,18 +64,19 @@ const getConditionScope = (condition: Scopable, path: string): string => {
6464

6565
const evaluateCondition = (
6666
data: any,
67+
uischema: UISchemaElement,
6768
condition: Condition,
6869
path: string,
6970
ajv: Ajv
7071
): boolean => {
7172
if (isAndCondition(condition)) {
7273
return condition.conditions.reduce(
73-
(acc, cur) => acc && evaluateCondition(data, cur, path, ajv),
74+
(acc, cur) => acc && evaluateCondition(data, uischema, cur, path, ajv),
7475
true
7576
);
7677
} else if (isOrCondition(condition)) {
7778
return condition.conditions.reduce(
78-
(acc, cur) => acc || evaluateCondition(data, cur, path, ajv),
79+
(acc, cur) => acc || evaluateCondition(data, uischema, cur, path, ajv),
7980
false
8081
);
8182
} else if (isLeafCondition(condition)) {
@@ -89,7 +90,13 @@ const evaluateCondition = (
8990
return ajv.validate(condition.schema, value) as boolean;
9091
} else if (isValidateFunctionCondition(condition)) {
9192
const value = resolveData(data, getConditionScope(condition, path));
92-
return condition.validate(value);
93+
const context = {
94+
data: value,
95+
fullData: data,
96+
path,
97+
uischemaElement: uischema,
98+
};
99+
return condition.validate(context);
93100
} else {
94101
// unknown condition
95102
return true;
@@ -103,7 +110,7 @@ const isRuleFulfilled = (
103110
ajv: Ajv
104111
): boolean => {
105112
const condition = uischema.rule.condition;
106-
return evaluateCondition(data, condition, path, ajv);
113+
return evaluateCondition(data, uischema, condition, path, ajv);
107114
};
108115

109116
export const evalVisibility = (

packages/core/test/util/runtime.test.ts

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
RuleEffect,
3535
SchemaBasedCondition,
3636
ValidateFunctionCondition,
37+
ValidateFunctionContext,
3738
} from '../../src';
3839
import { evalEnablement, evalVisibility } from '../../src/util/runtime';
3940

@@ -496,7 +497,7 @@ test('evalEnablement disable valid case', (t) => {
496497
test('evalEnablement enable valid case based on ValidateFunctionCondition', (t) => {
497498
const condition: ValidateFunctionCondition = {
498499
scope: '#/properties/ruleValue',
499-
validate: (data) => data === 'bar',
500+
validate: (context: ValidateFunctionContext) => context.data === 'bar',
500501
};
501502
const uischema: ControlElement = {
502503
type: 'Control',
@@ -517,7 +518,7 @@ test('evalEnablement enable valid case based on ValidateFunctionCondition', (t)
517518
test('evalEnablement enable invalid case based on ValidateFunctionCondition', (t) => {
518519
const condition: ValidateFunctionCondition = {
519520
scope: '#/properties/ruleValue',
520-
validate: (data) => data === 'bar',
521+
validate: (context: ValidateFunctionContext) => context.data === 'bar',
521522
};
522523
const uischema: ControlElement = {
523524
type: 'Control',
@@ -538,7 +539,7 @@ test('evalEnablement enable invalid case based on ValidateFunctionCondition', (t
538539
test('evalEnablement disable valid case based on ValidateFunctionCondition', (t) => {
539540
const condition: ValidateFunctionCondition = {
540541
scope: '#/properties/ruleValue',
541-
validate: (data) => data === 'bar',
542+
validate: (context: ValidateFunctionContext) => context.data === 'bar',
542543
};
543544
const uischema: ControlElement = {
544545
type: 'Control',
@@ -559,7 +560,7 @@ test('evalEnablement disable valid case based on ValidateFunctionCondition', (t)
559560
test('evalEnablement disable invalid case based on ValidateFunctionCondition', (t) => {
560561
const condition: ValidateFunctionCondition = {
561562
scope: '#/properties/ruleValue',
562-
validate: (data) => data === 'bar',
563+
validate: (context: ValidateFunctionContext) => context.data === 'bar',
563564
};
564565
const uischema: ControlElement = {
565566
type: 'Control',
@@ -576,6 +577,35 @@ test('evalEnablement disable invalid case based on ValidateFunctionCondition', (
576577
t.is(evalEnablement(uischema, data, undefined, createAjv()), true);
577578
});
578579

580+
// Test context properties for ValidateFunctionCondition
581+
test('ValidateFunctionCondition correctly passes context parameters', (t) => {
582+
const condition: ValidateFunctionCondition = {
583+
scope: '#/properties/ruleValue',
584+
validate: (context: ValidateFunctionContext) => {
585+
// Verify all context properties are passed correctly
586+
return (
587+
context.data === 'bar' &&
588+
(context.fullData as any).value === 'foo' &&
589+
context.path === undefined &&
590+
(context.uischemaElement as any).scope === '#/properties/value'
591+
);
592+
},
593+
};
594+
const uischema: ControlElement = {
595+
type: 'Control',
596+
scope: '#/properties/value',
597+
rule: {
598+
effect: RuleEffect.ENABLE,
599+
condition: condition,
600+
},
601+
};
602+
const data = {
603+
value: 'foo',
604+
ruleValue: 'bar',
605+
};
606+
t.is(evalEnablement(uischema, data, undefined, createAjv()), true);
607+
});
608+
579609
test('evalEnablement disable invalid case', (t) => {
580610
const leafCondition: LeafCondition = {
581611
type: 'LEAF',

0 commit comments

Comments
 (0)