From b1446152d6f6cd4843ab206d667a7d744c2a14fc Mon Sep 17 00:00:00 2001 From: Abdelrahman Awad Date: Fri, 13 Aug 2021 23:53:33 +0200 Subject: [PATCH] fix: ensure option bound value type is preserved closes #3440 --- packages/vee-validate/src/utils/assertions.ts | 9 ++++- packages/vee-validate/src/utils/events.ts | 10 +++++- packages/vee-validate/tests/Field.spec.ts | 34 +++++++++++++++++++ packages/vee-validate/tests/helpers/index.ts | 4 +-- 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/packages/vee-validate/src/utils/assertions.ts b/packages/vee-validate/src/utils/assertions.ts index 6da184711..ab5ccc381 100644 --- a/packages/vee-validate/src/utils/assertions.ts +++ b/packages/vee-validate/src/utils/assertions.ts @@ -54,7 +54,14 @@ export function isNotNestedPath(path: string) { * Checks if an element is a native HTML5 multi-select input element */ export function isNativeMultiSelect(el: HTMLElement): el is HTMLSelectElement { - return el.tagName === 'SELECT' && (el as HTMLSelectElement).multiple; + return isNativeSelect(el) && el.multiple; +} + +/** + * Checks if an element is a native HTML5 select input element + */ +export function isNativeSelect(el: HTMLElement): el is HTMLSelectElement { + return el.tagName === 'SELECT'; } /** diff --git a/packages/vee-validate/src/utils/events.ts b/packages/vee-validate/src/utils/events.ts index 63cd35781..53b91bcaf 100644 --- a/packages/vee-validate/src/utils/events.ts +++ b/packages/vee-validate/src/utils/events.ts @@ -1,4 +1,4 @@ -import { hasCheckedAttr, isNativeMultiSelect, isEvent } from './assertions'; +import { hasCheckedAttr, isNativeMultiSelect, isNativeSelect, isEvent } from './assertions'; import { getBoundValue, hasValueBinding } from './vnode'; export function normalizeEventValue(value: Event | unknown): unknown { @@ -23,5 +23,13 @@ export function normalizeEventValue(value: Event | unknown): unknown { .map(getBoundValue); } + // makes sure we get the actual `option` bound value + // #3440 + if (isNativeSelect(input)) { + const selectedOption = Array.from(input.options).find(opt => opt.selected); + + return selectedOption ? getBoundValue(selectedOption) : input.value; + } + return input.value; } diff --git a/packages/vee-validate/tests/Field.spec.ts b/packages/vee-validate/tests/Field.spec.ts index cc6f70f38..c80de9566 100644 --- a/packages/vee-validate/tests/Field.spec.ts +++ b/packages/vee-validate/tests/Field.spec.ts @@ -1004,4 +1004,38 @@ describe('', () => { await flushPromises(); expect((form as any).field).toBe('hello'); }); + + // #3440 + test('should preserve select input options value type', async () => { + const value = ref(); + + const wrapper = mountWithHoc({ + setup() { + return { + value, + }; + }, + template: ` + + + + + `, + }); + + await flushPromises(); + const select = document.querySelector('select') as HTMLSelectElement; + const optTrue = document.querySelector('#true') as HTMLOptionElement; + const optFalse = document.querySelector('#false') as HTMLOptionElement; + + optTrue.selected = true; + dispatchEvent(select, 'change'); + await flushPromises(); + expect(value.value).toBe(true); + + optFalse.selected = true; + dispatchEvent(select, 'change'); + await flushPromises(); + expect(value.value).toBe(false); + }); }); diff --git a/packages/vee-validate/tests/helpers/index.ts b/packages/vee-validate/tests/helpers/index.ts index 19606c032..f8a91154d 100644 --- a/packages/vee-validate/tests/helpers/index.ts +++ b/packages/vee-validate/tests/helpers/index.ts @@ -41,9 +41,9 @@ export function setChecked(node: HTMLInputElement, status?: boolean) { node.dispatchEvent(new window.Event('input')); } -export function dispatchEvent(node: ComponentPublicInstance | HTMLInputElement, eventName: string) { +export function dispatchEvent(node: ComponentPublicInstance | HTMLElement, eventName: string) { if (HTML_TAGS.includes((node as any).tagName)) { - const input = node as HTMLInputElement; + const input = node as HTMLElement; input.dispatchEvent(new window.Event(eventName)); return; }