@inglorious/web 2.6.1 → 3.0.1
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/README.md +21 -15
- package/package.json +27 -4
- package/src/{form.js → form/index.js} +6 -6
- package/src/index.js +1 -6
- package/src/{list.js → list/index.js} +2 -2
- package/src/{router.js → router/index.js} +15 -3
- package/src/select/index.js +7 -0
- package/src/table/filters.js +5 -5
- package/src/table/index.js +7 -0
- package/src/table/rendering.js +2 -2
- package/src/table/theme.css +83 -83
- package/src/form.test.js +0 -372
- package/src/list.test.js +0 -228
- package/src/router.test.js +0 -208
- package/src/select.js +0 -7
- package/src/select.test.js +0 -415
- package/src/table.js +0 -7
- package/src/table.test.js +0 -393
package/src/router.test.js
DELETED
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @vitest-environment jsdom
|
|
3
|
-
*/
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
|
|
5
|
-
|
|
6
|
-
import { router } from "./router.js"
|
|
7
|
-
|
|
8
|
-
describe("router", () => {
|
|
9
|
-
let entity
|
|
10
|
-
let api
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
entity = {
|
|
14
|
-
id: "router",
|
|
15
|
-
type: "router",
|
|
16
|
-
routes: {
|
|
17
|
-
"/": "homePage",
|
|
18
|
-
"/users": "userListPage",
|
|
19
|
-
"/users/:id": "userPage",
|
|
20
|
-
"/users/:id/posts/:postId": "postPage",
|
|
21
|
-
"*": "notFoundPage",
|
|
22
|
-
},
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
api = {
|
|
26
|
-
getEntity: vi.fn().mockReturnValue(entity),
|
|
27
|
-
notify: vi.fn(),
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Mock window.location and history
|
|
31
|
-
vi.spyOn(window, "location", "get").mockReturnValue({
|
|
32
|
-
pathname: "/",
|
|
33
|
-
search: "",
|
|
34
|
-
hash: "",
|
|
35
|
-
origin: "http://localhost:3000",
|
|
36
|
-
})
|
|
37
|
-
vi.spyOn(history, "pushState").mockImplementation(() => {})
|
|
38
|
-
vi.spyOn(history, "replaceState").mockImplementation(() => {})
|
|
39
|
-
vi.spyOn(history, "go").mockImplementation(() => {})
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
afterEach(() => {
|
|
43
|
-
vi.restoreAllMocks()
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
describe("init()", () => {
|
|
47
|
-
it("should initialize with the current window.location, set up a popstate listener, and set up a click listener for link interception", () => {
|
|
48
|
-
vi.spyOn(window, "location", "get").mockReturnValue({
|
|
49
|
-
pathname: "/users/123",
|
|
50
|
-
search: "?sort=asc",
|
|
51
|
-
hash: "#details",
|
|
52
|
-
origin: "http://localhost:3000",
|
|
53
|
-
})
|
|
54
|
-
const windowSpy = vi.spyOn(window, "addEventListener")
|
|
55
|
-
const documentSpy = vi.spyOn(document, "addEventListener")
|
|
56
|
-
|
|
57
|
-
router.init(entity, undefined, api)
|
|
58
|
-
|
|
59
|
-
expect(api.notify).toHaveBeenCalledWith("#router:navigate", {
|
|
60
|
-
to: "/users/123?sort=asc",
|
|
61
|
-
params: { id: "123" },
|
|
62
|
-
replace: true,
|
|
63
|
-
})
|
|
64
|
-
expect(windowSpy).toHaveBeenCalledWith("popstate", expect.any(Function))
|
|
65
|
-
expect(documentSpy).toHaveBeenCalledWith("click", expect.any(Function))
|
|
66
|
-
})
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
describe("navigate()", () => {
|
|
70
|
-
it("should navigate to a new path and update the entity", () => {
|
|
71
|
-
router.navigate(entity, "/users/456?q=test", api)
|
|
72
|
-
|
|
73
|
-
expect(entity.path).toBe("/users/456")
|
|
74
|
-
expect(entity.route).toBe("userPage")
|
|
75
|
-
expect(entity.params).toEqual({ id: "456" })
|
|
76
|
-
expect(entity.query).toEqual({ q: "test" })
|
|
77
|
-
expect(history.pushState).toHaveBeenCalledWith(
|
|
78
|
-
expect.any(Object),
|
|
79
|
-
"",
|
|
80
|
-
"/users/456?q=test",
|
|
81
|
-
)
|
|
82
|
-
expect(api.notify).toHaveBeenCalledWith("routeChange", expect.any(Object))
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
it("should use replaceState when replace is true", () => {
|
|
86
|
-
router.navigate(entity, { to: "/users", replace: true }, api)
|
|
87
|
-
expect(history.replaceState).toHaveBeenCalled()
|
|
88
|
-
expect(history.pushState).not.toHaveBeenCalled()
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
it("should handle numeric navigation", () => {
|
|
92
|
-
router.navigate(entity, -1, api)
|
|
93
|
-
expect(history.go).toHaveBeenCalledWith(-1)
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
it("should build path from params", () => {
|
|
97
|
-
router.navigate(
|
|
98
|
-
entity,
|
|
99
|
-
{ to: "/users/:id/posts/:postId", params: { id: 1, postId: 2 } },
|
|
100
|
-
api,
|
|
101
|
-
)
|
|
102
|
-
expect(history.pushState).toHaveBeenCalledWith(
|
|
103
|
-
expect.any(Object),
|
|
104
|
-
"",
|
|
105
|
-
"/users/1/posts/2",
|
|
106
|
-
)
|
|
107
|
-
expect(entity.route).toBe("postPage")
|
|
108
|
-
expect(entity.params).toEqual({ id: "1", postId: "2" })
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
it("should use the fallback route for unknown paths", () => {
|
|
112
|
-
router.navigate(entity, "/some/unknown/path", api)
|
|
113
|
-
expect(entity.route).toBe("notFoundPage")
|
|
114
|
-
expect(entity.params).toEqual({})
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
it("should not navigate if the path is identical", () => {
|
|
118
|
-
entity.path = "/users"
|
|
119
|
-
vi.spyOn(window, "location", "get").mockReturnValue({
|
|
120
|
-
pathname: "/users",
|
|
121
|
-
search: "",
|
|
122
|
-
hash: "",
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
router.navigate(entity, "/users", api)
|
|
126
|
-
|
|
127
|
-
expect(history.pushState).not.toHaveBeenCalled()
|
|
128
|
-
expect(api.notify).not.toHaveBeenCalledWith(
|
|
129
|
-
"routeChange",
|
|
130
|
-
expect.any(Object),
|
|
131
|
-
)
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
it("should navigate if the path is identical but force is true", () => {
|
|
135
|
-
entity.path = "/users"
|
|
136
|
-
vi.spyOn(window, "location", "get").mockReturnValue({
|
|
137
|
-
pathname: "/users",
|
|
138
|
-
search: "",
|
|
139
|
-
hash: "",
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
router.navigate(entity, { to: "/users", force: true }, api)
|
|
143
|
-
|
|
144
|
-
expect(history.pushState).toHaveBeenCalled()
|
|
145
|
-
expect(api.notify).toHaveBeenCalledWith("routeChange", expect.any(Object))
|
|
146
|
-
})
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
describe("routeSync()", () => {
|
|
150
|
-
it("should update the entity state from a payload", () => {
|
|
151
|
-
const payload = {
|
|
152
|
-
path: "/new?a=1",
|
|
153
|
-
entityType: "newPage",
|
|
154
|
-
params: {},
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
vi.spyOn(window, "location", "get").mockReturnValue({ hash: "#section" })
|
|
158
|
-
|
|
159
|
-
router.routeSync(entity, payload)
|
|
160
|
-
|
|
161
|
-
expect(entity.path).toBe("/new")
|
|
162
|
-
expect(entity.route).toBe("newPage")
|
|
163
|
-
expect(entity.query).toEqual({ a: "1" })
|
|
164
|
-
expect(entity.hash).toBe("#section")
|
|
165
|
-
})
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
describe("loadSuccess()", () => {
|
|
169
|
-
it("should handle lazy loaded modules", () => {
|
|
170
|
-
const module = { myPage: { render: () => {} } }
|
|
171
|
-
const route = { pattern: "/lazy", params: {} }
|
|
172
|
-
const payload = {
|
|
173
|
-
module,
|
|
174
|
-
route,
|
|
175
|
-
path: "/lazy",
|
|
176
|
-
replace: false,
|
|
177
|
-
state: {},
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
router.loadSuccess(entity, payload, api)
|
|
181
|
-
|
|
182
|
-
expect(api.notify).toHaveBeenCalledWith("morph", {
|
|
183
|
-
name: "myPage",
|
|
184
|
-
type: module.myPage,
|
|
185
|
-
})
|
|
186
|
-
expect(entity.routes["/lazy"]).toBe("myPage")
|
|
187
|
-
expect(entity.loading).toBe(false)
|
|
188
|
-
expect(entity.route).toBe("myPage")
|
|
189
|
-
expect(history.pushState).toHaveBeenCalled()
|
|
190
|
-
})
|
|
191
|
-
})
|
|
192
|
-
|
|
193
|
-
describe("loadError()", () => {
|
|
194
|
-
it("should handle load errors", () => {
|
|
195
|
-
const error = new Error("Failed")
|
|
196
|
-
const payload = { error, path: "/lazy" }
|
|
197
|
-
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {})
|
|
198
|
-
|
|
199
|
-
router.loadError(entity, payload)
|
|
200
|
-
|
|
201
|
-
expect(entity.path).toBe("/lazy")
|
|
202
|
-
expect(entity.loading).toBe(false)
|
|
203
|
-
expect(entity.error).toBe(error)
|
|
204
|
-
|
|
205
|
-
consoleSpy.mockRestore()
|
|
206
|
-
})
|
|
207
|
-
})
|
|
208
|
-
})
|
package/src/select.js
DELETED
package/src/select.test.js
DELETED
|
@@ -1,415 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @vitest-environment jsdom
|
|
3
|
-
*/
|
|
4
|
-
import { beforeEach, describe, expect, it } from "vitest"
|
|
5
|
-
|
|
6
|
-
import { select } from "./select.js"
|
|
7
|
-
import {
|
|
8
|
-
filterOptions,
|
|
9
|
-
findOptionIndex,
|
|
10
|
-
formatOption,
|
|
11
|
-
getOptionLabel,
|
|
12
|
-
getOptionValue,
|
|
13
|
-
groupOptions,
|
|
14
|
-
isOptionSelected,
|
|
15
|
-
} from "./select/logic.js"
|
|
16
|
-
|
|
17
|
-
const sampleOptions = [
|
|
18
|
-
{ value: "br", label: "Brazil" },
|
|
19
|
-
{ value: "it", label: "Italy" },
|
|
20
|
-
{ value: "ca", label: "Canada" },
|
|
21
|
-
{ value: "us", label: "United States" },
|
|
22
|
-
{ value: "uk", label: "United Kingdom" },
|
|
23
|
-
{ value: "fr", label: "France" },
|
|
24
|
-
]
|
|
25
|
-
|
|
26
|
-
describe("select", () => {
|
|
27
|
-
let entity
|
|
28
|
-
|
|
29
|
-
beforeEach(() => {
|
|
30
|
-
entity = {
|
|
31
|
-
id: "test-select",
|
|
32
|
-
type: "select",
|
|
33
|
-
options: JSON.parse(JSON.stringify(sampleOptions)),
|
|
34
|
-
}
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
describe("logic", () => {
|
|
38
|
-
describe("create()", () => {
|
|
39
|
-
it("should initialize with default state", () => {
|
|
40
|
-
select.create(entity)
|
|
41
|
-
|
|
42
|
-
expect(entity.isOpen).toBe(false)
|
|
43
|
-
expect(entity.searchTerm).toBe("")
|
|
44
|
-
expect(entity.focusedIndex).toBe(-1)
|
|
45
|
-
expect(entity.isMulti).toBe(false)
|
|
46
|
-
expect(entity.selectedValue).toBe(null)
|
|
47
|
-
expect(entity.options).toEqual(sampleOptions)
|
|
48
|
-
expect(entity.isLoading).toBe(false)
|
|
49
|
-
expect(entity.isDisabled).toBe(false)
|
|
50
|
-
expect(entity.isSearchable).toBe(true)
|
|
51
|
-
expect(entity.isClearable).toBe(true)
|
|
52
|
-
expect(entity.isCreatable).toBe(false)
|
|
53
|
-
expect(entity.placeholder).toBe("Select...")
|
|
54
|
-
expect(entity.noOptionsMessage).toBe("No options")
|
|
55
|
-
expect(entity.loadingMessage).toBe("Loading...")
|
|
56
|
-
expect(entity.groupBy).toBe(null)
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it("should initialize multi-select with empty array", () => {
|
|
60
|
-
entity.isMulti = true
|
|
61
|
-
select.create(entity)
|
|
62
|
-
expect(entity.selectedValue).toEqual([])
|
|
63
|
-
})
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
describe("open() and close()", () => {
|
|
67
|
-
beforeEach(() => {
|
|
68
|
-
select.create(entity)
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
it("open: should open the dropdown", () => {
|
|
72
|
-
select.open(entity)
|
|
73
|
-
expect(entity.isOpen).toBe(true)
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
it("open: should not open if disabled", () => {
|
|
77
|
-
entity.isDisabled = true
|
|
78
|
-
select.open(entity)
|
|
79
|
-
expect(entity.isOpen).toBe(false)
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
it("open: should focus first option if available", () => {
|
|
83
|
-
select.open(entity)
|
|
84
|
-
expect(entity.focusedIndex).toBe(0)
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
it("open: should not focus if no options", () => {
|
|
88
|
-
entity.options = []
|
|
89
|
-
select.open(entity)
|
|
90
|
-
expect(entity.focusedIndex).toBe(-1)
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
it("close: should close the dropdown", () => {
|
|
94
|
-
entity.isOpen = true
|
|
95
|
-
entity.focusedIndex = 2
|
|
96
|
-
select.close(entity)
|
|
97
|
-
expect(entity.isOpen).toBe(false)
|
|
98
|
-
expect(entity.focusedIndex).toBe(-1)
|
|
99
|
-
})
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
describe("toggle()", () => {
|
|
103
|
-
beforeEach(() => {
|
|
104
|
-
select.create(entity)
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
it("should open if closed", () => {
|
|
108
|
-
select.toggle(entity)
|
|
109
|
-
expect(entity.isOpen).toBe(true)
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
it("should close if open", () => {
|
|
113
|
-
entity.isOpen = true
|
|
114
|
-
select.toggle(entity)
|
|
115
|
-
expect(entity.isOpen).toBe(false)
|
|
116
|
-
})
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
describe("optionSelect()", () => {
|
|
120
|
-
beforeEach(() => {
|
|
121
|
-
select.create(entity)
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
it("should select an option in single-select mode", () => {
|
|
125
|
-
const option = sampleOptions[0]
|
|
126
|
-
select.optionSelect(entity, option)
|
|
127
|
-
expect(entity.selectedValue).toBe("br")
|
|
128
|
-
expect(entity.isOpen).toBe(false)
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
it("should add option in multi-select mode", () => {
|
|
132
|
-
entity.isMulti = true
|
|
133
|
-
select.create(entity)
|
|
134
|
-
entity.isOpen = true // Open dropdown first
|
|
135
|
-
const option = sampleOptions[0]
|
|
136
|
-
select.optionSelect(entity, option)
|
|
137
|
-
expect(entity.selectedValue).toContain("br")
|
|
138
|
-
expect(entity.isOpen).toBe(true) // Multi-select doesn't close
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
it("should remove option in multi-select mode if already selected", () => {
|
|
142
|
-
entity.isMulti = true
|
|
143
|
-
select.create(entity)
|
|
144
|
-
const option = sampleOptions[0]
|
|
145
|
-
select.optionSelect(entity, option) // Add
|
|
146
|
-
select.optionSelect(entity, option) // Remove
|
|
147
|
-
expect(entity.selectedValue).not.toContain("br")
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
it("should not select if disabled", () => {
|
|
151
|
-
entity.isDisabled = true
|
|
152
|
-
select.optionSelect(entity, sampleOptions[0])
|
|
153
|
-
expect(entity.selectedValue).toBe(null)
|
|
154
|
-
})
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
describe("clear()", () => {
|
|
158
|
-
beforeEach(() => {
|
|
159
|
-
select.create(entity)
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
it("should clear selection in single-select mode", () => {
|
|
163
|
-
entity.selectedValue = "br"
|
|
164
|
-
select.clear(entity)
|
|
165
|
-
expect(entity.selectedValue).toBe(null)
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
it("should clear selection in multi-select mode", () => {
|
|
169
|
-
entity.isMulti = true
|
|
170
|
-
select.create(entity)
|
|
171
|
-
entity.selectedValue = ["br", "us"]
|
|
172
|
-
select.clear(entity)
|
|
173
|
-
expect(entity.selectedValue).toEqual([])
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
it("should not clear if disabled", () => {
|
|
177
|
-
entity.selectedValue = "br"
|
|
178
|
-
entity.isDisabled = true
|
|
179
|
-
select.clear(entity)
|
|
180
|
-
expect(entity.selectedValue).toBe("br")
|
|
181
|
-
})
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
describe("searchChange()", () => {
|
|
185
|
-
beforeEach(() => {
|
|
186
|
-
select.create(entity)
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
it("should update searchTerm and filter options", () => {
|
|
190
|
-
select.searchChange(entity, "bra")
|
|
191
|
-
expect(entity.searchTerm).toBe("bra")
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
it("should reset focusedIndex when search changes", () => {
|
|
195
|
-
entity.focusedIndex = 2
|
|
196
|
-
select.searchChange(entity, "bra")
|
|
197
|
-
expect(entity.focusedIndex).toBe(0)
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
it("should set focusedIndex to -1 if no results", () => {
|
|
201
|
-
select.searchChange(entity, "xyz")
|
|
202
|
-
expect(entity.focusedIndex).toBe(-1)
|
|
203
|
-
})
|
|
204
|
-
|
|
205
|
-
it("should show all options if searchTerm is empty", () => {
|
|
206
|
-
select.searchChange(entity, "bra")
|
|
207
|
-
select.searchChange(entity, "")
|
|
208
|
-
expect(entity.focusedIndex).toBe(0)
|
|
209
|
-
})
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
describe("keyboard navigation", () => {
|
|
213
|
-
beforeEach(() => {
|
|
214
|
-
select.create(entity)
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
it("focusNext: should move to next option", () => {
|
|
218
|
-
entity.focusedIndex = 0
|
|
219
|
-
select.focusNext(entity)
|
|
220
|
-
expect(entity.focusedIndex).toBe(1)
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
it("focusNext: should not go past last option", () => {
|
|
224
|
-
entity.focusedIndex = sampleOptions.length - 1
|
|
225
|
-
select.focusNext(entity)
|
|
226
|
-
expect(entity.focusedIndex).toBe(sampleOptions.length - 1)
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
it("focusPrev: should move to previous option", () => {
|
|
230
|
-
entity.focusedIndex = 2
|
|
231
|
-
select.focusPrev(entity)
|
|
232
|
-
expect(entity.focusedIndex).toBe(1)
|
|
233
|
-
})
|
|
234
|
-
|
|
235
|
-
it("focusPrev: should not go before first option", () => {
|
|
236
|
-
entity.focusedIndex = 0
|
|
237
|
-
select.focusPrev(entity)
|
|
238
|
-
expect(entity.focusedIndex).toBe(-1)
|
|
239
|
-
})
|
|
240
|
-
|
|
241
|
-
it("focusFirst: should move to first option", () => {
|
|
242
|
-
entity.focusedIndex = 3
|
|
243
|
-
select.focusFirst(entity)
|
|
244
|
-
expect(entity.focusedIndex).toBe(0)
|
|
245
|
-
})
|
|
246
|
-
|
|
247
|
-
it("focusLast: should move to last option", () => {
|
|
248
|
-
entity.focusedIndex = 0
|
|
249
|
-
select.focusLast(entity)
|
|
250
|
-
expect(entity.focusedIndex).toBe(sampleOptions.length - 1)
|
|
251
|
-
})
|
|
252
|
-
|
|
253
|
-
it("focusNext: should not move if no options", () => {
|
|
254
|
-
entity.options = []
|
|
255
|
-
entity.focusedIndex = -1
|
|
256
|
-
select.focusNext(entity)
|
|
257
|
-
expect(entity.focusedIndex).toBe(-1)
|
|
258
|
-
})
|
|
259
|
-
})
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
describe("helpers", () => {
|
|
263
|
-
describe("getOptionValue()", () => {
|
|
264
|
-
it("should return value from object", () => {
|
|
265
|
-
expect(getOptionValue({ value: "test", label: "Test" })).toBe("test")
|
|
266
|
-
})
|
|
267
|
-
|
|
268
|
-
it("should return the option itself if not an object", () => {
|
|
269
|
-
expect(getOptionValue("test")).toBe("test")
|
|
270
|
-
expect(getOptionValue(123)).toBe(123)
|
|
271
|
-
})
|
|
272
|
-
|
|
273
|
-
it("should return the object itself if it has no value property", () => {
|
|
274
|
-
const option = { label: "Test" }
|
|
275
|
-
expect(getOptionValue(option)).toBe(option)
|
|
276
|
-
})
|
|
277
|
-
})
|
|
278
|
-
|
|
279
|
-
describe("getOptionLabel()", () => {
|
|
280
|
-
it("should return label from object", () => {
|
|
281
|
-
expect(getOptionLabel({ value: "test", label: "Test" })).toBe("Test")
|
|
282
|
-
})
|
|
283
|
-
|
|
284
|
-
it("should return value as string if no label", () => {
|
|
285
|
-
expect(getOptionLabel({ value: "test" })).toBe("test")
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
it("should return stringified option if not an object", () => {
|
|
289
|
-
expect(getOptionLabel("test")).toBe("test")
|
|
290
|
-
expect(getOptionLabel(123)).toBe("123")
|
|
291
|
-
})
|
|
292
|
-
})
|
|
293
|
-
|
|
294
|
-
describe("isOptionSelected()", () => {
|
|
295
|
-
it("should return true for selected option in single-select", () => {
|
|
296
|
-
const option = { value: "br", label: "Brazil" }
|
|
297
|
-
expect(isOptionSelected(option, "br", false)).toBe(true)
|
|
298
|
-
expect(isOptionSelected(option, "us", false)).toBe(false)
|
|
299
|
-
})
|
|
300
|
-
|
|
301
|
-
it("should return true for selected option in multi-select", () => {
|
|
302
|
-
const option = { value: "br", label: "Brazil" }
|
|
303
|
-
expect(isOptionSelected(option, ["br", "us"], true)).toBe(true)
|
|
304
|
-
expect(isOptionSelected(option, ["us"], true)).toBe(false)
|
|
305
|
-
})
|
|
306
|
-
|
|
307
|
-
it("should handle string/number options", () => {
|
|
308
|
-
expect(isOptionSelected("test", "test", false)).toBe(true)
|
|
309
|
-
expect(isOptionSelected(123, 123, false)).toBe(true)
|
|
310
|
-
})
|
|
311
|
-
})
|
|
312
|
-
|
|
313
|
-
describe("filterOptions()", () => {
|
|
314
|
-
it("should return all options if searchTerm is empty", () => {
|
|
315
|
-
const result = filterOptions(sampleOptions, "")
|
|
316
|
-
expect(result).toEqual(sampleOptions)
|
|
317
|
-
})
|
|
318
|
-
|
|
319
|
-
it("should filter options by label (case-insensitive)", () => {
|
|
320
|
-
const result = filterOptions(sampleOptions, "bra")
|
|
321
|
-
expect(result).toHaveLength(1)
|
|
322
|
-
expect(result[0].label).toBe("Brazil")
|
|
323
|
-
})
|
|
324
|
-
|
|
325
|
-
it("should filter options case-insensitively", () => {
|
|
326
|
-
const result = filterOptions(sampleOptions, "BRA")
|
|
327
|
-
expect(result).toHaveLength(1)
|
|
328
|
-
expect(result[0].label).toBe("Brazil")
|
|
329
|
-
})
|
|
330
|
-
|
|
331
|
-
it("should return empty array if no matches", () => {
|
|
332
|
-
const result = filterOptions(sampleOptions, "xyz")
|
|
333
|
-
expect(result).toHaveLength(0)
|
|
334
|
-
})
|
|
335
|
-
|
|
336
|
-
it("should trim searchTerm", () => {
|
|
337
|
-
const result = filterOptions(sampleOptions, " bra ")
|
|
338
|
-
expect(result).toHaveLength(1)
|
|
339
|
-
})
|
|
340
|
-
})
|
|
341
|
-
|
|
342
|
-
describe("findOptionIndex()", () => {
|
|
343
|
-
it("should find index of option by value", () => {
|
|
344
|
-
expect(findOptionIndex(sampleOptions, "br")).toBe(0)
|
|
345
|
-
expect(findOptionIndex(sampleOptions, "it")).toBe(1)
|
|
346
|
-
})
|
|
347
|
-
|
|
348
|
-
it("should return -1 if not found", () => {
|
|
349
|
-
expect(findOptionIndex(sampleOptions, "xyz")).toBe(-1)
|
|
350
|
-
})
|
|
351
|
-
})
|
|
352
|
-
|
|
353
|
-
describe("groupOptions()", () => {
|
|
354
|
-
const groupedOptions = [
|
|
355
|
-
{ value: "a", label: "A", category: "letters" },
|
|
356
|
-
{ value: "b", label: "B", category: "letters" },
|
|
357
|
-
{ value: "1", label: "1", category: "numbers" },
|
|
358
|
-
{ value: "2", label: "2", category: "numbers" },
|
|
359
|
-
]
|
|
360
|
-
|
|
361
|
-
it("should group options by property", () => {
|
|
362
|
-
const result = groupOptions(groupedOptions, "category")
|
|
363
|
-
expect(result).toHaveLength(2)
|
|
364
|
-
expect(result[0].label).toBe("letters")
|
|
365
|
-
expect(result[0].options).toHaveLength(2)
|
|
366
|
-
expect(result[1].label).toBe("numbers")
|
|
367
|
-
expect(result[1].options).toHaveLength(2)
|
|
368
|
-
})
|
|
369
|
-
|
|
370
|
-
it("should return null if groupBy is not provided", () => {
|
|
371
|
-
expect(groupOptions(sampleOptions, null)).toBeNull()
|
|
372
|
-
expect(groupOptions(sampleOptions, "")).toBeNull()
|
|
373
|
-
})
|
|
374
|
-
|
|
375
|
-
it("should handle options without group property", () => {
|
|
376
|
-
const mixedOptions = [
|
|
377
|
-
...groupedOptions,
|
|
378
|
-
{ value: "x", label: "X" }, // No category
|
|
379
|
-
]
|
|
380
|
-
const result = groupOptions(mixedOptions, "category")
|
|
381
|
-
expect(result).toBeDefined()
|
|
382
|
-
const ungrouped = result.find((g) => g.label === "Ungrouped")
|
|
383
|
-
expect(ungrouped).toBeDefined()
|
|
384
|
-
expect(ungrouped.options).toContainEqual({ value: "x", label: "X" })
|
|
385
|
-
})
|
|
386
|
-
})
|
|
387
|
-
|
|
388
|
-
describe("formatOption()", () => {
|
|
389
|
-
it("should format string as option", () => {
|
|
390
|
-
const result = formatOption("test")
|
|
391
|
-
expect(result).toEqual({ value: "test", label: "test" })
|
|
392
|
-
})
|
|
393
|
-
|
|
394
|
-
it("should format number as option", () => {
|
|
395
|
-
const result = formatOption(123)
|
|
396
|
-
expect(result).toEqual({ value: 123, label: "123" })
|
|
397
|
-
})
|
|
398
|
-
|
|
399
|
-
it("should preserve object option", () => {
|
|
400
|
-
const option = { value: "test", label: "Test", disabled: true }
|
|
401
|
-
const result = formatOption(option)
|
|
402
|
-
expect(result).toEqual({
|
|
403
|
-
value: "test",
|
|
404
|
-
label: "Test",
|
|
405
|
-
disabled: true,
|
|
406
|
-
})
|
|
407
|
-
})
|
|
408
|
-
|
|
409
|
-
it("should create label from value if missing", () => {
|
|
410
|
-
const result = formatOption({ value: "test" })
|
|
411
|
-
expect(result).toEqual({ value: "test", label: "test" })
|
|
412
|
-
})
|
|
413
|
-
})
|
|
414
|
-
})
|
|
415
|
-
})
|