mtrl-addons 0.2.2 → 0.2.4
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/{src/components/index.ts → dist/components/index.d.ts} +0 -2
- package/dist/components/vlist/config.d.ts +86 -0
- package/{src/components/vlist/constants.ts → dist/components/vlist/constants.d.ts} +10 -11
- package/dist/components/vlist/features/api.d.ts +7 -0
- package/{src/components/vlist/features/index.ts → dist/components/vlist/features/index.d.ts} +0 -2
- package/dist/components/vlist/features/selection.d.ts +6 -0
- package/dist/components/vlist/features/viewport.d.ts +9 -0
- package/dist/components/vlist/features.d.ts +31 -0
- package/{src/components/vlist/index.ts → dist/components/vlist/index.d.ts} +1 -10
- package/dist/components/vlist/types.d.ts +596 -0
- package/dist/components/vlist/vlist.d.ts +29 -0
- package/dist/core/compose/features/gestures/index.d.ts +86 -0
- package/dist/core/compose/features/gestures/longpress.d.ts +85 -0
- package/dist/core/compose/features/gestures/pan.d.ts +108 -0
- package/dist/core/compose/features/gestures/pinch.d.ts +111 -0
- package/dist/core/compose/features/gestures/rotate.d.ts +111 -0
- package/dist/core/compose/features/gestures/swipe.d.ts +149 -0
- package/dist/core/compose/features/gestures/tap.d.ts +79 -0
- package/{src/core/compose/features/index.ts → dist/core/compose/features/index.d.ts} +1 -2
- package/{src/core/compose/index.ts → dist/core/compose/index.d.ts} +2 -11
- package/{src/core/gestures/index.ts → dist/core/gestures/index.d.ts} +1 -20
- package/dist/core/gestures/longpress.d.ts +23 -0
- package/dist/core/gestures/manager.d.ts +14 -0
- package/dist/core/gestures/pan.d.ts +12 -0
- package/dist/core/gestures/pinch.d.ts +14 -0
- package/dist/core/gestures/rotate.d.ts +14 -0
- package/dist/core/gestures/swipe.d.ts +20 -0
- package/dist/core/gestures/tap.d.ts +12 -0
- package/dist/core/gestures/types.d.ts +320 -0
- package/dist/core/gestures/utils.d.ts +57 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/layout/config.d.ts +33 -0
- package/dist/core/layout/index.d.ts +51 -0
- package/dist/core/layout/jsx.d.ts +65 -0
- package/dist/core/layout/schema.d.ts +112 -0
- package/dist/core/layout/types.d.ts +69 -0
- package/dist/core/viewport/constants.d.ts +105 -0
- package/dist/core/viewport/features/base.d.ts +14 -0
- package/dist/core/viewport/features/collection.d.ts +41 -0
- package/dist/core/viewport/features/events.d.ts +13 -0
- package/{src/core/viewport/features/index.ts → dist/core/viewport/features/index.d.ts} +0 -7
- package/dist/core/viewport/features/item-size.d.ts +30 -0
- package/dist/core/viewport/features/loading.d.ts +34 -0
- package/dist/core/viewport/features/momentum.d.ts +17 -0
- package/dist/core/viewport/features/performance.d.ts +53 -0
- package/dist/core/viewport/features/placeholders.d.ts +38 -0
- package/dist/core/viewport/features/rendering.d.ts +16 -0
- package/dist/core/viewport/features/scrollbar.d.ts +26 -0
- package/dist/core/viewport/features/scrolling.d.ts +16 -0
- package/dist/core/viewport/features/utils.d.ts +43 -0
- package/dist/core/viewport/features/virtual.d.ts +18 -0
- package/{src/core/viewport/index.ts → dist/core/viewport/index.d.ts} +1 -17
- package/dist/core/viewport/types.d.ts +96 -0
- package/dist/core/viewport/utils/speed-tracker.d.ts +22 -0
- package/dist/core/viewport/viewport.d.ts +11 -0
- package/{src/index.ts → dist/index.d.ts} +0 -4
- package/dist/index.js +5143 -0
- package/dist/index.mjs +5111 -0
- package/dist/styles.css +254 -0
- package/dist/styles.css.map +1 -0
- package/package.json +16 -2
- package/.cursorrules +0 -117
- package/AI.md +0 -39
- package/CLAUDE.md +0 -882
- package/build.js +0 -377
- package/index.ts +0 -7
- package/scripts/analyze-orphaned-functions.ts +0 -387
- package/scripts/debug/vlist-selection.ts +0 -121
- package/src/components/vlist/config.ts +0 -323
- package/src/components/vlist/features/api.ts +0 -626
- package/src/components/vlist/features/selection.ts +0 -436
- package/src/components/vlist/features/viewport.ts +0 -59
- package/src/components/vlist/features.ts +0 -112
- package/src/components/vlist/types.ts +0 -723
- package/src/components/vlist/vlist.ts +0 -92
- package/src/core/compose/features/gestures/index.ts +0 -227
- package/src/core/compose/features/gestures/longpress.ts +0 -383
- package/src/core/compose/features/gestures/pan.ts +0 -424
- package/src/core/compose/features/gestures/pinch.ts +0 -475
- package/src/core/compose/features/gestures/rotate.ts +0 -485
- package/src/core/compose/features/gestures/swipe.ts +0 -492
- package/src/core/compose/features/gestures/tap.ts +0 -334
- package/src/core/gestures/longpress.ts +0 -68
- package/src/core/gestures/manager.ts +0 -418
- package/src/core/gestures/pan.ts +0 -48
- package/src/core/gestures/pinch.ts +0 -58
- package/src/core/gestures/rotate.ts +0 -58
- package/src/core/gestures/swipe.ts +0 -66
- package/src/core/gestures/tap.ts +0 -45
- package/src/core/gestures/types.ts +0 -387
- package/src/core/gestures/utils.ts +0 -128
- package/src/core/index.ts +0 -43
- package/src/core/layout/config.ts +0 -102
- package/src/core/layout/index.ts +0 -168
- package/src/core/layout/jsx.ts +0 -174
- package/src/core/layout/schema.ts +0 -1044
- package/src/core/layout/types.ts +0 -95
- package/src/core/viewport/constants.ts +0 -145
- package/src/core/viewport/features/base.ts +0 -73
- package/src/core/viewport/features/collection.ts +0 -1182
- package/src/core/viewport/features/events.ts +0 -130
- package/src/core/viewport/features/item-size.ts +0 -271
- package/src/core/viewport/features/loading.ts +0 -263
- package/src/core/viewport/features/momentum.ts +0 -269
- package/src/core/viewport/features/performance.ts +0 -161
- package/src/core/viewport/features/placeholders.ts +0 -335
- package/src/core/viewport/features/rendering.ts +0 -962
- package/src/core/viewport/features/scrollbar.ts +0 -434
- package/src/core/viewport/features/scrolling.ts +0 -634
- package/src/core/viewport/features/utils.ts +0 -94
- package/src/core/viewport/features/virtual.ts +0 -525
- package/src/core/viewport/types.ts +0 -133
- package/src/core/viewport/utils/speed-tracker.ts +0 -79
- package/src/core/viewport/viewport.ts +0 -265
- package/test/benchmarks/layout/advanced.test.ts +0 -656
- package/test/benchmarks/layout/comparison.test.ts +0 -519
- package/test/benchmarks/layout/performance-comparison.test.ts +0 -274
- package/test/benchmarks/layout/real-components.test.ts +0 -733
- package/test/benchmarks/layout/simple.test.ts +0 -321
- package/test/benchmarks/layout/stress.test.ts +0 -990
- package/test/collection/basic.test.ts +0 -304
- package/test/components/vlist-selection.test.ts +0 -240
- package/test/components/vlist.test.ts +0 -63
- package/test/core/collection/adapter.test.ts +0 -161
- package/test/core/collection/collection.test.ts +0 -394
- package/test/core/layout/layout.test.ts +0 -201
- package/test/utils/dom-helpers.ts +0 -275
- package/test/utils/performance-helpers.ts +0 -392
- package/tsconfig.json +0 -20
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Basic collection tests
|
|
3
|
-
*
|
|
4
|
-
* Tests the core functionality of the collection system
|
|
5
|
-
* including creation, data operations, and template rendering.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, test, expect, beforeEach } from "bun:test";
|
|
9
|
-
import { JSDOM } from "jsdom";
|
|
10
|
-
|
|
11
|
-
// Mock DOM environment for testing
|
|
12
|
-
const dom = new JSDOM("<!DOCTYPE html><html><body></body></html>");
|
|
13
|
-
global.document = dom.window.document;
|
|
14
|
-
global.HTMLElement = dom.window.HTMLElement;
|
|
15
|
-
global.requestAnimationFrame = (cb: FrameRequestCallback) => {
|
|
16
|
-
setTimeout(cb, 0);
|
|
17
|
-
return 0;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
// Import our collection system
|
|
21
|
-
import {
|
|
22
|
-
createCollection,
|
|
23
|
-
type CollectionItem,
|
|
24
|
-
} from "../../src/core/collection";
|
|
25
|
-
|
|
26
|
-
// Test data interface
|
|
27
|
-
interface TestUser extends CollectionItem {
|
|
28
|
-
id: string;
|
|
29
|
-
name: string;
|
|
30
|
-
email: string;
|
|
31
|
-
age: number;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Test data
|
|
35
|
-
const testUsers: TestUser[] = [
|
|
36
|
-
{ id: "1", name: "John Doe", email: "john@example.com", age: 30 },
|
|
37
|
-
{ id: "2", name: "Jane Smith", email: "jane@example.com", age: 25 },
|
|
38
|
-
{ id: "3", name: "Bob Johnson", email: "bob@example.com", age: 35 },
|
|
39
|
-
];
|
|
40
|
-
|
|
41
|
-
describe("Collection System", () => {
|
|
42
|
-
let container: HTMLElement;
|
|
43
|
-
|
|
44
|
-
beforeEach(() => {
|
|
45
|
-
// Create a fresh container for each test
|
|
46
|
-
container = document.createElement("div");
|
|
47
|
-
document.body.appendChild(container);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test("creates collection with basic configuration", () => {
|
|
51
|
-
const collection = createCollection<TestUser>({
|
|
52
|
-
container,
|
|
53
|
-
items: testUsers,
|
|
54
|
-
className: "user-list",
|
|
55
|
-
ariaLabel: "User List",
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
expect(collection.element).toBe(container);
|
|
59
|
-
expect(collection.getSize()).toBe(3);
|
|
60
|
-
expect(collection.isEmpty()).toBe(false);
|
|
61
|
-
expect(container.classList.contains("mtrl-collection")).toBe(true);
|
|
62
|
-
expect(container.classList.contains("user-list")).toBe(true);
|
|
63
|
-
expect(container.getAttribute("aria-label")).toBe("User List");
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test("renders items with default template", async () => {
|
|
67
|
-
const collection = createCollection<TestUser>({
|
|
68
|
-
container,
|
|
69
|
-
items: testUsers,
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// Wait for rendering
|
|
73
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
74
|
-
|
|
75
|
-
expect(container.children.length).toBe(3);
|
|
76
|
-
expect(container.children[0].textContent).toBe("1");
|
|
77
|
-
expect(container.children[1].textContent).toBe("2");
|
|
78
|
-
expect(container.children[2].textContent).toBe("3");
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
test("renders items with object template", async () => {
|
|
82
|
-
const collection = createCollection<TestUser>({
|
|
83
|
-
container,
|
|
84
|
-
items: testUsers,
|
|
85
|
-
template: {
|
|
86
|
-
tag: "div",
|
|
87
|
-
className: "user-item",
|
|
88
|
-
children: [
|
|
89
|
-
{ tag: "div", className: "user-name", textContent: "{{name}}" },
|
|
90
|
-
{ tag: "div", className: "user-email", textContent: "{{email}}" },
|
|
91
|
-
],
|
|
92
|
-
},
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// Wait for rendering
|
|
96
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
97
|
-
|
|
98
|
-
expect(container.children.length).toBe(3);
|
|
99
|
-
|
|
100
|
-
const firstUser = container.children[0] as HTMLElement;
|
|
101
|
-
expect(firstUser.className).toBe("user-item");
|
|
102
|
-
expect(firstUser.children.length).toBe(2);
|
|
103
|
-
expect(firstUser.children[0].textContent).toBe("John Doe");
|
|
104
|
-
expect(firstUser.children[1].textContent).toBe("john@example.com");
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
test("renders items with string template", async () => {
|
|
108
|
-
const collection = createCollection<TestUser>({
|
|
109
|
-
container,
|
|
110
|
-
items: testUsers,
|
|
111
|
-
template: "<div class='user'>{{name}} ({{email}})</div>",
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
// Wait for rendering
|
|
115
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
116
|
-
|
|
117
|
-
expect(container.children.length).toBe(3);
|
|
118
|
-
expect(container.children[0].innerHTML).toBe(
|
|
119
|
-
"<div class='user'>John Doe (john@example.com)</div>"
|
|
120
|
-
);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
test("adds new items", async () => {
|
|
124
|
-
const collection = createCollection<TestUser>({
|
|
125
|
-
container,
|
|
126
|
-
items: [testUsers[0]],
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
expect(collection.getSize()).toBe(1);
|
|
130
|
-
|
|
131
|
-
const newUser: TestUser = {
|
|
132
|
-
id: "4",
|
|
133
|
-
name: "Alice Brown",
|
|
134
|
-
email: "alice@example.com",
|
|
135
|
-
age: 28,
|
|
136
|
-
};
|
|
137
|
-
await collection.add(newUser);
|
|
138
|
-
|
|
139
|
-
expect(collection.getSize()).toBe(2);
|
|
140
|
-
const items = collection.getItems();
|
|
141
|
-
expect(items.length).toBe(2);
|
|
142
|
-
expect(items[1].name).toBe("Alice Brown");
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
test("updates existing items", async () => {
|
|
146
|
-
const collection = createCollection<TestUser>({
|
|
147
|
-
container,
|
|
148
|
-
items: testUsers,
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
const updatedUser: TestUser = {
|
|
152
|
-
id: "1",
|
|
153
|
-
name: "John Smith",
|
|
154
|
-
email: "johnsmith@example.com",
|
|
155
|
-
age: 31,
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
await collection.update(updatedUser);
|
|
159
|
-
|
|
160
|
-
const user = collection.getItem("1");
|
|
161
|
-
expect(user?.name).toBe("John Smith");
|
|
162
|
-
expect(user?.email).toBe("johnsmith@example.com");
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
test("removes items", async () => {
|
|
166
|
-
const collection = createCollection<TestUser>({
|
|
167
|
-
container,
|
|
168
|
-
items: testUsers,
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
expect(collection.getSize()).toBe(3);
|
|
172
|
-
|
|
173
|
-
await collection.remove("2");
|
|
174
|
-
|
|
175
|
-
expect(collection.getSize()).toBe(2);
|
|
176
|
-
expect(collection.getItem("2")).toBeUndefined();
|
|
177
|
-
expect(collection.getItem("1")).toBeDefined();
|
|
178
|
-
expect(collection.getItem("3")).toBeDefined();
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
test("clears all items", () => {
|
|
182
|
-
const collection = createCollection<TestUser>({
|
|
183
|
-
container,
|
|
184
|
-
items: testUsers,
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
expect(collection.getSize()).toBe(3);
|
|
188
|
-
|
|
189
|
-
collection.clear();
|
|
190
|
-
|
|
191
|
-
expect(collection.getSize()).toBe(0);
|
|
192
|
-
expect(collection.isEmpty()).toBe(true);
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
test("filters items with query", () => {
|
|
196
|
-
const collection = createCollection<TestUser>({
|
|
197
|
-
container,
|
|
198
|
-
items: testUsers,
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
expect(collection.getSize()).toBe(3);
|
|
202
|
-
|
|
203
|
-
// Filter users over 30
|
|
204
|
-
collection.query((user) => user.age > 30);
|
|
205
|
-
|
|
206
|
-
expect(collection.getSize()).toBe(1);
|
|
207
|
-
expect(collection.getItems()[0].name).toBe("Bob Johnson");
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
test("sorts items", () => {
|
|
211
|
-
const collection = createCollection<TestUser>({
|
|
212
|
-
container,
|
|
213
|
-
items: testUsers,
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
// Sort by age ascending
|
|
217
|
-
collection.sort((a, b) => a.age - b.age);
|
|
218
|
-
|
|
219
|
-
const items = collection.getItems();
|
|
220
|
-
expect(items[0].name).toBe("Jane Smith"); // age 25
|
|
221
|
-
expect(items[1].name).toBe("John Doe"); // age 30
|
|
222
|
-
expect(items[2].name).toBe("Bob Johnson"); // age 35
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
test("validates items when adding", async () => {
|
|
226
|
-
const collection = createCollection<TestUser>({
|
|
227
|
-
container,
|
|
228
|
-
items: [],
|
|
229
|
-
validate: (user) => user.age >= 18,
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
const validUser: TestUser = {
|
|
233
|
-
id: "1",
|
|
234
|
-
name: "John Doe",
|
|
235
|
-
email: "john@example.com",
|
|
236
|
-
age: 30,
|
|
237
|
-
};
|
|
238
|
-
const invalidUser: TestUser = {
|
|
239
|
-
id: "2",
|
|
240
|
-
name: "Minor",
|
|
241
|
-
email: "minor@example.com",
|
|
242
|
-
age: 16,
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
await collection.add([validUser, invalidUser]);
|
|
246
|
-
|
|
247
|
-
expect(collection.getSize()).toBe(1);
|
|
248
|
-
expect(collection.getItems()[0].name).toBe("John Doe");
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
test("transforms items when adding", async () => {
|
|
252
|
-
const collection = createCollection<TestUser>({
|
|
253
|
-
container,
|
|
254
|
-
items: [],
|
|
255
|
-
transform: (user: any) => ({
|
|
256
|
-
...user,
|
|
257
|
-
name: user.name.toUpperCase(),
|
|
258
|
-
}),
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
const user: TestUser = {
|
|
262
|
-
id: "1",
|
|
263
|
-
name: "John Doe",
|
|
264
|
-
email: "john@example.com",
|
|
265
|
-
age: 30,
|
|
266
|
-
};
|
|
267
|
-
await collection.add(user);
|
|
268
|
-
|
|
269
|
-
expect(collection.getItems()[0].name).toBe("JOHN DOE");
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
test("handles template changes", async () => {
|
|
273
|
-
const collection = createCollection<TestUser>({
|
|
274
|
-
container,
|
|
275
|
-
items: [testUsers[0]],
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
// Change template
|
|
279
|
-
collection.setTemplate({
|
|
280
|
-
tag: "div",
|
|
281
|
-
className: "new-template",
|
|
282
|
-
textContent: "User: {{name}}",
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
// Wait for rendering
|
|
286
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
287
|
-
|
|
288
|
-
expect(container.children[0].textContent).toBe("User: John Doe");
|
|
289
|
-
expect(container.children[0].className).toBe("new-template");
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
test("destroys collection properly", () => {
|
|
293
|
-
const collection = createCollection<TestUser>({
|
|
294
|
-
container,
|
|
295
|
-
items: testUsers,
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
expect(container.children.length).toBeGreaterThan(0);
|
|
299
|
-
|
|
300
|
-
collection.destroy();
|
|
301
|
-
|
|
302
|
-
expect(container.children.length).toBe(0);
|
|
303
|
-
});
|
|
304
|
-
});
|
|
@@ -1,240 +0,0 @@
|
|
|
1
|
-
// test/components/vlist-selection.test.ts
|
|
2
|
-
|
|
3
|
-
import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
|
4
|
-
import { createVList } from "../../src/components/vlist";
|
|
5
|
-
import type { VListComponent } from "../../src/components/vlist/types";
|
|
6
|
-
|
|
7
|
-
describe("VList Selection", () => {
|
|
8
|
-
let container: HTMLElement;
|
|
9
|
-
let vlist: VListComponent<any>;
|
|
10
|
-
|
|
11
|
-
beforeEach(() => {
|
|
12
|
-
// Create container
|
|
13
|
-
container = document.createElement("div");
|
|
14
|
-
container.style.height = "400px";
|
|
15
|
-
container.style.width = "300px";
|
|
16
|
-
document.body.appendChild(container);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
afterEach(() => {
|
|
20
|
-
// Cleanup
|
|
21
|
-
if (vlist?.destroy) {
|
|
22
|
-
vlist.destroy();
|
|
23
|
-
}
|
|
24
|
-
document.body.removeChild(container);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test("should initialize without selection when not enabled", () => {
|
|
28
|
-
const items = Array.from({ length: 10 }, (_, i) => ({
|
|
29
|
-
id: `item-${i}`,
|
|
30
|
-
name: `Item ${i}`,
|
|
31
|
-
}));
|
|
32
|
-
|
|
33
|
-
vlist = createVList({
|
|
34
|
-
container,
|
|
35
|
-
items,
|
|
36
|
-
template: (item) => {
|
|
37
|
-
const div = document.createElement("div");
|
|
38
|
-
div.className = "list-item";
|
|
39
|
-
div.textContent = item.name;
|
|
40
|
-
return div;
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// Selection methods should not be available
|
|
45
|
-
expect(vlist.selectItems).toBeUndefined();
|
|
46
|
-
expect(vlist.getSelectedItems).toBeUndefined();
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test("should initialize with selection when enabled", () => {
|
|
50
|
-
const items = Array.from({ length: 10 }, (_, i) => ({
|
|
51
|
-
id: `item-${i}`,
|
|
52
|
-
name: `Item ${i}`,
|
|
53
|
-
}));
|
|
54
|
-
|
|
55
|
-
vlist = createVList({
|
|
56
|
-
container,
|
|
57
|
-
items,
|
|
58
|
-
selection: {
|
|
59
|
-
enabled: true,
|
|
60
|
-
mode: "single",
|
|
61
|
-
},
|
|
62
|
-
template: (item) => {
|
|
63
|
-
const div = document.createElement("div");
|
|
64
|
-
div.className = "list-item";
|
|
65
|
-
div.textContent = item.name;
|
|
66
|
-
return div;
|
|
67
|
-
},
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
// Selection methods should be available
|
|
71
|
-
expect(vlist.selectItems).toBeDefined();
|
|
72
|
-
expect(vlist.getSelectedItems).toBeDefined();
|
|
73
|
-
expect(vlist.clearSelection).toBeDefined();
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test("should handle single selection mode", () => {
|
|
77
|
-
const items = Array.from({ length: 10 }, (_, i) => ({
|
|
78
|
-
id: `item-${i}`,
|
|
79
|
-
name: `Item ${i}`,
|
|
80
|
-
}));
|
|
81
|
-
|
|
82
|
-
vlist = createVList({
|
|
83
|
-
container,
|
|
84
|
-
items,
|
|
85
|
-
selection: {
|
|
86
|
-
enabled: true,
|
|
87
|
-
mode: "single",
|
|
88
|
-
},
|
|
89
|
-
template: (item) => {
|
|
90
|
-
const div = document.createElement("div");
|
|
91
|
-
div.className = "list-item";
|
|
92
|
-
div.textContent = item.name;
|
|
93
|
-
return div;
|
|
94
|
-
},
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
// Select first item
|
|
98
|
-
vlist.selectItems([0]);
|
|
99
|
-
expect(vlist.getSelectedIndices()).toEqual([0]);
|
|
100
|
-
expect(vlist.getSelectedItems()).toEqual([items[0]]);
|
|
101
|
-
|
|
102
|
-
// Select another item - should replace selection
|
|
103
|
-
vlist.selectItems([2]);
|
|
104
|
-
expect(vlist.getSelectedIndices()).toEqual([2]);
|
|
105
|
-
expect(vlist.getSelectedItems()).toEqual([items[2]]);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test("should handle multiple selection mode", () => {
|
|
109
|
-
const items = Array.from({ length: 10 }, (_, i) => ({
|
|
110
|
-
id: `item-${i}`,
|
|
111
|
-
name: `Item ${i}`,
|
|
112
|
-
}));
|
|
113
|
-
|
|
114
|
-
vlist = createVList({
|
|
115
|
-
container,
|
|
116
|
-
items,
|
|
117
|
-
selection: {
|
|
118
|
-
enabled: true,
|
|
119
|
-
mode: "multiple",
|
|
120
|
-
},
|
|
121
|
-
template: (item) => {
|
|
122
|
-
const div = document.createElement("div");
|
|
123
|
-
div.className = "list-item";
|
|
124
|
-
div.textContent = item.name;
|
|
125
|
-
return div;
|
|
126
|
-
},
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
// Select multiple items
|
|
130
|
-
vlist.selectItems([0, 2, 4]);
|
|
131
|
-
expect(vlist.getSelectedIndices().sort()).toEqual([0, 2, 4]);
|
|
132
|
-
|
|
133
|
-
// Deselect one item
|
|
134
|
-
vlist.deselectItems([2]);
|
|
135
|
-
expect(vlist.getSelectedIndices().sort()).toEqual([0, 4]);
|
|
136
|
-
|
|
137
|
-
// Clear selection
|
|
138
|
-
vlist.clearSelection();
|
|
139
|
-
expect(vlist.getSelectedIndices()).toEqual([]);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
test("should handle click selection", async () => {
|
|
143
|
-
const items = Array.from({ length: 10 }, (_, i) => ({
|
|
144
|
-
id: `item-${i}`,
|
|
145
|
-
name: `Item ${i}`,
|
|
146
|
-
}));
|
|
147
|
-
|
|
148
|
-
vlist = createVList({
|
|
149
|
-
container,
|
|
150
|
-
items,
|
|
151
|
-
selection: {
|
|
152
|
-
enabled: true,
|
|
153
|
-
mode: "single",
|
|
154
|
-
},
|
|
155
|
-
template: (item) => {
|
|
156
|
-
const div = document.createElement("div");
|
|
157
|
-
div.className = "list-item";
|
|
158
|
-
div.textContent = item.name;
|
|
159
|
-
return div;
|
|
160
|
-
},
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
// Wait for render
|
|
164
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
165
|
-
|
|
166
|
-
// Find first item element
|
|
167
|
-
const firstItem = container.querySelector(
|
|
168
|
-
'[data-index="0"]'
|
|
169
|
-
) as HTMLElement;
|
|
170
|
-
expect(firstItem).toBeTruthy();
|
|
171
|
-
|
|
172
|
-
// Click on first item
|
|
173
|
-
firstItem.click();
|
|
174
|
-
|
|
175
|
-
// Check selection
|
|
176
|
-
expect(vlist.getSelectedIndices()).toEqual([0]);
|
|
177
|
-
expect(firstItem.classList.contains("mtrl-list-item--selected")).toBe(true);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
test("should emit selection events", async () => {
|
|
181
|
-
const items = Array.from({ length: 10 }, (_, i) => ({
|
|
182
|
-
id: `item-${i}`,
|
|
183
|
-
name: `Item ${i}`,
|
|
184
|
-
}));
|
|
185
|
-
|
|
186
|
-
let selectionChangeEvent: any = null;
|
|
187
|
-
|
|
188
|
-
vlist = createVList({
|
|
189
|
-
container,
|
|
190
|
-
items,
|
|
191
|
-
selection: {
|
|
192
|
-
enabled: true,
|
|
193
|
-
mode: "single",
|
|
194
|
-
onSelectionChange: (selectedItems, selectedIndices) => {
|
|
195
|
-
selectionChangeEvent = { selectedItems, selectedIndices };
|
|
196
|
-
},
|
|
197
|
-
},
|
|
198
|
-
template: (item) => {
|
|
199
|
-
const div = document.createElement("div");
|
|
200
|
-
div.className = "list-item";
|
|
201
|
-
div.textContent = item.name;
|
|
202
|
-
return div;
|
|
203
|
-
},
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
// Select an item
|
|
207
|
-
vlist.selectItems([3]);
|
|
208
|
-
|
|
209
|
-
// Check event was fired
|
|
210
|
-
expect(selectionChangeEvent).toBeTruthy();
|
|
211
|
-
expect(selectionChangeEvent.selectedIndices).toEqual([3]);
|
|
212
|
-
expect(selectionChangeEvent.selectedItems).toEqual([items[3]]);
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
test("should handle initial selection", () => {
|
|
216
|
-
const items = Array.from({ length: 10 }, (_, i) => ({
|
|
217
|
-
id: `item-${i}`,
|
|
218
|
-
name: `Item ${i}`,
|
|
219
|
-
}));
|
|
220
|
-
|
|
221
|
-
vlist = createVList({
|
|
222
|
-
container,
|
|
223
|
-
items,
|
|
224
|
-
selection: {
|
|
225
|
-
enabled: true,
|
|
226
|
-
mode: "multiple",
|
|
227
|
-
selectedIndices: [1, 3, 5],
|
|
228
|
-
},
|
|
229
|
-
template: (item) => {
|
|
230
|
-
const div = document.createElement("div");
|
|
231
|
-
div.className = "list-item";
|
|
232
|
-
div.textContent = item.name;
|
|
233
|
-
return div;
|
|
234
|
-
},
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
// Check initial selection
|
|
238
|
-
expect(vlist.getSelectedIndices().sort()).toEqual([1, 3, 5]);
|
|
239
|
-
});
|
|
240
|
-
});
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
// test/components/vlist.test.ts
|
|
2
|
-
|
|
3
|
-
import { describe, it, expect, beforeEach } from "bun:test";
|
|
4
|
-
import { JSDOM } from "jsdom";
|
|
5
|
-
|
|
6
|
-
// Mock DOM environment for testing
|
|
7
|
-
const dom = new JSDOM("<!DOCTYPE html><html><body></body></html>");
|
|
8
|
-
global.document = dom.window.document;
|
|
9
|
-
global.HTMLElement = dom.window.HTMLElement;
|
|
10
|
-
global.window = dom.window as any;
|
|
11
|
-
global.navigator = dom.window.navigator;
|
|
12
|
-
global.requestAnimationFrame = (cb: FrameRequestCallback) => {
|
|
13
|
-
setTimeout(cb, 0);
|
|
14
|
-
return 0;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
// Import VList after DOM setup
|
|
18
|
-
import { createVList } from "../../src/components/vlist";
|
|
19
|
-
|
|
20
|
-
describe("VList Component", () => {
|
|
21
|
-
let container: HTMLElement;
|
|
22
|
-
|
|
23
|
-
beforeEach(() => {
|
|
24
|
-
container = document.createElement("div");
|
|
25
|
-
document.body.appendChild(container);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("should create a VList component", () => {
|
|
29
|
-
const vlist = createVList({
|
|
30
|
-
container,
|
|
31
|
-
items: ["Item 1", "Item 2", "Item 3"],
|
|
32
|
-
template: (item) => `<div>${item}</div>`,
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
expect(vlist).toBeDefined();
|
|
36
|
-
expect(vlist.element).toBeDefined();
|
|
37
|
-
expect(vlist.element.tagName).toBe("DIV");
|
|
38
|
-
expect(vlist.element.className).toContain("mtrl-vlist");
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("should have viewport functionality", () => {
|
|
42
|
-
const vlist = createVList({
|
|
43
|
-
container,
|
|
44
|
-
items: Array.from({ length: 100 }, (_, i) => `Item ${i}`),
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
expect(vlist.viewport).toBeDefined();
|
|
48
|
-
expect(typeof vlist.viewport.scrollToIndex).toBe("function");
|
|
49
|
-
expect(typeof vlist.viewport.getVisibleRange).toBe("function");
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("should have public API methods", () => {
|
|
53
|
-
const vlist = createVList({ container });
|
|
54
|
-
|
|
55
|
-
expect(typeof vlist.setItems).toBe("function");
|
|
56
|
-
expect(typeof vlist.getItems).toBe("function");
|
|
57
|
-
expect(typeof vlist.scrollToIndex).toBe("function");
|
|
58
|
-
expect(typeof vlist.scrollToTop).toBe("function");
|
|
59
|
-
expect(typeof vlist.scrollToBottom).toBe("function");
|
|
60
|
-
expect(typeof vlist.getState).toBe("function");
|
|
61
|
-
expect(typeof vlist.destroy).toBe("function");
|
|
62
|
-
});
|
|
63
|
-
});
|