Skip to content

Commit

Permalink
fix: prevent toggle checkboxes when form resets closes #3551
Browse files Browse the repository at this point in the history
  • Loading branch information
logaretm committed Oct 31, 2021
1 parent 4b800e3 commit cad12ba
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 2 deletions.
13 changes: 11 additions & 2 deletions packages/vee-validate/src/useForm.ts
Expand Up @@ -53,6 +53,10 @@ export function useForm<TValues extends Record<string, any> = Record<string, any
): FormContext<TValues> {
const formId = FORM_COUNTER++;

// Prevents fields from double resetting their values, which causes checkboxes to toggle their initial value
// TODO: This won't be needed if we centralize all the state inside the `form` for form inputs
let RESET_LOCK = false;

// A lookup containing fields or field groups
const fieldsByPath: Ref<FieldPathLookup<TValues>> = ref({} as any);

Expand Down Expand Up @@ -210,8 +214,8 @@ export function useForm<TValues extends Record<string, any> = Record<string, any
return;
}

// Multiple checkboxes, and only one of them got updated
if (isFieldGroup(fieldInstance) && fieldInstance[0]?.type === 'checkbox' && !Array.isArray(value)) {
// Multiple checkboxes, and only one of them got updated
const newValue = deepCopy(
resolveNextCheckboxValue(getFromPath(formValues, field as string) || [], value, undefined)
);
Expand All @@ -222,7 +226,7 @@ export function useForm<TValues extends Record<string, any> = Record<string, any

let newValue = value;
// Single Checkbox: toggles the field value unless the field is being reset then force it
if (!isFieldGroup(fieldInstance) && fieldInstance.type === 'checkbox' && !force) {
if (!isFieldGroup(fieldInstance) && fieldInstance.type === 'checkbox' && !force && !RESET_LOCK) {
newValue = deepCopy(
resolveNextCheckboxValue<TValues[T]>(
getFromPath<TValues[T]>(formValues, field as string) as TValues[T],
Expand Down Expand Up @@ -277,6 +281,7 @@ export function useForm<TValues extends Record<string, any> = Record<string, any
* Resets all fields
*/
function resetForm(state?: Partial<FormState<TValues>>) {
RESET_LOCK = true;
// set initial values if provided
if (state?.values) {
setInitialValues(state.values);
Expand All @@ -293,6 +298,7 @@ export function useForm<TValues extends Record<string, any> = Record<string, any
return;
}

// avoid resetting the field values, because they should've been reset already.
applyFieldMutation(field, f => f.resetField());
});

Expand All @@ -302,6 +308,9 @@ export function useForm<TValues extends Record<string, any> = Record<string, any

setErrors(state?.errors || {});
submitCount.value = state?.submitCount || 0;
nextTick(() => {
RESET_LOCK = false;
});
}

function insertFieldAtPath(field: PrivateFieldContext, path: string) {
Expand Down
52 changes: 52 additions & 0 deletions packages/vee-validate/tests/Form.spec.ts
Expand Up @@ -2231,4 +2231,56 @@ describe('<Form />', () => {
} as InvalidSubmissionContext);
expect(validSpy).not.toHaveBeenCalled();
});

// #3551
test('resets checkboxes according to initial values', async () => {
const wrapper = mountWithHoc({
setup() {
return {
values: {
terms: true,
termsUnslotted: true,
array: ['coffee', 'tea'],
},
};
},
template: `
<VForm as="form" v-slot="{ resetForm }" :initial-values="values">
<Field v-slot="{ field }" name="terms" type="checkbox" :value="true" :unchecked-value="false">
<label>
<input type="checkbox" name="terms" v-bind="field" :value="true" :unchecked-value="false" />
</label>
</Field>
<Field name="termsUnslotted" type="checkbox" :value="true" :unchecked-value="false"></Field>
<Field name="array" type="checkbox" value="coffee"></Field>
<Field name="array" type="checkbox" value="tea"></Field>
<button id="reset1" type="button" @click="resetForm()">Reset</button>
<button id="reset2" type="button" @click="resetForm({ values: { terms: false, termsUnslotted: true, array: ['coffee'] } })">Reset</button>
</VForm>
`,
});

const inputAt = (idx: number) => wrapper.$el.querySelectorAll('input')[idx] as HTMLInputElement;
expect(inputAt(0).checked).toBe(true);
expect(inputAt(1).checked).toBe(true);
expect(inputAt(2).checked).toBe(true);
expect(inputAt(3).checked).toBe(true);

dispatchEvent('#reset1', 'click');
await flushPromises();
expect(inputAt(0).checked).toBe(true);
expect(inputAt(1).checked).toBe(true);
expect(inputAt(2).checked).toBe(true);
expect(inputAt(3).checked).toBe(true);

dispatchEvent('#reset2', 'click');
await flushPromises();
expect(inputAt(0).checked).toBe(false);
expect(inputAt(1).checked).toBe(true);
expect(inputAt(2).checked).toBe(true);
expect(inputAt(3).checked).toBe(false);
});
});

0 comments on commit cad12ba

Please sign in to comment.