Skip to content

Commit

Permalink
feat: respect context for object's children (#1971)
Browse files Browse the repository at this point in the history
Pass context to nested elements for both `getDefault` and `cast`
That solves the issue where conditional defaults of nested elements were not resolving correctly if they had context dependency
  • Loading branch information
FedorSherbakov authored Apr 6, 2023
1 parent 297f168 commit edfe6ac
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 7 deletions.
8 changes: 5 additions & 3 deletions src/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ export default class ObjectSchema<
let value = super._cast(_value, options);

//should ignore nulls here
if (value === undefined) return this.getDefault();
if (value === undefined) return this.getDefault(options);

if (!this._typeCheck(value)) return value;

Expand Down Expand Up @@ -320,7 +320,9 @@ export default class ObjectSchema<
);
}

protected _getDefault() {
protected _getDefault(
options?: ResolveOptions<TContext>,
) {
if ('default' in this.spec) {
return super._getDefault();
}
Expand All @@ -334,7 +336,7 @@ export default class ObjectSchema<
this._nodes.forEach((key) => {
const field = this.fields[key] as any;
dft[key] =
field && 'getDefault' in field ? field.getDefault() : undefined;
field && 'getDefault' in field ? field.getDefault(options) : undefined;
});

return dft;
Expand Down
10 changes: 6 additions & 4 deletions src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ export default abstract class Schema<
return result;
}

protected _cast(rawValue: any, _options: CastOptions<TContext>): any {
protected _cast(rawValue: any, options: CastOptions<TContext>): any {
let value =
rawValue === undefined
? rawValue
Expand All @@ -394,7 +394,7 @@ export default abstract class Schema<
);

if (value === undefined) {
value = this.getDefault();
value = this.getDefault(options);
}

return value;
Expand Down Expand Up @@ -607,7 +607,9 @@ export default abstract class Schema<
}
}

protected _getDefault() {
protected _getDefault(
_options?: ResolveOptions<TContext>,
) {
let defaultValue = this.spec.default;

if (defaultValue == null) {
Expand All @@ -624,7 +626,7 @@ export default abstract class Schema<
// If schema is defaulted we know it's at least not undefined
): TDefault {
let schema = this.resolve(options || {});
return schema._getDefault();
return schema._getDefault(options);
}

default(def: Thunk<any>): any {
Expand Down
47 changes: 47 additions & 0 deletions test/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,53 @@ describe('Object types', () => {
other: { x: { b: undefined } },
});
});

it('should pass options to children', () => {
const objectWithConditions = object({
child: string().when('$variable', {
is: 'foo',
then: (s) => s.default('is foo'),
otherwise: (s) => s.default('not foo'),
}),
});

expect(
objectWithConditions.getDefault({ context: { variable: 'foo' } }))
.toEqual({ child: 'is foo' },
);

expect(
objectWithConditions.getDefault({ context: { variable: 'somethingElse' } }))
.toEqual({ child: 'not foo' },
);

expect(
objectWithConditions.getDefault())
.toEqual({ child: 'not foo' },
);
});

it('should respect options when casting to default', () => {
const objectWithConditions = object({
child: string().when('$variable', {
is: 'foo',
then: (s) => s.default('is foo'),
otherwise: (s) => s.default('not foo'),
}),
});

expect(
objectWithConditions.cast(undefined, { context: { variable: 'foo' } })
).toEqual({ child: 'is foo' });

expect(
objectWithConditions.cast(undefined, { context: { variable: 'somethingElse' } })
).toEqual({ child: 'not foo' });

expect(
objectWithConditions.cast(undefined)
).toEqual({ child: 'not foo' });
});
});

it('should handle empty keys', () => {
Expand Down

0 comments on commit edfe6ac

Please sign in to comment.