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,561 +0,0 @@
1
- // test/components/fab.test.ts
2
- import { describe, test, expect, mock, beforeAll, afterAll } from 'bun:test';
3
- import { JSDOM } from 'jsdom';
4
- import {
5
- type FabComponent,
6
- type FabConfig,
7
- type FabVariant,
8
- type FabSize,
9
- type FabPosition
10
- } from '../../src/components/fab/types';
11
-
12
- // Setup jsdom environment
13
- let dom: JSDOM;
14
- let window: Window;
15
- let document: Document;
16
- let originalGlobalDocument: any;
17
- let originalGlobalWindow: any;
18
-
19
- beforeAll(() => {
20
- // Create a new JSDOM instance
21
- dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {
22
- url: 'http://localhost/',
23
- pretendToBeVisual: true
24
- });
25
-
26
- // Get window and document from jsdom
27
- window = dom.window;
28
- document = window.document;
29
-
30
- // Store original globals
31
- originalGlobalDocument = global.document;
32
- originalGlobalWindow = global.window;
33
-
34
- // Set globals to use jsdom
35
- global.document = document;
36
- global.window = window;
37
- global.Element = window.Element;
38
- global.HTMLElement = window.HTMLElement;
39
- global.HTMLButtonElement = window.HTMLButtonElement;
40
- global.Event = window.Event;
41
- });
42
-
43
- afterAll(() => {
44
- // Restore original globals
45
- global.document = originalGlobalDocument;
46
- global.window = originalGlobalWindow;
47
-
48
- // Clean up jsdom
49
- window.close();
50
- });
51
-
52
- // Constants for fab variants
53
- const FAB_VARIANTS = {
54
- PRIMARY: 'primary',
55
- SECONDARY: 'secondary',
56
- TERTIARY: 'tertiary',
57
- SURFACE: 'surface'
58
- } as const;
59
-
60
- // Constants for fab sizes
61
- const FAB_SIZES = {
62
- SMALL: 'small',
63
- DEFAULT: 'default',
64
- LARGE: 'large'
65
- } as const;
66
-
67
- // Constants for fab positions
68
- const FAB_POSITIONS = {
69
- TOP_RIGHT: 'top-right',
70
- TOP_LEFT: 'top-left',
71
- BOTTOM_RIGHT: 'bottom-right',
72
- BOTTOM_LEFT: 'bottom-left'
73
- } as const;
74
-
75
- // Mock fab implementation
76
- const createMockFab = (config: FabConfig = {}): FabComponent => {
77
- // Create the main element
78
- const element = document.createElement('button');
79
- element.className = 'mtrl-fab';
80
- element.type = config.type || 'button';
81
-
82
- // Set default configuration
83
- const settings = {
84
- componentName: 'fab',
85
- prefix: config.prefix || 'mtrl',
86
- variant: config.variant || FAB_VARIANTS.PRIMARY,
87
- size: config.size || FAB_SIZES.DEFAULT,
88
- disabled: config.disabled || false,
89
- ripple: config.ripple !== undefined ? config.ripple : true,
90
- animate: config.animate || false
91
- };
92
-
93
- // Apply variant class
94
- if (settings.variant) {
95
- element.classList.add(`mtrl-fab--${settings.variant}`);
96
- }
97
-
98
- // Apply size class
99
- if (settings.size && settings.size !== FAB_SIZES.DEFAULT) {
100
- element.classList.add(`mtrl-fab--${settings.size}`);
101
- }
102
-
103
- // Apply disabled state
104
- if (settings.disabled) {
105
- element.disabled = true;
106
- element.classList.add('mtrl-fab--disabled');
107
- }
108
-
109
- // Apply position if provided
110
- if (config.position) {
111
- element.classList.add(`mtrl-fab--${config.position}`);
112
- }
113
-
114
- // Apply animation if configured
115
- if (settings.animate) {
116
- element.classList.add('mtrl-fab--animate');
117
- }
118
-
119
- // Apply additional classes
120
- if (config.class) {
121
- const classes = config.class.split(' ');
122
- classes.forEach(className => element.classList.add(className));
123
- }
124
-
125
- // Apply value if provided
126
- if (config.value) {
127
- element.setAttribute('value', config.value);
128
- }
129
-
130
- // Apply aria-label if provided
131
- if (config.ariaLabel) {
132
- element.setAttribute('aria-label', config.ariaLabel);
133
- }
134
-
135
- // Create ripple element if enabled
136
- if (settings.ripple) {
137
- const ripple = document.createElement('span');
138
- ripple.className = 'mtrl-fab__ripple';
139
- element.appendChild(ripple);
140
- }
141
-
142
- // Create icon element if provided
143
- let iconElement: HTMLElement | null = null;
144
- if (config.icon) {
145
- iconElement = document.createElement('span');
146
- iconElement.className = 'mtrl-fab__icon';
147
- iconElement.innerHTML = config.icon;
148
-
149
- if (config.iconSize) {
150
- iconElement.style.width = config.iconSize;
151
- iconElement.style.height = config.iconSize;
152
- }
153
-
154
- element.appendChild(iconElement);
155
- }
156
-
157
- // Initialize icon API
158
- const iconAPI = {
159
- setIcon: (html: string) => {
160
- if (!iconElement) {
161
- iconElement = document.createElement('span');
162
- iconElement.className = 'mtrl-fab__icon';
163
- element.appendChild(iconElement);
164
- }
165
-
166
- iconElement.innerHTML = html;
167
- return iconAPI;
168
- },
169
-
170
- getIcon: () => iconElement ? iconElement.innerHTML : '',
171
-
172
- getElement: () => iconElement
173
- };
174
-
175
- // Set up event handlers
176
- const eventHandlers: Record<string, Function[]> = {};
177
-
178
- // Create the fab component
179
- const fab: FabComponent = {
180
- element,
181
- icon: iconAPI,
182
-
183
- disabled: {
184
- enable: () => {
185
- element.disabled = false;
186
- element.classList.remove('mtrl-fab--disabled');
187
- },
188
-
189
- disable: () => {
190
- element.disabled = true;
191
- element.classList.add('mtrl-fab--disabled');
192
- },
193
-
194
- isDisabled: () => element.disabled
195
- },
196
-
197
- lifecycle: {
198
- destroy: () => {
199
- fab.destroy();
200
- }
201
- },
202
-
203
- getClass: (name: string) => {
204
- const prefix = settings.prefix;
205
- return name ? `${prefix}-${name}` : `${prefix}-fab`;
206
- },
207
-
208
- getValue: () => element.getAttribute('value') || '',
209
-
210
- setValue: (value: string) => {
211
- element.setAttribute('value', value);
212
- return fab;
213
- },
214
-
215
- enable: () => {
216
- fab.disabled.enable();
217
- return fab;
218
- },
219
-
220
- disable: () => {
221
- fab.disabled.disable();
222
- return fab;
223
- },
224
-
225
- setIcon: (icon: string) => {
226
- iconAPI.setIcon(icon);
227
- return fab;
228
- },
229
-
230
- getIcon: () => iconAPI.getIcon(),
231
-
232
- setPosition: (position: string) => {
233
- // Remove existing position classes
234
- const positionValues = Object.values(FAB_POSITIONS);
235
- positionValues.forEach(pos => {
236
- element.classList.remove(`mtrl-fab--${pos}`);
237
- });
238
-
239
- // Add new position class
240
- element.classList.add(`mtrl-fab--${position}`);
241
- return fab;
242
- },
243
-
244
- getPosition: () => {
245
- const positionValues = Object.values(FAB_POSITIONS);
246
- for (const pos of positionValues) {
247
- if (element.classList.contains(`mtrl-fab--${pos}`)) {
248
- return pos;
249
- }
250
- }
251
- return null;
252
- },
253
-
254
- lower: () => {
255
- element.classList.add('mtrl-fab--lowered');
256
- return fab;
257
- },
258
-
259
- raise: () => {
260
- element.classList.remove('mtrl-fab--lowered');
261
- return fab;
262
- },
263
-
264
- destroy: () => {
265
- // Remove element from DOM if it has a parent
266
- if (element.parentNode) {
267
- element.parentNode.removeChild(element);
268
- }
269
-
270
- // Clear event handlers
271
- for (const event in eventHandlers) {
272
- eventHandlers[event] = [];
273
- }
274
- },
275
-
276
- on: (event: string, handler: Function) => {
277
- if (!eventHandlers[event]) {
278
- eventHandlers[event] = [];
279
- }
280
-
281
- eventHandlers[event].push(handler);
282
-
283
- element.addEventListener(event, handler as EventListener);
284
- return fab;
285
- },
286
-
287
- off: (event: string, handler: Function) => {
288
- if (eventHandlers[event]) {
289
- eventHandlers[event] = eventHandlers[event].filter(h => h !== handler);
290
- }
291
-
292
- element.removeEventListener(event, handler as EventListener);
293
- return fab;
294
- },
295
-
296
- addClass: (...classes: string[]) => {
297
- classes.forEach(className => element.classList.add(className));
298
- return fab;
299
- }
300
- };
301
-
302
- return fab;
303
- };
304
-
305
- describe('FAB Component', () => {
306
- test('should create a fab button element', () => {
307
- const fab = createMockFab();
308
-
309
- expect(fab.element).toBeDefined();
310
- expect(fab.element.tagName).toBe('BUTTON');
311
- expect(fab.element.className).toContain('mtrl-fab');
312
- });
313
-
314
- test('should apply variant classes', () => {
315
- const variants: FabVariant[] = [
316
- FAB_VARIANTS.PRIMARY,
317
- FAB_VARIANTS.SECONDARY,
318
- FAB_VARIANTS.TERTIARY,
319
- FAB_VARIANTS.SURFACE
320
- ];
321
-
322
- variants.forEach(variant => {
323
- const fab = createMockFab({ variant });
324
- expect(fab.element.className).toContain(`mtrl-fab--${variant}`);
325
- });
326
- });
327
-
328
- test('should apply size classes', () => {
329
- const sizes: FabSize[] = [
330
- FAB_SIZES.SMALL,
331
- FAB_SIZES.LARGE
332
- ];
333
-
334
- sizes.forEach(size => {
335
- const fab = createMockFab({ size });
336
- expect(fab.element.className).toContain(`mtrl-fab--${size}`);
337
- });
338
-
339
- // Default size should not add a class
340
- const defaultFab = createMockFab({ size: FAB_SIZES.DEFAULT });
341
- expect(defaultFab.element.className).not.toContain(`mtrl-fab--${FAB_SIZES.DEFAULT}`);
342
- });
343
-
344
- test('should set initial icon', () => {
345
- const iconHtml = '<svg><path></path></svg>';
346
- const fab = createMockFab({
347
- icon: iconHtml
348
- });
349
-
350
- const iconElement = fab.element.querySelector('.mtrl-fab__icon');
351
- expect(iconElement).toBeDefined();
352
- expect(iconElement?.innerHTML).toBe(iconHtml);
353
- expect(fab.getIcon()).toBe(iconHtml);
354
- });
355
-
356
- test('should apply disabled state', () => {
357
- const fab = createMockFab({
358
- disabled: true
359
- });
360
-
361
- expect(fab.element.disabled).toBe(true);
362
- expect(fab.element.className).toContain('mtrl-fab--disabled');
363
- expect(fab.disabled.isDisabled()).toBe(true);
364
- });
365
-
366
- test('should apply position classes', () => {
367
- const positions: FabPosition[] = [
368
- FAB_POSITIONS.TOP_RIGHT,
369
- FAB_POSITIONS.TOP_LEFT,
370
- FAB_POSITIONS.BOTTOM_RIGHT,
371
- FAB_POSITIONS.BOTTOM_LEFT
372
- ];
373
-
374
- positions.forEach(position => {
375
- const fab = createMockFab({ position });
376
- expect(fab.element.className).toContain(`mtrl-fab--${position}`);
377
- expect(fab.getPosition()).toBe(position);
378
- });
379
- });
380
-
381
- test('should set value attribute', () => {
382
- const fab = createMockFab({
383
- value: 'new-item'
384
- });
385
-
386
- expect(fab.element.getAttribute('value')).toBe('new-item');
387
- expect(fab.getValue()).toBe('new-item');
388
- });
389
-
390
- test('should set aria-label attribute', () => {
391
- const fab = createMockFab({
392
- ariaLabel: 'Create new item'
393
- });
394
-
395
- expect(fab.element.getAttribute('aria-label')).toBe('Create new item');
396
- });
397
-
398
- test('should create ripple element by default', () => {
399
- const fab = createMockFab();
400
-
401
- const rippleElement = fab.element.querySelector('.mtrl-fab__ripple');
402
- expect(rippleElement).toBeDefined();
403
- });
404
-
405
- test('should not create ripple when disabled', () => {
406
- const fab = createMockFab({
407
- ripple: false
408
- });
409
-
410
- const rippleElement = fab.element.querySelector('.mtrl-fab__ripple');
411
- expect(rippleElement).toBeNull();
412
- });
413
-
414
- test('should apply custom icon size', () => {
415
- const fab = createMockFab({
416
- icon: '<svg></svg>',
417
- iconSize: '32px'
418
- });
419
-
420
- const iconElement = fab.element.querySelector('.mtrl-fab__icon');
421
- expect(iconElement).toBeDefined();
422
- expect((iconElement as HTMLElement).style.width).toBe('32px');
423
- expect((iconElement as HTMLElement).style.height).toBe('32px');
424
- });
425
-
426
- test('should apply animation class when configured', () => {
427
- const fab = createMockFab({
428
- animate: true
429
- });
430
-
431
- expect(fab.element.className).toContain('mtrl-fab--animate');
432
- });
433
-
434
- test('should be able to change icon', () => {
435
- const fab = createMockFab();
436
-
437
- const initialIconElement = fab.element.querySelector('.mtrl-fab__icon');
438
- expect(initialIconElement).toBeNull();
439
-
440
- const iconHtml = '<svg><path></path></svg>';
441
- fab.setIcon(iconHtml);
442
-
443
- const updatedIconElement = fab.element.querySelector('.mtrl-fab__icon');
444
- expect(updatedIconElement).toBeDefined();
445
- expect(updatedIconElement?.innerHTML).toBe(iconHtml);
446
- expect(fab.getIcon()).toBe(iconHtml);
447
- });
448
-
449
- test('should be able to change disabled state', () => {
450
- const fab = createMockFab();
451
-
452
- expect(fab.disabled.isDisabled()).toBe(false);
453
-
454
- fab.disable();
455
-
456
- expect(fab.disabled.isDisabled()).toBe(true);
457
- expect(fab.element.disabled).toBe(true);
458
- expect(fab.element.className).toContain('mtrl-fab--disabled');
459
-
460
- fab.enable();
461
-
462
- expect(fab.disabled.isDisabled()).toBe(false);
463
- expect(fab.element.disabled).toBe(false);
464
- expect(fab.element.className).not.toContain('mtrl-fab--disabled');
465
- });
466
-
467
- test('should be able to change value', () => {
468
- const fab = createMockFab();
469
-
470
- expect(fab.getValue()).toBe('');
471
-
472
- fab.setValue('new-value');
473
-
474
- expect(fab.getValue()).toBe('new-value');
475
- expect(fab.element.getAttribute('value')).toBe('new-value');
476
- });
477
-
478
- test('should be able to change position', () => {
479
- const fab = createMockFab({
480
- position: FAB_POSITIONS.BOTTOM_RIGHT
481
- });
482
-
483
- expect(fab.getPosition()).toBe(FAB_POSITIONS.BOTTOM_RIGHT);
484
-
485
- fab.setPosition(FAB_POSITIONS.TOP_LEFT);
486
-
487
- expect(fab.getPosition()).toBe(FAB_POSITIONS.TOP_LEFT);
488
- expect(fab.element.className).toContain(`mtrl-fab--${FAB_POSITIONS.TOP_LEFT}`);
489
- expect(fab.element.className).not.toContain(`mtrl-fab--${FAB_POSITIONS.BOTTOM_RIGHT}`);
490
- });
491
-
492
- test('should be able to lower and raise', () => {
493
- const fab = createMockFab();
494
-
495
- expect(fab.element.className).not.toContain('mtrl-fab--lowered');
496
-
497
- fab.lower();
498
-
499
- expect(fab.element.className).toContain('mtrl-fab--lowered');
500
-
501
- fab.raise();
502
-
503
- expect(fab.element.className).not.toContain('mtrl-fab--lowered');
504
- });
505
-
506
- test('should add event listeners', () => {
507
- const fab = createMockFab();
508
- let clicked = false;
509
-
510
- fab.on('click', () => {
511
- clicked = true;
512
- });
513
-
514
- // Simulate click
515
- fab.element.dispatchEvent(new Event('click'));
516
-
517
- expect(clicked).toBe(true);
518
- });
519
-
520
- test('should remove event listeners', () => {
521
- const fab = createMockFab();
522
- let count = 0;
523
-
524
- const handler = () => {
525
- count++;
526
- };
527
-
528
- fab.on('click', handler);
529
-
530
- // First click
531
- fab.element.dispatchEvent(new Event('click'));
532
- expect(count).toBe(1);
533
-
534
- // Remove listener
535
- fab.off('click', handler);
536
-
537
- // Second click
538
- fab.element.dispatchEvent(new Event('click'));
539
- expect(count).toBe(1); // Count should not increase
540
- });
541
-
542
- test('should add CSS classes', () => {
543
- const fab = createMockFab();
544
-
545
- fab.addClass('custom-class', 'special-fab');
546
-
547
- expect(fab.element.className).toContain('custom-class');
548
- expect(fab.element.className).toContain('special-fab');
549
- });
550
-
551
- test('should be properly destroyed', () => {
552
- const fab = createMockFab();
553
- document.body.appendChild(fab.element);
554
-
555
- expect(document.body.contains(fab.element)).toBe(true);
556
-
557
- fab.destroy();
558
-
559
- expect(document.body.contains(fab.element)).toBe(false);
560
- });
561
- });