@furystack/shades-common-components 10.0.35 → 11.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +66 -0
- package/esm/components/animations.spec.d.ts +2 -0
- package/esm/components/animations.spec.d.ts.map +1 -0
- package/esm/components/animations.spec.js +201 -0
- package/esm/components/animations.spec.js.map +1 -0
- package/esm/components/app-bar-link.js +21 -20
- package/esm/components/app-bar-link.js.map +1 -1
- package/esm/components/app-bar-link.spec.d.ts +2 -0
- package/esm/components/app-bar-link.spec.d.ts.map +1 -0
- package/esm/components/app-bar-link.spec.js +252 -0
- package/esm/components/app-bar-link.spec.js.map +1 -0
- package/esm/components/app-bar.js +21 -21
- package/esm/components/app-bar.js.map +1 -1
- package/esm/components/app-bar.spec.d.ts +2 -0
- package/esm/components/app-bar.spec.d.ts.map +1 -0
- package/esm/components/app-bar.spec.js +117 -0
- package/esm/components/app-bar.spec.js.map +1 -0
- package/esm/components/avatar.d.ts.map +1 -1
- package/esm/components/avatar.js +15 -19
- package/esm/components/avatar.js.map +1 -1
- package/esm/components/avatar.spec.d.ts +2 -0
- package/esm/components/avatar.spec.d.ts.map +1 -0
- package/esm/components/avatar.spec.js +114 -0
- package/esm/components/avatar.spec.js.map +1 -0
- package/esm/components/button.d.ts.map +1 -1
- package/esm/components/button.js +145 -156
- package/esm/components/button.js.map +1 -1
- package/esm/components/button.spec.d.ts +2 -0
- package/esm/components/button.spec.d.ts.map +1 -0
- package/esm/components/button.spec.js +155 -0
- package/esm/components/button.spec.js.map +1 -0
- package/esm/components/command-palette/command-palette-input.d.ts.map +1 -1
- package/esm/components/command-palette/command-palette-input.js +18 -16
- package/esm/components/command-palette/command-palette-input.js.map +1 -1
- package/esm/components/command-palette/command-palette-input.spec.d.ts +2 -0
- package/esm/components/command-palette/command-palette-input.spec.d.ts.map +1 -0
- package/esm/components/command-palette/command-palette-input.spec.js +233 -0
- package/esm/components/command-palette/command-palette-input.spec.js.map +1 -0
- package/esm/components/command-palette/command-palette-manager.spec.d.ts +2 -0
- package/esm/components/command-palette/command-palette-manager.spec.d.ts.map +1 -0
- package/esm/components/command-palette/command-palette-manager.spec.js +362 -0
- package/esm/components/command-palette/command-palette-manager.spec.js.map +1 -0
- package/esm/components/command-palette/command-palette-suggestion-list.d.ts.map +1 -1
- package/esm/components/command-palette/command-palette-suggestion-list.js +42 -46
- package/esm/components/command-palette/command-palette-suggestion-list.js.map +1 -1
- package/esm/components/command-palette/command-palette-suggestion-list.spec.d.ts +2 -0
- package/esm/components/command-palette/command-palette-suggestion-list.spec.d.ts.map +1 -0
- package/esm/components/command-palette/command-palette-suggestion-list.spec.js +376 -0
- package/esm/components/command-palette/command-palette-suggestion-list.spec.js.map +1 -0
- package/esm/components/command-palette/index.d.ts.map +1 -1
- package/esm/components/command-palette/index.js +100 -110
- package/esm/components/command-palette/index.js.map +1 -1
- package/esm/components/command-palette/index.spec.d.ts +2 -0
- package/esm/components/command-palette/index.spec.d.ts.map +1 -0
- package/esm/components/command-palette/index.spec.js +509 -0
- package/esm/components/command-palette/index.spec.js.map +1 -0
- package/esm/components/data-grid/body.js +1 -1
- package/esm/components/data-grid/body.js.map +1 -1
- package/esm/components/data-grid/body.spec.d.ts +2 -0
- package/esm/components/data-grid/body.spec.d.ts.map +1 -0
- package/esm/components/data-grid/body.spec.js +228 -0
- package/esm/components/data-grid/body.spec.js.map +1 -0
- package/esm/components/data-grid/data-grid-row.d.ts.map +1 -1
- package/esm/components/data-grid/data-grid-row.js +49 -73
- package/esm/components/data-grid/data-grid-row.js.map +1 -1
- package/esm/components/data-grid/data-grid-row.spec.d.ts +2 -0
- package/esm/components/data-grid/data-grid-row.spec.d.ts.map +1 -0
- package/esm/components/data-grid/data-grid-row.spec.js +296 -0
- package/esm/components/data-grid/data-grid-row.spec.js.map +1 -0
- package/esm/components/data-grid/data-grid.d.ts.map +1 -1
- package/esm/components/data-grid/data-grid.js +35 -28
- package/esm/components/data-grid/data-grid.js.map +1 -1
- package/esm/components/data-grid/data-grid.spec.d.ts +2 -0
- package/esm/components/data-grid/data-grid.spec.d.ts.map +1 -0
- package/esm/components/data-grid/data-grid.spec.js +544 -0
- package/esm/components/data-grid/data-grid.spec.js.map +1 -0
- package/esm/components/data-grid/footer.js +21 -15
- package/esm/components/data-grid/footer.js.map +1 -1
- package/esm/components/data-grid/footer.spec.d.ts +2 -0
- package/esm/components/data-grid/footer.spec.d.ts.map +1 -0
- package/esm/components/data-grid/footer.spec.js +264 -0
- package/esm/components/data-grid/footer.spec.js.map +1 -0
- package/esm/components/data-grid/header.d.ts.map +1 -1
- package/esm/components/data-grid/header.js +55 -33
- package/esm/components/data-grid/header.js.map +1 -1
- package/esm/components/data-grid/header.spec.d.ts +2 -0
- package/esm/components/data-grid/header.spec.d.ts.map +1 -0
- package/esm/components/data-grid/header.spec.js +421 -0
- package/esm/components/data-grid/header.spec.js.map +1 -0
- package/esm/components/data-grid/selection-cell.d.ts.map +1 -1
- package/esm/components/data-grid/selection-cell.js +13 -6
- package/esm/components/data-grid/selection-cell.js.map +1 -1
- package/esm/components/data-grid/selection-cell.spec.d.ts +2 -0
- package/esm/components/data-grid/selection-cell.spec.d.ts.map +1 -0
- package/esm/components/data-grid/selection-cell.spec.js +118 -0
- package/esm/components/data-grid/selection-cell.spec.js.map +1 -0
- package/esm/components/fab.d.ts.map +1 -1
- package/esm/components/fab.js +10 -1
- package/esm/components/fab.js.map +1 -1
- package/esm/components/fab.spec.d.ts +2 -0
- package/esm/components/fab.spec.d.ts.map +1 -0
- package/esm/components/fab.spec.js +95 -0
- package/esm/components/fab.spec.js.map +1 -0
- package/esm/components/form.spec.d.ts +2 -0
- package/esm/components/form.spec.d.ts.map +1 -0
- package/esm/components/form.spec.js +314 -0
- package/esm/components/form.spec.js.map +1 -0
- package/esm/components/grid.d.ts.map +1 -1
- package/esm/components/grid.js +40 -37
- package/esm/components/grid.js.map +1 -1
- package/esm/components/grid.spec.d.ts +2 -0
- package/esm/components/grid.spec.d.ts.map +1 -0
- package/esm/components/grid.spec.js +316 -0
- package/esm/components/grid.spec.js.map +1 -0
- package/esm/components/inputs/autocomplete.spec.d.ts +2 -0
- package/esm/components/inputs/autocomplete.spec.d.ts.map +1 -0
- package/esm/components/inputs/autocomplete.spec.js +194 -0
- package/esm/components/inputs/autocomplete.spec.js.map +1 -0
- package/esm/components/inputs/input.d.ts.map +1 -1
- package/esm/components/inputs/input.js +141 -109
- package/esm/components/inputs/input.js.map +1 -1
- package/esm/components/inputs/input.spec.d.ts +2 -0
- package/esm/components/inputs/input.spec.d.ts.map +1 -0
- package/esm/components/inputs/input.spec.js +577 -0
- package/esm/components/inputs/input.spec.js.map +1 -0
- package/esm/components/inputs/text-area.d.ts.map +1 -1
- package/esm/components/inputs/text-area.js +54 -58
- package/esm/components/inputs/text-area.js.map +1 -1
- package/esm/components/inputs/text-area.spec.d.ts +2 -0
- package/esm/components/inputs/text-area.spec.d.ts.map +1 -0
- package/esm/components/inputs/text-area.spec.js +214 -0
- package/esm/components/inputs/text-area.spec.js.map +1 -0
- package/esm/components/loader.js +1 -1
- package/esm/components/loader.js.map +1 -1
- package/esm/components/loader.spec.d.ts +2 -0
- package/esm/components/loader.spec.d.ts.map +1 -0
- package/esm/components/loader.spec.js +251 -0
- package/esm/components/loader.spec.js.map +1 -0
- package/esm/components/modal.d.ts.map +1 -1
- package/esm/components/modal.js +11 -9
- package/esm/components/modal.js.map +1 -1
- package/esm/components/modal.spec.d.ts +2 -0
- package/esm/components/modal.spec.d.ts.map +1 -0
- package/esm/components/modal.spec.js +227 -0
- package/esm/components/modal.spec.js.map +1 -0
- package/esm/components/noty-list.d.ts.map +1 -1
- package/esm/components/noty-list.js +39 -40
- package/esm/components/noty-list.js.map +1 -1
- package/esm/components/noty-list.spec.d.ts +2 -0
- package/esm/components/noty-list.spec.d.ts.map +1 -0
- package/esm/components/noty-list.spec.js +486 -0
- package/esm/components/noty-list.spec.js.map +1 -0
- package/esm/components/paper.d.ts.map +1 -1
- package/esm/components/paper.js +15 -12
- package/esm/components/paper.js.map +1 -1
- package/esm/components/paper.spec.d.ts +2 -0
- package/esm/components/paper.spec.d.ts.map +1 -0
- package/esm/components/paper.spec.js +63 -0
- package/esm/components/paper.spec.js.map +1 -0
- package/esm/components/skeleton.js +1 -1
- package/esm/components/skeleton.js.map +1 -1
- package/esm/components/skeleton.spec.d.ts +2 -0
- package/esm/components/skeleton.spec.d.ts.map +1 -0
- package/esm/components/skeleton.spec.js +159 -0
- package/esm/components/skeleton.spec.js.map +1 -0
- package/esm/components/styles.spec.d.ts +2 -0
- package/esm/components/styles.spec.d.ts.map +1 -0
- package/esm/components/styles.spec.js +56 -0
- package/esm/components/styles.spec.js.map +1 -0
- package/esm/components/suggest/index.d.ts.map +1 -1
- package/esm/components/suggest/index.js +74 -83
- package/esm/components/suggest/index.js.map +1 -1
- package/esm/components/suggest/index.spec.d.ts +2 -0
- package/esm/components/suggest/index.spec.d.ts.map +1 -0
- package/esm/components/suggest/index.spec.js +515 -0
- package/esm/components/suggest/index.spec.js.map +1 -0
- package/esm/components/suggest/suggest-input.d.ts.map +1 -1
- package/esm/components/suggest/suggest-input.js +16 -17
- package/esm/components/suggest/suggest-input.js.map +1 -1
- package/esm/components/suggest/suggest-input.spec.d.ts +2 -0
- package/esm/components/suggest/suggest-input.spec.d.ts.map +1 -0
- package/esm/components/suggest/suggest-input.spec.js +138 -0
- package/esm/components/suggest/suggest-input.spec.js.map +1 -0
- package/esm/components/suggest/suggest-manager.spec.d.ts +2 -0
- package/esm/components/suggest/suggest-manager.spec.d.ts.map +1 -0
- package/esm/components/suggest/suggest-manager.spec.js +308 -0
- package/esm/components/suggest/suggest-manager.spec.js.map +1 -0
- package/esm/components/suggest/suggestion-list.d.ts.map +1 -1
- package/esm/components/suggest/suggestion-list.js +43 -48
- package/esm/components/suggest/suggestion-list.js.map +1 -1
- package/esm/components/suggest/suggestion-list.spec.d.ts +2 -0
- package/esm/components/suggest/suggestion-list.spec.d.ts.map +1 -0
- package/esm/components/suggest/suggestion-list.spec.js +252 -0
- package/esm/components/suggest/suggestion-list.spec.js.map +1 -0
- package/esm/components/tabs.d.ts.map +1 -1
- package/esm/components/tabs.js +32 -18
- package/esm/components/tabs.js.map +1 -1
- package/esm/components/tabs.spec.d.ts +2 -0
- package/esm/components/tabs.spec.d.ts.map +1 -0
- package/esm/components/tabs.spec.js +187 -0
- package/esm/components/tabs.spec.js.map +1 -0
- package/esm/components/wizard/index.d.ts.map +1 -1
- package/esm/components/wizard/index.js +10 -7
- package/esm/components/wizard/index.js.map +1 -1
- package/esm/components/wizard/index.spec.d.ts +2 -0
- package/esm/components/wizard/index.spec.d.ts.map +1 -0
- package/esm/components/wizard/index.spec.js +171 -0
- package/esm/components/wizard/index.spec.js.map +1 -0
- package/esm/services/collection-service.spec.js +391 -2
- package/esm/services/collection-service.spec.js.map +1 -1
- package/esm/services/css-variable-theme.d.ts.map +1 -1
- package/esm/services/css-variable-theme.js +21 -1
- package/esm/services/css-variable-theme.js.map +1 -1
- package/esm/services/css-variable-theme.spec.d.ts +2 -0
- package/esm/services/css-variable-theme.spec.d.ts.map +1 -0
- package/esm/services/css-variable-theme.spec.js +169 -0
- package/esm/services/css-variable-theme.spec.js.map +1 -0
- package/esm/services/default-palette.d.ts +4 -0
- package/esm/services/default-palette.d.ts.map +1 -1
- package/esm/services/default-palette.js +22 -0
- package/esm/services/default-palette.js.map +1 -1
- package/esm/services/theme-provider-service.d.ts +59 -1
- package/esm/services/theme-provider-service.d.ts.map +1 -1
- package/esm/services/theme-provider-service.js.map +1 -1
- package/esm/services/theme-provider-service.spec.d.ts +2 -0
- package/esm/services/theme-provider-service.spec.d.ts.map +1 -0
- package/esm/services/theme-provider-service.spec.js +166 -0
- package/esm/services/theme-provider-service.spec.js.map +1 -0
- package/package.json +2 -2
- package/src/components/animations.spec.ts +299 -0
- package/src/components/app-bar-link.spec.tsx +341 -0
- package/src/components/app-bar-link.tsx +21 -21
- package/src/components/app-bar.spec.tsx +142 -0
- package/src/components/app-bar.tsx +22 -22
- package/src/components/avatar.spec.tsx +146 -0
- package/src/components/avatar.tsx +17 -20
- package/src/components/button.spec.tsx +193 -0
- package/src/components/button.tsx +162 -197
- package/src/components/command-palette/command-palette-input.spec.tsx +320 -0
- package/src/components/command-palette/command-palette-input.tsx +19 -22
- package/src/components/command-palette/command-palette-manager.spec.ts +470 -0
- package/src/components/command-palette/command-palette-suggestion-list.spec.tsx +499 -0
- package/src/components/command-palette/command-palette-suggestion-list.tsx +42 -46
- package/src/components/command-palette/index.spec.tsx +684 -0
- package/src/components/command-palette/index.tsx +107 -136
- package/src/components/data-grid/body.spec.tsx +340 -0
- package/src/components/data-grid/body.tsx +1 -1
- package/src/components/data-grid/data-grid-row.spec.tsx +382 -0
- package/src/components/data-grid/data-grid-row.tsx +50 -82
- package/src/components/data-grid/data-grid.spec.tsx +939 -0
- package/src/components/data-grid/data-grid.tsx +38 -35
- package/src/components/data-grid/footer.spec.tsx +344 -0
- package/src/components/data-grid/footer.tsx +19 -19
- package/src/components/data-grid/header.spec.tsx +563 -0
- package/src/components/data-grid/header.tsx +53 -44
- package/src/components/data-grid/selection-cell.spec.tsx +150 -0
- package/src/components/data-grid/selection-cell.tsx +12 -6
- package/src/components/fab.spec.tsx +108 -0
- package/src/components/fab.tsx +10 -1
- package/src/components/form.spec.tsx +481 -0
- package/src/components/grid.spec.tsx +334 -0
- package/src/components/grid.tsx +57 -63
- package/src/components/inputs/autocomplete.spec.tsx +258 -0
- package/src/components/inputs/input.spec.tsx +808 -0
- package/src/components/inputs/input.tsx +153 -139
- package/src/components/inputs/text-area.spec.tsx +285 -0
- package/src/components/inputs/text-area.tsx +53 -79
- package/src/components/loader.spec.tsx +346 -0
- package/src/components/loader.tsx +1 -1
- package/src/components/modal.spec.tsx +304 -0
- package/src/components/modal.tsx +11 -9
- package/src/components/noty-list.spec.tsx +631 -0
- package/src/components/noty-list.tsx +39 -50
- package/src/components/paper.spec.tsx +72 -0
- package/src/components/paper.tsx +15 -13
- package/src/components/skeleton.spec.tsx +219 -0
- package/src/components/skeleton.tsx +1 -1
- package/src/components/styles.spec.ts +70 -0
- package/src/components/suggest/index.spec.tsx +861 -0
- package/src/components/suggest/index.tsx +74 -101
- package/src/components/suggest/suggest-input.spec.tsx +181 -0
- package/src/components/suggest/suggest-input.tsx +16 -24
- package/src/components/suggest/suggest-manager.spec.ts +409 -0
- package/src/components/suggest/suggestion-list.spec.tsx +334 -0
- package/src/components/suggest/suggestion-list.tsx +43 -48
- package/src/components/tabs.spec.tsx +236 -0
- package/src/components/tabs.tsx +33 -21
- package/src/components/wizard/index.spec.tsx +224 -0
- package/src/components/wizard/index.tsx +10 -9
- package/src/services/collection-service.spec.ts +492 -3
- package/src/services/css-variable-theme.spec.ts +204 -0
- package/src/services/css-variable-theme.ts +21 -1
- package/src/services/default-palette.ts +22 -0
- package/src/services/theme-provider-service.spec.ts +195 -0
- package/src/services/theme-provider-service.ts +60 -2
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import { Injector } from '@furystack/inject'
|
|
2
|
+
import { using, usingAsync } from '@furystack/utils'
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
4
|
+
import type { SuggestionResult } from './suggestion-result.js'
|
|
5
|
+
import { SuggestManager } from './suggest-manager.js'
|
|
6
|
+
|
|
7
|
+
type TestEntry = { id: number; name: string }
|
|
8
|
+
|
|
9
|
+
const createTestEntries = (): TestEntry[] => [
|
|
10
|
+
{ id: 1, name: 'alpha' },
|
|
11
|
+
{ id: 2, name: 'beta' },
|
|
12
|
+
{ id: 3, name: 'gamma' },
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
const createSuggestionResult = (entry: TestEntry): SuggestionResult => ({
|
|
16
|
+
element: { tagName: 'div', textContent: entry.name } as unknown as JSX.Element,
|
|
17
|
+
score: entry.id,
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
describe('SuggestManager', () => {
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
vi.useFakeTimers()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
vi.useRealTimers()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
describe('Construction and disposal', () => {
|
|
30
|
+
it('Should be constructed with getEntries and getSuggestionEntry functions', () => {
|
|
31
|
+
const getEntries = vi.fn().mockResolvedValue([])
|
|
32
|
+
const getSuggestionEntry = vi.fn()
|
|
33
|
+
|
|
34
|
+
using(new SuggestManager(getEntries, getSuggestionEntry), (manager) => {
|
|
35
|
+
expect(manager.isOpened.getValue()).toBe(false)
|
|
36
|
+
expect(manager.isLoading.getValue()).toBe(false)
|
|
37
|
+
expect(manager.term.getValue()).toBe('')
|
|
38
|
+
expect(manager.selectedIndex.getValue()).toBe(0)
|
|
39
|
+
expect(manager.currentSuggestions.getValue()).toEqual([])
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('Should register keyboard and click listeners on construction', () => {
|
|
44
|
+
const addEventListenerSpy = vi.spyOn(window, 'addEventListener')
|
|
45
|
+
const getEntries = vi.fn().mockResolvedValue([])
|
|
46
|
+
const getSuggestionEntry = vi.fn()
|
|
47
|
+
|
|
48
|
+
using(new SuggestManager(getEntries, getSuggestionEntry), () => {
|
|
49
|
+
expect(addEventListenerSpy).toHaveBeenCalledWith('keyup', expect.any(Function), true)
|
|
50
|
+
expect(addEventListenerSpy).toHaveBeenCalledWith('click', expect.any(Function), true)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
addEventListenerSpy.mockRestore()
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('Should dispose all observables and remove event listeners', () => {
|
|
57
|
+
const removeEventListenerSpy = vi.spyOn(window, 'removeEventListener')
|
|
58
|
+
const getEntries = vi.fn().mockResolvedValue([])
|
|
59
|
+
const getSuggestionEntry = vi.fn()
|
|
60
|
+
|
|
61
|
+
const manager = new SuggestManager(getEntries, getSuggestionEntry)
|
|
62
|
+
|
|
63
|
+
const isOpenedDisposeSpy = vi.spyOn(manager.isOpened, Symbol.dispose)
|
|
64
|
+
const isLoadingDisposeSpy = vi.spyOn(manager.isLoading, Symbol.dispose)
|
|
65
|
+
const termDisposeSpy = vi.spyOn(manager.term, Symbol.dispose)
|
|
66
|
+
const selectedIndexDisposeSpy = vi.spyOn(manager.selectedIndex, Symbol.dispose)
|
|
67
|
+
const currentSuggestionsDisposeSpy = vi.spyOn(manager.currentSuggestions, Symbol.dispose)
|
|
68
|
+
|
|
69
|
+
manager[Symbol.dispose]()
|
|
70
|
+
|
|
71
|
+
expect(isOpenedDisposeSpy).toHaveBeenCalled()
|
|
72
|
+
expect(isLoadingDisposeSpy).toHaveBeenCalled()
|
|
73
|
+
expect(termDisposeSpy).toHaveBeenCalled()
|
|
74
|
+
expect(selectedIndexDisposeSpy).toHaveBeenCalled()
|
|
75
|
+
expect(currentSuggestionsDisposeSpy).toHaveBeenCalled()
|
|
76
|
+
|
|
77
|
+
expect(removeEventListenerSpy).toHaveBeenCalledWith('keyup', manager.keyPressListener, true)
|
|
78
|
+
expect(removeEventListenerSpy).toHaveBeenCalledWith('click', manager.clickOutsideListener, true)
|
|
79
|
+
|
|
80
|
+
removeEventListenerSpy.mockRestore()
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
describe('Keyboard listener', () => {
|
|
85
|
+
it('Should close suggestions when Escape is pressed', () => {
|
|
86
|
+
const getEntries = vi.fn().mockResolvedValue([])
|
|
87
|
+
const getSuggestionEntry = vi.fn()
|
|
88
|
+
|
|
89
|
+
using(new SuggestManager(getEntries, getSuggestionEntry), (manager) => {
|
|
90
|
+
manager.isOpened.setValue(true)
|
|
91
|
+
expect(manager.isOpened.getValue()).toBe(true)
|
|
92
|
+
|
|
93
|
+
manager.keyPressListener({ key: 'Escape' } as KeyboardEvent)
|
|
94
|
+
|
|
95
|
+
expect(manager.isOpened.getValue()).toBe(false)
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('Should not close suggestions for other keys', () => {
|
|
100
|
+
const getEntries = vi.fn().mockResolvedValue([])
|
|
101
|
+
const getSuggestionEntry = vi.fn()
|
|
102
|
+
|
|
103
|
+
using(new SuggestManager(getEntries, getSuggestionEntry), (manager) => {
|
|
104
|
+
manager.isOpened.setValue(true)
|
|
105
|
+
|
|
106
|
+
manager.keyPressListener({ key: 'Enter' } as KeyboardEvent)
|
|
107
|
+
expect(manager.isOpened.getValue()).toBe(true)
|
|
108
|
+
|
|
109
|
+
manager.keyPressListener({ key: 'ArrowDown' } as KeyboardEvent)
|
|
110
|
+
expect(manager.isOpened.getValue()).toBe(true)
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
describe('Click-outside listener', () => {
|
|
116
|
+
it('Should close suggestions when clicking outside the element', () => {
|
|
117
|
+
const getEntries = vi.fn().mockResolvedValue([])
|
|
118
|
+
const getSuggestionEntry = vi.fn()
|
|
119
|
+
|
|
120
|
+
using(new SuggestManager(getEntries, getSuggestionEntry), (manager) => {
|
|
121
|
+
const element = document.createElement('div')
|
|
122
|
+
element.setAttribute('data-testid', 'suggest')
|
|
123
|
+
document.body.appendChild(element)
|
|
124
|
+
|
|
125
|
+
manager.element = element
|
|
126
|
+
manager.isOpened.setValue(true)
|
|
127
|
+
|
|
128
|
+
const outsideElement = document.createElement('span')
|
|
129
|
+
document.body.appendChild(outsideElement)
|
|
130
|
+
|
|
131
|
+
manager.clickOutsideListener({ target: outsideElement } as unknown as MouseEvent)
|
|
132
|
+
|
|
133
|
+
expect(manager.isOpened.getValue()).toBe(false)
|
|
134
|
+
|
|
135
|
+
document.body.removeChild(element)
|
|
136
|
+
document.body.removeChild(outsideElement)
|
|
137
|
+
})
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('Should not close suggestions when clicking inside the element', () => {
|
|
141
|
+
const getEntries = vi.fn().mockResolvedValue([])
|
|
142
|
+
const getSuggestionEntry = vi.fn()
|
|
143
|
+
|
|
144
|
+
using(new SuggestManager(getEntries, getSuggestionEntry), (manager) => {
|
|
145
|
+
const element = document.createElement('div')
|
|
146
|
+
const childElement = document.createElement('span')
|
|
147
|
+
element.appendChild(childElement)
|
|
148
|
+
document.body.appendChild(element)
|
|
149
|
+
|
|
150
|
+
manager.element = element
|
|
151
|
+
manager.isOpened.setValue(true)
|
|
152
|
+
|
|
153
|
+
manager.clickOutsideListener({ target: childElement } as unknown as MouseEvent)
|
|
154
|
+
|
|
155
|
+
expect(manager.isOpened.getValue()).toBe(true)
|
|
156
|
+
|
|
157
|
+
document.body.removeChild(element)
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
it('Should not close when element is not set', () => {
|
|
162
|
+
const getEntries = vi.fn().mockResolvedValue([])
|
|
163
|
+
const getSuggestionEntry = vi.fn()
|
|
164
|
+
|
|
165
|
+
using(new SuggestManager(getEntries, getSuggestionEntry), (manager) => {
|
|
166
|
+
manager.isOpened.setValue(true)
|
|
167
|
+
|
|
168
|
+
const outsideElement = document.createElement('span')
|
|
169
|
+
manager.clickOutsideListener({ target: outsideElement } as unknown as MouseEvent)
|
|
170
|
+
|
|
171
|
+
expect(manager.isOpened.getValue()).toBe(true)
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('Should not close when already closed', () => {
|
|
176
|
+
const getEntries = vi.fn().mockResolvedValue([])
|
|
177
|
+
const getSuggestionEntry = vi.fn()
|
|
178
|
+
|
|
179
|
+
using(new SuggestManager(getEntries, getSuggestionEntry), (manager) => {
|
|
180
|
+
const element = document.createElement('div')
|
|
181
|
+
document.body.appendChild(element)
|
|
182
|
+
manager.element = element
|
|
183
|
+
manager.isOpened.setValue(false)
|
|
184
|
+
|
|
185
|
+
const outsideElement = document.createElement('span')
|
|
186
|
+
document.body.appendChild(outsideElement)
|
|
187
|
+
|
|
188
|
+
manager.clickOutsideListener({ target: outsideElement } as unknown as MouseEvent)
|
|
189
|
+
|
|
190
|
+
expect(manager.isOpened.getValue()).toBe(false)
|
|
191
|
+
|
|
192
|
+
document.body.removeChild(element)
|
|
193
|
+
document.body.removeChild(outsideElement)
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
describe('getSuggestion (debounced search)', () => {
|
|
199
|
+
it('Should load suggestions after debounce delay', async () => {
|
|
200
|
+
const testEntries = createTestEntries()
|
|
201
|
+
const getEntries = vi.fn().mockResolvedValue(testEntries)
|
|
202
|
+
const getSuggestionEntry = vi.fn().mockImplementation(createSuggestionResult)
|
|
203
|
+
const injector = new Injector()
|
|
204
|
+
|
|
205
|
+
await usingAsync(new SuggestManager(getEntries, getSuggestionEntry), async (manager) => {
|
|
206
|
+
void manager.getSuggestion({ injector, term: 'test' })
|
|
207
|
+
|
|
208
|
+
expect(getEntries).not.toHaveBeenCalled()
|
|
209
|
+
expect(manager.isLoading.getValue()).toBe(false)
|
|
210
|
+
|
|
211
|
+
await vi.advanceTimersByTimeAsync(250)
|
|
212
|
+
|
|
213
|
+
expect(getEntries).toHaveBeenCalledWith('test')
|
|
214
|
+
expect(manager.isOpened.getValue()).toBe(true)
|
|
215
|
+
expect(manager.currentSuggestions.getValue()).toHaveLength(3)
|
|
216
|
+
})
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it('Should set isLoading while fetching', async () => {
|
|
220
|
+
const testEntries = createTestEntries()
|
|
221
|
+
let resolveEntries: (value: TestEntry[]) => void
|
|
222
|
+
const entriesPromise = new Promise<TestEntry[]>((resolve) => {
|
|
223
|
+
resolveEntries = resolve
|
|
224
|
+
})
|
|
225
|
+
const getEntries = vi.fn().mockReturnValue(entriesPromise)
|
|
226
|
+
const getSuggestionEntry = vi.fn().mockImplementation(createSuggestionResult)
|
|
227
|
+
const injector = new Injector()
|
|
228
|
+
|
|
229
|
+
await usingAsync(new SuggestManager(getEntries, getSuggestionEntry), async (manager) => {
|
|
230
|
+
void manager.getSuggestion({ injector, term: 'test' })
|
|
231
|
+
await vi.advanceTimersByTimeAsync(250)
|
|
232
|
+
|
|
233
|
+
expect(manager.isLoading.getValue()).toBe(true)
|
|
234
|
+
|
|
235
|
+
resolveEntries!(testEntries)
|
|
236
|
+
await vi.advanceTimersByTimeAsync(0)
|
|
237
|
+
|
|
238
|
+
expect(manager.isLoading.getValue()).toBe(false)
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
it('Should debounce multiple rapid calls', async () => {
|
|
243
|
+
const testEntries = createTestEntries()
|
|
244
|
+
const getEntries = vi.fn().mockResolvedValue(testEntries)
|
|
245
|
+
const getSuggestionEntry = vi.fn().mockImplementation(createSuggestionResult)
|
|
246
|
+
const injector = new Injector()
|
|
247
|
+
|
|
248
|
+
await usingAsync(new SuggestManager(getEntries, getSuggestionEntry), async (manager) => {
|
|
249
|
+
void manager.getSuggestion({ injector, term: 'a' })
|
|
250
|
+
await vi.advanceTimersByTimeAsync(100)
|
|
251
|
+
|
|
252
|
+
void manager.getSuggestion({ injector, term: 'ab' })
|
|
253
|
+
await vi.advanceTimersByTimeAsync(100)
|
|
254
|
+
|
|
255
|
+
void manager.getSuggestion({ injector, term: 'abc' })
|
|
256
|
+
await vi.advanceTimersByTimeAsync(250)
|
|
257
|
+
|
|
258
|
+
expect(getEntries).toHaveBeenCalledTimes(1)
|
|
259
|
+
expect(getEntries).toHaveBeenCalledWith('abc')
|
|
260
|
+
})
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
it('Should not fetch again if term is unchanged', async () => {
|
|
264
|
+
const testEntries = createTestEntries()
|
|
265
|
+
const getEntries = vi.fn().mockResolvedValue(testEntries)
|
|
266
|
+
const getSuggestionEntry = vi.fn().mockImplementation(createSuggestionResult)
|
|
267
|
+
const injector = new Injector()
|
|
268
|
+
|
|
269
|
+
await usingAsync(new SuggestManager(getEntries, getSuggestionEntry), async (manager) => {
|
|
270
|
+
void manager.getSuggestion({ injector, term: 'test' })
|
|
271
|
+
await vi.advanceTimersByTimeAsync(250)
|
|
272
|
+
|
|
273
|
+
expect(getEntries).toHaveBeenCalledTimes(1)
|
|
274
|
+
|
|
275
|
+
void manager.getSuggestion({ injector, term: 'test' })
|
|
276
|
+
await vi.advanceTimersByTimeAsync(250)
|
|
277
|
+
|
|
278
|
+
expect(getEntries).toHaveBeenCalledTimes(1)
|
|
279
|
+
})
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
it('Should map entries to suggestions with getSuggestionEntry', async () => {
|
|
283
|
+
const testEntries = createTestEntries()
|
|
284
|
+
const getEntries = vi.fn().mockResolvedValue(testEntries)
|
|
285
|
+
const getSuggestionEntry = vi.fn().mockImplementation(createSuggestionResult)
|
|
286
|
+
const injector = new Injector()
|
|
287
|
+
|
|
288
|
+
await usingAsync(new SuggestManager(getEntries, getSuggestionEntry), async (manager) => {
|
|
289
|
+
void manager.getSuggestion({ injector, term: 'test' })
|
|
290
|
+
await vi.advanceTimersByTimeAsync(250)
|
|
291
|
+
|
|
292
|
+
expect(getSuggestionEntry).toHaveBeenCalledTimes(3)
|
|
293
|
+
expect(getSuggestionEntry).toHaveBeenCalledWith(testEntries[0])
|
|
294
|
+
expect(getSuggestionEntry).toHaveBeenCalledWith(testEntries[1])
|
|
295
|
+
expect(getSuggestionEntry).toHaveBeenCalledWith(testEntries[2])
|
|
296
|
+
|
|
297
|
+
const suggestions = manager.currentSuggestions.getValue()
|
|
298
|
+
expect(suggestions[0].entry).toBe(testEntries[0])
|
|
299
|
+
expect(suggestions[0].suggestion.score).toBe(1)
|
|
300
|
+
})
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
it('Should preserve selected index when suggestion exists in new results', async () => {
|
|
304
|
+
const testEntries = createTestEntries()
|
|
305
|
+
const getEntries = vi.fn().mockResolvedValue(testEntries)
|
|
306
|
+
const getSuggestionEntry = vi.fn().mockImplementation(createSuggestionResult)
|
|
307
|
+
const injector = new Injector()
|
|
308
|
+
|
|
309
|
+
await usingAsync(new SuggestManager(getEntries, getSuggestionEntry), async (manager) => {
|
|
310
|
+
void manager.getSuggestion({ injector, term: 'test' })
|
|
311
|
+
await vi.advanceTimersByTimeAsync(250)
|
|
312
|
+
|
|
313
|
+
manager.selectedIndex.setValue(1)
|
|
314
|
+
|
|
315
|
+
const newEntries = [testEntries[1], testEntries[2]]
|
|
316
|
+
getEntries.mockResolvedValue(newEntries)
|
|
317
|
+
getSuggestionEntry.mockImplementation(createSuggestionResult)
|
|
318
|
+
|
|
319
|
+
void manager.getSuggestion({ injector, term: 'test2' })
|
|
320
|
+
await vi.advanceTimersByTimeAsync(250)
|
|
321
|
+
|
|
322
|
+
expect(manager.selectedIndex.getValue()).toBe(0)
|
|
323
|
+
})
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
it('Should reset selected index to 0 when selection not found in new results', async () => {
|
|
327
|
+
const testEntries = createTestEntries()
|
|
328
|
+
const getEntries = vi.fn().mockResolvedValue(testEntries)
|
|
329
|
+
const getSuggestionEntry = vi.fn().mockImplementation(createSuggestionResult)
|
|
330
|
+
const injector = new Injector()
|
|
331
|
+
|
|
332
|
+
await usingAsync(new SuggestManager(getEntries, getSuggestionEntry), async (manager) => {
|
|
333
|
+
void manager.getSuggestion({ injector, term: 'test' })
|
|
334
|
+
await vi.advanceTimersByTimeAsync(250)
|
|
335
|
+
|
|
336
|
+
manager.selectedIndex.setValue(2)
|
|
337
|
+
|
|
338
|
+
const newEntries = [{ id: 4, name: 'delta' }]
|
|
339
|
+
getEntries.mockResolvedValue(newEntries)
|
|
340
|
+
|
|
341
|
+
void manager.getSuggestion({ injector, term: 'test2' })
|
|
342
|
+
await vi.advanceTimersByTimeAsync(250)
|
|
343
|
+
|
|
344
|
+
expect(manager.selectedIndex.getValue()).toBe(0)
|
|
345
|
+
})
|
|
346
|
+
})
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
describe('selectSuggestion', () => {
|
|
350
|
+
it('Should emit onSelectSuggestion event with selected entry', async () => {
|
|
351
|
+
const testEntries = createTestEntries()
|
|
352
|
+
const getEntries = vi.fn().mockResolvedValue(testEntries)
|
|
353
|
+
const getSuggestionEntry = vi.fn().mockImplementation(createSuggestionResult)
|
|
354
|
+
const injector = new Injector()
|
|
355
|
+
|
|
356
|
+
await usingAsync(new SuggestManager(getEntries, getSuggestionEntry), async (manager) => {
|
|
357
|
+
void manager.getSuggestion({ injector, term: 'test' })
|
|
358
|
+
await vi.advanceTimersByTimeAsync(250)
|
|
359
|
+
|
|
360
|
+
const onSelect = vi.fn()
|
|
361
|
+
manager.subscribe('onSelectSuggestion', onSelect)
|
|
362
|
+
|
|
363
|
+
manager.selectSuggestion(1)
|
|
364
|
+
|
|
365
|
+
expect(onSelect).toHaveBeenCalledWith(testEntries[1])
|
|
366
|
+
expect(manager.isOpened.getValue()).toBe(false)
|
|
367
|
+
})
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
it('Should use current selectedIndex when no index provided', async () => {
|
|
371
|
+
const testEntries = createTestEntries()
|
|
372
|
+
const getEntries = vi.fn().mockResolvedValue(testEntries)
|
|
373
|
+
const getSuggestionEntry = vi.fn().mockImplementation(createSuggestionResult)
|
|
374
|
+
const injector = new Injector()
|
|
375
|
+
|
|
376
|
+
await usingAsync(new SuggestManager(getEntries, getSuggestionEntry), async (manager) => {
|
|
377
|
+
void manager.getSuggestion({ injector, term: 'test' })
|
|
378
|
+
await vi.advanceTimersByTimeAsync(250)
|
|
379
|
+
|
|
380
|
+
manager.selectedIndex.setValue(2)
|
|
381
|
+
|
|
382
|
+
const onSelect = vi.fn()
|
|
383
|
+
manager.subscribe('onSelectSuggestion', onSelect)
|
|
384
|
+
|
|
385
|
+
manager.selectSuggestion()
|
|
386
|
+
|
|
387
|
+
expect(onSelect).toHaveBeenCalledWith(testEntries[2])
|
|
388
|
+
})
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
it('Should close the suggestion list after selection', async () => {
|
|
392
|
+
const testEntries = createTestEntries()
|
|
393
|
+
const getEntries = vi.fn().mockResolvedValue(testEntries)
|
|
394
|
+
const getSuggestionEntry = vi.fn().mockImplementation(createSuggestionResult)
|
|
395
|
+
const injector = new Injector()
|
|
396
|
+
|
|
397
|
+
await usingAsync(new SuggestManager(getEntries, getSuggestionEntry), async (manager) => {
|
|
398
|
+
void manager.getSuggestion({ injector, term: 'test' })
|
|
399
|
+
await vi.advanceTimersByTimeAsync(250)
|
|
400
|
+
|
|
401
|
+
expect(manager.isOpened.getValue()).toBe(true)
|
|
402
|
+
|
|
403
|
+
manager.selectSuggestion(0)
|
|
404
|
+
|
|
405
|
+
expect(manager.isOpened.getValue()).toBe(false)
|
|
406
|
+
})
|
|
407
|
+
})
|
|
408
|
+
})
|
|
409
|
+
})
|