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,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
- });