Repository: jquense/yup
Pull request #1971 (2023-04-05 23:29:15)

Respect context for object's children
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

Added tests are covering examples of use cases that are being fixed 


Patch:
diff --git a/src/object.ts b/src/object.ts
index f290aa7..4b534e7 100644
--- a/src/object.ts
+++ b/src/object.ts
@@ -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;
 
@@ -320,7 +320,9 @@ export default class ObjectSchema<
     );
   }
 
-  protected _getDefault() {
+  protected _getDefault(
+    options?: ResolveOptions<TContext>,
+  ) {
     if ('default' in this.spec) {
       return super._getDefault();
     }
@@ -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;
diff --git a/src/schema.ts b/src/schema.ts
index 363bec3..322edcb 100644
--- a/src/schema.ts
+++ b/src/schema.ts
@@ -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
@@ -394,7 +394,7 @@ export default abstract class Schema<
           );
 
     if (value === undefined) {
-      value = this.getDefault();
+      value = this.getDefault(options);
     }
 
     return value;
@@ -607,7 +607,9 @@ export default abstract class Schema<
     }
   }
 
-  protected _getDefault() {
+  protected _getDefault(
+    _options?: ResolveOptions<TContext>,
+  ) {
     let defaultValue = this.spec.default;
 
     if (defaultValue == null) {
@@ -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 {


Test patch:
diff --git a/test/object.ts b/test/object.ts
index 6aaece3..8b859f8 100644
--- a/test/object.ts
+++ b/test/object.ts
@@ -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', () => {