Skip to content

Commit

Permalink
fix: Upstream changes from blueprint.
Browse files Browse the repository at this point in the history
  • Loading branch information
stephenh committed Apr 29, 2021
1 parent d6beac2 commit d8f52e1
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 7 deletions.
24 changes: 24 additions & 0 deletions src/formState.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,30 @@ describe("formState", () => {
expect(a1.dirty).toBeFalsy();
});

it("knows list of primitives are dirty", () => {
const a1 = createObjectState<AuthorInput>({ favoriteColors: { type: "value" } }, {});
expect(a1.favoriteColors.dirty).toBeFalsy();
a1.favoriteColors.set(["blue"]);
expect(a1.dirty).toBeTruthy();
a1.favoriteColors.set(undefined!);
expect(a1.dirty).toBeFalsy();
// Because we were originally undefined, setting as `[]` coerces to `undefined`
a1.favoriteColors.set([]);
expect(a1.dirty).toBeFalsy();
});

it("knows list of primitives are dirty with initialized as empty list", () => {
const a1 = createObjectState<AuthorInput>({ favoriteColors: { type: "value" } }, { favoriteColors: [] });
expect(a1.favoriteColors.dirty).toBeFalsy();
a1.favoriteColors.set(["blue"]);
expect(a1.dirty).toBeTruthy();
a1.favoriteColors.set([]);
expect(a1.dirty).toBeFalsy();
// Because we were originally undefined, setting as `[]` coerces to `undefined`
a1.favoriteColors.set([]);
expect(a1.dirty).toBeFalsy();
});

it("knows originally unset fields are dirty", () => {
// Given firstName is purposefully not set when originally initialized
const a1 = createAuthorInputState({});
Expand Down
21 changes: 14 additions & 7 deletions src/formState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ type Builtin = Date | Function | Uint8Array | string | number | boolean;
/** For a given input type `T`, decorate each field into the "field state" type that holds our form-relevant state, i.e. valid/touched/etc. */
type FieldStates<T> = {
[P in keyof T]-?: T[P] extends Array<infer U> | null | undefined
? ListFieldState<U>
? U extends Builtin
? FieldState<U[]>
: ListFieldState<U>
: T[P] extends Builtin
? FieldState<T[P]>
: ObjectState<T[P]>;
Expand Down Expand Up @@ -156,7 +158,9 @@ export type ObjectConfig<T> = {
// We ignore functions (the OmitIf) to support observable classes that have
// helper methods, i.e. `.toInput()`.
[P in keyof OmitIf<T, Function>]: T[P] extends Array<infer U> | null | undefined
? ListFieldConfig<U>
? U extends Builtin
? ValueFieldConfig<U[]>
: ListFieldConfig<U>
: ValueFieldConfig<T[P]> | ObjectFieldConfig<T[P]>;
};

Expand Down Expand Up @@ -234,10 +238,7 @@ function newObjectState<T>(config: ObjectConfig<T>, instance: T, key: keyof T |

const fieldNames = Object.keys(config);
function getFields(proxyThis: any): FieldState<any>[] {
return fieldNames.map((name) => {
const field = proxyThis[name];
return field;
}) as FieldState<any>[];
return fieldNames.map((name) => proxyThis[name]) as FieldState<any>[];
}

const obj = {
Expand Down Expand Up @@ -401,7 +402,9 @@ function newValueFieldState<T, K extends keyof T>(
// If the user has deleted/emptied a value that was originally set, keep it as `null`
// so that our partial update to the backend correctly unsets it.
const keepNull = !isEmpty(this.originalValue) && isEmpty(value);
const newValue = keepNull ? null : isEmpty(value) ? undefined : value;
// If a list of primitives was originally undefined, coerce `[]` to `undefined`
const coerceEmptyList = value && value instanceof Array && value.length === 0 && isEmpty(this.originalValue);
const newValue = keepNull ? null : isEmpty(value) || coerceEmptyList ? undefined : value;

// Set the value on our parent object
parentInstance[key] = newValue!;
Expand Down Expand Up @@ -589,6 +592,7 @@ function newListFieldState<T, K extends keyof T, U>(
r.save();
});
originalCopy = (parentInstance[key] as any) as U[];
_tick.value++;
},

ensureSet() {
Expand Down Expand Up @@ -628,6 +632,9 @@ function areEqual<T>(a?: T, b?: T): boolean {
const b1 = hasToJSON(b) ? b.toJSON() : b;
return equal(a1, b1);
}
if (a && b && a instanceof Array && b instanceof Array) {
return equal(a, b);
}
return a === b;
}

Expand Down
1 change: 1 addition & 0 deletions src/formStateDomain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface AuthorInput {
birthday?: Date | null;
books?: BookInput[] | null;
address?: AuthorAddress | null;
favoriteColors?: string[] | null;
}

export interface AuthorAddress {
Expand Down

0 comments on commit d8f52e1

Please sign in to comment.