mtrl 0.3.1 → 0.3.3

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 (162) hide show
  1. package/.env +15 -0
  2. package/CONTRIBUTING.md +62 -23
  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/navigation/nav-item.ts +13 -2
  83. package/src/components/sheet/config.ts +1 -2
  84. package/src/components/sheet/features/gestures.ts +1 -1
  85. package/src/components/sheet/features/position.ts +1 -2
  86. package/src/components/sheet/features/state.ts +1 -1
  87. package/src/components/sheet/index.ts +10 -2
  88. package/src/components/sheet/sheet.ts +1 -2
  89. package/src/components/sheet/types.ts +29 -1
  90. package/src/components/slider/api.ts +1 -1
  91. package/src/components/slider/config.ts +1 -1
  92. package/src/components/slider/features/controller.ts +1 -1
  93. package/src/components/slider/features/handlers.ts +1 -1
  94. package/src/components/slider/features/states.ts +1 -1
  95. package/src/components/slider/index.ts +12 -5
  96. package/src/components/slider/schema.ts +1 -1
  97. package/src/components/slider/types.ts +31 -0
  98. package/src/components/tabs/tab-api.ts +1 -1
  99. package/src/components/tabs/types.ts +1 -1
  100. package/src/components/tooltip/api.ts +6 -2
  101. package/src/components/tooltip/config.ts +9 -28
  102. package/src/components/tooltip/index.ts +10 -1
  103. package/src/components/tooltip/types.ts +38 -3
  104. package/src/core/dom/create.ts +57 -51
  105. package/src/index.ts +129 -31
  106. package/src/styles/abstract/_mixins.scss +23 -9
  107. package/src/styles/abstract/_variables.scss +14 -4
  108. package/src/styles/components/_card.scss +1 -1
  109. package/src/styles/components/_chip.scss +323 -113
  110. package/src/styles/components/_tabs.scss +1 -1
  111. package/src/styles/themes/_autumn.scss +3 -0
  112. package/CLAUDE.md +0 -33
  113. package/src/components/checkbox/constants.ts +0 -37
  114. package/src/components/chip/chip-set.ts +0 -225
  115. package/src/components/chip/chip.ts +0 -118
  116. package/src/components/chip/constants.ts +0 -28
  117. package/src/components/chip/index.ts +0 -12
  118. package/src/components/list/constants.ts +0 -116
  119. package/src/components/sheet/constants.ts +0 -20
  120. package/src/components/slider/constants.ts +0 -32
  121. package/src/components/tooltip/constants.ts +0 -27
  122. package/test/components/badge.test.ts +0 -545
  123. package/test/components/bottom-app-bar.test.ts +0 -303
  124. package/test/components/button.test.ts +0 -233
  125. package/test/components/card.test.ts +0 -560
  126. package/test/components/carousel.test.ts +0 -951
  127. package/test/components/checkbox.test.ts +0 -462
  128. package/test/components/chip.test.ts +0 -692
  129. package/test/components/datepicker.test.ts +0 -1124
  130. package/test/components/dialog.test.ts +0 -990
  131. package/test/components/divider.test.ts +0 -412
  132. package/test/components/extended-fab.test.ts +0 -672
  133. package/test/components/fab.test.ts +0 -561
  134. package/test/components/list.test.ts +0 -365
  135. package/test/components/menu.test.ts +0 -718
  136. package/test/components/navigation.test.ts +0 -186
  137. package/test/components/progress.test.ts +0 -567
  138. package/test/components/radios.test.ts +0 -699
  139. package/test/components/search.test.ts +0 -1135
  140. package/test/components/segmented-button.test.ts +0 -732
  141. package/test/components/sheet.test.ts +0 -641
  142. package/test/components/slider.test.ts +0 -1220
  143. package/test/components/snackbar.test.ts +0 -461
  144. package/test/components/switch.test.ts +0 -452
  145. package/test/components/tabs.test.ts +0 -1369
  146. package/test/components/textfield.test.ts +0 -400
  147. package/test/components/timepicker.test.ts +0 -592
  148. package/test/components/tooltip.test.ts +0 -630
  149. package/test/components/top-app-bar.test.ts +0 -566
  150. package/test/core/dom.attributes.test.ts +0 -148
  151. package/test/core/dom.classes.test.ts +0 -152
  152. package/test/core/dom.events.test.ts +0 -243
  153. package/test/core/emitter.test.ts +0 -141
  154. package/test/core/ripple.test.ts +0 -99
  155. package/test/core/state.store.test.ts +0 -189
  156. package/test/core/utils.normalize.test.ts +0 -61
  157. package/test/core/utils.object.test.ts +0 -120
  158. package/test/setup.js +0 -371
  159. package/test/setup.ts +0 -451
  160. package/tsconfig.json +0 -22
  161. package/typedoc.json +0 -28
  162. package/typedoc.simple.json +0 -14
@@ -1,461 +0,0 @@
1
- // test/components/snackbar.test.ts
2
- import { describe, test, expect, mock, beforeEach, afterEach } from 'bun:test';
3
- import { JSDOM } from 'jsdom';
4
-
5
- // Set up JSDOM
6
- const dom = new JSDOM(`<!DOCTYPE html><html><body></body></html>`);
7
- global.document = dom.window.document;
8
- global.window = dom.window;
9
- global.Element = dom.window.Element;
10
- global.HTMLElement = dom.window.HTMLElement;
11
- global.Event = dom.window.Event;
12
- global.CustomEvent = dom.window.CustomEvent;
13
-
14
- // Import types directly to avoid circular dependencies
15
- import type {
16
- SnackbarComponent,
17
- SnackbarConfig,
18
- SnackbarEvent,
19
- SnackbarVariant,
20
- SnackbarPosition
21
- } from '../../src/components/snackbar/types';
22
-
23
- // Define constants here to avoid circular dependencies
24
- const SNACKBAR_VARIANTS = {
25
- BASIC: 'basic',
26
- ACTION: 'action'
27
- } as const;
28
-
29
- const SNACKBAR_POSITIONS = {
30
- CENTER: 'center',
31
- START: 'start',
32
- END: 'end'
33
- } as const;
34
-
35
- // Define a mock emit function type
36
- type EmitFunction = (eventName: string, eventData?: any) => void;
37
-
38
- // Create a mock snackbar implementation
39
- const createMockSnackbar = (config: SnackbarConfig): SnackbarComponent => {
40
- // Default configuration
41
- const defaultConfig: SnackbarConfig = {
42
- message: '',
43
- variant: SNACKBAR_VARIANTS.BASIC,
44
- position: SNACKBAR_POSITIONS.CENTER,
45
- duration: 4000,
46
- prefix: 'mtrl'
47
- };
48
-
49
- // Merge with user configuration
50
- const mergedConfig = {
51
- ...defaultConfig,
52
- ...config
53
- };
54
-
55
- // Create main element
56
- const element = document.createElement('div');
57
- element.className = `${mergedConfig.prefix}-snackbar`;
58
-
59
- if (mergedConfig.class) {
60
- element.className += ` ${mergedConfig.class}`;
61
- }
62
-
63
- // Add variant class
64
- element.className += ` ${mergedConfig.prefix}-snackbar--${mergedConfig.variant}`;
65
-
66
- // Add position class
67
- element.className += ` ${mergedConfig.prefix}-snackbar--${mergedConfig.position}`;
68
-
69
- // Create message element
70
- const messageElement = document.createElement('div');
71
- messageElement.className = `${mergedConfig.prefix}-snackbar__message`;
72
- messageElement.textContent = mergedConfig.message;
73
- element.appendChild(messageElement);
74
-
75
- // Create action button if specified
76
- let actionButton: HTMLButtonElement | undefined;
77
-
78
- if (mergedConfig.action) {
79
- actionButton = document.createElement('button');
80
- actionButton.className = `${mergedConfig.prefix}-snackbar__action`;
81
- actionButton.textContent = mergedConfig.action;
82
- element.appendChild(actionButton);
83
- }
84
-
85
- // Event handlers
86
- const eventHandlers: Record<string, Function[]> = {};
87
-
88
- // Timer implementation
89
- const timer = {
90
- timeoutId: null as number | null,
91
- start: () => {
92
- if (mergedConfig.duration === 0) return;
93
- if (timer.timeoutId) clearTimeout(timer.timeoutId);
94
- timer.timeoutId = setTimeout(() => {
95
- snackbar.hide();
96
- }, mergedConfig.duration) as unknown as number;
97
- },
98
- stop: () => {
99
- if (timer.timeoutId) {
100
- clearTimeout(timer.timeoutId);
101
- timer.timeoutId = null;
102
- }
103
- }
104
- };
105
-
106
- // Position implementation
107
- const position = {
108
- getPosition: () => mergedConfig.position as SnackbarPosition,
109
- setPosition: (pos: SnackbarPosition) => {
110
- // Remove old position class
111
- element.classList.remove(`${mergedConfig.prefix}-snackbar--${mergedConfig.position}`);
112
-
113
- // Update position
114
- mergedConfig.position = pos;
115
-
116
- // Add new position class
117
- element.classList.add(`${mergedConfig.prefix}-snackbar--${mergedConfig.position}`);
118
-
119
- return snackbar;
120
- }
121
- };
122
-
123
- // Emit function
124
- const emit: EmitFunction = (eventName, eventData = {}) => {
125
- if (!eventHandlers[eventName]) return;
126
-
127
- const event = {
128
- snackbar,
129
- originalEvent: null,
130
- preventDefault: () => { event.defaultPrevented = true; },
131
- defaultPrevented: false,
132
- ...eventData
133
- };
134
-
135
- eventHandlers[eventName].forEach(handler => handler(event));
136
- };
137
-
138
- // Create the snackbar instance
139
- const snackbar: SnackbarComponent = {
140
- element,
141
- config: mergedConfig,
142
- actionButton,
143
- timer,
144
- position,
145
- state: 'hidden',
146
- emit,
147
-
148
- show: () => {
149
- element.classList.add(`${mergedConfig.prefix}-snackbar--visible`);
150
- snackbar.state = 'visible';
151
- timer.start();
152
- emit('open');
153
- return snackbar;
154
- },
155
-
156
- hide: () => {
157
- element.classList.remove(`${mergedConfig.prefix}-snackbar--visible`);
158
- snackbar.state = 'hidden';
159
- timer.stop();
160
- emit('close');
161
- return snackbar;
162
- },
163
-
164
- setMessage: (message: string) => {
165
- mergedConfig.message = message;
166
- messageElement.textContent = message;
167
- return snackbar;
168
- },
169
-
170
- getMessage: () => mergedConfig.message,
171
-
172
- setAction: (text: string) => {
173
- if (!actionButton) {
174
- actionButton = document.createElement('button');
175
- actionButton.className = `${mergedConfig.prefix}-snackbar__action`;
176
- element.appendChild(actionButton);
177
- }
178
-
179
- actionButton.textContent = text;
180
- mergedConfig.action = text;
181
-
182
- return snackbar;
183
- },
184
-
185
- getAction: () => mergedConfig.action || '',
186
-
187
- setDuration: (duration: number) => {
188
- mergedConfig.duration = duration;
189
- return snackbar;
190
- },
191
-
192
- getDuration: () => mergedConfig.duration,
193
-
194
- setPosition: (newPosition: SnackbarPosition) => {
195
- return position.setPosition(newPosition);
196
- },
197
-
198
- getPosition: () => position.getPosition(),
199
-
200
- on: (event: string, handler: Function) => {
201
- if (!eventHandlers[event]) {
202
- eventHandlers[event] = [];
203
- }
204
-
205
- eventHandlers[event].push(handler);
206
- return snackbar;
207
- },
208
-
209
- off: (event: string, handler: Function) => {
210
- if (!eventHandlers[event]) return snackbar;
211
-
212
- eventHandlers[event] = eventHandlers[event].filter(h => h !== handler);
213
- return snackbar;
214
- },
215
-
216
- destroy: () => {
217
- timer.stop();
218
-
219
- // Clear event handlers
220
- Object.keys(eventHandlers).forEach(key => {
221
- eventHandlers[key] = [];
222
- });
223
-
224
- // Remove from DOM if attached
225
- if (element.parentNode) {
226
- element.parentNode.removeChild(element);
227
- }
228
- }
229
- };
230
-
231
- return snackbar;
232
- };
233
-
234
- describe('Snackbar Component', () => {
235
- let originalBody: any;
236
- let mockBodyAppendChild: ReturnType<typeof mock>;
237
- let mockBodyRemoveChild: ReturnType<typeof mock>;
238
-
239
- beforeEach(() => {
240
- // Mock document.body methods for testing
241
- originalBody = document.body;
242
- mockBodyAppendChild = mock(() => {});
243
- mockBodyRemoveChild = mock(() => {});
244
-
245
- Object.defineProperty(document, 'body', {
246
- value: {
247
- appendChild: mockBodyAppendChild,
248
- removeChild: mockBodyRemoveChild,
249
- children: []
250
- },
251
- writable: true
252
- });
253
- });
254
-
255
- afterEach(() => {
256
- // Restore original document.body
257
- Object.defineProperty(document, 'body', {
258
- value: originalBody,
259
- writable: true
260
- });
261
- });
262
-
263
- test('should create a snackbar element', () => {
264
- const snackbar = createMockSnackbar({
265
- message: 'Test message'
266
- });
267
-
268
- expect(snackbar.element).toBeDefined();
269
- expect(snackbar.element.tagName).toBe('DIV');
270
- expect(snackbar.element.className).toContain('mtrl-snackbar');
271
- });
272
-
273
- test('should apply variant class', () => {
274
- const variant = SNACKBAR_VARIANTS.ACTION;
275
- const snackbar = createMockSnackbar({
276
- message: 'Test message',
277
- variant
278
- });
279
-
280
- expect(snackbar.config.variant).toBe(variant);
281
- });
282
-
283
- test('should use basic as default variant', () => {
284
- const snackbar = createMockSnackbar({
285
- message: 'Test message'
286
- });
287
-
288
- expect(snackbar.config.variant).toBe(SNACKBAR_VARIANTS.BASIC);
289
- });
290
-
291
- test('should apply position class', () => {
292
- const position = SNACKBAR_POSITIONS.START;
293
- const snackbar = createMockSnackbar({
294
- message: 'Test message',
295
- position
296
- });
297
-
298
- expect(snackbar.config.position).toBe(position);
299
- });
300
-
301
- test('should use center as default position', () => {
302
- const snackbar = createMockSnackbar({
303
- message: 'Test message'
304
- });
305
-
306
- expect(snackbar.config.position).toBe(SNACKBAR_POSITIONS.CENTER);
307
- });
308
-
309
- test('should set message text', () => {
310
- const message = 'Test message';
311
- const snackbar = createMockSnackbar({
312
- message
313
- });
314
-
315
- expect(snackbar.config.message).toBe(message);
316
- expect(typeof snackbar.getMessage).toBe('function');
317
- });
318
-
319
- test('should add action button when specified', () => {
320
- const action = 'Undo';
321
- const snackbar = createMockSnackbar({
322
- message: 'Action completed',
323
- action
324
- });
325
-
326
- expect(snackbar.actionButton).toBeDefined();
327
- expect(snackbar.actionButton?.textContent).toBe(action);
328
- });
329
-
330
- test('should not add action button when not specified', () => {
331
- const snackbar = createMockSnackbar({
332
- message: 'Simple message'
333
- });
334
-
335
- expect(snackbar.actionButton).toBeUndefined();
336
- });
337
-
338
- test('should set default duration when not specified', () => {
339
- const snackbar = createMockSnackbar({
340
- message: 'Test message'
341
- });
342
-
343
- expect(snackbar.config.duration).toBe(4000);
344
- });
345
-
346
- test('should respect custom duration', () => {
347
- const duration = 2000;
348
- const snackbar = createMockSnackbar({
349
- message: 'Test message',
350
- duration
351
- });
352
-
353
- expect(snackbar.config.duration).toBe(duration);
354
- });
355
-
356
- test('should allow duration of 0 (no auto-dismiss)', () => {
357
- const snackbar = createMockSnackbar({
358
- message: 'Test message',
359
- duration: 0
360
- });
361
-
362
- expect(snackbar.config.duration).toBe(0);
363
- });
364
-
365
- test('should register event handlers', () => {
366
- const snackbar = createMockSnackbar({
367
- message: 'Test message'
368
- });
369
-
370
- // Verify event API exists
371
- expect(typeof snackbar.on).toBe('function');
372
- expect(typeof snackbar.off).toBe('function');
373
-
374
- // Check event handling
375
- const handler = mock(() => {});
376
- snackbar.on('dismiss', handler);
377
-
378
- // Trigger dismiss event
379
- if (snackbar.emit) {
380
- snackbar.emit('dismiss');
381
- expect(handler).toHaveBeenCalled();
382
- }
383
- });
384
-
385
- test('should expose show and hide methods', () => {
386
- const snackbar = createMockSnackbar({
387
- message: 'Test message'
388
- });
389
-
390
- expect(typeof snackbar.show).toBe('function');
391
- expect(typeof snackbar.hide).toBe('function');
392
- });
393
-
394
- test('should allow updating message', () => {
395
- const initialMessage = 'Initial message';
396
- const snackbar = createMockSnackbar({
397
- message: initialMessage
398
- });
399
-
400
- expect(snackbar.getMessage()).toBe(initialMessage);
401
-
402
- const updatedMessage = 'Updated message';
403
- snackbar.setMessage(updatedMessage);
404
-
405
- expect(snackbar.getMessage()).toBe(updatedMessage);
406
- });
407
-
408
- test('should have dismiss timer functionality', () => {
409
- const snackbar = createMockSnackbar({
410
- message: 'Test message'
411
- });
412
-
413
- expect(snackbar.timer).toBeDefined();
414
- expect(typeof snackbar.timer?.start).toBe('function');
415
- expect(typeof snackbar.timer?.stop).toBe('function');
416
- });
417
-
418
- test('should have an API for position management', () => {
419
- const snackbar = createMockSnackbar({
420
- message: 'Test message'
421
- });
422
-
423
- expect(snackbar.position).toBeDefined();
424
-
425
- if (snackbar.position) {
426
- expect(typeof snackbar.position.getPosition).toBe('function');
427
- expect(typeof snackbar.position.setPosition).toBe('function');
428
- }
429
- });
430
-
431
- test('should clean up resources on destroy', () => {
432
- const snackbar = createMockSnackbar({
433
- message: 'Test message'
434
- });
435
-
436
- expect(typeof snackbar.destroy).toBe('function');
437
-
438
- // Mock any internal methods that might be called during destroy
439
- if (snackbar.timer) {
440
- snackbar.timer.stop = mock(snackbar.timer.stop);
441
- }
442
-
443
- // Call destroy
444
- snackbar.destroy();
445
-
446
- // Check if timer was stopped
447
- if (snackbar.timer && typeof snackbar.timer.stop === 'function') {
448
- expect(snackbar.timer.stop).toHaveBeenCalled();
449
- }
450
- });
451
-
452
- test('should apply custom class', () => {
453
- const customClass = 'custom-snackbar';
454
- const snackbar = createMockSnackbar({
455
- message: 'Test message',
456
- class: customClass
457
- });
458
-
459
- expect(snackbar.element.className).toContain(customClass);
460
- });
461
- });