mtrl 0.3.1 → 0.3.2

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 (159) hide show
  1. package/.env +15 -0
  2. package/CONTRIBUTING.md +8 -8
  3. package/DOCS.md +3 -3
  4. package/README.md +43 -20
  5. package/TESTING.md +128 -18
  6. package/dist/index.js +14865 -0
  7. package/git-user-stats.js +545 -0
  8. package/index.ts +9 -67
  9. package/package.json +8 -3
  10. package/src/components/badge/api.ts +15 -1
  11. package/src/components/badge/badge.ts +43 -4
  12. package/src/components/badge/config.ts +40 -8
  13. package/src/components/badge/index.ts +64 -3
  14. package/src/components/badge/types.ts +175 -33
  15. package/src/components/button/api.ts +63 -1
  16. package/src/components/button/button.ts +39 -3
  17. package/src/components/button/config.ts +21 -4
  18. package/src/components/button/index.ts +26 -1
  19. package/src/components/button/types.ts +7 -1
  20. package/src/components/card/api.ts +78 -9
  21. package/src/components/card/card.ts +58 -3
  22. package/src/components/card/config.ts +41 -11
  23. package/src/components/card/features.ts +39 -12
  24. package/src/components/card/index.ts +84 -19
  25. package/src/components/card/types.ts +218 -29
  26. package/src/components/carousel/carousel.ts +92 -28
  27. package/src/components/carousel/constants.ts +107 -21
  28. package/src/components/carousel/index.ts +31 -13
  29. package/src/components/checkbox/checkbox.ts +83 -16
  30. package/src/components/checkbox/index.ts +43 -1
  31. package/src/components/checkbox/types.ts +219 -32
  32. package/src/components/chips/api.ts +194 -0
  33. package/src/components/{chip → chips/chip}/api.ts +42 -2
  34. package/src/components/chips/chip/chip.ts +131 -0
  35. package/src/components/{chip → chips/chip}/config.ts +3 -3
  36. package/src/components/chips/chip/index.ts +3 -0
  37. package/src/components/chips/chips.md +481 -0
  38. package/src/components/chips/chips.ts +75 -0
  39. package/src/components/chips/config.ts +109 -0
  40. package/src/components/chips/constants.ts +61 -0
  41. package/src/components/chips/features/chip-items.ts +33 -0
  42. package/src/components/chips/features/container.ts +77 -0
  43. package/src/components/chips/features/controller.ts +448 -0
  44. package/src/components/chips/features/index.ts +5 -0
  45. package/src/components/chips/features/label.ts +108 -0
  46. package/src/components/chips/index.ts +11 -0
  47. package/src/components/chips/schema.ts +61 -0
  48. package/src/components/{chip → chips}/types.ts +203 -92
  49. package/src/components/dialog/dialog.ts +99 -16
  50. package/src/components/dialog/index.ts +97 -1
  51. package/src/components/dialog/types.ts +375 -69
  52. package/src/components/divider/config.ts +90 -6
  53. package/src/components/divider/divider.ts +32 -2
  54. package/src/components/divider/features.ts +26 -0
  55. package/src/components/divider/index.ts +30 -0
  56. package/src/components/divider/types.ts +86 -9
  57. package/src/components/extended-fab/api.ts +53 -1
  58. package/src/components/extended-fab/config.ts +29 -1
  59. package/src/components/extended-fab/extended-fab.ts +28 -0
  60. package/src/components/extended-fab/index.ts +36 -0
  61. package/src/components/extended-fab/types.ts +458 -13
  62. package/src/components/fab/api.ts +42 -2
  63. package/src/components/fab/config.ts +29 -1
  64. package/src/components/fab/fab.ts +16 -2
  65. package/src/components/fab/index.ts +35 -0
  66. package/src/components/fab/types.ts +374 -10
  67. package/src/components/list/api.ts +12 -2
  68. package/src/components/list/config.ts +21 -0
  69. package/src/components/list/features.ts +6 -0
  70. package/src/components/list/index.ts +56 -1
  71. package/src/components/list/list-item.ts +46 -2
  72. package/src/components/list/list.ts +73 -2
  73. package/src/components/list/types.ts +172 -0
  74. package/src/components/list/utils.ts +26 -2
  75. package/src/components/menu/api.ts +217 -20
  76. package/src/components/menu/config.ts +27 -0
  77. package/src/components/menu/features/visibility.ts +55 -6
  78. package/src/components/menu/index.ts +64 -0
  79. package/src/components/menu/menu-item.ts +46 -3
  80. package/src/components/menu/menu.ts +77 -1
  81. package/src/components/menu/types.ts +404 -39
  82. package/src/components/sheet/config.ts +1 -2
  83. package/src/components/sheet/features/gestures.ts +1 -1
  84. package/src/components/sheet/features/position.ts +1 -2
  85. package/src/components/sheet/features/state.ts +1 -1
  86. package/src/components/sheet/index.ts +10 -2
  87. package/src/components/sheet/sheet.ts +1 -2
  88. package/src/components/sheet/types.ts +29 -1
  89. package/src/components/slider/api.ts +1 -1
  90. package/src/components/slider/config.ts +1 -1
  91. package/src/components/slider/features/controller.ts +1 -1
  92. package/src/components/slider/features/handlers.ts +1 -1
  93. package/src/components/slider/features/states.ts +1 -1
  94. package/src/components/slider/index.ts +12 -5
  95. package/src/components/slider/schema.ts +1 -1
  96. package/src/components/slider/types.ts +31 -0
  97. package/src/components/tabs/tab-api.ts +1 -1
  98. package/src/components/tabs/types.ts +1 -1
  99. package/src/components/tooltip/api.ts +6 -2
  100. package/src/components/tooltip/config.ts +9 -28
  101. package/src/components/tooltip/index.ts +10 -1
  102. package/src/components/tooltip/types.ts +38 -3
  103. package/src/index.ts +129 -31
  104. package/src/styles/abstract/_mixins.scss +23 -9
  105. package/src/styles/abstract/_variables.scss +14 -4
  106. package/src/styles/components/_card.scss +1 -1
  107. package/src/styles/components/_chip.scss +323 -113
  108. package/src/styles/components/_tabs.scss +1 -1
  109. package/CLAUDE.md +0 -33
  110. package/src/components/checkbox/constants.ts +0 -37
  111. package/src/components/chip/chip-set.ts +0 -225
  112. package/src/components/chip/chip.ts +0 -118
  113. package/src/components/chip/constants.ts +0 -28
  114. package/src/components/chip/index.ts +0 -12
  115. package/src/components/list/constants.ts +0 -116
  116. package/src/components/sheet/constants.ts +0 -20
  117. package/src/components/slider/constants.ts +0 -32
  118. package/src/components/tooltip/constants.ts +0 -27
  119. package/test/components/badge.test.ts +0 -545
  120. package/test/components/bottom-app-bar.test.ts +0 -303
  121. package/test/components/button.test.ts +0 -233
  122. package/test/components/card.test.ts +0 -560
  123. package/test/components/carousel.test.ts +0 -951
  124. package/test/components/checkbox.test.ts +0 -462
  125. package/test/components/chip.test.ts +0 -692
  126. package/test/components/datepicker.test.ts +0 -1124
  127. package/test/components/dialog.test.ts +0 -990
  128. package/test/components/divider.test.ts +0 -412
  129. package/test/components/extended-fab.test.ts +0 -672
  130. package/test/components/fab.test.ts +0 -561
  131. package/test/components/list.test.ts +0 -365
  132. package/test/components/menu.test.ts +0 -718
  133. package/test/components/navigation.test.ts +0 -186
  134. package/test/components/progress.test.ts +0 -567
  135. package/test/components/radios.test.ts +0 -699
  136. package/test/components/search.test.ts +0 -1135
  137. package/test/components/segmented-button.test.ts +0 -732
  138. package/test/components/sheet.test.ts +0 -641
  139. package/test/components/slider.test.ts +0 -1220
  140. package/test/components/snackbar.test.ts +0 -461
  141. package/test/components/switch.test.ts +0 -452
  142. package/test/components/tabs.test.ts +0 -1369
  143. package/test/components/textfield.test.ts +0 -400
  144. package/test/components/timepicker.test.ts +0 -592
  145. package/test/components/tooltip.test.ts +0 -630
  146. package/test/components/top-app-bar.test.ts +0 -566
  147. package/test/core/dom.attributes.test.ts +0 -148
  148. package/test/core/dom.classes.test.ts +0 -152
  149. package/test/core/dom.events.test.ts +0 -243
  150. package/test/core/emitter.test.ts +0 -141
  151. package/test/core/ripple.test.ts +0 -99
  152. package/test/core/state.store.test.ts +0 -189
  153. package/test/core/utils.normalize.test.ts +0 -61
  154. package/test/core/utils.object.test.ts +0 -120
  155. package/test/setup.js +0 -371
  156. package/test/setup.ts +0 -451
  157. package/tsconfig.json +0 -22
  158. package/typedoc.json +0 -28
  159. package/typedoc.simple.json +0 -14
@@ -1,566 +0,0 @@
1
- // test/components/top-app-bar.test.ts
2
- import { describe, test, expect, mock, beforeAll, afterAll } from 'bun:test';
3
- import { JSDOM } from 'jsdom';
4
-
5
- // Setup jsdom environment
6
- let dom: JSDOM;
7
- let window: Window;
8
- let document: Document;
9
- let originalGlobalDocument: any;
10
- let originalGlobalWindow: any;
11
-
12
- beforeAll(() => {
13
- // Create a new JSDOM instance
14
- dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {
15
- url: 'http://localhost/',
16
- pretendToBeVisual: true
17
- });
18
-
19
- // Get window and document from jsdom
20
- window = dom.window;
21
- document = window.document;
22
-
23
- // Store original globals
24
- originalGlobalDocument = global.document;
25
- originalGlobalWindow = global.window;
26
-
27
- // Set globals to use jsdom
28
- global.document = document;
29
- global.window = window;
30
- global.Element = window.Element;
31
- global.HTMLElement = window.HTMLElement;
32
- global.HTMLButtonElement = window.HTMLButtonElement;
33
- global.Event = window.Event;
34
- });
35
-
36
- afterAll(() => {
37
- // Restore original globals
38
- global.document = originalGlobalDocument;
39
- global.window = originalGlobalWindow;
40
-
41
- // Clean up jsdom
42
- window.close();
43
- });
44
-
45
- // Import only the types we need from the component
46
- import type { TopAppBarConfig, TopAppBar } from '../../src/components/top-app-bar/types';
47
- import type { TopAppBarType } from '../../src/components/top-app-bar/types';
48
-
49
- // Create a mock implementation of the TopAppBar component
50
- const createMockTopAppBar = (config: TopAppBarConfig = {}): TopAppBar => {
51
- // Default configuration
52
- const defaultConfig = {
53
- tag: 'header',
54
- type: 'small',
55
- scrollable: true,
56
- compressible: true,
57
- scrollThreshold: 4,
58
- ...config
59
- };
60
-
61
- // Create the main element
62
- const element = document.createElement(defaultConfig.tag || 'header');
63
- element.className = `mtrl-top-app-bar`;
64
- element.setAttribute('role', 'banner');
65
- element.setAttribute('aria-label', 'Top app bar');
66
-
67
- if (defaultConfig.class) {
68
- element.className += ` ${defaultConfig.class}`;
69
- }
70
-
71
- // Add type class if not the default 'small'
72
- if (defaultConfig.type !== 'small') {
73
- element.className += ` mtrl-top-app-bar--${defaultConfig.type}`;
74
- }
75
-
76
- // Add compressible class if enabled and type is medium or large
77
- if (defaultConfig.compressible &&
78
- (defaultConfig.type === 'medium' || defaultConfig.type === 'large')) {
79
- element.className += ' mtrl-top-app-bar--compressible';
80
- }
81
-
82
- // Create containers
83
- const leadingContainer = document.createElement('div');
84
- leadingContainer.className = 'mtrl-top-app-bar-leading';
85
-
86
- const headlineElement = document.createElement('h1');
87
- headlineElement.className = 'mtrl-top-app-bar-headline';
88
-
89
- if (defaultConfig.title) {
90
- headlineElement.textContent = defaultConfig.title;
91
- }
92
-
93
- const trailingContainer = document.createElement('div');
94
- trailingContainer.className = 'mtrl-top-app-bar-trailing';
95
-
96
- // Build DOM structure based on type
97
- if (defaultConfig.type === 'medium' || defaultConfig.type === 'large') {
98
- // For medium and large, create rows
99
- const topRow = document.createElement('div');
100
- topRow.className = 'mtrl-top-app-bar-row';
101
- topRow.appendChild(leadingContainer);
102
- topRow.appendChild(trailingContainer);
103
-
104
- const bottomRow = document.createElement('div');
105
- bottomRow.className = 'mtrl-top-app-bar-row';
106
- bottomRow.appendChild(headlineElement);
107
-
108
- element.appendChild(topRow);
109
- element.appendChild(bottomRow);
110
- } else {
111
- // For small and center-aligned
112
- element.appendChild(leadingContainer);
113
- element.appendChild(headlineElement);
114
- element.appendChild(trailingContainer);
115
- }
116
-
117
- // Track scrolled state
118
- let isScrolled = false;
119
-
120
- // Set up scroll events if scrollable is enabled
121
- if (defaultConfig.scrollable) {
122
- const handleScroll = () => {
123
- const shouldBeScrolled = window.scrollY > (defaultConfig.scrollThreshold || 4);
124
-
125
- if (isScrolled !== shouldBeScrolled) {
126
- isScrolled = shouldBeScrolled;
127
-
128
- // Toggle scrolled class
129
- if (isScrolled) {
130
- element.classList.add('mtrl-top-app-bar--scrolled');
131
- } else {
132
- element.classList.remove('mtrl-top-app-bar--scrolled');
133
- }
134
-
135
- // Call the onScroll callback if provided
136
- if (defaultConfig.onScroll) {
137
- defaultConfig.onScroll(isScrolled);
138
- }
139
- }
140
- };
141
-
142
- window.addEventListener('scroll', handleScroll, { passive: true });
143
- }
144
-
145
- // Helper function to reorganize DOM for different types
146
- const reorganizeDom = (type: TopAppBarType) => {
147
- // Clear existing content
148
- while (element.firstChild) {
149
- element.removeChild(element.firstChild);
150
- }
151
-
152
- // Update type classes
153
- ['small', 'medium', 'large', 'center'].forEach(t => {
154
- element.classList.remove(`mtrl-top-app-bar--${t}`);
155
- });
156
-
157
- if (type !== 'small') {
158
- element.classList.add(`mtrl-top-app-bar--${type}`);
159
- }
160
-
161
- // Update compressible class
162
- element.classList.toggle(
163
- 'mtrl-top-app-bar--compressible',
164
- defaultConfig.compressible && (type === 'medium' || type === 'large')
165
- );
166
-
167
- // Rebuild the DOM structure
168
- if (type === 'medium' || type === 'large') {
169
- // For medium and large, create rows
170
- const topRow = document.createElement('div');
171
- topRow.className = 'mtrl-top-app-bar-row';
172
- topRow.appendChild(leadingContainer);
173
- topRow.appendChild(trailingContainer);
174
-
175
- const bottomRow = document.createElement('div');
176
- bottomRow.className = 'mtrl-top-app-bar-row';
177
- bottomRow.appendChild(headlineElement);
178
-
179
- element.appendChild(topRow);
180
- element.appendChild(bottomRow);
181
- } else {
182
- // For small and center-aligned
183
- element.appendChild(leadingContainer);
184
- element.appendChild(headlineElement);
185
- element.appendChild(trailingContainer);
186
- }
187
- };
188
-
189
- // Create the API object
190
- const topAppBar: TopAppBar = {
191
- element,
192
-
193
- // Base component event methods
194
- on: () => topAppBar,
195
- off: () => topAppBar,
196
- emit: () => topAppBar,
197
-
198
- // Lifecycle methods
199
- lifecycle: {
200
- destroy: () => {
201
- // Remove event listener if scrollable
202
- if (defaultConfig.scrollable) {
203
- window.removeEventListener('scroll', () => {});
204
- }
205
-
206
- // Remove element from DOM
207
- if (element.parentNode) {
208
- element.parentNode.removeChild(element);
209
- }
210
- }
211
- },
212
-
213
- // Utility methods
214
- getClass: (name: string) => `mtrl-${name}`,
215
-
216
- // Component specific methods
217
- setTitle(title: string) {
218
- headlineElement.textContent = title;
219
- return this;
220
- },
221
-
222
- getTitle() {
223
- return headlineElement.textContent || '';
224
- },
225
-
226
- addLeadingElement(el: HTMLElement) {
227
- leadingContainer.appendChild(el);
228
- return this;
229
- },
230
-
231
- addTrailingElement(el: HTMLElement) {
232
- trailingContainer.appendChild(el);
233
- return this;
234
- },
235
-
236
- setType(type: TopAppBarType) {
237
- defaultConfig.type = type;
238
- reorganizeDom(type);
239
- return this;
240
- },
241
-
242
- setScrollState(scrolled: boolean) {
243
- isScrolled = scrolled;
244
-
245
- if (scrolled) {
246
- element.classList.add('mtrl-top-app-bar--scrolled');
247
- } else {
248
- element.classList.remove('mtrl-top-app-bar--scrolled');
249
- }
250
-
251
- return this;
252
- },
253
-
254
- getHeadlineElement() {
255
- return headlineElement;
256
- },
257
-
258
- getLeadingContainer() {
259
- return leadingContainer;
260
- },
261
-
262
- getTrailingContainer() {
263
- return trailingContainer;
264
- },
265
-
266
- destroy() {
267
- this.lifecycle.destroy();
268
- }
269
- };
270
-
271
- return topAppBar;
272
- };
273
-
274
- describe('TopAppBar Component', () => {
275
- test('should create a top app bar element', () => {
276
- const topAppBar = createMockTopAppBar();
277
-
278
- expect(topAppBar.element).toBeDefined();
279
- expect(topAppBar.element.tagName).toBe('HEADER');
280
- expect(topAppBar.element.className).toContain('mtrl-top-app-bar');
281
- expect(topAppBar.element.getAttribute('role')).toBe('banner');
282
-
283
- // Clean up
284
- topAppBar.destroy();
285
- });
286
-
287
- test('should create a top app bar with custom tag', () => {
288
- const topAppBar = createMockTopAppBar({
289
- tag: 'div'
290
- });
291
-
292
- expect(topAppBar.element.tagName).toBe('DIV');
293
-
294
- // Clean up
295
- topAppBar.destroy();
296
- });
297
-
298
- test('should initialize with a title', () => {
299
- const title = 'Application Title';
300
- const topAppBar = createMockTopAppBar({
301
- title
302
- });
303
-
304
- expect(topAppBar.getTitle()).toBe(title);
305
- expect(topAppBar.getHeadlineElement().textContent).toBe(title);
306
-
307
- // Clean up
308
- topAppBar.destroy();
309
- });
310
-
311
- test('should apply correct structure for small type', () => {
312
- const topAppBar = createMockTopAppBar({
313
- type: 'small'
314
- });
315
-
316
- // Small type should have a flat structure
317
- expect(topAppBar.element.children.length).toBe(3);
318
- expect(topAppBar.element.children[0]).toBe(topAppBar.getLeadingContainer());
319
- expect(topAppBar.element.children[1]).toBe(topAppBar.getHeadlineElement());
320
- expect(topAppBar.element.children[2]).toBe(topAppBar.getTrailingContainer());
321
-
322
- // Shouldn't have the small variant class (as it's the default)
323
- expect(topAppBar.element.className).not.toContain('mtrl-top-app-bar--small');
324
-
325
- // Clean up
326
- topAppBar.destroy();
327
- });
328
-
329
- test('should apply correct structure for medium type', () => {
330
- const topAppBar = createMockTopAppBar({
331
- type: 'medium'
332
- });
333
-
334
- // Medium type should have two rows
335
- expect(topAppBar.element.className).toContain('mtrl-top-app-bar--medium');
336
- expect(topAppBar.element.children.length).toBe(2);
337
-
338
- // First row should contain leading and trailing
339
- const topRow = topAppBar.element.children[0] as HTMLElement;
340
- expect(topRow.className).toContain('mtrl-top-app-bar-row');
341
- expect(topRow.children[0]).toBe(topAppBar.getLeadingContainer());
342
- expect(topRow.children[1]).toBe(topAppBar.getTrailingContainer());
343
-
344
- // Second row should contain headline
345
- const bottomRow = topAppBar.element.children[1] as HTMLElement;
346
- expect(bottomRow.className).toContain('mtrl-top-app-bar-row');
347
- expect(bottomRow.children[0]).toBe(topAppBar.getHeadlineElement());
348
-
349
- // Clean up
350
- topAppBar.destroy();
351
- });
352
-
353
- test('should apply correct structure for large type', () => {
354
- const topAppBar = createMockTopAppBar({
355
- type: 'large'
356
- });
357
-
358
- // Large type should have two rows like medium
359
- expect(topAppBar.element.className).toContain('mtrl-top-app-bar--large');
360
- expect(topAppBar.element.children.length).toBe(2);
361
-
362
- // Clean up
363
- topAppBar.destroy();
364
- });
365
-
366
- test('should apply correct structure for center type', () => {
367
- const topAppBar = createMockTopAppBar({
368
- type: 'center'
369
- });
370
-
371
- // Center type should have a flat structure like small
372
- expect(topAppBar.element.className).toContain('mtrl-top-app-bar--center');
373
- expect(topAppBar.element.children.length).toBe(3);
374
- expect(topAppBar.element.children[0]).toBe(topAppBar.getLeadingContainer());
375
- expect(topAppBar.element.children[1]).toBe(topAppBar.getHeadlineElement());
376
- expect(topAppBar.element.children[2]).toBe(topAppBar.getTrailingContainer());
377
-
378
- // Clean up
379
- topAppBar.destroy();
380
- });
381
-
382
- test('should add compressible class to medium/large types when compressible is true', () => {
383
- const mediumTopAppBar = createMockTopAppBar({
384
- type: 'medium',
385
- compressible: true
386
- });
387
-
388
- expect(mediumTopAppBar.element.className).toContain('mtrl-top-app-bar--compressible');
389
- mediumTopAppBar.destroy();
390
-
391
- const largeTopAppBar = createMockTopAppBar({
392
- type: 'large',
393
- compressible: true
394
- });
395
-
396
- expect(largeTopAppBar.element.className).toContain('mtrl-top-app-bar--compressible');
397
- largeTopAppBar.destroy();
398
- });
399
-
400
- test('should not add compressible class when compressible is false', () => {
401
- const topAppBar = createMockTopAppBar({
402
- type: 'medium',
403
- compressible: false
404
- });
405
-
406
- expect(topAppBar.element.className).not.toContain('mtrl-top-app-bar--compressible');
407
-
408
- // Clean up
409
- topAppBar.destroy();
410
- });
411
-
412
- test('should set and get title text', () => {
413
- const topAppBar = createMockTopAppBar();
414
- const initialTitle = 'Initial Title';
415
- const updatedTitle = 'Updated Title';
416
-
417
- // Set initial title
418
- topAppBar.setTitle(initialTitle);
419
- expect(topAppBar.getTitle()).toBe(initialTitle);
420
- expect(topAppBar.getHeadlineElement().textContent).toBe(initialTitle);
421
-
422
- // Update title
423
- topAppBar.setTitle(updatedTitle);
424
- expect(topAppBar.getTitle()).toBe(updatedTitle);
425
- expect(topAppBar.getHeadlineElement().textContent).toBe(updatedTitle);
426
-
427
- // Clean up
428
- topAppBar.destroy();
429
- });
430
-
431
- test('should add leading elements', () => {
432
- const topAppBar = createMockTopAppBar();
433
- const menuButton = document.createElement('button');
434
- menuButton.textContent = 'Menu';
435
-
436
- topAppBar.addLeadingElement(menuButton);
437
-
438
- expect(topAppBar.getLeadingContainer().children.length).toBe(1);
439
- expect(topAppBar.getLeadingContainer().children[0]).toBe(menuButton);
440
-
441
- // Add another element
442
- const backButton = document.createElement('button');
443
- backButton.textContent = 'Back';
444
-
445
- topAppBar.addLeadingElement(backButton);
446
-
447
- expect(topAppBar.getLeadingContainer().children.length).toBe(2);
448
- expect(topAppBar.getLeadingContainer().children[1]).toBe(backButton);
449
-
450
- // Clean up
451
- topAppBar.destroy();
452
- });
453
-
454
- test('should add trailing elements', () => {
455
- const topAppBar = createMockTopAppBar();
456
- const searchButton = document.createElement('button');
457
- searchButton.textContent = 'Search';
458
-
459
- topAppBar.addTrailingElement(searchButton);
460
-
461
- expect(topAppBar.getTrailingContainer().children.length).toBe(1);
462
- expect(topAppBar.getTrailingContainer().children[0]).toBe(searchButton);
463
-
464
- // Add another element
465
- const moreButton = document.createElement('button');
466
- moreButton.textContent = 'More';
467
-
468
- topAppBar.addTrailingElement(moreButton);
469
-
470
- expect(topAppBar.getTrailingContainer().children.length).toBe(2);
471
- expect(topAppBar.getTrailingContainer().children[1]).toBe(moreButton);
472
-
473
- // Clean up
474
- topAppBar.destroy();
475
- });
476
-
477
- test('should change type and reorganize DOM structure', () => {
478
- const topAppBar = createMockTopAppBar({
479
- type: 'small'
480
- });
481
-
482
- // Initially small type (flat structure)
483
- expect(topAppBar.element.children.length).toBe(3);
484
- expect(topAppBar.element.className).not.toContain('mtrl-top-app-bar--medium');
485
-
486
- // Change to medium type
487
- topAppBar.setType('medium');
488
-
489
- // Now should have medium type structure (two rows)
490
- expect(topAppBar.element.className).toContain('mtrl-top-app-bar--medium');
491
- expect(topAppBar.element.children.length).toBe(2);
492
-
493
- const topRow = topAppBar.element.children[0] as HTMLElement;
494
- expect(topRow.className).toContain('mtrl-top-app-bar-row');
495
- expect(topRow.children[0]).toBe(topAppBar.getLeadingContainer());
496
- expect(topRow.children[1]).toBe(topAppBar.getTrailingContainer());
497
-
498
- // Change back to small type
499
- topAppBar.setType('small');
500
-
501
- // Should be back to flat structure
502
- expect(topAppBar.element.className).not.toContain('mtrl-top-app-bar--medium');
503
- expect(topAppBar.element.children.length).toBe(3);
504
- expect(topAppBar.element.children[0]).toBe(topAppBar.getLeadingContainer());
505
- expect(topAppBar.element.children[1]).toBe(topAppBar.getHeadlineElement());
506
- expect(topAppBar.element.children[2]).toBe(topAppBar.getTrailingContainer());
507
-
508
- // Clean up
509
- topAppBar.destroy();
510
- });
511
-
512
- test('should set scroll state manually', () => {
513
- const topAppBar = createMockTopAppBar();
514
-
515
- // Initially not scrolled
516
- expect(topAppBar.element.className).not.toContain('mtrl-top-app-bar--scrolled');
517
-
518
- // Set scrolled state to true
519
- topAppBar.setScrollState(true);
520
- expect(topAppBar.element.className).toContain('mtrl-top-app-bar--scrolled');
521
-
522
- // Set scrolled state back to false
523
- topAppBar.setScrollState(false);
524
- expect(topAppBar.element.className).not.toContain('mtrl-top-app-bar--scrolled');
525
-
526
- // Clean up
527
- topAppBar.destroy();
528
- });
529
-
530
- test('should call onScroll callback when scroll state changes', () => {
531
- const onScrollMock = mock((scrolled: boolean) => {});
532
-
533
- const topAppBar = createMockTopAppBar({
534
- onScroll: onScrollMock
535
- });
536
-
537
- // Simulate scroll event
538
- window.scrollY = 10; // Above the default threshold of 4
539
- const scrollEvent = new Event('scroll');
540
- window.dispatchEvent(scrollEvent);
541
-
542
- // Hard to test in JSDOM, so we'll just test manual state change
543
- topAppBar.setScrollState(true);
544
-
545
- // Clean up
546
- topAppBar.destroy();
547
- });
548
-
549
- test('should clean up resources on destroy', () => {
550
- const parent = document.createElement('div');
551
- document.body.appendChild(parent);
552
-
553
- const topAppBar = createMockTopAppBar();
554
- parent.appendChild(topAppBar.element);
555
-
556
- expect(parent.contains(topAppBar.element)).toBe(true);
557
-
558
- // Destroy the top app bar
559
- topAppBar.destroy();
560
-
561
- expect(parent.contains(topAppBar.element)).toBe(false);
562
-
563
- // Clean up
564
- document.body.removeChild(parent);
565
- });
566
- });
@@ -1,148 +0,0 @@
1
- // test/core/dom.attributes.test.ts
2
- import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
3
- import { JSDOM } from 'jsdom';
4
- import { setAttributes, removeAttributes } from '../../src/core/dom/attributes';
5
-
6
- // Setup jsdom environment
7
- let dom: JSDOM;
8
- let window: Window;
9
- let document: Document;
10
- let originalGlobalDocument: any;
11
- let originalGlobalWindow: any;
12
-
13
- beforeAll(() => {
14
- // Create a new JSDOM instance
15
- dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {
16
- url: 'http://localhost/',
17
- pretendToBeVisual: true
18
- });
19
-
20
- // Get window and document from jsdom
21
- window = dom.window;
22
- document = window.document;
23
-
24
- // Store original globals
25
- originalGlobalDocument = global.document;
26
- originalGlobalWindow = global.window;
27
-
28
- // Set globals to use jsdom
29
- global.document = document;
30
- global.window = window;
31
- global.Element = window.Element;
32
- global.HTMLElement = window.HTMLElement;
33
- });
34
-
35
- afterAll(() => {
36
- // Restore original globals
37
- global.document = originalGlobalDocument;
38
- global.window = originalGlobalWindow;
39
-
40
- // Clean up jsdom
41
- window.close();
42
- });
43
-
44
- describe('DOM Attributes Utilities', () => {
45
- test('should set a single attribute', () => {
46
- const element = document.createElement('div');
47
- setAttributes(element, { id: 'test-id' });
48
- expect(element.getAttribute('id')).toBe('test-id');
49
- });
50
-
51
- test('should set multiple attributes', () => {
52
- const element = document.createElement('div');
53
- setAttributes(element, {
54
- id: 'test-id',
55
- class: 'test-class',
56
- 'data-test': 'test-data'
57
- });
58
-
59
- expect(element.getAttribute('id')).toBe('test-id');
60
- expect(element.getAttribute('class')).toBe('test-class');
61
- expect(element.getAttribute('data-test')).toBe('test-data');
62
- });
63
-
64
- test('should convert attribute values to strings', () => {
65
- const element = document.createElement('div');
66
- setAttributes(element, {
67
- 'data-number': 123,
68
- 'data-boolean': true,
69
- 'data-object': { toString: () => 'object-string' }
70
- });
71
-
72
- expect(element.getAttribute('data-number')).toBe('123');
73
- expect(element.getAttribute('data-boolean')).toBe('true');
74
- expect(element.getAttribute('data-object')).toBe('object-string');
75
- });
76
-
77
- test('should handle empty attributes object', () => {
78
- const element = document.createElement('div');
79
- element.setAttribute('test', 'value');
80
-
81
- setAttributes(element, {});
82
-
83
- expect(element.getAttribute('test')).toBe('value');
84
- });
85
-
86
- test('should skip null or undefined attribute values', () => {
87
- const element = document.createElement('div');
88
- setAttributes(element, {
89
- 'data-defined': 'value',
90
- 'data-null': null,
91
- 'data-undefined': undefined
92
- });
93
-
94
- expect(element.getAttribute('data-defined')).toBe('value');
95
- expect(element.hasAttribute('data-null')).toBe(false);
96
- expect(element.hasAttribute('data-undefined')).toBe(false);
97
- });
98
-
99
- test('should remove a single attribute', () => {
100
- const element = document.createElement('div');
101
- element.setAttribute('id', 'test-id');
102
-
103
- removeAttributes(element, ['id']);
104
-
105
- expect(element.hasAttribute('id')).toBe(false);
106
- });
107
-
108
- test('should remove multiple attributes', () => {
109
- const element = document.createElement('div');
110
- element.setAttribute('id', 'test-id');
111
- element.setAttribute('class', 'test-class');
112
- element.setAttribute('data-test', 'test-data');
113
-
114
- removeAttributes(element, ['id', 'data-test']);
115
-
116
- expect(element.hasAttribute('id')).toBe(false);
117
- expect(element.getAttribute('class')).toBe('test-class');
118
- expect(element.hasAttribute('data-test')).toBe(false);
119
- });
120
-
121
- test('should handle empty attributes array', () => {
122
- const element = document.createElement('div');
123
- element.setAttribute('test', 'value');
124
-
125
- removeAttributes(element, []);
126
-
127
- expect(element.getAttribute('test')).toBe('value');
128
- });
129
-
130
- test('should silently ignore non-existent attributes', () => {
131
- const element = document.createElement('div');
132
- element.setAttribute('id', 'test-id');
133
-
134
- removeAttributes(element, ['id', 'non-existent']);
135
-
136
- expect(element.hasAttribute('id')).toBe(false);
137
- });
138
-
139
- test('should return the element from modifier functions', () => {
140
- const element = document.createElement('div');
141
-
142
- const result1 = setAttributes(element, { test: 'value' });
143
- const result2 = removeAttributes(element, ['test']);
144
-
145
- expect(result1).toBe(element);
146
- expect(result2).toBe(element);
147
- });
148
- });