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,672 +0,0 @@
1
- // test/components/extended-fab.test.ts
2
- import { describe, test, expect, mock, beforeAll, afterAll } from 'bun:test';
3
- import { JSDOM } from 'jsdom';
4
- import {
5
- type ExtendedFabComponent,
6
- type ExtendedFabConfig,
7
- type ExtendedFabVariant,
8
- type ExtendedFabWidth,
9
- type ExtendedFabPosition
10
- } from '../../src/components/extended-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 extended fab variants
53
- const EXTENDED_FAB_VARIANTS = {
54
- PRIMARY: 'primary',
55
- SECONDARY: 'secondary',
56
- TERTIARY: 'tertiary',
57
- SURFACE: 'surface'
58
- } as const;
59
-
60
- // Constants for extended fab positions
61
- const EXTENDED_FAB_POSITIONS = {
62
- TOP_RIGHT: 'top-right',
63
- TOP_LEFT: 'top-left',
64
- BOTTOM_RIGHT: 'bottom-right',
65
- BOTTOM_LEFT: 'bottom-left'
66
- } as const;
67
-
68
- // Constants for extended fab width behaviors
69
- const EXTENDED_FAB_WIDTHS = {
70
- FIXED: 'fixed',
71
- FLUID: 'fluid'
72
- } as const;
73
-
74
- // Mock extended fab implementation
75
- const createMockExtendedFab = (config: ExtendedFabConfig = {}): ExtendedFabComponent => {
76
- // Create the main element
77
- const element = document.createElement('button');
78
- element.className = 'mtrl-extended-fab';
79
- element.type = config.type || 'button';
80
-
81
- // Set default configuration
82
- const settings = {
83
- componentName: 'extended-fab',
84
- prefix: config.prefix || 'mtrl',
85
- variant: config.variant || EXTENDED_FAB_VARIANTS.PRIMARY,
86
- disabled: config.disabled || false,
87
- iconPosition: config.iconPosition || 'start',
88
- ripple: config.ripple !== undefined ? config.ripple : true,
89
- width: config.width || EXTENDED_FAB_WIDTHS.FIXED,
90
- collapseOnScroll: config.collapseOnScroll || false,
91
- animate: config.animate || false
92
- };
93
-
94
- // Apply variant class
95
- if (settings.variant) {
96
- element.classList.add(`mtrl-extended-fab--${settings.variant}`);
97
- }
98
-
99
- // Apply disabled state
100
- if (settings.disabled) {
101
- element.disabled = true;
102
- element.classList.add('mtrl-extended-fab--disabled');
103
- }
104
-
105
- // Apply width behavior
106
- if (settings.width) {
107
- element.classList.add(`mtrl-extended-fab--${settings.width}`);
108
- }
109
-
110
- // Apply position if provided
111
- if (config.position) {
112
- element.classList.add(`mtrl-extended-fab--${config.position}`);
113
- }
114
-
115
- // Apply animation if configured
116
- if (settings.animate) {
117
- element.classList.add('mtrl-extended-fab--animate');
118
- }
119
-
120
- // Apply additional classes
121
- if (config.class) {
122
- const classes = config.class.split(' ');
123
- classes.forEach(className => element.classList.add(className));
124
- }
125
-
126
- // Apply value if provided
127
- if (config.value) {
128
- element.setAttribute('value', config.value);
129
- }
130
-
131
- // Apply aria-label if provided
132
- if (config.ariaLabel) {
133
- element.setAttribute('aria-label', config.ariaLabel);
134
- }
135
-
136
- // Create ripple element if enabled
137
- if (settings.ripple) {
138
- const ripple = document.createElement('span');
139
- ripple.className = 'mtrl-extended-fab__ripple';
140
- element.appendChild(ripple);
141
- }
142
-
143
- // Create icon element if provided
144
- let iconElement: HTMLElement | null = null;
145
- if (config.icon) {
146
- iconElement = document.createElement('span');
147
- iconElement.className = 'mtrl-extended-fab__icon';
148
- iconElement.innerHTML = config.icon;
149
-
150
- if (config.iconSize) {
151
- iconElement.style.width = config.iconSize;
152
- iconElement.style.height = config.iconSize;
153
- }
154
-
155
- if (settings.iconPosition === 'end') {
156
- iconElement.classList.add('mtrl-extended-fab__icon--end');
157
- }
158
-
159
- element.appendChild(iconElement);
160
- }
161
-
162
- // Create text element
163
- const textElement = document.createElement('span');
164
- textElement.className = 'mtrl-extended-fab__text';
165
- if (config.text) {
166
- textElement.textContent = config.text;
167
- }
168
- element.appendChild(textElement);
169
-
170
- // Initialize icon API
171
- const iconAPI = {
172
- setIcon: (html: string) => {
173
- if (!iconElement) {
174
- iconElement = document.createElement('span');
175
- iconElement.className = 'mtrl-extended-fab__icon';
176
-
177
- if (settings.iconPosition === 'end') {
178
- iconElement.classList.add('mtrl-extended-fab__icon--end');
179
- element.appendChild(iconElement);
180
- } else {
181
- element.insertBefore(iconElement, element.firstChild);
182
- }
183
- }
184
-
185
- iconElement.innerHTML = html;
186
- return iconAPI;
187
- },
188
-
189
- getIcon: () => iconElement ? iconElement.innerHTML : '',
190
-
191
- getElement: () => iconElement
192
- };
193
-
194
- // Initialize text API
195
- const textAPI = {
196
- setText: (text: string) => {
197
- textElement.textContent = text;
198
- return textAPI;
199
- },
200
-
201
- getText: () => textElement.textContent || '',
202
-
203
- getElement: () => textElement
204
- };
205
-
206
- // Set up event handlers
207
- const eventHandlers: Record<string, Function[]> = {};
208
-
209
- // Create the extended fab component
210
- const extendedFab: ExtendedFabComponent = {
211
- element,
212
- icon: iconAPI,
213
- text: textAPI,
214
-
215
- disabled: {
216
- enable: () => {
217
- element.disabled = false;
218
- element.classList.remove('mtrl-extended-fab--disabled');
219
- },
220
-
221
- disable: () => {
222
- element.disabled = true;
223
- element.classList.add('mtrl-extended-fab--disabled');
224
- },
225
-
226
- isDisabled: () => element.disabled
227
- },
228
-
229
- lifecycle: {
230
- destroy: () => {
231
- extendedFab.destroy();
232
- }
233
- },
234
-
235
- getClass: (name: string) => {
236
- const prefix = settings.prefix;
237
- return name ? `${prefix}-${name}` : `${prefix}-extended-fab`;
238
- },
239
-
240
- getValue: () => element.getAttribute('value') || '',
241
-
242
- setValue: (value: string) => {
243
- element.setAttribute('value', value);
244
- return extendedFab;
245
- },
246
-
247
- enable: () => {
248
- extendedFab.disabled.enable();
249
- return extendedFab;
250
- },
251
-
252
- disable: () => {
253
- extendedFab.disabled.disable();
254
- return extendedFab;
255
- },
256
-
257
- setIcon: (icon: string) => {
258
- iconAPI.setIcon(icon);
259
- return extendedFab;
260
- },
261
-
262
- getIcon: () => iconAPI.getIcon(),
263
-
264
- setText: (text: string) => {
265
- textAPI.setText(text);
266
- return extendedFab;
267
- },
268
-
269
- getText: () => textAPI.getText(),
270
-
271
- setPosition: (position: string) => {
272
- // Remove existing position classes
273
- const positionValues = Object.values(EXTENDED_FAB_POSITIONS);
274
- positionValues.forEach(pos => {
275
- element.classList.remove(`mtrl-extended-fab--${pos}`);
276
- });
277
-
278
- // Add new position class
279
- element.classList.add(`mtrl-extended-fab--${position}`);
280
- return extendedFab;
281
- },
282
-
283
- getPosition: () => {
284
- const positionValues = Object.values(EXTENDED_FAB_POSITIONS);
285
- for (const pos of positionValues) {
286
- if (element.classList.contains(`mtrl-extended-fab--${pos}`)) {
287
- return pos;
288
- }
289
- }
290
- return null;
291
- },
292
-
293
- lower: () => {
294
- element.classList.add('mtrl-extended-fab--lowered');
295
- return extendedFab;
296
- },
297
-
298
- raise: () => {
299
- element.classList.remove('mtrl-extended-fab--lowered');
300
- return extendedFab;
301
- },
302
-
303
- collapse: () => {
304
- element.classList.add('mtrl-extended-fab--collapsed');
305
- return extendedFab;
306
- },
307
-
308
- expand: () => {
309
- element.classList.remove('mtrl-extended-fab--collapsed');
310
- return extendedFab;
311
- },
312
-
313
- destroy: () => {
314
- // Remove element from DOM if it has a parent
315
- if (element.parentNode) {
316
- element.parentNode.removeChild(element);
317
- }
318
-
319
- // Clear event handlers
320
- for (const event in eventHandlers) {
321
- eventHandlers[event] = [];
322
- }
323
- },
324
-
325
- on: (event: string, handler: Function) => {
326
- if (!eventHandlers[event]) {
327
- eventHandlers[event] = [];
328
- }
329
-
330
- eventHandlers[event].push(handler);
331
-
332
- element.addEventListener(event, handler as EventListener);
333
- return extendedFab;
334
- },
335
-
336
- off: (event: string, handler: Function) => {
337
- if (eventHandlers[event]) {
338
- eventHandlers[event] = eventHandlers[event].filter(h => h !== handler);
339
- }
340
-
341
- element.removeEventListener(event, handler as EventListener);
342
- return extendedFab;
343
- },
344
-
345
- addClass: (...classes: string[]) => {
346
- classes.forEach(className => element.classList.add(className));
347
- return extendedFab;
348
- }
349
- };
350
-
351
- return extendedFab;
352
- };
353
-
354
- describe('Extended FAB Component', () => {
355
- test('should create an extended fab button element', () => {
356
- const extendedFab = createMockExtendedFab();
357
-
358
- expect(extendedFab.element).toBeDefined();
359
- expect(extendedFab.element.tagName).toBe('BUTTON');
360
- expect(extendedFab.element.className).toContain('mtrl-extended-fab');
361
- });
362
-
363
- test('should apply variant classes', () => {
364
- const variants: ExtendedFabVariant[] = [
365
- EXTENDED_FAB_VARIANTS.PRIMARY,
366
- EXTENDED_FAB_VARIANTS.SECONDARY,
367
- EXTENDED_FAB_VARIANTS.TERTIARY,
368
- EXTENDED_FAB_VARIANTS.SURFACE
369
- ];
370
-
371
- variants.forEach(variant => {
372
- const extendedFab = createMockExtendedFab({ variant });
373
- expect(extendedFab.element.className).toContain(`mtrl-extended-fab--${variant}`);
374
- });
375
- });
376
-
377
- test('should set initial text content', () => {
378
- const extendedFab = createMockExtendedFab({
379
- text: 'Create Item'
380
- });
381
-
382
- const textElement = extendedFab.element.querySelector('.mtrl-extended-fab__text');
383
- expect(textElement).toBeDefined();
384
- expect(textElement?.textContent).toBe('Create Item');
385
- expect(extendedFab.getText()).toBe('Create Item');
386
- });
387
-
388
- test('should set initial icon', () => {
389
- const iconHtml = '<svg><path></path></svg>';
390
- const extendedFab = createMockExtendedFab({
391
- icon: iconHtml
392
- });
393
-
394
- const iconElement = extendedFab.element.querySelector('.mtrl-extended-fab__icon');
395
- expect(iconElement).toBeDefined();
396
- expect(iconElement?.innerHTML).toBe(iconHtml);
397
- expect(extendedFab.getIcon()).toBe(iconHtml);
398
- });
399
-
400
- test('should position icon at start by default', () => {
401
- const extendedFab = createMockExtendedFab({
402
- icon: '<svg></svg>'
403
- });
404
-
405
- const iconElement = extendedFab.element.querySelector('.mtrl-extended-fab__icon');
406
- expect(iconElement?.classList.contains('mtrl-extended-fab__icon--end')).toBe(false);
407
-
408
- // Icon should be a child of the extended fab
409
- expect(iconElement?.parentNode).toBe(extendedFab.element);
410
- });
411
-
412
- test('should position icon at end when configured', () => {
413
- const extendedFab = createMockExtendedFab({
414
- icon: '<svg></svg>',
415
- iconPosition: 'end'
416
- });
417
-
418
- const iconElement = extendedFab.element.querySelector('.mtrl-extended-fab__icon');
419
- expect(iconElement?.classList.contains('mtrl-extended-fab__icon--end')).toBe(true);
420
-
421
- // Check if the icon is added to the end (among the last children rather than checking exact lastChild)
422
- const children = Array.from(extendedFab.element.childNodes);
423
- expect(children.includes(iconElement as ChildNode)).toBe(true);
424
- });
425
-
426
- test('should apply disabled state', () => {
427
- const extendedFab = createMockExtendedFab({
428
- disabled: true
429
- });
430
-
431
- expect(extendedFab.element.disabled).toBe(true);
432
- expect(extendedFab.element.className).toContain('mtrl-extended-fab--disabled');
433
- expect(extendedFab.disabled.isDisabled()).toBe(true);
434
- });
435
-
436
- test('should apply width behavior', () => {
437
- const widths: ExtendedFabWidth[] = [
438
- EXTENDED_FAB_WIDTHS.FIXED,
439
- EXTENDED_FAB_WIDTHS.FLUID
440
- ];
441
-
442
- widths.forEach(width => {
443
- const extendedFab = createMockExtendedFab({ width });
444
- expect(extendedFab.element.className).toContain(`mtrl-extended-fab--${width}`);
445
- });
446
- });
447
-
448
- test('should apply position classes', () => {
449
- const positions: ExtendedFabPosition[] = [
450
- EXTENDED_FAB_POSITIONS.TOP_RIGHT,
451
- EXTENDED_FAB_POSITIONS.TOP_LEFT,
452
- EXTENDED_FAB_POSITIONS.BOTTOM_RIGHT,
453
- EXTENDED_FAB_POSITIONS.BOTTOM_LEFT
454
- ];
455
-
456
- positions.forEach(position => {
457
- const extendedFab = createMockExtendedFab({ position });
458
- expect(extendedFab.element.className).toContain(`mtrl-extended-fab--${position}`);
459
- expect(extendedFab.getPosition()).toBe(position);
460
- });
461
- });
462
-
463
- test('should set value attribute', () => {
464
- const extendedFab = createMockExtendedFab({
465
- value: 'new-item'
466
- });
467
-
468
- expect(extendedFab.element.getAttribute('value')).toBe('new-item');
469
- expect(extendedFab.getValue()).toBe('new-item');
470
- });
471
-
472
- test('should set aria-label attribute', () => {
473
- const extendedFab = createMockExtendedFab({
474
- ariaLabel: 'Create new item'
475
- });
476
-
477
- expect(extendedFab.element.getAttribute('aria-label')).toBe('Create new item');
478
- });
479
-
480
- test('should create ripple element by default', () => {
481
- const extendedFab = createMockExtendedFab();
482
-
483
- const rippleElement = extendedFab.element.querySelector('.mtrl-extended-fab__ripple');
484
- expect(rippleElement).toBeDefined();
485
- });
486
-
487
- test('should not create ripple when disabled', () => {
488
- const extendedFab = createMockExtendedFab({
489
- ripple: false
490
- });
491
-
492
- const rippleElement = extendedFab.element.querySelector('.mtrl-extended-fab__ripple');
493
- expect(rippleElement).toBeNull();
494
- });
495
-
496
- test('should apply custom icon size', () => {
497
- const extendedFab = createMockExtendedFab({
498
- icon: '<svg></svg>',
499
- iconSize: '32px'
500
- });
501
-
502
- const iconElement = extendedFab.element.querySelector('.mtrl-extended-fab__icon');
503
- expect(iconElement).toBeDefined();
504
- expect((iconElement as HTMLElement).style.width).toBe('32px');
505
- expect((iconElement as HTMLElement).style.height).toBe('32px');
506
- });
507
-
508
- test('should apply animation class when configured', () => {
509
- const extendedFab = createMockExtendedFab({
510
- animate: true
511
- });
512
-
513
- expect(extendedFab.element.className).toContain('mtrl-extended-fab--animate');
514
- });
515
-
516
- test('should be able to change text content', () => {
517
- const extendedFab = createMockExtendedFab({
518
- text: 'Initial Text'
519
- });
520
-
521
- expect(extendedFab.getText()).toBe('Initial Text');
522
-
523
- extendedFab.setText('Updated Text');
524
-
525
- expect(extendedFab.getText()).toBe('Updated Text');
526
-
527
- const textElement = extendedFab.element.querySelector('.mtrl-extended-fab__text');
528
- expect(textElement?.textContent).toBe('Updated Text');
529
- });
530
-
531
- test('should be able to change icon', () => {
532
- const extendedFab = createMockExtendedFab();
533
-
534
- const initialIconElement = extendedFab.element.querySelector('.mtrl-extended-fab__icon');
535
- expect(initialIconElement).toBeNull();
536
-
537
- const iconHtml = '<svg><path></path></svg>';
538
- extendedFab.setIcon(iconHtml);
539
-
540
- const updatedIconElement = extendedFab.element.querySelector('.mtrl-extended-fab__icon');
541
- expect(updatedIconElement).toBeDefined();
542
- expect(updatedIconElement?.innerHTML).toBe(iconHtml);
543
- expect(extendedFab.getIcon()).toBe(iconHtml);
544
- });
545
-
546
- test('should be able to change disabled state', () => {
547
- const extendedFab = createMockExtendedFab();
548
-
549
- expect(extendedFab.disabled.isDisabled()).toBe(false);
550
-
551
- extendedFab.disable();
552
-
553
- expect(extendedFab.disabled.isDisabled()).toBe(true);
554
- expect(extendedFab.element.disabled).toBe(true);
555
- expect(extendedFab.element.className).toContain('mtrl-extended-fab--disabled');
556
-
557
- extendedFab.enable();
558
-
559
- expect(extendedFab.disabled.isDisabled()).toBe(false);
560
- expect(extendedFab.element.disabled).toBe(false);
561
- expect(extendedFab.element.className).not.toContain('mtrl-extended-fab--disabled');
562
- });
563
-
564
- test('should be able to change value', () => {
565
- const extendedFab = createMockExtendedFab();
566
-
567
- expect(extendedFab.getValue()).toBe('');
568
-
569
- extendedFab.setValue('new-value');
570
-
571
- expect(extendedFab.getValue()).toBe('new-value');
572
- expect(extendedFab.element.getAttribute('value')).toBe('new-value');
573
- });
574
-
575
- test('should be able to change position', () => {
576
- const extendedFab = createMockExtendedFab({
577
- position: EXTENDED_FAB_POSITIONS.BOTTOM_RIGHT
578
- });
579
-
580
- expect(extendedFab.getPosition()).toBe(EXTENDED_FAB_POSITIONS.BOTTOM_RIGHT);
581
-
582
- extendedFab.setPosition(EXTENDED_FAB_POSITIONS.TOP_LEFT);
583
-
584
- expect(extendedFab.getPosition()).toBe(EXTENDED_FAB_POSITIONS.TOP_LEFT);
585
- expect(extendedFab.element.className).toContain(`mtrl-extended-fab--${EXTENDED_FAB_POSITIONS.TOP_LEFT}`);
586
- expect(extendedFab.element.className).not.toContain(`mtrl-extended-fab--${EXTENDED_FAB_POSITIONS.BOTTOM_RIGHT}`);
587
- });
588
-
589
- test('should be able to lower and raise', () => {
590
- const extendedFab = createMockExtendedFab();
591
-
592
- expect(extendedFab.element.className).not.toContain('mtrl-extended-fab--lowered');
593
-
594
- extendedFab.lower();
595
-
596
- expect(extendedFab.element.className).toContain('mtrl-extended-fab--lowered');
597
-
598
- extendedFab.raise();
599
-
600
- expect(extendedFab.element.className).not.toContain('mtrl-extended-fab--lowered');
601
- });
602
-
603
- test('should be able to collapse and expand', () => {
604
- const extendedFab = createMockExtendedFab();
605
-
606
- expect(extendedFab.element.className).not.toContain('mtrl-extended-fab--collapsed');
607
-
608
- extendedFab.collapse();
609
-
610
- expect(extendedFab.element.className).toContain('mtrl-extended-fab--collapsed');
611
-
612
- extendedFab.expand();
613
-
614
- expect(extendedFab.element.className).not.toContain('mtrl-extended-fab--collapsed');
615
- });
616
-
617
- test('should add event listeners', () => {
618
- const extendedFab = createMockExtendedFab();
619
- let clicked = false;
620
-
621
- extendedFab.on('click', () => {
622
- clicked = true;
623
- });
624
-
625
- // Simulate click
626
- extendedFab.element.dispatchEvent(new Event('click'));
627
-
628
- expect(clicked).toBe(true);
629
- });
630
-
631
- test('should remove event listeners', () => {
632
- const extendedFab = createMockExtendedFab();
633
- let count = 0;
634
-
635
- const handler = () => {
636
- count++;
637
- };
638
-
639
- extendedFab.on('click', handler);
640
-
641
- // First click
642
- extendedFab.element.dispatchEvent(new Event('click'));
643
- expect(count).toBe(1);
644
-
645
- // Remove listener
646
- extendedFab.off('click', handler);
647
-
648
- // Second click
649
- extendedFab.element.dispatchEvent(new Event('click'));
650
- expect(count).toBe(1); // Count should not increase
651
- });
652
-
653
- test('should add CSS classes', () => {
654
- const extendedFab = createMockExtendedFab();
655
-
656
- extendedFab.addClass('custom-class', 'special-fab');
657
-
658
- expect(extendedFab.element.className).toContain('custom-class');
659
- expect(extendedFab.element.className).toContain('special-fab');
660
- });
661
-
662
- test('should be properly destroyed', () => {
663
- const extendedFab = createMockExtendedFab();
664
- document.body.appendChild(extendedFab.element);
665
-
666
- expect(document.body.contains(extendedFab.element)).toBe(true);
667
-
668
- extendedFab.destroy();
669
-
670
- expect(document.body.contains(extendedFab.element)).toBe(false);
671
- });
672
- });