mtrl-addons 0.1.0 → 0.1.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.
Files changed (92) hide show
  1. package/.cursorrules +117 -0
  2. package/AI.md +241 -0
  3. package/build.js +170 -0
  4. package/bun.lock +792 -0
  5. package/index.ts +7 -0
  6. package/package.json +10 -17
  7. package/scripts/analyze-orphaned-functions.ts +387 -0
  8. package/src/components/index.ts +45 -0
  9. package/src/components/list/api.ts +314 -0
  10. package/src/components/list/config.ts +352 -0
  11. package/src/components/list/constants.ts +56 -0
  12. package/src/components/list/features/api.ts +428 -0
  13. package/src/components/list/features/index.ts +31 -0
  14. package/src/components/list/features/list-manager.ts +502 -0
  15. package/src/components/list/features.ts +112 -0
  16. package/src/components/list/index.ts +39 -0
  17. package/src/components/list/list.ts +234 -0
  18. package/src/components/list/types.ts +513 -0
  19. package/src/core/collection/base-collection.ts +100 -0
  20. package/src/core/collection/collection-composer.ts +178 -0
  21. package/src/core/collection/collection.ts +745 -0
  22. package/src/core/collection/constants.ts +172 -0
  23. package/src/core/collection/events.ts +428 -0
  24. package/src/core/collection/features/api/loading.ts +279 -0
  25. package/src/core/collection/features/operations/data-operations.ts +147 -0
  26. package/src/core/collection/index.ts +104 -0
  27. package/src/core/collection/state.ts +497 -0
  28. package/src/core/collection/types.ts +404 -0
  29. package/src/core/compose/features/collection.ts +119 -0
  30. package/src/core/compose/features/index.ts +39 -0
  31. package/src/core/compose/features/performance.ts +161 -0
  32. package/src/core/compose/features/selection.ts +213 -0
  33. package/src/core/compose/features/styling.ts +108 -0
  34. package/src/core/compose/index.ts +31 -0
  35. package/src/core/index.ts +167 -0
  36. package/src/core/layout/config.ts +102 -0
  37. package/src/core/layout/index.ts +168 -0
  38. package/src/core/layout/jsx.ts +174 -0
  39. package/src/core/layout/schema.ts +963 -0
  40. package/src/core/layout/types.ts +92 -0
  41. package/src/core/list-manager/api.ts +599 -0
  42. package/src/core/list-manager/config.ts +593 -0
  43. package/src/core/list-manager/constants.ts +268 -0
  44. package/src/core/list-manager/features/api.ts +58 -0
  45. package/src/core/list-manager/features/collection/collection.ts +705 -0
  46. package/src/core/list-manager/features/collection/index.ts +17 -0
  47. package/src/core/list-manager/features/viewport/constants.ts +42 -0
  48. package/src/core/list-manager/features/viewport/index.ts +16 -0
  49. package/src/core/list-manager/features/viewport/item-size.ts +274 -0
  50. package/src/core/list-manager/features/viewport/loading.ts +263 -0
  51. package/src/core/list-manager/features/viewport/placeholders.ts +281 -0
  52. package/src/core/list-manager/features/viewport/rendering.ts +575 -0
  53. package/src/core/list-manager/features/viewport/scrollbar.ts +495 -0
  54. package/src/core/list-manager/features/viewport/scrolling.ts +795 -0
  55. package/src/core/list-manager/features/viewport/template.ts +220 -0
  56. package/src/core/list-manager/features/viewport/viewport.ts +654 -0
  57. package/src/core/list-manager/features/viewport/virtual.ts +309 -0
  58. package/src/core/list-manager/index.ts +279 -0
  59. package/src/core/list-manager/list-manager.ts +206 -0
  60. package/src/core/list-manager/types.ts +439 -0
  61. package/src/core/list-manager/utils/calculations.ts +290 -0
  62. package/src/core/list-manager/utils/range-calculator.ts +349 -0
  63. package/src/core/list-manager/utils/speed-tracker.ts +273 -0
  64. package/src/index.ts +17 -0
  65. package/src/styles/components/_list.scss +244 -0
  66. package/src/styles/index.scss +12 -0
  67. package/src/types/mtrl.d.ts +6 -0
  68. package/test/benchmarks/layout/advanced.test.ts +656 -0
  69. package/test/benchmarks/layout/comparison.test.ts +519 -0
  70. package/test/benchmarks/layout/performance-comparison.test.ts +274 -0
  71. package/test/benchmarks/layout/real-components.test.ts +733 -0
  72. package/test/benchmarks/layout/simple.test.ts +321 -0
  73. package/test/benchmarks/layout/stress.test.ts +990 -0
  74. package/test/collection/basic.test.ts +304 -0
  75. package/test/components/list.test.ts +256 -0
  76. package/test/core/collection/collection.test.ts +394 -0
  77. package/test/core/collection/failed-ranges.test.ts +270 -0
  78. package/test/core/compose/features.test.ts +183 -0
  79. package/test/core/layout/layout.test.ts +201 -0
  80. package/test/core/list-manager/features/collection.test.ts +704 -0
  81. package/test/core/list-manager/features/viewport.test.ts +698 -0
  82. package/test/core/list-manager/list-manager.test.ts +593 -0
  83. package/test/core/list-manager/utils/calculations.test.ts +433 -0
  84. package/test/core/list-manager/utils/range-calculator.test.ts +569 -0
  85. package/test/core/list-manager/utils/speed-tracker.test.ts +530 -0
  86. package/test/utils/dom-helpers.ts +275 -0
  87. package/test/utils/performance-helpers.ts +392 -0
  88. package/tsconfig.build.json +14 -0
  89. package/tsconfig.json +20 -0
  90. package/dist/index.d.ts +0 -5
  91. package/dist/index.js +0 -38
  92. package/dist/index.mjs +0 -8
@@ -0,0 +1,593 @@
1
+ import {
2
+ describe,
3
+ it,
4
+ expect,
5
+ beforeEach,
6
+ afterEach,
7
+ mock,
8
+ spyOn,
9
+ } from "bun:test";
10
+ import {
11
+ createListManager,
12
+ ListManagerImpl,
13
+ } from "../../../src/core/list-manager/list-manager";
14
+ import { ListManagerEvents } from "../../../src/core/list-manager/types";
15
+ import type {
16
+ ListManagerConfig,
17
+ ListManagerConfigUpdate,
18
+ } from "../../../src/core/list-manager/types";
19
+
20
+ // Mock DOM environment
21
+ const mockContainer = {
22
+ getBoundingClientRect: () => ({ width: 400, height: 600, top: 0, left: 0 }),
23
+ scrollTop: 0,
24
+ scrollLeft: 0,
25
+ clientWidth: 400,
26
+ clientHeight: 600,
27
+ addEventListener: mock(),
28
+ removeEventListener: mock(),
29
+ appendChild: mock(),
30
+ removeChild: mock(),
31
+ style: {},
32
+ } as unknown as HTMLElement;
33
+
34
+ // Mock features
35
+ const mockViewportFeature = {
36
+ initialize: mock(),
37
+ destroy: mock(),
38
+ scrollToIndex: mock(),
39
+ calculateVisibleRange: mock(() => ({ start: 0, end: 10 })),
40
+ getViewportInfo: mock(() => ({
41
+ containerSize: 600,
42
+ totalVirtualSize: 50000,
43
+ visibleRange: { start: 0, end: 10 },
44
+ virtualScrollPosition: 0,
45
+ })),
46
+ updateContainerPosition: mock(),
47
+ updateScrollbar: mock(),
48
+ setTotalItems: mock(),
49
+ virtualScrollPosition: 0,
50
+ orientation: "vertical",
51
+ estimatedItemSize: 50,
52
+ };
53
+
54
+ const mockCollectionFeature = {
55
+ initialize: mock(),
56
+ destroy: mock(),
57
+ setTemplate: mock(),
58
+ setItems: mock(),
59
+ getItemsInRange: mock(() => [] as any[]),
60
+ handleVisibleRangeChange: mock(),
61
+ handleScrollPositionChange: mock(),
62
+ adaptPaginationStrategy: mock(),
63
+ paginationStrategy: "page",
64
+ };
65
+
66
+ // Mock feature factories
67
+ mock.module("../../../src/core/list-manager/features/viewport", () => ({
68
+ createViewportFeature: mock(() => mockViewportFeature),
69
+ }));
70
+
71
+ mock.module("../../../src/core/list-manager/features/collection", () => ({
72
+ createCollectionFeature: mock(() => mockCollectionFeature),
73
+ }));
74
+
75
+ describe("ListManager", () => {
76
+ let config: ListManagerConfig;
77
+ let listManager: ListManagerImpl;
78
+
79
+ beforeEach(() => {
80
+ // Reset all mocks
81
+ mock.restore();
82
+
83
+ // Create test configuration with all required properties
84
+ config = {
85
+ container: mockContainer,
86
+ items: Array.from({ length: 100 }, (_, i) => ({
87
+ id: i,
88
+ name: `Item ${i}`,
89
+ })),
90
+ template: {
91
+ template: (item: any, index: number) => {
92
+ const div = document.createElement("div");
93
+ div.textContent = `${index}: ${item.name}`;
94
+ return div;
95
+ },
96
+ },
97
+ virtual: {
98
+ enabled: true,
99
+ itemSize: "auto",
100
+ estimatedItemSize: 50,
101
+ overscan: 5,
102
+ },
103
+ orientation: {
104
+ orientation: "vertical",
105
+ reverse: false,
106
+ crossAxisAlignment: "stretch",
107
+ },
108
+ initialLoad: {
109
+ strategy: "placeholders",
110
+ viewportMultiplier: 1.5,
111
+ minItems: 10,
112
+ maxItems: 100,
113
+ },
114
+ errorHandling: {
115
+ timeout: 5000,
116
+ showErrorItems: true,
117
+ retryAttempts: 3,
118
+ preserveScrollOnError: true,
119
+ },
120
+ positioning: {
121
+ precisePositioning: true,
122
+ allowPartialItems: true,
123
+ snapToItems: false,
124
+ },
125
+ boundaries: {
126
+ preventOverscroll: true,
127
+ maintainEdgeRanges: true,
128
+ boundaryResistance: 0.15,
129
+ },
130
+ recycling: {
131
+ enabled: false,
132
+ maxPoolSize: 50,
133
+ minPoolSize: 10,
134
+ },
135
+ performance: {
136
+ frameScheduling: true,
137
+ memoryCleanup: true,
138
+ },
139
+ intersection: {
140
+ pagination: {
141
+ enabled: false,
142
+ rootMargin: "100px",
143
+ threshold: 0.1,
144
+ },
145
+ loading: {
146
+ enabled: false,
147
+ },
148
+ },
149
+ debug: true,
150
+ prefix: "test-list",
151
+ componentName: "TestList",
152
+ };
153
+ });
154
+
155
+ afterEach(() => {
156
+ if (listManager) {
157
+ listManager.destroy();
158
+ }
159
+ });
160
+
161
+ describe("Initialization", () => {
162
+ it("should create a ListManager with valid config", () => {
163
+ listManager = new ListManagerImpl(config);
164
+ expect(listManager).toBeDefined();
165
+ expect(listManager.getTotalItems()).toBe(100);
166
+ });
167
+
168
+ it("should throw error with invalid container", () => {
169
+ const invalidConfig = { ...config, container: null as any };
170
+ expect(() => new ListManagerImpl(invalidConfig)).toThrow();
171
+ });
172
+
173
+ it("should initialize features when initialize() is called", () => {
174
+ listManager = new ListManagerImpl(config);
175
+ listManager.initialize();
176
+
177
+ expect(mockViewportFeature.initialize).toHaveBeenCalled();
178
+ expect(mockCollectionFeature.initialize).toHaveBeenCalled();
179
+ });
180
+
181
+ it("should not initialize twice", () => {
182
+ listManager = new ListManagerImpl(config);
183
+ listManager.initialize();
184
+ listManager.initialize();
185
+
186
+ expect(mockViewportFeature.initialize).toHaveBeenCalledTimes(1);
187
+ });
188
+
189
+ it("should set template if provided", () => {
190
+ listManager = new ListManagerImpl(config);
191
+ listManager.initialize();
192
+
193
+ expect(mockCollectionFeature.setTemplate).toHaveBeenCalledWith(
194
+ config.template!.template
195
+ );
196
+ });
197
+
198
+ it("should set items if provided", () => {
199
+ listManager = new ListManagerImpl(config);
200
+ listManager.initialize();
201
+
202
+ expect(mockCollectionFeature.setItems).toHaveBeenCalledWith(config.items);
203
+ });
204
+ });
205
+
206
+ describe("Virtual Scrolling API", () => {
207
+ beforeEach(() => {
208
+ listManager = new ListManagerImpl(config);
209
+ listManager.initialize();
210
+ });
211
+
212
+ it("should scroll to index", () => {
213
+ listManager.scrollToIndex(50, "center");
214
+ expect(mockViewportFeature.scrollToIndex).toHaveBeenCalledWith(
215
+ 50,
216
+ "center"
217
+ );
218
+ });
219
+
220
+ it("should not scroll to invalid index", () => {
221
+ const consoleSpy = spyOn(console, "warn").mockImplementation(() => {});
222
+
223
+ listManager.scrollToIndex(-1);
224
+ listManager.scrollToIndex(1000);
225
+
226
+ expect(mockViewportFeature.scrollToIndex).not.toHaveBeenCalled();
227
+ expect(consoleSpy).toHaveBeenCalledTimes(2);
228
+
229
+ consoleSpy.mockRestore();
230
+ });
231
+
232
+ it("should scroll to page", () => {
233
+ listManager.scrollToPage(5, "start");
234
+
235
+ // Page 5 should target index 80 (assuming default page size of 20)
236
+ expect(mockViewportFeature.scrollToIndex).toHaveBeenCalledWith(
237
+ 80,
238
+ "start"
239
+ );
240
+ });
241
+
242
+ it("should not scroll to page out of range", () => {
243
+ const consoleSpy = spyOn(console, "warn").mockImplementation(() => {});
244
+
245
+ listManager.scrollToPage(100); // Way beyond item count
246
+
247
+ expect(mockViewportFeature.scrollToIndex).not.toHaveBeenCalled();
248
+ expect(consoleSpy).toHaveBeenCalled();
249
+
250
+ consoleSpy.mockRestore();
251
+ });
252
+
253
+ it("should get scroll position", () => {
254
+ mockViewportFeature.virtualScrollPosition = 1000;
255
+ expect(listManager.getScrollPosition()).toBe(1000);
256
+ });
257
+ });
258
+
259
+ describe("Viewport Management", () => {
260
+ beforeEach(() => {
261
+ listManager = new ListManagerImpl(config);
262
+ listManager.initialize();
263
+ });
264
+
265
+ it("should get visible range", () => {
266
+ const range = listManager.getVisibleRange();
267
+ expect(range).toEqual({ start: 0, end: 10 });
268
+ expect(mockViewportFeature.calculateVisibleRange).toHaveBeenCalled();
269
+ });
270
+
271
+ it("should get viewport info", () => {
272
+ const info = listManager.getViewportInfo();
273
+ expect(info).toEqual({
274
+ containerSize: 600,
275
+ totalVirtualSize: 50000,
276
+ visibleRange: { start: 0, end: 10 },
277
+ virtualScrollPosition: 0,
278
+ });
279
+ });
280
+
281
+ it("should update viewport", () => {
282
+ listManager.updateViewport();
283
+
284
+ expect(mockViewportFeature.updateContainerPosition).toHaveBeenCalled();
285
+ expect(mockViewportFeature.updateScrollbar).toHaveBeenCalled();
286
+ expect(mockCollectionFeature.handleVisibleRangeChange).toHaveBeenCalled();
287
+ });
288
+
289
+ it("should return default values when not initialized", () => {
290
+ const uninitializedManager = new ListManagerImpl(config);
291
+
292
+ expect(uninitializedManager.getVisibleRange()).toEqual({
293
+ start: 0,
294
+ end: 0,
295
+ });
296
+ expect(uninitializedManager.getScrollPosition()).toBe(0);
297
+ });
298
+ });
299
+
300
+ describe("Collection Integration", () => {
301
+ beforeEach(() => {
302
+ listManager = new ListManagerImpl(config);
303
+ listManager.initialize();
304
+ });
305
+
306
+ it("should set items", () => {
307
+ const newItems = [{ id: 1, name: "New Item" }];
308
+ listManager.setItems(newItems);
309
+
310
+ expect(mockViewportFeature.setTotalItems).toHaveBeenCalledWith(1);
311
+ expect(mockCollectionFeature.setItems).toHaveBeenCalledWith(newItems);
312
+ expect(listManager.getTotalItems()).toBe(1);
313
+ });
314
+
315
+ it("should set total items", () => {
316
+ listManager.setTotalItems(500);
317
+
318
+ expect(mockViewportFeature.setTotalItems).toHaveBeenCalledWith(500);
319
+ expect(listManager.getTotalItems()).toBe(500);
320
+ });
321
+
322
+ it("should get items in visible range", () => {
323
+ const mockItems: any[] = [
324
+ { item: { id: 1, name: "Item 1" }, index: 0 },
325
+ { item: { id: 2, name: "Item 2" }, index: 1 },
326
+ ];
327
+ mockCollectionFeature.getItemsInRange.mockReturnValue(mockItems);
328
+
329
+ const items = listManager.getItems();
330
+ expect(items).toEqual([
331
+ { id: 1, name: "Item 1" },
332
+ { id: 2, name: "Item 2" },
333
+ ]);
334
+ });
335
+
336
+ it("should set pagination strategy", () => {
337
+ listManager.setPaginationStrategy("cursor");
338
+
339
+ expect(
340
+ mockCollectionFeature.adaptPaginationStrategy
341
+ ).toHaveBeenCalledWith("cursor");
342
+ });
343
+
344
+ it("should get pagination strategy", () => {
345
+ mockCollectionFeature.paginationStrategy = "offset";
346
+ expect(listManager.getPaginationStrategy()).toBe("offset");
347
+ });
348
+ });
349
+
350
+ describe("Configuration Management", () => {
351
+ beforeEach(() => {
352
+ listManager = new ListManagerImpl(config);
353
+ listManager.initialize();
354
+ });
355
+
356
+ it("should update configuration", () => {
357
+ const configUpdate: ListManagerConfigUpdate = {
358
+ virtual: {
359
+ enabled: true,
360
+ itemSize: "auto",
361
+ estimatedItemSize: 100,
362
+ overscan: 5,
363
+ },
364
+ debug: false,
365
+ };
366
+
367
+ listManager.updateConfig(configUpdate);
368
+
369
+ const updatedConfig = listManager.getConfig();
370
+ expect(updatedConfig.virtual.estimatedItemSize).toBe(100);
371
+ expect(updatedConfig.debug).toBe(false);
372
+ });
373
+
374
+ it("should return copy of config", () => {
375
+ const returnedConfig = listManager.getConfig();
376
+ returnedConfig.debug = false;
377
+
378
+ expect(listManager.getConfig().debug).toBe(true); // Original should be unchanged
379
+ });
380
+ });
381
+
382
+ describe("Event System", () => {
383
+ beforeEach(() => {
384
+ listManager = new ListManagerImpl(config);
385
+ listManager.initialize();
386
+ });
387
+
388
+ it("should subscribe to events", () => {
389
+ const observer = mock();
390
+ const unsubscribe = listManager.subscribe(observer);
391
+
392
+ listManager.emit(ListManagerEvents.INITIALIZED, { config });
393
+
394
+ expect(observer).toHaveBeenCalledWith(ListManagerEvents.INITIALIZED, {
395
+ config,
396
+ });
397
+
398
+ unsubscribe();
399
+ });
400
+
401
+ it("should unsubscribe from events", () => {
402
+ const observer = mock();
403
+ const unsubscribe = listManager.subscribe(observer);
404
+
405
+ unsubscribe();
406
+ listManager.emit(ListManagerEvents.INITIALIZED, { config });
407
+
408
+ expect(observer).not.toHaveBeenCalled();
409
+ });
410
+
411
+ it("should handle observer errors gracefully", () => {
412
+ const badObserver = mock(() => {
413
+ throw new Error("Observer error");
414
+ });
415
+ const consoleSpy = spyOn(console, "error").mockImplementation(() => {});
416
+
417
+ listManager.subscribe(badObserver);
418
+ listManager.emit(ListManagerEvents.INITIALIZED, { config });
419
+
420
+ expect(consoleSpy).toHaveBeenCalledWith(
421
+ "[List Manager] Observer error:",
422
+ expect.any(Error)
423
+ );
424
+ consoleSpy.mockRestore();
425
+ });
426
+
427
+ it("should emit events to multiple observers", () => {
428
+ const observer1 = mock();
429
+ const observer2 = mock();
430
+
431
+ listManager.subscribe(observer1);
432
+ listManager.subscribe(observer2);
433
+
434
+ listManager.emit(ListManagerEvents.INITIALIZED, { config });
435
+
436
+ expect(observer1).toHaveBeenCalled();
437
+ expect(observer2).toHaveBeenCalled();
438
+ });
439
+ });
440
+
441
+ describe("Feature Coordination", () => {
442
+ beforeEach(() => {
443
+ listManager = new ListManagerImpl(config);
444
+ listManager.initialize();
445
+ });
446
+
447
+ it("should coordinate scroll events between features", () => {
448
+ // Simulate scroll position change
449
+ listManager.emit(ListManagerEvents.SCROLL_POSITION_CHANGED, {
450
+ position: 1000,
451
+ direction: "forward",
452
+ });
453
+
454
+ expect(
455
+ mockCollectionFeature.handleScrollPositionChange
456
+ ).toHaveBeenCalledWith(1000, "forward");
457
+ });
458
+
459
+ it("should coordinate range change events", () => {
460
+ const newRange = { start: 10, end: 20 };
461
+
462
+ listManager.emit(ListManagerEvents.VIRTUAL_RANGE_CHANGED, newRange);
463
+
464
+ expect(
465
+ mockCollectionFeature.handleVisibleRangeChange
466
+ ).toHaveBeenCalledWith(newRange);
467
+ });
468
+
469
+ it("should coordinate viewport change events", () => {
470
+ const viewportData = {
471
+ containerSize: 600,
472
+ totalVirtualSize: 50000,
473
+ visibleRange: { start: 5, end: 15 },
474
+ virtualScrollPosition: 500,
475
+ };
476
+
477
+ listManager.emit(ListManagerEvents.VIEWPORT_CHANGED, viewportData);
478
+
479
+ expect(
480
+ mockCollectionFeature.handleVisibleRangeChange
481
+ ).toHaveBeenCalledWith(viewportData.visibleRange);
482
+ });
483
+ });
484
+
485
+ describe("Lifecycle Management", () => {
486
+ beforeEach(() => {
487
+ listManager = new ListManagerImpl(config);
488
+ listManager.initialize();
489
+ });
490
+
491
+ it("should destroy properly", () => {
492
+ listManager.destroy();
493
+
494
+ expect(mockViewportFeature.destroy).toHaveBeenCalled();
495
+ expect(mockCollectionFeature.destroy).toHaveBeenCalled();
496
+ });
497
+
498
+ it("should not destroy twice", () => {
499
+ listManager.destroy();
500
+ listManager.destroy();
501
+
502
+ expect(mockViewportFeature.destroy).toHaveBeenCalledTimes(1);
503
+ });
504
+
505
+ it("should emit destroy event", () => {
506
+ const observer = mock();
507
+ listManager.subscribe(observer);
508
+
509
+ listManager.destroy();
510
+
511
+ expect(observer).toHaveBeenCalledWith(ListManagerEvents.DESTROYED, {
512
+ reason: "manual-destroy",
513
+ });
514
+ });
515
+ });
516
+ });
517
+
518
+ describe("createListManager factory", () => {
519
+ it("should create and auto-initialize ListManager", () => {
520
+ const consoleSpy = spyOn(console, "log").mockImplementation(() => {});
521
+
522
+ const factoryConfig: ListManagerConfig = {
523
+ container: mockContainer,
524
+ virtual: {
525
+ enabled: true,
526
+ itemSize: "auto",
527
+ estimatedItemSize: 50,
528
+ overscan: 5,
529
+ },
530
+ orientation: {
531
+ orientation: "vertical",
532
+ reverse: false,
533
+ crossAxisAlignment: "stretch",
534
+ },
535
+ initialLoad: {
536
+ strategy: "placeholders",
537
+ viewportMultiplier: 1.5,
538
+ minItems: 10,
539
+ maxItems: 100,
540
+ },
541
+ errorHandling: {
542
+ timeout: 5000,
543
+ showErrorItems: true,
544
+ retryAttempts: 3,
545
+ preserveScrollOnError: true,
546
+ },
547
+ positioning: {
548
+ precisePositioning: true,
549
+ allowPartialItems: true,
550
+ snapToItems: false,
551
+ },
552
+ boundaries: {
553
+ preventOverscroll: true,
554
+ maintainEdgeRanges: true,
555
+ boundaryResistance: 0.15,
556
+ },
557
+ recycling: {
558
+ enabled: false,
559
+ maxPoolSize: 50,
560
+ minPoolSize: 10,
561
+ },
562
+ performance: {
563
+ frameScheduling: true,
564
+ memoryCleanup: true,
565
+ },
566
+ intersection: {
567
+ pagination: {
568
+ enabled: false,
569
+ rootMargin: "100px",
570
+ threshold: 0.1,
571
+ },
572
+ loading: {
573
+ enabled: false,
574
+ },
575
+ },
576
+ debug: true,
577
+ prefix: "test-list",
578
+ componentName: "TestList",
579
+ };
580
+
581
+ const listManager = createListManager(factoryConfig);
582
+
583
+ expect(listManager).toBeDefined();
584
+ expect(listManager.getTotalItems()).toBe(0);
585
+ expect(consoleSpy).toHaveBeenCalledWith(
586
+ "[List Manager] Created with config:",
587
+ expect.any(Object)
588
+ );
589
+
590
+ consoleSpy.mockRestore();
591
+ listManager.destroy();
592
+ });
593
+ });