Skip to content

Commit

Permalink
fix: reset meta correctly with resetField
Browse files Browse the repository at this point in the history
  • Loading branch information
logaretm committed Dec 4, 2020
1 parent 1659657 commit 012658c
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 22 deletions.
31 changes: 12 additions & 19 deletions packages/vee-validate/src/useField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import {
hasCheckedAttr,
getFromPath,
setInPath,
keysOf,
injectWithSelf,
} from './utils';
import { isCallable } from '../../shared';
Expand Down Expand Up @@ -141,16 +140,7 @@ export function useField(name: MaybeReactive<string>, rules?: RuleExpression, op

function resetField(state?: Partial<FieldState>) {
unwatchValue?.();
resetValidationState(state?.value);
if (state?.dirty) {
setTouched(state.dirty);
}
if (state?.touched) {
setTouched(state.touched);
}
if (state?.errors) {
errors.value = state.errors;
}
resetValidationState(state);
watchValue();
}

Expand Down Expand Up @@ -321,10 +311,12 @@ function useValidationState({
}

// Resets the validation state
function resetValidationState(newValue?: any) {
value.value = newValue ?? getFromPath(unref(formInitialValues), unref(name)) ?? initValue;
errors.value = [];
resetMeta();
function resetValidationState(state?: Partial<FieldState>) {
value.value =
state && 'value' in state ? state.value : getFromPath(unref(formInitialValues), unref(name)) ?? initValue;
errors.value = state?.errors || [];

resetMeta(state);
}

return {
Expand Down Expand Up @@ -356,11 +348,12 @@ function useMeta(initialValue: any) {
/**
* Resets the flag state
*/
function resetMeta() {
function resetMeta(state?: Pick<Partial<FieldState>, 'dirty' | 'touched' | 'value'>) {
const defaults = initialMeta();
keysOf(meta).forEach(key => {
meta[key] = defaults[key];
});
meta.pending = defaults.pending;
meta.touched = state?.touched ?? defaults.touched;
meta.dirty = state?.dirty ?? defaults.dirty;
meta.initialValue = state?.value ?? defaults.initialValue;
}

return {
Expand Down
64 changes: 61 additions & 3 deletions packages/vee-validate/tests/Field.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,8 +322,7 @@ describe('<Field />', () => {
expect(error.textContent).toBeTruthy();
});

test('validates file input', async () => {
// FIXME: typing here should be more lax
test('validates file input in scoped slots', async () => {
defineRule('atLeastOne', files => {
return files && files.length >= 1;
});
Expand All @@ -347,6 +346,28 @@ describe('<Field />', () => {
expect(error.textContent).toBeTruthy();
});

test('validates file input by rendering', async () => {
defineRule('atLeastOne', files => {
return files && files.length >= 1;
});

const wrapper = mountWithHoc({
template: `
<VForm v-slot="{ errors }">
<Field name="field" rules="required|atLeastOne" type="file" />
<span id="error">{{ errors.field }}</span>
</VForm>
`,
});

const input = wrapper.$el.querySelector('input');
dispatchEvent(input, 'change');
await flushPromises();

const error = wrapper.$el.querySelector('#error');
expect(error.textContent).toBeTruthy();
});

test('setting bails prop to false disables exit on first error', async () => {
const wrapper = mountWithHoc({
template: `
Expand Down Expand Up @@ -435,7 +456,7 @@ describe('<Field />', () => {
expect(error.textContent).toBe(REQUIRED_MESSAGE);
});

test('resets validation state using reset method in slot scope data', async () => {
test('resets validation state using handleReset() in slot scope props', async () => {
const wrapper = mountWithHoc({
template: `
<div>
Expand Down Expand Up @@ -464,6 +485,43 @@ describe('<Field />', () => {
expect(input.value).toBe('');
});

test('resets validation state using resetField() in slot scope props', async () => {
const resetMessage = 'field is bad';
const resetValue = 'val';
const wrapper = mountWithHoc({
template: `
<div>
<Field name="field" rules="required" v-slot="{ field, errors, resetField, meta }">
<input type="text" v-bind="field">
<span id="error">{{ errors && errors[0] }}</span>
<span id="touched">{{ meta.touched.toString() }}</span>
<span id="dirty">{{ meta.dirty.toString() }}</span>
<button @click="resetField({ value: '${resetValue}', dirty: true, touched: true, errors: ['${resetMessage}'] })">Reset</button>
</Field>
</div>
`,
});

const error = wrapper.$el.querySelector('#error');
const input = wrapper.$el.querySelector('input');
const dirty = wrapper.$el.querySelector('#dirty');
const touched = wrapper.$el.querySelector('#touched');

expect(error.textContent).toBe('');

setValue(input, '');
await flushPromises();
expect(error.textContent).toBe(REQUIRED_MESSAGE);
expect(dirty.textContent).toBe('true');
expect(touched.textContent).toBe('false');
wrapper.$el.querySelector('button').click();
await flushPromises();
expect(error.textContent).toBe(resetMessage);
expect(input.value).toBe(resetValue);
expect(dirty.textContent).toBe('true');
expect(touched.textContent).toBe('true');
});

test('yup abortEarly is set by bails global option', async () => {
configure({
bails: false,
Expand Down

0 comments on commit 012658c

Please sign in to comment.