diff --git a/components/AutoComplete/__test__/__snapshots__/auto-complete.spec.ts.snap b/components/AutoComplete/__test__/__snapshots__/auto-complete.spec.ts.snap new file mode 100644 index 00000000..99bd2dee --- /dev/null +++ b/components/AutoComplete/__test__/__snapshots__/auto-complete.spec.ts.snap @@ -0,0 +1,35 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Test: KAutoComplete > basic functional testing 1`] = ` +" " +`; + +exports[`Test: KAutoComplete > props: cls 1`] = `" "`; + +exports[`Test: KAutoComplete > props: disabled 1`] = `" "`; + +exports[`Test: KAutoComplete > props: fitInputWidth 1`] = ` +" " +`; + +exports[`Test: KAutoComplete > props: placeholder 1`] = `" "`; + +exports[`Test: KAutoComplete > props: size 1`] = `" "`; + +exports[`Test: KAutoComplete > props: value 1`] = `" "`; + +exports[`Test: KAutoComplete > slot: default 1`] = `" "`; + +exports[`Test: KAutoComplete > slot: prefix and suffix 1`] = `" "`; diff --git a/components/AutoComplete/__test__/auto-complete.spec.ts b/components/AutoComplete/__test__/auto-complete.spec.ts new file mode 100644 index 00000000..7919bb8d --- /dev/null +++ b/components/AutoComplete/__test__/auto-complete.spec.ts @@ -0,0 +1,332 @@ +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; +import KAutoComplete from '../src'; +import { tick } from 'svelte'; +import KAutoCompleteFit from './fixture/autocomplete.fit.svelte'; +import KAutoCompleteBasic from './fixture/autocomplete.basic.svelte'; +import KAutoCompleteClear from './fixture/autocomplete.clear.svelte'; +import KAutoCompleteDisabled from './fixture/autocomplete.disabled.svelte'; +import KAutoCompleteSlot from './fixture/autocomplete.slot.svelte'; +import KAutoCompleteDefault from './fixture/autocomplete.default.svelte'; +let host; + +const initHost = () => { + host = globalThis.document.createElement('div'); + host.setAttribute('id', 'host'); + globalThis.document.body.appendChild(host); +}; +beforeEach(() => { + initHost(); + vi.useFakeTimers(); +}); +afterEach(() => { + host.remove(); + vi.useRealTimers(); +}); + +describe('Test: KAutoComplete', () => { + test('props: cls', async () => { + const instance = new KAutoComplete({ + target: host, + props: { + cls: 'k-auto-complete--test' + } + }); + expect(instance).toBeTruthy(); + expect(host!.innerHTML.includes('k-auto-complete--test')).toBeTruthy(); + expect(host.innerHTML).matchSnapshot(); + }); + test('props: value', async () => { + const instance = new KAutoComplete({ + target: host, + props: { + value: 'svelte' + } + }); + expect(instance).toBeTruthy(); + await tick(); + const selectElm = host.getElementsByTagName('input')[0] as HTMLInputElement; + expect(selectElm).not.toBeNull(); + expect(selectElm.value).toBe('svelte'); + expect(host.innerHTML).matchSnapshot(); + }); + + test('props: placeholder', async () => { + const instance = new KAutoComplete({ + target: host, + props: { + placeholder: '绣面芙蓉一笑开' + } + }); + expect(instance).toBeTruthy(); + await tick(); + expect(host.innerHTML.includes('placeholder="绣面芙蓉一笑开"')).toBeTruthy(); + expect(host.innerHTML).matchSnapshot(); + }); + + test('props: size', async () => { + const instanceSm = new KAutoComplete({ + target: host, + props: { + size: 'sm' + } + }); + const instanceMd = new KAutoComplete({ + target: host, + props: { + size: 'md' + } + }); + const instanceLg = new KAutoComplete({ + target: host, + props: { + size: 'lg' + } + }); + expect(instanceSm).toBeTruthy(); + expect(instanceMd).toBeTruthy(); + expect(instanceLg).toBeTruthy(); + await tick(); + const selectElms = document.querySelectorAll('.k-input--base'); + expect(selectElms[0].className.includes('k-input__sm')).toBeTruthy(); + expect(selectElms[1].className.includes('k-input__md')).toBeTruthy(); + expect(selectElms[2].className.includes('k-input__lg')).toBeTruthy(); + expect(host.innerHTML).matchSnapshot(); + }); + + test('props: fitInputWidth', async () => { + const instance = new KAutoCompleteFit({ + target: host + }); + expect(instance).toBeTruthy(); + await tick(); + + const triggerEl = host.querySelector('.k-input--inner'); + (triggerEl as HTMLElement)?.dispatchEvent(new Event('input')); + await tick(); + await vi.advanceTimersByTimeAsync(300); + expect(host.innerHTML.includes('k-select--option__fit')).toBeTruthy(); + expect(host.innerHTML).matchSnapshot(); + }); + + test('props: clearable', async () => { + const instance = new KAutoCompleteClear({ + target: host + }); + expect(instance).toBeTruthy(); + await tick(); + const triggerEl = host.querySelector('.k-input--inner'); + expect(triggerEl.value === 'vue').toBeTruthy(); + const clearEl = host.querySelector('.k-input--clear-icon'); + (clearEl as HTMLElement)?.click(); + await tick(); + await vi.advanceTimersByTimeAsync(300); + expect(triggerEl.value === 'vue').not.toBeTruthy(); + }); + + test('basic functional testing', async () => { + const instance = new KAutoCompleteBasic({ + target: host + }); + expect(instance).toBeTruthy(); + await tick(); + + const triggerEl = host.querySelector('.k-input--inner'); + (triggerEl as HTMLElement)?.dispatchEvent(new Event('input', { cancelable: true })); + await tick(); + await vi.advanceTimersByTimeAsync(300); + const options = host.querySelector('[data-kv-key="vue-router"]').children[0]; + options.click(); + await tick(); + await vi.advanceTimersByTimeAsync(400); + expect(triggerEl.value === 'vue-router').toBeTruthy(); + expect(host.innerHTML).matchSnapshot(); + }); + + test('props: disabled', async () => { + const instance = new KAutoCompleteDisabled({ + target: host + }); + expect(instance).toBeTruthy(); + await tick(); + + const triggerEl = host.querySelector('.k-input--inner'); + (triggerEl as HTMLElement)?.dispatchEvent(new Event('input', { cancelable: true })); + await tick(); + await vi.advanceTimersByTimeAsync(300); + const options = host.querySelector('[data-kv-key="vue-router"]'); + expect(options).not.toBeTruthy(); + expect(host.innerHTML).matchSnapshot(); + }); + + test('slot: prefix and suffix', async () => { + const instance = new KAutoCompleteSlot({ + target: host + }); + expect(instance).toBeTruthy(); + await tick(); + expect(host.innerHTML).matchSnapshot(); + }); + + test('slot: default', async () => { + const instance = new KAutoCompleteDefault({ + target: host + }); + expect(instance).toBeTruthy(); + const triggerEl = host.querySelector('.k-input--inner'); + (triggerEl as HTMLElement)?.dispatchEvent(new Event('input', { cancelable: true })); + await tick(); + await vi.advanceTimersByTimeAsync(300); + expect(host.innerHTML).matchSnapshot(); + }); + + test('event: should trigger composition input event', async () => { + let value = ''; + const mockCompositionInputFn = vi.fn(); + const mockCompositionStart = vi.fn(); + const mockCompositionEnd = vi.fn(); + // @ts-ignore + const instance = new KAutoComplete({ + target: host, + props: { + value, + useCompositionInput: true + } + }); + await tick(); + // @ts-ignore + instance.$on('compositionstart', () => { + mockCompositionStart(); + }); + // @ts-ignore + instance.$on('compositionend', () => { + mockCompositionEnd(); + }); + // @ts-ignore + instance.$on('compositionInput', (v) => { + value = v.detail; + mockCompositionInputFn(); + }); + + const inputElm = host.getElementsByTagName('input')[0]; + inputElm.value = 'ikun'; + inputElm.dispatchEvent(new Event('compositionstart')); + inputElm.dispatchEvent(new Event('input')); + await tick(); + expect(instance).toBeTruthy(); + expect(mockCompositionStart).toBeCalled(); + expect(mockCompositionInputFn.mock.calls.length).toBe(0); + expect(value).toBe(''); + + inputElm.value = 'ikun ikun'; + inputElm.dispatchEvent(new Event('compositionend')); + await tick(); + expect(mockCompositionEnd).toBeCalled(); + expect(mockCompositionInputFn.mock.calls.length).toBe(1); + expect(value).toBe('ikun ikun'); + }); + + test('event: should trigger enter event when pressing enter', async () => { + const mockFn = vi.fn(); + // @ts-ignore + const instance = new KAutoComplete({ + target: host + }); + await tick(); + // @ts-ignore + instance.$on('enter', mockFn); + const inputElm = host.getElementsByTagName('input')[0]; + inputElm.dispatchEvent( + // @ts-ignore + new KeyboardEvent('keydown', { + bubbles: true, + cancelable: true, + key: 'Enter', + code: 'Enter' + }) + ); + await tick(); + expect(mockFn).toBeCalled(); + }); + + test('event: should trigger keydown event when not pressing enter', async () => { + const mockFn = vi.fn(); + // @ts-ignore + const instance = new KAutoComplete({ + target: host + }); + await tick(); + // @ts-ignore + instance.$on('enter', mockFn); + const inputElm = host.getElementsByTagName('input')[0]; + inputElm.dispatchEvent( + // @ts-ignore + new KeyboardEvent('keydown', { + bubbles: true, + cancelable: true, + key: 'A', + code: 'KeyA' + }) + ); + await tick(); + expect(mockFn).not.toBeCalled(); + }); + + test('event: should trigger change event', async () => { + let value = ''; + const mockFn = vi.fn(); + // @ts-ignore + const instance = new KAutoComplete({ + target: host, + props: { + value + } + }); + await tick(); + // @ts-ignore + instance.$on('change', (v) => { + value = v.detail.target.value; + mockFn(); + }); + const inputElm = host.getElementsByTagName('input')[0]; + inputElm.value = 'ikun'; + inputElm.dispatchEvent(new Event('change')); + await tick(); + expect(instance).toBeTruthy(); + expect(mockFn).toBeCalled(); + expect(inputElm).toBeTruthy(); + expect(value).toEqual('ikun'); + }); + + test('event: should not trigger event when disabled', async () => { + const mockFn = vi.fn(); + // @ts-ignore + const instance = new KAutoComplete({ + target: host, + props: { + disabled: true + } + }); + expect(instance).toBeTruthy(); + await tick(); + // @ts-ignore + instance.$on('change', mockFn); + // @ts-ignore + instance.$on('enter', mockFn); + const inputElm = host.getElementsByTagName('input')[0]; + inputElm.dispatchEvent(new Event('input')); + inputElm.dispatchEvent( + // @ts-ignore + new KeyboardEvent('keydown', { + bubbles: true, + cancelable: true, + key: 'Enter', + code: 'KeyEnter' + }) + ); + inputElm.value = 'ikun'; + + inputElm.dispatchEvent(new Event('change')); + await tick(); + expect(mockFn).not.toBeCalled(); + }); +}); diff --git a/components/AutoComplete/__test__/fixture/autocomplete.basic.svelte b/components/AutoComplete/__test__/fixture/autocomplete.basic.svelte new file mode 100644 index 00000000..e0aeb358 --- /dev/null +++ b/components/AutoComplete/__test__/fixture/autocomplete.basic.svelte @@ -0,0 +1,39 @@ + + + diff --git a/components/AutoComplete/__test__/fixture/autocomplete.clear.svelte b/components/AutoComplete/__test__/fixture/autocomplete.clear.svelte new file mode 100644 index 00000000..7c3ff25d --- /dev/null +++ b/components/AutoComplete/__test__/fixture/autocomplete.clear.svelte @@ -0,0 +1,39 @@ + + + diff --git a/components/AutoComplete/__test__/fixture/autocomplete.default.svelte b/components/AutoComplete/__test__/fixture/autocomplete.default.svelte new file mode 100644 index 00000000..20a29930 --- /dev/null +++ b/components/AutoComplete/__test__/fixture/autocomplete.default.svelte @@ -0,0 +1,42 @@ + + + +
test {data.label}
+
diff --git a/components/AutoComplete/__test__/fixture/autocomplete.disabled.svelte b/components/AutoComplete/__test__/fixture/autocomplete.disabled.svelte new file mode 100644 index 00000000..271e4619 --- /dev/null +++ b/components/AutoComplete/__test__/fixture/autocomplete.disabled.svelte @@ -0,0 +1,39 @@ + + + diff --git a/components/AutoComplete/__test__/fixture/autocomplete.fit.svelte b/components/AutoComplete/__test__/fixture/autocomplete.fit.svelte new file mode 100644 index 00000000..e0aeb358 --- /dev/null +++ b/components/AutoComplete/__test__/fixture/autocomplete.fit.svelte @@ -0,0 +1,39 @@ + + + diff --git a/components/AutoComplete/__test__/fixture/autocomplete.slot.svelte b/components/AutoComplete/__test__/fixture/autocomplete.slot.svelte new file mode 100644 index 00000000..6ee435d5 --- /dev/null +++ b/components/AutoComplete/__test__/fixture/autocomplete.slot.svelte @@ -0,0 +1,42 @@ + + + +
🏀
+
🏀
+
diff --git a/components/AutoComplete/package.json b/components/AutoComplete/package.json new file mode 100644 index 00000000..76616367 --- /dev/null +++ b/components/AutoComplete/package.json @@ -0,0 +1,53 @@ +{ + "name": "@ikun-ui/auto-complete", + "version": "0.1.5", + "type": "module", + "main": "src/index.ts", + "types": "src/index.d.ts", + "svelte": "src/index.ts", + "keywords": [ + "svelte", + "svelte3", + "web component", + "component", + "react", + "vue", + "svelte-kit", + "dx" + ], + "files": [ + "dist", + "package.json" + ], + "scripts": { + "build": "npm run build:js && npm run build:svelte", + "build:js": "tsc -p . --outDir dist/ --rootDir src/", + "build:svelte": "svelte-strip strip src/ dist", + "publish:pre": "node ../../scripts/pre-publish.js", + "publish:npm": "pnpm run publish:pre && pnpm publish --no-git-checks --access public" + }, + "publishConfig": { + "access": "public", + "main": "dist/index.js", + "module": "dist/index.js", + "svelte": "dist/index.js", + "types": "dist/index.d.ts" + }, + "dependencies": { + "@ikun-ui/icon": "workspace:*", + "@ikun-ui/input": "workspace:*", + "@ikun-ui/button": "workspace:*", + "@ikun-ui/utils": "workspace:*", + "@ikun-ui/form": "workspace:*", + "@ikun-ui/popover": "workspace:*", + "@ikun-ui/virtual-list": "workspace:*", + "baiwusanyu-utils": "^1.0.18", + "clsx": "^2.0.0" + }, + "devDependencies": { + "@tsconfig/svelte": "^5.0.2", + "svelte-strip": "^2.0.0", + "tslib": "^2.6.2", + "typescript": "^5.3.3" + } +} diff --git a/components/AutoComplete/src/index.svelte b/components/AutoComplete/src/index.svelte new file mode 100644 index 00000000..47ed9aea --- /dev/null +++ b/components/AutoComplete/src/index.svelte @@ -0,0 +1,222 @@ + + + + +
+ {#if list.length > 0} + + {#if !$$slots.default} + handleSelect(data)} + > + {:else} + + {/if} + + {:else} +

{text}

+ {/if} +
+
diff --git a/components/AutoComplete/src/index.ts b/components/AutoComplete/src/index.ts new file mode 100644 index 00000000..132bd3f4 --- /dev/null +++ b/components/AutoComplete/src/index.ts @@ -0,0 +1,5 @@ +/// +import AutoComplete from './index.svelte'; +export { AutoComplete as KAutoComplete }; + +export default AutoComplete; diff --git a/components/AutoComplete/src/option.svelte b/components/AutoComplete/src/option.svelte new file mode 100644 index 00000000..62cdc088 --- /dev/null +++ b/components/AutoComplete/src/option.svelte @@ -0,0 +1,38 @@ + + + + + diff --git a/components/AutoComplete/src/types.d.ts b/components/AutoComplete/src/types.d.ts new file mode 100644 index 00000000..726487ba --- /dev/null +++ b/components/AutoComplete/src/types.d.ts @@ -0,0 +1,30 @@ +/// +import type { ClassValue } from 'clsx'; +import { IKunSize } from '@ikun-ui/utils'; + +export type KAutoCompleteProps = { + size: IKunSize; + value: string; + disabled: boolean; + + clearable: boolean; + placeholder: string; + append: string; + prepend: string; + triggerOnFocus: boolean; + fetchSuggestions: + | undefined + | ((params: string | number, cb: (res: AutoCompleteItems[]) => void) => void); + cls: ClassValue; + attrs: Record; + useCompositionInput: boolean; + // option + isActive: boolean; + label: string; + fitInputWidth: boolean; + maxHeight: number; + key: string; +}; + +// TODO: unit test +// TODO: document diff --git a/components/AutoComplete/tsconfig.json b/components/AutoComplete/tsconfig.json new file mode 100644 index 00000000..fe0d7c4f --- /dev/null +++ b/components/AutoComplete/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@tsconfig/svelte/tsconfig.json", + + "compilerOptions": { + "noImplicitAny": true, + "strict": true, + "declaration": true + }, + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.svelte"], + "exclude": ["node_modules/*", "**/*.spec.ts"] +} diff --git a/components/Dropdown/__test__/dropdown.spec.ts b/components/Dropdown/__test__/dropdown.spec.ts index 2269d20f..b13e6989 100644 --- a/components/Dropdown/__test__/dropdown.spec.ts +++ b/components/Dropdown/__test__/dropdown.spec.ts @@ -92,7 +92,7 @@ describe('Test: KDropdown', () => { await vi.advanceTimersByTimeAsync(300); expect(show).toBeTruthy(); expect(mockFn).toBeCalledTimes(1); - await fireEvent.click(btn); + await fireEvent.click(host); await tick(); await vi.advanceTimersByTimeAsync(300); expect(show).not.toBeTruthy(); diff --git a/components/Form/__test__/__snapshots__/form.spec.ts.snap b/components/Form/__test__/__snapshots__/form.spec.ts.snap index efa44205..1a6366e0 100644 --- a/components/Form/__test__/__snapshots__/form.spec.ts.snap +++ b/components/Form/__test__/__snapshots__/form.spec.ts.snap @@ -48,9 +48,10 @@ exports[`Test: KForm > api: KForm clearValidateField 3`] = ` } `; -exports[`Test: KForm > api: KForm validateForm > {"data":{"KInput":"KInput","KSwitch":true,"KInputNumber":2,"KRate":null,"KRadio":"","KCheckbox":[],"KSelect":null,"KSelectString":""},"isValid":false,"invalidFields":[{"message":"KInput 3 ~5","fieldValue":"KInput","field":"KInput"},{"message":"KInputNumber 3 ~5","fieldValue":2,"field":"KInputNumber"},{"message":"KRate error","fieldValue":null,"field":"KRate"},{"message":"KRadio error","fieldValue":"","field":"KRadio"},{"message":"KRadio error","fieldValue":[],"field":"KCheckbox"},{"message":"KSelect error","fieldValue":null,"field":"KSelect"},{"message":"KSelectString custom error","fieldValue":"","field":"KSelectString"}]} 1`] = ` +exports[`Test: KForm > api: KForm validateForm > {"data":{"KInput":"KInput","KSwitch":true,"KInputNumber":2,"KRate":null,"KRadio":"","KCheckbox":[],"KSelect":null,"KSelectString":"","KAutoComplete":""},"isValid":false,"invalidFields":[{"message":"KInput 3 ~5","fieldValue":"KInput","field":"KInput"},{"message":"KInputNumber 3 ~5","fieldValue":2,"field":"KInputNumber"},{"message":"KRate error","fieldValue":null,"field":"KRate"},{"message":"KRadio error","fieldValue":"","field":"KRadio"},{"message":"KRadio error","fieldValue":[],"field":"KCheckbox"},{"message":"KSelect error","fieldValue":null,"field":"KSelect"},{"message":"KSelectString custom error","fieldValue":"","field":"KSelectString"},{"message":"KAutoComplete required","fieldValue":"","field":"KAutoComplete"}]} 1`] = ` { "data": { + "KAutoComplete": "", "KCheckbox": [], "KInput": "KInput", "KInputNumber": 2, @@ -96,6 +97,11 @@ exports[`Test: KForm > api: KForm validateForm > {"data":{"KInput":"KInput","KSw "fieldValue": "", "message": "KSelectString custom error", }, + { + "field": "KAutoComplete", + "fieldValue": "", + "message": "KAutoComplete required", + }, ], "isValid": false, } @@ -155,7 +161,13 @@ CustomEvent { } `; -exports[`Test: KForm > props: KForm initValue 1`] = `"
{"KInput":"KInput","KSwitch":true,"KRate":4,"KInputNumber":4,"KRadio":"3","KCheckbox":["2"],"KSelect":{"label":"不知明镜里","value":"不知","id":"3"},"KSelectString":"Huge","slider":43}
"`; +exports[`Test: KForm > events: KForm validate & input interaction 8`] = ` +CustomEvent { + "isTrusted": false, +} +`; + +exports[`Test: KForm > props: KForm initValue 1`] = `"
{"KInput":"KInput","KSwitch":true,"KRate":4,"KInputNumber":4,"KRadio":"3","KCheckbox":["2"],"KSelect":{"label":"不知明镜里","value":"不知","id":"3"},"KSelectString":"Huge","slider":43,"KAutoComplete":"vue"}
"`; exports[`Test: KForm > props: KForm labelWidth 1`] = `"
"`; diff --git a/components/Form/__test__/fixture/disabled.svelte b/components/Form/__test__/fixture/disabled.svelte index 97f93501..a9b4fcab 100644 --- a/components/Form/__test__/fixture/disabled.svelte +++ b/components/Form/__test__/fixture/disabled.svelte @@ -10,6 +10,7 @@ import { KSelect } from '@ikun-ui/select'; import { KSlider } from '@ikun-ui/slider'; import { KInputNumber } from '@ikun-ui/input-number'; + import { KAutoComplete } from '@ikun-ui/auto-complete'; const initValue = { KInput: 'KInput', KSwitch: true, @@ -19,7 +20,8 @@ KCheckbox: ['2'], KSelect: { label: '不知明镜里', value: '不知', id: '3' }, KSelectString: 'Huge', - slider: 43 + slider: 43, + KAutoComplete: 'vue' }; let KFormInst: KForm | undefined = undefined; const dataList = [ @@ -30,6 +32,25 @@ ]; let disabled = true; + + const restaurants = [ + { value: 'vue', link: 'https://github.com/vuejs/vue' }, + { value: 'element', link: 'https://github.com/ElemeFE/element' }, + { value: 'cooking', link: 'https://github.com/ElemeFE/cooking' }, + { value: 'mint-ui', link: 'https://github.com/ElemeFE/mint-ui' }, + { value: 'vuex', link: 'https://github.com/vuejs/vuex' }, + { value: 'vue-router', link: 'https://github.com/vuejs/vue-router' }, + { value: 'babel', link: 'https://github.com/babel/babel' } + ]; + const createFilter = (queryString) => { + return (restaurant) => { + return restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0; + }; + }; + const fetchFn = (queryString, cb) => { + const results = queryString ? restaurants.filter(createFilter(queryString)) : restaurants; + cb(results); + }; @@ -71,6 +92,9 @@ + + + diff --git a/components/Form/__test__/fixture/getForm.svelte b/components/Form/__test__/fixture/getForm.svelte index 9e057c1a..0e923cb6 100644 --- a/components/Form/__test__/fixture/getForm.svelte +++ b/components/Form/__test__/fixture/getForm.svelte @@ -10,6 +10,7 @@ import { KSelect } from '@ikun-ui/select'; import { createEventDispatcher } from 'svelte'; import { KInputNumber } from '@ikun-ui/input-number'; + import { KAutoComplete } from '@ikun-ui/auto-complete'; export let initValue = {}; let KFormInst: KForm | undefined = undefined; const dataList = [ @@ -77,6 +78,25 @@ dispatch('getRes', KFormInst.getForm()); } }; + + const restaurants = [ + { value: 'vue', link: 'https://github.com/vuejs/vue' }, + { value: 'element', link: 'https://github.com/ElemeFE/element' }, + { value: 'cooking', link: 'https://github.com/ElemeFE/cooking' }, + { value: 'mint-ui', link: 'https://github.com/ElemeFE/mint-ui' }, + { value: 'vuex', link: 'https://github.com/vuejs/vuex' }, + { value: 'vue-router', link: 'https://github.com/vuejs/vue-router' }, + { value: 'babel', link: 'https://github.com/babel/babel' } + ]; + const createFilter = (queryString) => { + return (restaurant) => { + return restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0; + }; + }; + const fetchFn = (queryString, cb) => { + const results = queryString ? restaurants.filter(createFilter(queryString)) : restaurants; + cb(results); + }; @@ -115,5 +135,8 @@ + + + diff --git a/components/Form/__test__/fixture/init.svelte b/components/Form/__test__/fixture/init.svelte index 98e1375f..54b80626 100644 --- a/components/Form/__test__/fixture/init.svelte +++ b/components/Form/__test__/fixture/init.svelte @@ -11,6 +11,7 @@ import { onMount } from 'svelte'; import { KSlider } from '@ikun-ui/slider'; import { KInputNumber } from '@ikun-ui/input-number'; + import { KAutoComplete } from '@ikun-ui/auto-complete'; const initValue = { KInput: 'KInput', KSwitch: true, @@ -20,7 +21,8 @@ KCheckbox: ['2'], KSelect: { label: '不知明镜里', value: '不知', id: '3' }, KSelectString: 'Huge', - slider: 43 + slider: 43, + KAutoComplete: 'vue' }; let KFormInst: KForm | undefined = undefined; const dataList = [ @@ -36,6 +38,25 @@ currentValue = KFormInst.getForm(); } }); + + const restaurants = [ + { value: 'vue', link: 'https://github.com/vuejs/vue' }, + { value: 'element', link: 'https://github.com/ElemeFE/element' }, + { value: 'cooking', link: 'https://github.com/ElemeFE/cooking' }, + { value: 'mint-ui', link: 'https://github.com/ElemeFE/mint-ui' }, + { value: 'vuex', link: 'https://github.com/vuejs/vuex' }, + { value: 'vue-router', link: 'https://github.com/vuejs/vue-router' }, + { value: 'babel', link: 'https://github.com/babel/babel' } + ]; + const createFilter = (queryString) => { + return (restaurant) => { + return restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0; + }; + }; + const fetchFn = (queryString, cb) => { + const results = queryString ? restaurants.filter(createFilter(queryString)) : restaurants; + cb(results); + }; @@ -80,4 +101,7 @@ {JSON.stringify(currentValue)} + + + diff --git a/components/Form/__test__/fixture/resetForm.svelte b/components/Form/__test__/fixture/resetForm.svelte index 62c35fcf..d66a8a0f 100644 --- a/components/Form/__test__/fixture/resetForm.svelte +++ b/components/Form/__test__/fixture/resetForm.svelte @@ -11,6 +11,7 @@ import { createEventDispatcher } from 'svelte'; import { KSlider } from '@ikun-ui/slider'; import { KInputNumber } from '@ikun-ui/input-number'; + import { KAutoComplete } from '@ikun-ui/auto-complete'; export let initValue = {}; let KFormInst: KForm | undefined = undefined; const dataList = [ @@ -90,6 +91,25 @@ KFormInst.resetForm(); } }; + + const restaurants = [ + { value: 'vue', link: 'https://github.com/vuejs/vue' }, + { value: 'element', link: 'https://github.com/ElemeFE/element' }, + { value: 'cooking', link: 'https://github.com/ElemeFE/cooking' }, + { value: 'mint-ui', link: 'https://github.com/ElemeFE/mint-ui' }, + { value: 'vuex', link: 'https://github.com/vuejs/vuex' }, + { value: 'vue-router', link: 'https://github.com/vuejs/vue-router' }, + { value: 'babel', link: 'https://github.com/babel/babel' } + ]; + const createFilter = (queryString) => { + return (restaurant) => { + return restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0; + }; + }; + const fetchFn = (queryString, cb) => { + const results = queryString ? restaurants.filter(createFilter(queryString)) : restaurants; + cb(results); + }; @@ -131,6 +151,10 @@ + + + + diff --git a/components/Form/__test__/fixture/setForm.svelte b/components/Form/__test__/fixture/setForm.svelte index a2711b33..72e9faaa 100644 --- a/components/Form/__test__/fixture/setForm.svelte +++ b/components/Form/__test__/fixture/setForm.svelte @@ -10,6 +10,7 @@ import { KSelect } from '@ikun-ui/select'; import { createEventDispatcher } from 'svelte'; import { KInputNumber } from '@ikun-ui/input-number'; + import { KAutoComplete } from '@ikun-ui/auto-complete'; export let isValidate = true; export let initValue = { KInput: 'KInput', @@ -19,7 +20,8 @@ KRadio: '', KCheckbox: [], KSelect: null, - KSelectString: '' + KSelectString: '', + KAutoComplete: '白雾三语' }; let KFormInst: KForm | undefined = undefined; const dataList = [ @@ -105,7 +107,8 @@ value: '白发', id: '1' }, - KSelectString: 'Huge' + KSelectString: 'Huge', + KAutoComplete: 'baiwusanyu' }, isValidate ); @@ -117,6 +120,25 @@ dispatch('getRes', KFormInst.getForm()); } }; + + const restaurants = [ + { value: 'vue', link: 'https://github.com/vuejs/vue' }, + { value: 'element', link: 'https://github.com/ElemeFE/element' }, + { value: 'cooking', link: 'https://github.com/ElemeFE/cooking' }, + { value: 'mint-ui', link: 'https://github.com/ElemeFE/mint-ui' }, + { value: 'vuex', link: 'https://github.com/vuejs/vuex' }, + { value: 'vue-router', link: 'https://github.com/vuejs/vue-router' }, + { value: 'babel', link: 'https://github.com/babel/babel' } + ]; + const createFilter = (queryString) => { + return (restaurant) => { + return restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0; + }; + }; + const fetchFn = (queryString, cb) => { + const results = queryString ? restaurants.filter(createFilter(queryString)) : restaurants; + cb(results); + }; @@ -155,6 +177,9 @@ + + + diff --git a/components/Form/__test__/fixture/size.svelte b/components/Form/__test__/fixture/size.svelte index cc827c95..f0cf0fa2 100644 --- a/components/Form/__test__/fixture/size.svelte +++ b/components/Form/__test__/fixture/size.svelte @@ -10,6 +10,7 @@ import { KSelect } from '@ikun-ui/select'; import { KSlider } from '@ikun-ui/slider'; import { KInputNumber } from '@ikun-ui/input-number'; + import { KAutoComplete } from '@ikun-ui/auto-complete'; const initValue = { KInput: 'KInput', KSwitch: true, @@ -19,7 +20,8 @@ KCheckbox: ['2'], KSelect: { label: '不知明镜里', value: '不知', id: '3' }, KSelectString: 'Huge', - slider: 43 + slider: 43, + KAutoComplete: '白雾三语' }; let KFormInst: KForm | undefined = undefined; const dataList = [ @@ -30,6 +32,25 @@ ]; let size = 'sm'; + + const restaurants = [ + { value: 'vue', link: 'https://github.com/vuejs/vue' }, + { value: 'element', link: 'https://github.com/ElemeFE/element' }, + { value: 'cooking', link: 'https://github.com/ElemeFE/cooking' }, + { value: 'mint-ui', link: 'https://github.com/ElemeFE/mint-ui' }, + { value: 'vuex', link: 'https://github.com/vuejs/vuex' }, + { value: 'vue-router', link: 'https://github.com/vuejs/vue-router' }, + { value: 'babel', link: 'https://github.com/babel/babel' } + ]; + const createFilter = (queryString) => { + return (restaurant) => { + return restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0; + }; + }; + const fetchFn = (queryString, cb) => { + const results = queryString ? restaurants.filter(createFilter(queryString)) : restaurants; + cb(results); + }; @@ -71,6 +92,9 @@ + + + diff --git a/components/Form/__test__/fixture/validateEvent.svelte b/components/Form/__test__/fixture/validateEvent.svelte index 18c177fd..1a901a60 100644 --- a/components/Form/__test__/fixture/validateEvent.svelte +++ b/components/Form/__test__/fixture/validateEvent.svelte @@ -10,6 +10,7 @@ import { KSelect } from '@ikun-ui/select'; import { createEventDispatcher } from 'svelte'; import { KInputNumber } from '@ikun-ui/input-number'; + import { KAutoComplete } from '@ikun-ui/auto-complete'; const initValue = { KInput: 'KInput', KSwitch: true, @@ -18,7 +19,8 @@ KRadio: '', KCheckbox: [], KSelect: null, - KSelectString: '' + KSelectString: '', + KAutoComplete: 'vue' }; let KFormInst: KForm | undefined = undefined; const dataList = [ @@ -77,6 +79,12 @@ } } } + ], + KAutoComplete: [ + { + required: true, + msg: 'KAutoComplete required' + } ] }; @@ -84,6 +92,25 @@ const handleValidate = (data: any) => { dispatch('getRes', data); }; + + const restaurants = [ + { value: 'vue', link: 'https://github.com/vuejs/vue' }, + { value: 'element', link: 'https://github.com/ElemeFE/element' }, + { value: 'cooking', link: 'https://github.com/ElemeFE/cooking' }, + { value: 'mint-ui', link: 'https://github.com/ElemeFE/mint-ui' }, + { value: 'vuex', link: 'https://github.com/vuejs/vuex' }, + { value: 'vue-router', link: 'https://github.com/vuejs/vue-router' }, + { value: 'babel', link: 'https://github.com/babel/babel' } + ]; + const createFilter = (queryString) => { + return (restaurant) => { + return restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0; + }; + }; + const fetchFn = (queryString, cb) => { + const results = queryString ? restaurants.filter(createFilter(queryString)) : restaurants; + cb(results); + }; @@ -122,5 +149,8 @@ + + + diff --git a/components/Form/__test__/fixture/validateForm.svelte b/components/Form/__test__/fixture/validateForm.svelte index 69733c15..8cd6bd2a 100644 --- a/components/Form/__test__/fixture/validateForm.svelte +++ b/components/Form/__test__/fixture/validateForm.svelte @@ -10,6 +10,7 @@ import { KSelect } from '@ikun-ui/select'; import { createEventDispatcher } from 'svelte'; import { KInputNumber } from '@ikun-ui/input-number'; + import { KAutoComplete } from '@ikun-ui/auto-complete'; const initValue = { KInput: 'KInput', KSwitch: true, @@ -18,7 +19,8 @@ KRadio: '', KCheckbox: [], KSelect: null, - KSelectString: '' + KSelectString: '', + KAutoComplete: '' }; let KFormInst: KForm | undefined = undefined; const dataList = [ @@ -77,6 +79,12 @@ } } } + ], + KAutoComplete: [ + { + required: true, + msg: 'KAutoComplete required' + } ] }; @@ -92,6 +100,25 @@ }); } }; + + const restaurants = [ + { value: 'vue', link: 'https://github.com/vuejs/vue' }, + { value: 'element', link: 'https://github.com/ElemeFE/element' }, + { value: 'cooking', link: 'https://github.com/ElemeFE/cooking' }, + { value: 'mint-ui', link: 'https://github.com/ElemeFE/mint-ui' }, + { value: 'vuex', link: 'https://github.com/vuejs/vuex' }, + { value: 'vue-router', link: 'https://github.com/vuejs/vue-router' }, + { value: 'babel', link: 'https://github.com/babel/babel' } + ]; + const createFilter = (queryString) => { + return (restaurant) => { + return restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0; + }; + }; + const fetchFn = (queryString, cb) => { + const results = queryString ? restaurants.filter(createFilter(queryString)) : restaurants; + cb(results); + }; @@ -130,5 +157,8 @@ + + + diff --git a/components/Form/__test__/form.spec.ts b/components/Form/__test__/form.spec.ts index 37fd8cbf..77eaeec7 100644 --- a/components/Form/__test__/form.spec.ts +++ b/components/Form/__test__/form.spec.ts @@ -57,8 +57,10 @@ describe('Test: KForm', () => { }); expect(instance).toBeTruthy(); - const input = host.querySelector('.k-input--inner'); - expect(input.value).toBe('KInput'); + const input = host.querySelectorAll('.k-input--inner'); + expect(input[0].value).toBe('KInput'); + + expect(input[2].value).toBe('vue'); expect(host.innerHTML.includes(`k-form-item-label__right`)).toBeTruthy(); @@ -98,7 +100,8 @@ describe('Test: KForm', () => { id: '3' }, KSelectString: 'Huge', - slider: 43 + slider: 43, + KAutoComplete: 'vue' }); expect(host.innerHTML).matchSnapshot(); }); @@ -170,9 +173,9 @@ describe('Test: KForm', () => { expect(instance).toBeTruthy(); await tick(); - expect(host.querySelectorAll('.k-form-item-label__disabled').length).toBe(9); + expect(host.querySelectorAll('.k-form-item-label__disabled').length).toBe(10); expect(host.querySelector('.k-input__disabled')).toBeTruthy(); - expect(host.querySelectorAll('.k-input__disabled').length).toBe(4); + expect(host.querySelectorAll('.k-input__disabled').length).toBe(6); expect(host.querySelector('.k-switch__disabled')).toBeTruthy(); expect(host.querySelector('.k-rate--item__disabled')).toBeTruthy(); expect(host.querySelectorAll('.k-radio--box__disabled').length).toBe(4); @@ -195,9 +198,9 @@ describe('Test: KForm', () => { btnEl?.click(); await tick(); - expect(host.querySelectorAll('.k-form-item-label__disabled').length).toBe(9); + expect(host.querySelectorAll('.k-form-item-label__disabled').length).toBe(10); expect(host.querySelector('.k-input__disabled')).toBeTruthy(); - expect(host.querySelectorAll('.k-input__disabled').length).toBe(4); + expect(host.querySelectorAll('.k-input__disabled').length).toBe(6); expect(host.querySelector('.k-switch__disabled')).toBeTruthy(); expect(host.querySelector('.k-rate--item__disabled')).toBeTruthy(); expect(host.querySelectorAll('.k-radio--box__disabled').length).toBe(4); @@ -214,8 +217,8 @@ describe('Test: KForm', () => { expect(instance).toBeTruthy(); await tick(); - expect(host.querySelectorAll('.k-form-item-label__sm').length).toBe(9); - expect(host.querySelector('.k-input__sm')).toBeTruthy(); + expect(host.querySelectorAll('.k-form-item-label__sm').length).toBe(10); + expect(host.querySelectorAll('.k-input__sm').length).toBe(2); expect(host.querySelector('.k-input-number__sm')).toBeTruthy(); expect(host.querySelector('.k-switch--sm')).toBeTruthy(); expect(host.querySelector('.k-rate--sm')).toBeTruthy(); @@ -229,8 +232,8 @@ describe('Test: KForm', () => { let btnEl = host.querySelector('#size_lg'); btnEl?.click(); await tick(); - expect(host.querySelectorAll('.k-form-item-label__lg').length).toBe(9); - expect(host.querySelector('.k-input__lg')).toBeTruthy(); + expect(host.querySelectorAll('.k-form-item-label__lg').length).toBe(10); + expect(host.querySelectorAll('.k-input__lg').length).toBe(2); expect(host.querySelector('.k-input-number__lg')).toBeTruthy(); expect(host.querySelector('.k-switch--lg')).toBeTruthy(); expect(host.querySelector('.k-rate--lg')).toBeTruthy(); @@ -244,8 +247,8 @@ describe('Test: KForm', () => { btnEl = host.querySelector('#size_md'); btnEl?.click(); await tick(); - expect(host.querySelectorAll('.k-form-item-label__md').length).toBe(9); - expect(host.querySelector('.k-input__md')).toBeTruthy(); + expect(host.querySelectorAll('.k-form-item-label__md').length).toBe(10); + expect(host.querySelectorAll('.k-input__md').length).toBe(2); expect(host.querySelector('.k-input-number__md')).toBeTruthy(); expect(host.querySelector('.k-switch--md')).toBeTruthy(); expect(host.querySelector('.k-rate--md')).toBeTruthy(); @@ -259,8 +262,8 @@ describe('Test: KForm', () => { btnEl = host.querySelector('#size_sm'); btnEl?.click(); await tick(); - expect(host.querySelectorAll('.k-form-item-label__sm').length).toBe(9); - expect(host.querySelector('.k-input__sm')).toBeTruthy(); + expect(host.querySelectorAll('.k-form-item-label__sm').length).toBe(10); + expect(host.querySelectorAll('.k-input__sm').length).toBe(2); expect(host.querySelector('.k-input-number__sm')).toBeTruthy(); expect(host.querySelector('.k-switch--sm')).toBeTruthy(); expect(host.querySelector('.k-rate--sm')).toBeTruthy(); @@ -399,9 +402,15 @@ describe('Test: KForm', () => { expect(mockFn).toBeCalledTimes(3); expect(value).matchSnapshot(); + const autoEl = host.querySelectorAll('.k-input--inner')[2]; + await fireEvent.input(autoEl, { target: { value: '' } }); + expect(host.innerHTML.includes('KAutoComplete required')).toBeTruthy(); + expect(mockFn).toBeCalledTimes(4); + expect(value).matchSnapshot(); + await fireEvent.input(inputNumEl, { target: { value: 2 } }); expect(host.innerHTML.includes('KInputNumber 3 ~5')).toBeTruthy(); - expect(mockFn).toBeCalledTimes(4); + expect(mockFn).toBeCalledTimes(5); expect(value).matchSnapshot(); const checkboxEl = host.querySelectorAll('[type="checkbox"]')[0]; @@ -412,7 +421,7 @@ describe('Test: KForm', () => { await tick(); await vi.advanceTimersByTimeAsync(300); expect(host.innerHTML.includes('KCheckbox error')).toBeTruthy(); - expect(mockFn).toBeCalledTimes(5); + expect(mockFn).toBeCalledTimes(6); expect(value).matchSnapshot(); const selectInput = host.querySelectorAll('.k-select--inner'); @@ -435,7 +444,7 @@ describe('Test: KForm', () => { await tick(); await vi.advanceTimersByTimeAsync(300); expect(host.innerHTML.includes('KSelect error')).toBeTruthy(); - expect(mockFn).toBeCalledTimes(6); + expect(mockFn).toBeCalledTimes(7); expect(value).matchSnapshot(); const selectInput2 = host.querySelectorAll('.k-select--inner'); @@ -458,7 +467,7 @@ describe('Test: KForm', () => { await tick(); await vi.advanceTimersByTimeAsync(300); expect(host.innerHTML.includes('KSelect error')).toBeTruthy(); - expect(mockFn).toBeCalledTimes(7); + expect(mockFn).toBeCalledTimes(8); expect(value).matchSnapshot(); }); @@ -477,8 +486,8 @@ describe('Test: KForm', () => { const btn = host.querySelector('#validate'); btn?.click(); await tick(); - expect(host.querySelectorAll('.k-form-item-star').length).toBe(8); - expect(host.querySelectorAll('.k-form-item-msg_error').length).toBe(7); + expect(host.querySelectorAll('.k-form-item-star').length).toBe(9); + expect(host.querySelectorAll('.k-form-item-msg_error').length).toBe(8); expect(host.innerHTML.includes('KSelectString custom error')).toBeTruthy(); expect(value).toMatchSnapshot(JSON.stringify(value)); }); @@ -494,7 +503,8 @@ describe('Test: KForm', () => { KCheckbox: [], KSelect: null, KSelectString: '', - slider: 0 + slider: 0, + KAutoComplete: 'vue' }; // @ts-ignore const instance = new KFormResetForm({ @@ -514,8 +524,8 @@ describe('Test: KForm', () => { await tick(); expect(value.data).toMatchObject(initValue); - const inputEl = host.querySelectorAll('.k-input--inner')[0]; - await fireEvent.input(inputEl, { target: { value: 'test' } }); + const inputEl = host.querySelectorAll('.k-input--inner'); + await fireEvent.input(inputEl[0], { target: { value: 'test' } }); const inputNumEl = host.querySelector('.k-input-number--up'); await fireEvent.click(inputNumEl); @@ -546,6 +556,12 @@ describe('Test: KForm', () => { const itemEl2 = host.querySelector('[data-kv-key="Huge"]')?.children[0]; itemEl2?.click(); + await fireEvent.input(inputEl[2], { target: { value: 'el' } }); + await tick(); + await vi.advanceTimersByTimeAsync(300); + const autoOption = host.querySelector('[data-kv-key="element"]')?.children[0]; + autoOption?.click(); + btn?.click(); await tick(); @@ -562,7 +578,8 @@ describe('Test: KForm', () => { id: '1' }, KSelectString: 'Huge', - slider: 0 + slider: 0, + KAutoComplete: 'element' }); const btnReset = host.querySelector('#reset'); @@ -584,7 +601,8 @@ describe('Test: KForm', () => { KRadio: '', KCheckbox: [], KSelect: null, - KSelectString: '' + KSelectString: '', + KAutoComplete: 'vue' }; // @ts-ignore const instance = new KFormGetForm({ @@ -604,8 +622,8 @@ describe('Test: KForm', () => { await tick(); expect(value).toMatchObject(initValue); - const inputEl = host.querySelector('.k-input--inner'); - await fireEvent.input(inputEl, { target: { value: 'test' } }); + const inputEl = host.querySelectorAll('.k-input--inner'); + await fireEvent.input(inputEl[0], { target: { value: 'test' } }); const inputNumEl = host.querySelector('.k-input-number--up'); await fireEvent.click(inputNumEl); @@ -636,6 +654,12 @@ describe('Test: KForm', () => { const itemEl2 = host.querySelector('[data-kv-key="Huge"]')?.children[0]; itemEl2?.click(); + await fireEvent.input(inputEl[2], { target: { value: 'el' } }); + await tick(); + await vi.advanceTimersByTimeAsync(300); + const autoOption = host.querySelector('[data-kv-key="element"]')?.children[0]; + autoOption?.click(); + btn?.click(); await tick(); @@ -651,7 +675,8 @@ describe('Test: KForm', () => { value: '白发', id: '1' }, - KSelectString: 'Huge' + KSelectString: 'Huge', + KAutoComplete: 'element' }); }); @@ -665,7 +690,8 @@ describe('Test: KForm', () => { KRadio: '', KCheckbox: [], KSelect: null, - KSelectString: '' + KSelectString: '', + KAutoComplete: '白雾三语' }; // @ts-ignore const instance = new KFormSetForm({ @@ -691,10 +717,12 @@ describe('Test: KForm', () => { await tick(); expect(host.innerHTML.includes('KInput 3 ~5')).toBeTruthy(); - const input = host.querySelector('.k-input--inner'); - expect(input.value).toBe('KInput change'); + const input = host.querySelectorAll('.k-input--inner'); + expect(input[0].value).toBe('KInput change'); - expect(host.querySelectorAll('.k-input--inner')[1].value === '4').toBeTruthy(); + expect(input[1].value === '4').toBeTruthy(); + + expect(input[1].value === '4').toBeTruthy(); expect(host.querySelectorAll('.k-rate--active-icon').length === 1).toBeTruthy(); @@ -730,7 +758,8 @@ describe('Test: KForm', () => { value: '白发', id: '1' }, - KSelectString: 'Huge' + KSelectString: 'Huge', + KAutoComplete: 'baiwusanyu' }); }); diff --git a/components/Input/src/index.svelte b/components/Input/src/index.svelte index c234aca2..09626a68 100644 --- a/components/Input/src/index.svelte +++ b/components/Input/src/index.svelte @@ -80,7 +80,7 @@ if (disabledInner) return; const { value: inputValue } = e.target as HTMLInputElement; dispatch('input', inputValue, e); - formInstance && formInstance?.updateField(field!, inputValue, !formInstance.__manual_validate); + doUpdateFormField(inputValue); if (!useCompositionInput || !isComposing) { value = inputValue; if (useCompositionInput && !isComposing) { @@ -92,14 +92,17 @@ const onChange = (e: Event) => { if (disabledInner) return; dispatch('change', e); - formInstance && - formInstance?.updateField( - field!, - (e?.target as HTMLInputElement)?.value, - !formInstance.__manual_validate - ); + doUpdateFormField((e?.target as HTMLInputElement)?.value); }; + /** + * @internal + * @param value + */ + export function doUpdateFormField(value) { + formInstance && formInstance?.updateField(field!, value, !formInstance.__manual_validate); + } + const onClear = () => { if (disabledInner) return; value = ''; diff --git a/components/Input/src/types.d.ts b/components/Input/src/types.d.ts index fc80f3d2..6d9ed35e 100644 --- a/components/Input/src/types.d.ts +++ b/components/Input/src/types.d.ts @@ -14,7 +14,6 @@ export type KInputProps = { isError: boolean; center: boolean; search: boolean; - errorMsg: string; cls: ClassValue; attrs: Record; useCompositionInput: boolean; diff --git a/components/Popover/src/index.svelte b/components/Popover/src/index.svelte index e7760d1e..21781f02 100644 --- a/components/Popover/src/index.svelte +++ b/components/Popover/src/index.svelte @@ -115,12 +115,21 @@ arrowRef && arrowRef.removeAttribute(`data-popper-arrow-right`); arrowRef && arrowRef.setAttribute(`data-popper-arrow-${curPlacement}`, ''); } + + let popoverContainerRef: HTMLDivElement | null = null; function clickOutside(node: HTMLElement) { function handleClickOutside(e: MouseEvent) { const target = e.target as HTMLElement; const container = node; if (target && container && !container.contains(target)) { - doUpdateShow(false); + if (popoverContainerRef) { + const triggerEl = popoverContainerRef.querySelector('[slot="triggerEl"]'); + if (!triggerEl || !triggerEl.contains(target)) { + doUpdateShow(false); + } + } else { + doUpdateShow(false); + } } } trigger === 'click' && window.addEventListener('click', handleClickOutside); @@ -153,6 +162,7 @@