Skip to content

Commit

Permalink
feat(validation): add $touch/$reset methods and some return changes
Browse files Browse the repository at this point in the history
fix #753
  • Loading branch information
pikax committed Feb 20, 2021
1 parent a9a5f29 commit 7b3b83d
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 22 deletions.
7 changes: 7 additions & 0 deletions docs/composable/validation/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ type ValidatorObject<T> = {
// typed validationObject
type ValidationObject<T> = {
$value: T | Ref<T>;
$touch(): void;
$reset(): void;
} & Record<string, ValidatorFunction<T> | ValidatorObject<T>>;

const validationUsername = useValidation({
Expand Down Expand Up @@ -119,6 +121,8 @@ interface ValidationValue<T> {
$errors: any[]; // array of errors

toObject(): T;
$touch(): void;
$reset(): void;
}

// validator
Expand Down Expand Up @@ -212,6 +216,9 @@ form.personal.$anyInvalid;
form.personal.$errors;

form.toObject(); // returns { settings: { email: '' }, personal: { name: { first: '', last: '' } } }

form.$touch(); // sets all the validations to `$dirty: true`
form.$reset(); // sets all the validations to `$dirty: false`
```

```ts
Expand Down
53 changes: 52 additions & 1 deletion packages/vue-composable/__tests__/validation/validation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ describe("validation", () => {
});

expect(v.input.$errors).toMatchObject([$message]);
expect(v.otherInput.$errors).toMatchObject([]);
expect(v.otherInput.$errors).toMatchObject([true]);
});

describe("object", () => {
Expand Down Expand Up @@ -402,4 +402,55 @@ describe("validation", () => {
);
});
});

describe("touch/reset", () => {
it("should run validation", () => {
const required = (x: any) => !!x;
const v = useValidation({
test: {
$value: "",
required,
},
deep: {
v1: {
$value: "",
required,
},
v2: {
$value: "",
required,
},
},
});

expect(v.$anyDirty).toBe(false);

expect(v.test.$dirty).toBe(false);
expect(v.test.$anyInvalid).toBe(true);
v.test.$touch();
expect(v.test.$dirty).toBe(true);
v.test.$reset();
expect(v.test.$dirty).toBe(false);

expect(v.deep.v1.$dirty).toBe(false);
expect(v.deep.v2.$dirty).toBe(false);
v.deep.$touch();
expect(v.deep.v1.$dirty).toBe(true);
expect(v.deep.v2.$dirty).toBe(true);
v.deep.$reset();
expect(v.deep.v1.$dirty).toBe(false);
expect(v.deep.v2.$dirty).toBe(false);

v.$touch();
expect(v.$anyDirty).toBe(true);
expect(v.test.$dirty).toBe(true);
expect(v.deep.v1.$dirty).toBe(true);
expect(v.deep.v2.$dirty).toBe(true);
v.$reset();
expect(v.test.$dirty).toBe(false);
expect(v.deep.v1.$dirty).toBe(false);
expect(v.deep.v2.$dirty).toBe(false);
expect(v.$anyDirty).toBe(false);
});
});
});
65 changes: 44 additions & 21 deletions packages/vue-composable/src/validation/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ interface ValidationValue<T> {
$errors: Array<any>;
$anyInvalid: boolean;

// $touch(): void;
// $reset(): void;
$touch(): void;
$reset(): void;
}

interface ValidatorResult {
Expand All @@ -40,6 +40,9 @@ interface ValidationGroupResult {
$anyDirty: boolean;
$errors: Array<any>;
$anyInvalid: boolean;

$touch(): void;
$reset(): void;
}

interface ValidatorResultPromise {
Expand Down Expand Up @@ -132,7 +135,7 @@ const buildValidationFunction = (
) => {
const $promise: Ref<Promise<boolean> | null> = ref(null);
const $pending = ref(false);
const $error = ref<Error | string>();
const $error = ref<Error | string | true>();
const $invalid = ref(false);
let context: any = undefined;

Expand All @@ -147,7 +150,8 @@ const buildValidationFunction = (
} else {
$invalid.value = !result;
}
$error.value = $invalid.value ? m.value : undefined;
// @ts-ignore
$error.value = $invalid.value ? m.value || true : undefined;
} catch (e) {
$invalid.value = true;
throw e;
Expand Down Expand Up @@ -179,11 +183,17 @@ const buildValidationFunction = (
);
});

function $touch() {
onChange(r.value);
}

return {
$promise,
$pending,
$invalid,
$error,

$touch,
};
};

Expand All @@ -196,19 +206,21 @@ const buildValidationValue = (
? v
: { $validator: v, $message: undefined };

const { $pending, $promise, $invalid, $error } = buildValidationFunction(
r,
$validator,
ref($message),
handlers
);
const {
$pending,
$promise,
$invalid,
$error,
$touch,
} = buildValidationFunction(r, $validator, ref($message), handlers);

return {
$pending,
$error,
$promise,
$invalid,
$message,
$touch,
...$rest,
} as any;
};
Expand All @@ -234,6 +246,8 @@ const buildValidation = <T>(
);

(r as any)["$dirty"] = $dirty;
(r as any)["$reset"] = () => ($dirty.value = false);
(r as any)["$touch"] = () => ($dirty.value = true);

// @ts-ignore
r.toObject = () => unwrap($value);
Expand All @@ -251,13 +265,8 @@ const buildValidation = <T>(
handlers
);

r[k] = {
...validation,
$value,
toObject() {
return unwrap($value);
},
} as any;
// @ts-expect-error no valid type
r[k] = validation;
} else {
const validation = buildValidation(
(o as Record<string, any>)[k],
Expand All @@ -279,7 +288,7 @@ const buildValidation = <T>(
validations
.map((x) => x.$error)
.map((x) => unwrap(x))
.filter(Boolean)
.filter((x) => x !== undefined)
);
// $anyDirty = computed(() => validations.some(x => !!x));
$anyInvalid = computed(() =>
Expand All @@ -292,7 +301,6 @@ const buildValidation = <T>(
return Object.keys(validation)
.filter((x) => x[0] !== "$")
.reduce((p, c) => {
debugger;
//@ts-ignore
p[c] = validation[c].toObject();
return p;
Expand All @@ -305,15 +313,15 @@ const buildValidation = <T>(
$errors = computed(() => {
return validations
.map((x) => unwrap(x.$errors))
.filter(Boolean)
.filter((x) => x !== undefined)
.filter((x) => {
return x.some(Boolean);
});
});
$anyDirty = computed(() => {
return validations.some((x) => {
return (
x.$anyDirty ||
unwrap(x.$anyDirty) ||
(isBoolean(unwrap((x as any).$dirty)) &&
unwrap((x as any).$dirty))
);
Expand Down Expand Up @@ -345,6 +353,21 @@ const buildValidation = <T>(

if ($anyDirty) {
(r[k] as any).$anyDirty = $anyDirty;

const keys = Object.keys(r[k]).filter(
(x) => x[0] !== "$" && isObject(r[k][x])
);
r[k].$touch = () => {
// r[k].
keys.forEach((m) => {
r[k][m].$touch?.();
});
};
r[k].$reset = () => {
keys.forEach((m) => {
r[k][m].$reset?.();
});
};
}
}
}
Expand Down

0 comments on commit 7b3b83d

Please sign in to comment.