diff --git a/docs/src/core-concepts/validation-properties.md b/docs/src/core-concepts/validation-properties.md index 02cc9f3c..8b6d4e77 100644 --- a/docs/src/core-concepts/validation-properties.md +++ b/docs/src/core-concepts/validation-properties.md @@ -146,14 +146,19 @@ To know more about the rule properties check the [rules properties section](/cor ## Specific properties for nested objects ### `$fields` -- Type: `Record` +- Type: `Record` This represents all the children of your object. You can access any nested child at any depth to get the relevant data you need for your form. ## Specific properties for collections +- Type: `Array` ### `$each` +This will store the status of every item in your collection. Each item will be a classic field you can access, or map on it to display your elements. + ### `$field` + +Represent the status of the collection itself. You can have validations on the array like `minLength`, this field represent the isolated status of the collection. \ No newline at end of file diff --git a/packages/core/src/core/useRegle/useStateProperties/collections/createReactiveCollectionRoot.ts b/packages/core/src/core/useRegle/useStateProperties/collections/createReactiveCollectionRoot.ts index c54780ce..cfc3bb2c 100644 --- a/packages/core/src/core/useRegle/useStateProperties/collections/createReactiveCollectionRoot.ts +++ b/packages/core/src/core/useRegle/useStateProperties/collections/createReactiveCollectionRoot.ts @@ -1,19 +1,13 @@ -import type { RequiredDeep } from 'type-fest'; import type { ComputedRef, EffectScope, Ref, ToRefs, WatchStopHandle } from 'vue'; -import { computed, effectScope, reactive, ref, toRaw, toRef, watch, watchEffect } from 'vue'; +import { computed, effectScope, reactive, ref, toRef, watch, watchEffect } from 'vue'; +import { isEmpty } from '../../../../../../shared'; import type { - $InternalFormPropertyTypes, $InternalRegleCollectionErrors, $InternalRegleCollectionRuleDecl, $InternalRegleCollectionStatus, - $InternalRegleErrors, $InternalRegleFieldStatus, $InternalRegleResult, - $InternalRegleStatusType, CustomRulesDeclarationTree, - DeepMaybeRef, - RegleBehaviourOptions, - RegleCollectionRuleDeclKeyProperty, RegleShortcutDefinition, ResolvedRegleBehaviourOptions, } from '../../../../types'; @@ -21,9 +15,8 @@ import { cloneDeep, isObject, randomId, resetArrayValuesRecursively, unwrapGette import { isVueSuperiorOrEqualTo3dotFive } from '../../../../utils/version-compare'; import type { RegleStorage } from '../../../useStorage'; import { isNestedRulesStatus, isRuleDef } from '../../guards'; -import { createReactiveFieldStatus } from './../createReactiveFieldStatus'; -import { isEmpty } from '../../../../../../shared'; import type { StateWithId } from '../common/common-types'; +import { createReactiveFieldStatus } from './../createReactiveFieldStatus'; import { createCollectionElement } from './createReactiveCollectionElement'; interface CreateReactiveCollectionStatusArgs { @@ -170,7 +163,7 @@ export function createReactiveCollectionStatus({ path, storage, options, - externalErrors: toRef(externalErrors?.value ?? {}, `$errors`), + externalErrors: toRef(externalErrors?.value ?? {}, `$self`), $isArray: true, initialState: initialState, shortcuts, @@ -299,18 +292,18 @@ export function createReactiveCollectionStatus({ ); }); - const $errors = computed<$InternalRegleCollectionErrors>(() => { + const $errors = computed(() => { return { - $errors: $fieldStatus.value.$errors, + $self: $fieldStatus.value.$errors, $each: $eachStatus.value.map(($each) => $each.$errors), - }; + } satisfies $InternalRegleCollectionErrors; }); - const $silentErrors = computed<$InternalRegleCollectionErrors>(() => { + const $silentErrors = computed(() => { return { - $errors: $fieldStatus.value.$silentErrors, + $self: $fieldStatus.value.$silentErrors, $each: $eachStatus.value.map(($each) => $each.$silentErrors), - }; + } satisfies $InternalRegleCollectionErrors; }); const $name = computed(() => fieldName); diff --git a/packages/core/src/core/useRegle/useStateProperties/createReactiveNestedStatus.ts b/packages/core/src/core/useRegle/useStateProperties/createReactiveNestedStatus.ts index a6fa89f7..0c013aed 100644 --- a/packages/core/src/core/useRegle/useStateProperties/createReactiveNestedStatus.ts +++ b/packages/core/src/core/useRegle/useStateProperties/createReactiveNestedStatus.ts @@ -224,7 +224,7 @@ export function createReactiveNestedStatus({ }); const $error = computed(() => { - return $dirty.value && !$pending.value && $invalid.value; + return $anyDirty.value && !$pending.value && $invalid.value; }); const $ready = computed(() => { diff --git a/packages/core/src/types/rules/rule.errors.types.ts b/packages/core/src/types/rules/rule.errors.types.ts index 43e8252f..3bf56e06 100644 --- a/packages/core/src/types/rules/rule.errors.types.ts +++ b/packages/core/src/types/rules/rule.errors.types.ts @@ -23,13 +23,13 @@ export type RegleValidationErrors | any[] | u : string[]; export type RegleCollectionErrors> = { - readonly $errors: string[]; + readonly $self: string[]; readonly $each: RegleValidationErrors[]; }; /** @internal */ export type $InternalRegleCollectionErrors = { - readonly $errors?: string[]; + readonly $self?: string[]; readonly $each?: $InternalRegleErrors[]; }; diff --git a/packages/zod/src/types/core.types.ts b/packages/zod/src/types/core.types.ts index d7de903b..077f6cca 100644 --- a/packages/zod/src/types/core.types.ts +++ b/packages/zod/src/types/core.types.ts @@ -65,15 +65,19 @@ export interface ZodRegleFieldStatus< [Key in `${string & TSchema['_def']['typeName']}`]: RegleRuleStatus; }; $validate: () => Promise>; + $extractDirtyFields: (filterNullishValues?: boolean) => PartialDeep; } /** * @public */ export interface ZodRegleCollectionStatus - extends Omit, '$errors' | '$silentErrors'> { + extends Omit, '$errors' | '$silentErrors' | '$value'> { + $value: TState; readonly $each: Array, TState, number>>; + readonly $field: ZodRegleFieldStatus; readonly $errors: ZodToRegleCollectionErrors; readonly $silentErrors: ZodToRegleCollectionErrors; + $extractDirtyFields: (filterNullishValues?: boolean) => PartialDeep; $validate: () => Promise>; } diff --git a/packages/zod/src/types/errors.types.ts b/packages/zod/src/types/errors.types.ts index 7ea2ccc2..fb2fe5a6 100644 --- a/packages/zod/src/types/errors.types.ts +++ b/packages/zod/src/types/errors.types.ts @@ -16,6 +16,6 @@ export type ZodDefToRegleValidationErrors = : string[]; export type ZodToRegleCollectionErrors = { - readonly $errors: string[]; + readonly $self: string[]; readonly $each: ZodDefToRegleValidationErrors[]; }; diff --git a/tests/unit/useRegle/errors/errorsMessages.spec.ts b/tests/unit/useRegle/errors/errorsMessages.spec.ts index 53d9e8f3..1f559f5d 100644 --- a/tests/unit/useRegle/errors/errorsMessages.spec.ts +++ b/tests/unit/useRegle/errors/errorsMessages.spec.ts @@ -1,4 +1,4 @@ -import { defineRegleConfig, useRegle } from '@regle/core'; +import { createRule, defineRegleConfig, useRegle } from '@regle/core'; import { withMessage } from '@regle/rules'; import { ref } from 'vue'; import { createRegleComponent } from '../../../utils/test.utils'; @@ -15,22 +15,54 @@ function errorsRules() { () => ['Error 2.1', 'Error 2.2'] ); + const createRuleOneError = createRule({ validator: () => false, message: 'Error 3' }); + const createRuleMultipleError = createRule({ + validator: () => false, + message: ['Error 3.1', 'Error 3.2', 'Error 3.3'], + }); + const createRuleFunctionOneError = createRule({ validator: () => false, message: () => 'Error 4' }); + const createRuleFunctionMultipleError = createRule({ validator: () => false, message: () => ['Error 4.1'] }); + + const allValidators = { + ruleWithOneError, + ruleWithMultipleErrors, + ruleFunctionWithOneError, + ruleFunctionWithMultipleError, + createRuleOneError, + createRuleMultipleError, + createRuleFunctionOneError, + createRuleFunctionMultipleError, + }; + const form = ref({ email: '', user: { firstName: '', + nested: { + child: '', + collection: [{ name: '' }], + }, }, contacts: [{ name: '' }], }); return useRegle(form, { - email: { ruleWithOneError, ruleWithMultipleErrors, ruleFunctionWithOneError, ruleFunctionWithMultipleError }, + email: allValidators, user: { - firstName: { ruleWithOneError, ruleWithMultipleErrors, ruleFunctionWithOneError, ruleFunctionWithMultipleError }, + firstName: allValidators, + nested: { + child: allValidators, + collection: { + ...allValidators, + $each: { + name: allValidators, + }, + }, + }, }, contacts: { $each: { - name: { ruleWithOneError, ruleWithMultipleErrors, ruleFunctionWithOneError, ruleFunctionWithMultipleError }, + name: allValidators, }, }, }); @@ -43,10 +75,36 @@ describe('errors', () => { vm.r$.$touch(); await vm.$nextTick(); - const expectedErrors = ['Error', 'Error 1.1', 'Error 1.2', 'Error 2', 'Error 2.1', 'Error 2.2']; + const expectedErrors = [ + 'Error', + 'Error 1.1', + 'Error 1.2', + 'Error 2', + 'Error 2.1', + 'Error 2.2', + 'Error 3', + 'Error 3.1', + 'Error 3.2', + 'Error 3.3', + 'Error 4', + 'Error 4.1', + ]; expect(vm.r$.$fields.email.$errors).toStrictEqual(expectedErrors); expect(vm.r$.$fields.user.$fields.firstName.$errors).toStrictEqual(expectedErrors); + expect(vm.r$.$fields.user.$fields.nested.$fields.child.$errors).toStrictEqual(expectedErrors); + expect(vm.r$.$fields.user.$fields.nested.$fields.collection.$field.$errors).toStrictEqual(expectedErrors); + expect(vm.r$.$fields.user.$fields.nested.$fields.collection.$errors.$self).toStrictEqual(expectedErrors); + expect(vm.r$.$fields.user.$fields.nested.$fields.collection.$each[0].$fields.name.$errors).toStrictEqual( + expectedErrors + ); expect(vm.r$.$fields.contacts.$each[0].$fields.name.$errors).toStrictEqual(expectedErrors); + + vm.r$.$value.contacts.push({ name: '' }); + await vm.$nextTick(); + vm.r$.$touch(); + await vm.$nextTick(); + + expect(vm.r$.$fields.contacts.$each[1].$fields.name.$errors).toStrictEqual(expectedErrors); }); }); diff --git a/tests/unit/useRegle/properties/$errors.spec.ts b/tests/unit/useRegle/properties/$errors.spec.ts index 9a63ff94..e720fa6e 100644 --- a/tests/unit/useRegle/properties/$errors.spec.ts +++ b/tests/unit/useRegle/properties/$errors.spec.ts @@ -13,7 +13,7 @@ describe('$silentErrors', () => { name: [], }, ], - $errors: [], + $self: [], }, email: [], user: { @@ -33,7 +33,7 @@ describe('$silentErrors', () => { name: ['This field is required'], }, ], - $errors: [], + $self: [], }, email: ['This field is required'], user: { @@ -55,7 +55,7 @@ describe('$silentErrors', () => { name: ['This field is required'], }, ], - $errors: [], + $self: [], }, email: ['Value must be an valid email address'], user: { diff --git a/tests/unit/useRegle/properties/$params.spec.ts b/tests/unit/useRegle/properties/$params.spec.ts index 9a97e80a..8098ddf0 100644 --- a/tests/unit/useRegle/properties/$params.spec.ts +++ b/tests/unit/useRegle/properties/$params.spec.ts @@ -34,10 +34,7 @@ describe('$params', () => { contacts: { $each: { name: { - testParams: withParams( - (value: Maybe, min) => (value?.length ?? 0) > min, - [min] - ), + testParams: withParams((value: Maybe, min) => (value?.length ?? 0) > min, [min]), }, }, }, @@ -114,9 +111,7 @@ describe('$params', () => { expect(vm.r$.$fields.email.$rules.testParams.$params).toStrictEqual([6]); expect(vm.r$.$fields.user.$fields.firstName.$rules.testParams.$params).toStrictEqual([6]); - expect(vm.r$.$fields.contacts.$each[0].$fields.name.$rules.testParams.$params).toStrictEqual([ - 6, - ]); + expect(vm.r$.$fields.contacts.$each[0].$fields.name.$rules.testParams.$params).toStrictEqual([6]); vm.r$.$value.contacts.push({ name: '' }); await nextTick(); @@ -124,7 +119,7 @@ describe('$params', () => { vm.min = 10; await nextTick(); - shouldBeInvalidField(vm.r$); + shouldBeErrorField(vm.r$); shouldBeErrorField(vm.r$.$fields.email); shouldBeErrorField(vm.r$.$fields.user.$fields.firstName); shouldBeErrorField(vm.r$.$fields.contacts.$each[0].$fields.name); @@ -132,18 +127,14 @@ describe('$params', () => { expect(vm.r$.$fields.email.$rules.testParams.$params).toStrictEqual([10]); expect(vm.r$.$fields.user.$fields.firstName.$rules.testParams.$params).toStrictEqual([10]); - expect(vm.r$.$fields.contacts.$each[0].$fields.name.$rules.testParams.$params).toStrictEqual([ - 10, - ]); - expect(vm.r$.$fields.contacts.$each[1].$fields.name.$rules.testParams.$params).toStrictEqual([ - 10, - ]); + expect(vm.r$.$fields.contacts.$each[0].$fields.name.$rules.testParams.$params).toStrictEqual([10]); + expect(vm.r$.$fields.contacts.$each[1].$fields.name.$rules.testParams.$params).toStrictEqual([10]); vm.min = 5; await nextTick(); - shouldBeInvalidField(vm.r$); + shouldBeErrorField(vm.r$); shouldBeValidField(vm.r$.$fields.email); shouldBeCorrectNestedStatus(vm.r$.$fields.user); shouldBeValidField(vm.r$.$fields.user.$fields.firstName); @@ -153,12 +144,8 @@ describe('$params', () => { expect(vm.r$.$fields.email.$rules.testParams.$params).toStrictEqual([5]); expect(vm.r$.$fields.user.$fields.firstName.$rules.testParams.$params).toStrictEqual([5]); - expect(vm.r$.$fields.contacts.$each[0].$fields.name.$rules.testParams.$params).toStrictEqual([ - 5, - ]); - expect(vm.r$.$fields.contacts.$each[1].$fields.name.$rules.testParams.$params).toStrictEqual([ - 5, - ]); + expect(vm.r$.$fields.contacts.$each[0].$fields.name.$rules.testParams.$params).toStrictEqual([5]); + expect(vm.r$.$fields.contacts.$each[1].$fields.name.$rules.testParams.$params).toStrictEqual([5]); vm.r$.$value.contacts[1].name = 'aeeyeziyr'; await nextTick(); @@ -311,9 +298,7 @@ describe('$params', () => { expect(vm.r$.$fields.email.$rules.testParams.$params).toStrictEqual([6]); expect(vm.r$.$fields.user.$fields.firstName.$rules.testParams.$params).toStrictEqual([6]); - expect(vm.r$.$fields.contacts.$each[0].$fields.name.$rules.testParams.$params).toStrictEqual([ - 6, - ]); + expect(vm.r$.$fields.contacts.$each[0].$fields.name.$rules.testParams.$params).toStrictEqual([6]); vm.min = 20; @@ -338,9 +323,7 @@ describe('$params', () => { expect(vm.r$.$fields.email.$rules.testParams.$params).toStrictEqual([20]); expect(vm.r$.$fields.user.$fields.firstName.$rules.testParams.$params).toStrictEqual([20]); - expect(vm.r$.$fields.contacts.$each[0].$fields.name.$rules.testParams.$params).toStrictEqual([ - 20, - ]); + expect(vm.r$.$fields.contacts.$each[0].$fields.name.$rules.testParams.$params).toStrictEqual([20]); }); }); }); diff --git a/tests/unit/useRegle/properties/$silentErrors.spec.ts b/tests/unit/useRegle/properties/$silentErrors.spec.ts index 951db9ce..7917a306 100644 --- a/tests/unit/useRegle/properties/$silentErrors.spec.ts +++ b/tests/unit/useRegle/properties/$silentErrors.spec.ts @@ -12,7 +12,7 @@ describe('$silentErrors', () => { name: ['This field is required'], }, ], - $errors: [], + $self: [], }, email: ['This field is required', 'Value must be an valid email address'], user: { @@ -21,18 +21,12 @@ describe('$silentErrors', () => { }, }); - expect(vm.r$.$fields.contacts.$each[0].$fields.name.$silentErrors).toStrictEqual([ - 'This field is required', - ]); + expect(vm.r$.$fields.contacts.$each[0].$fields.name.$silentErrors).toStrictEqual(['This field is required']); expect(vm.r$.$fields.email.$silentErrors).toStrictEqual([ 'This field is required', 'Value must be an valid email address', ]); - expect(vm.r$.$fields.user.$fields.firstName.$silentErrors).toStrictEqual([ - 'This field is required', - ]); - expect(vm.r$.$fields.user.$fields.lastName.$silentErrors).toStrictEqual([ - 'This field is required', - ]); + expect(vm.r$.$fields.user.$fields.firstName.$silentErrors).toStrictEqual(['This field is required']); + expect(vm.r$.$fields.user.$fields.lastName.$silentErrors).toStrictEqual(['This field is required']); }); }); diff --git a/tests/unit/useRegle/stories/base-story.all-state-types.spec.ts b/tests/unit/useRegle/stories/base-story.all-state-types.spec.ts index bf706fd7..e53dcbab 100644 --- a/tests/unit/useRegle/stories/base-story.all-state-types.spec.ts +++ b/tests/unit/useRegle/stories/base-story.all-state-types.spec.ts @@ -41,7 +41,7 @@ describe.each([ child: [], }, collection: { - $errors: [], + $self: [], $each: [{ name: [] }], }, }, @@ -84,7 +84,7 @@ describe.each([ child: [], }, collection: { - $errors: [], + $self: [], $each: [{ name: [] }], }, }, @@ -123,10 +123,7 @@ describe.each([ shouldBeErrorField(vm.r$.$fields.level0); shouldBeErrorField(vm.r$.$fields.level1.$fields.collection.$each[1].$fields.name); - expect(vm.r$.$errors.level1.collection.$each).toStrictEqual([ - { name: [] }, - { name: ['This field is required'] }, - ]); + expect(vm.r$.$errors.level1.collection.$each).toStrictEqual([{ name: [] }, { name: ['This field is required'] }]); expect(vm.r$.$value).toStrictEqual({ level0: 1, @@ -230,10 +227,7 @@ describe.each([ shouldBeValidField(vm.r$.$fields.level1.$fields.collection.$each[0].$fields.name); - const [{ result, data }] = await Promise.all([ - vm.r$.$validate(), - vi.advanceTimersByTimeAsync(200), - ]); + const [{ result, data }] = await Promise.all([vm.r$.$validate(), vi.advanceTimersByTimeAsync(200)]); expect(result).toBe(true); expect(data).toStrictEqual({ @@ -263,7 +257,7 @@ describe.each([ child: [], }, collection: { - $errors: [], + $self: [], $each: [{ name: [] }], }, }, diff --git a/tests/unit/useZodRegle/useZodRegle.spec.ts b/tests/unit/useZodRegle/useZodRegle.spec.ts index 3096e932..cf4be0f8 100644 --- a/tests/unit/useZodRegle/useZodRegle.spec.ts +++ b/tests/unit/useZodRegle/useZodRegle.spec.ts @@ -46,7 +46,7 @@ function nestedReactiveObjectValidation() { level2: z.object({ child: zodIsEven.optional(), }), - collection: z.array(z.object({ name: zodIsEven })), + collection: z.array(z.object({ name: zodIsEven })).min(3), }), }) ); @@ -72,7 +72,7 @@ describe('useZodRegle ', async () => { child: [], }, collection: { - $errors: [], + $self: [], $each: [{ name: [] }], }, }, @@ -97,6 +97,7 @@ describe('useZodRegle ', async () => { shouldBeInvalidField(vm.r$.$fields.level1); shouldBeInvalidField(vm.r$.$fields.level1.$fields.child); shouldBePristineField(vm.r$.$fields.level1.$fields.level2.$fields.child); + shouldBeInvalidField(vm.r$.$fields.level1.$fields.collection.$field); shouldBePristineField(vm.r$.$fields.level1.$fields.collection.$each[0].$fields.name); }); @@ -112,7 +113,7 @@ describe('useZodRegle ', async () => { child: [], }, collection: { - $errors: [], + $self: ['This list should have at least 3 items'], $each: [{ name: [] }], }, }, @@ -130,16 +131,17 @@ describe('useZodRegle ', async () => { it('should update dirty state and errors when updating form', async () => { vm.r$.$value.level0 = 1; - vm.r$.$value.level1.collection.push({ name: null }); + vm.r$.$value.level1.collection.push({ name: null }, { name: null }); await nextTick(); expect(vm.r$.$errors.level0).toStrictEqual(['Custom error']); - expect(vm.r$.$errors.level1.collection.$each).toStrictEqual([{ name: [] }, { name: [] }]); + expect(vm.r$.$errors.level1.collection.$each).toStrictEqual([{ name: [] }, { name: [] }, { name: [] }]); shouldBeInvalidField(vm.r$.$fields.level1.$fields.collection.$each[1].$fields.name); + shouldBeInvalidField(vm.r$.$fields.level1.$fields.collection.$each[2].$fields.name); - vm.r$.$fields.level1.$fields.collection.$each[1].$fields.name.$touch(); + vm.r$.$fields.level1.$fields.collection.$touch(); await nextTick(); expect(vm.r$.$ready).toBe(false); @@ -147,10 +149,12 @@ describe('useZodRegle ', async () => { shouldBeErrorField(vm.r$); shouldBeErrorField(vm.r$.$fields.level0); shouldBeErrorField(vm.r$.$fields.level1.$fields.collection.$each[1].$fields.name); + shouldBeErrorField(vm.r$.$fields.level1.$fields.collection.$each[2].$fields.name); expect(vm.r$.$errors.level1.collection.$each).toStrictEqual([ { name: [] }, { name: ['This field is required'] }, + { name: ['This field is required'] }, ]); expect(vm.r$.$value).toStrictEqual({ @@ -160,7 +164,7 @@ describe('useZodRegle ', async () => { level2: { child: 2, }, - collection: [{ name: 0 }, { name: null }], + collection: [{ name: 0 }, { name: null }, { name: null }], }, }); @@ -174,6 +178,7 @@ describe('useZodRegle ', async () => { vm.r$.$value.level1.child = 3; vm.r$.$value.level1.level2.child = 3; vm.r$.$value.level1.collection[1].name = 3; + vm.r$.$value.level1.collection[2].name = 3; await nextTick(); @@ -195,7 +200,7 @@ describe('useZodRegle ', async () => { level2: { child: 3, }, - collection: [{ name: 0 }, { name: 3 }], + collection: [{ name: 0 }, { name: 3 }, { name: 3 }], }, }); @@ -210,13 +215,14 @@ describe('useZodRegle ', async () => { vm.r$.$value.level1.child = 2; vm.r$.$value.level1.level2.child = 2; vm.r$.$value.level1.collection[1].name = 2; + vm.r$.$value.level1.collection[2].name = 2; await nextTick(); expect(vm.r$.$errors.level0).toStrictEqual([]); expect(vm.r$.$errors.level1.child).toStrictEqual([]); expect(vm.r$.$errors.level1.level2.child).toStrictEqual([]); - expect(vm.r$.$errors.level1.collection.$each).toStrictEqual([{ name: [] }, { name: [] }]); + expect(vm.r$.$errors.level1.collection.$each).toStrictEqual([{ name: [] }, { name: [] }, { name: [] }]); expect(vm.r$.$ready).toBe(true); @@ -228,6 +234,7 @@ describe('useZodRegle ', async () => { shouldBeValidField(vm.r$.$fields.level1.$fields.level2.$fields.child); shouldBeValidField(vm.r$.$fields.level1.$fields.collection.$each[0].$fields.name); shouldBeValidField(vm.r$.$fields.level1.$fields.collection.$each[1].$fields.name); + shouldBeValidField(vm.r$.$fields.level1.$fields.collection.$each[2].$fields.name); expect(vm.r$.$value).toStrictEqual({ level0: 2, @@ -236,7 +243,7 @@ describe('useZodRegle ', async () => { level2: { child: 2, }, - collection: [{ name: 0 }, { name: 2 }], + collection: [{ name: 0 }, { name: 2 }, { name: 2 }], }, }); @@ -245,14 +252,15 @@ describe('useZodRegle ', async () => { await nextTick(); - expect(vm.r$.$errors.level1.collection.$each).toStrictEqual([{ name: [] }]); + expect(vm.r$.$errors.level1.collection.$each).toStrictEqual([{ name: [] }, { name: [] }]); shouldBeValidField(vm.r$.$fields.level1.$fields.collection.$each[0].$fields.name); - const [{ result, data }] = await Promise.all([ - vm.r$.$validate(), - vi.advanceTimersByTimeAsync(200), - ]); + vm.r$.$value.level1.collection.push({ name: 2 }); + await nextTick(); + vm.r$.$fields.level1.$fields.collection.$each[2].$fields.name.$touch(); + + const [{ result, data }] = await Promise.all([vm.r$.$validate(), vi.advanceTimersByTimeAsync(200)]); expect(result).toBe(true); expect(data).toStrictEqual({ @@ -262,7 +270,7 @@ describe('useZodRegle ', async () => { level2: { child: 2, }, - collection: [{ name: 2 }], + collection: [{ name: 2 }, { name: 2 }, { name: 2 }], }, }); }); @@ -280,7 +288,7 @@ describe('useZodRegle ', async () => { child: [], }, collection: { - $errors: [], + $self: [], $each: [{ name: [] }], }, }, diff --git a/tests/utils/validations.utils.ts b/tests/utils/validations.utils.ts index b28662c8..7bc6b556 100644 --- a/tests/utils/validations.utils.ts +++ b/tests/utils/validations.utils.ts @@ -39,7 +39,9 @@ export function shouldBeInvalidField(field?: PossibleFields) { export function shouldBeErrorField(field?: PossibleFields) { expect(field?.$invalid).toBe(true); expect(field?.$error).toBe(true); - expect(field?.$dirty).toBe(true); + if (field && !('$fields' in field) && !('$each' in field)) { + expect(field?.$dirty).toBe(true); + } expect(field?.$anyDirty).toBe(true); expect(field?.$pending).toBe(false); expect(field?.$valid).toBe(false); diff --git a/ui-tests/fixtures/ui-vue3/src/App.vue b/ui-tests/fixtures/ui-vue3/src/App.vue index 5cd0c886..76604720 100644 --- a/ui-tests/fixtures/ui-vue3/src/App.vue +++ b/ui-tests/fixtures/ui-vue3/src/App.vue @@ -35,11 +35,7 @@
-
+

Project {{ index + 1 }}

⊕ Add project
-
    -
  • +
      +
    • {{ error }}
    @@ -169,10 +165,7 @@ const { r$ } = useCustomRegle(form, { }, projects: { $autoDirty: false, - minLength: withMessage( - minLength(1), - (value, { $params: [min] }) => `You need at least ${min} project` - ), + minLength: withMessage(minLength(1), (value, { $params: [min] }) => `You need at least ${min} project`), $each: { name: { required }, price: { required, numeric, minValue: minValue(1), maxValue: maxValue(1000) },