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,692 +0,0 @@
1
- // test/components/chip.test.ts
2
- import { describe, test, expect } from 'bun:test';
3
- import {
4
- type ChipComponent,
5
- type ChipConfig,
6
- type ChipVariant
7
- } from '../../src/components/chip/types';
8
-
9
- // Constants for chip variants
10
- const CHIP_VARIANTS = {
11
- FILLED: 'filled',
12
- OUTLINED: 'outlined',
13
- ELEVATED: 'elevated',
14
- ASSIST: 'assist',
15
- FILTER: 'filter',
16
- INPUT: 'input',
17
- SUGGESTION: 'suggestion'
18
- } as const;
19
-
20
- // Mock chip implementation
21
- const createMockChip = (config: ChipConfig = {}): ChipComponent => {
22
- // Create the main element
23
- const element = document.createElement('div');
24
- element.className = 'mtrl-chip';
25
-
26
- // Set default configuration
27
- const componentConfig = {
28
- componentName: 'chip',
29
- prefix: config.prefix || 'mtrl',
30
- variant: config.variant || CHIP_VARIANTS.FILLED,
31
- disabled: config.disabled || false,
32
- selected: config.selected || false,
33
- ripple: config.ripple !== undefined ? config.ripple : true
34
- };
35
-
36
- // Apply variant class
37
- if (componentConfig.variant) {
38
- element.classList.add(`mtrl-chip--${componentConfig.variant}`);
39
- }
40
-
41
- // Apply disabled state
42
- if (componentConfig.disabled) {
43
- element.classList.add('mtrl-chip--disabled');
44
- element.setAttribute('aria-disabled', 'true');
45
- }
46
-
47
- // Apply selected state
48
- if (componentConfig.selected) {
49
- element.classList.add('mtrl-chip--selected');
50
- element.setAttribute('aria-selected', 'true');
51
- }
52
-
53
- // Apply value if provided
54
- if (config.value) {
55
- element.setAttribute('data-value', config.value);
56
- }
57
-
58
- // Apply additional classes
59
- if (config.class) {
60
- const classes = config.class.split(' ');
61
- classes.forEach(className => element.classList.add(className));
62
- }
63
-
64
- // Create ripple element if enabled
65
- if (componentConfig.ripple) {
66
- const ripple = document.createElement('span');
67
- ripple.className = 'mtrl-chip__ripple';
68
- element.appendChild(ripple);
69
- }
70
-
71
- // Create text element
72
- const textElement = document.createElement('span');
73
- textElement.className = 'mtrl-chip__text';
74
- if (config.text) {
75
- textElement.textContent = config.text;
76
- }
77
- element.appendChild(textElement);
78
-
79
- // Create leading icon element if provided
80
- let leadingIconElement: HTMLElement | null = null;
81
- if (config.leadingIcon || config.icon) {
82
- leadingIconElement = document.createElement('span');
83
- leadingIconElement.className = 'mtrl-chip__icon mtrl-chip__icon--leading';
84
- leadingIconElement.innerHTML = config.leadingIcon || config.icon || '';
85
- element.insertBefore(leadingIconElement, textElement);
86
- }
87
-
88
- // Create trailing icon element if provided
89
- let trailingIconElement: HTMLElement | null = null;
90
- if (config.trailingIcon) {
91
- trailingIconElement = document.createElement('span');
92
- trailingIconElement.className = 'mtrl-chip__icon mtrl-chip__icon--trailing';
93
- trailingIconElement.innerHTML = config.trailingIcon;
94
- element.appendChild(trailingIconElement);
95
-
96
- // Add click handler if provided
97
- if (config.onTrailingIconClick) {
98
- trailingIconElement.addEventListener('click', (e) => {
99
- e.stopPropagation();
100
- if (config.onTrailingIconClick) {
101
- config.onTrailingIconClick(chip);
102
- }
103
- });
104
- }
105
- }
106
-
107
- // Set up event handlers
108
- const eventHandlers: Record<string, Function[]> = {};
109
-
110
- const emit = (event: string, data?: any) => {
111
- if (eventHandlers[event]) {
112
- eventHandlers[event].forEach(handler => handler(data));
113
- }
114
- };
115
-
116
- // Click handler to toggle selected state if selectable
117
- const handleClick = () => {
118
- if (componentConfig.disabled) return;
119
-
120
- if (config.selectable ||
121
- componentConfig.variant === CHIP_VARIANTS.FILTER ||
122
- componentConfig.variant === CHIP_VARIANTS.INPUT) {
123
- chip.toggleSelected();
124
-
125
- if (config.onChange) {
126
- config.onChange(chip);
127
- }
128
-
129
- emit('change', { selected: chip.isSelected() });
130
- }
131
-
132
- emit('click');
133
- };
134
-
135
- // Add click handler to the element
136
- element.addEventListener('click', handleClick);
137
-
138
- // Initialize text API
139
- const textAPI = {
140
- setText: (content: string) => {
141
- textElement.textContent = content;
142
- return textAPI;
143
- },
144
- getText: () => textElement.textContent || '',
145
- getElement: () => textElement
146
- };
147
-
148
- // Initialize icon API
149
- const iconAPI = {
150
- setIcon: (html: string) => {
151
- if (!leadingIconElement) {
152
- leadingIconElement = document.createElement('span');
153
- leadingIconElement.className = 'mtrl-chip__icon mtrl-chip__icon--leading';
154
- element.insertBefore(leadingIconElement, textElement);
155
- }
156
- leadingIconElement.innerHTML = html;
157
- return iconAPI;
158
- },
159
- getIcon: () => leadingIconElement ? leadingIconElement.innerHTML : '',
160
- getElement: () => leadingIconElement
161
- };
162
-
163
- // Create the chip component
164
- const chip: ChipComponent = {
165
- element,
166
- text: textAPI,
167
- icon: iconAPI,
168
-
169
- disabled: {
170
- enable: () => {
171
- element.classList.remove('mtrl-chip--disabled');
172
- element.removeAttribute('aria-disabled');
173
- },
174
- disable: () => {
175
- element.classList.add('mtrl-chip--disabled');
176
- element.setAttribute('aria-disabled', 'true');
177
- },
178
- isDisabled: () => element.classList.contains('mtrl-chip--disabled')
179
- },
180
-
181
- lifecycle: {
182
- destroy: () => {
183
- chip.destroy();
184
- }
185
- },
186
-
187
- getClass: (name: string) => {
188
- const prefix = componentConfig.prefix;
189
- return name ? `${prefix}-${name}` : `${prefix}-chip`;
190
- },
191
-
192
- getValue: () => element.getAttribute('data-value'),
193
-
194
- setValue: (value: string) => {
195
- element.setAttribute('data-value', value);
196
- return chip;
197
- },
198
-
199
- enable: () => {
200
- chip.disabled.enable();
201
- return chip;
202
- },
203
-
204
- disable: () => {
205
- chip.disabled.disable();
206
- return chip;
207
- },
208
-
209
- isDisabled: () => chip.disabled.isDisabled(),
210
-
211
- setText: (content: string) => {
212
- textAPI.setText(content);
213
- return chip;
214
- },
215
-
216
- getText: () => textAPI.getText(),
217
-
218
- setIcon: (icon: string) => {
219
- iconAPI.setIcon(icon);
220
- return chip;
221
- },
222
-
223
- getIcon: () => iconAPI.getIcon(),
224
-
225
- setLeadingIcon: (icon: string) => {
226
- iconAPI.setIcon(icon);
227
- return chip;
228
- },
229
-
230
- setTrailingIcon: (icon: string, onClick?: (chip: ChipComponent) => void) => {
231
- if (!trailingIconElement) {
232
- trailingIconElement = document.createElement('span');
233
- trailingIconElement.className = 'mtrl-chip__icon mtrl-chip__icon--trailing';
234
- element.appendChild(trailingIconElement);
235
- }
236
-
237
- trailingIconElement.innerHTML = icon;
238
-
239
- // Clear previous handlers and add new one if provided
240
- const newTrailingElement = trailingIconElement.cloneNode(true);
241
- trailingIconElement.parentNode?.replaceChild(newTrailingElement, trailingIconElement);
242
- trailingIconElement = newTrailingElement as HTMLElement;
243
-
244
- if (onClick) {
245
- trailingIconElement.addEventListener('click', (e) => {
246
- e.stopPropagation();
247
- onClick(chip);
248
- });
249
- }
250
-
251
- return chip;
252
- },
253
-
254
- isSelected: () => element.classList.contains('mtrl-chip--selected'),
255
-
256
- setSelected: (selected: boolean) => {
257
- if (selected) {
258
- element.classList.add('mtrl-chip--selected');
259
- element.setAttribute('aria-selected', 'true');
260
-
261
- if (config.onSelect) {
262
- config.onSelect(chip);
263
- }
264
- } else {
265
- element.classList.remove('mtrl-chip--selected');
266
- element.removeAttribute('aria-selected');
267
- }
268
-
269
- return chip;
270
- },
271
-
272
- toggleSelected: () => {
273
- const newSelectedState = !chip.isSelected();
274
- chip.setSelected(newSelectedState);
275
- return chip;
276
- },
277
-
278
- destroy: () => {
279
- // Remove event listeners
280
- element.removeEventListener('click', handleClick);
281
-
282
- // Remove element from DOM if it has a parent
283
- if (element.parentNode) {
284
- element.parentNode.removeChild(element);
285
- }
286
-
287
- // Clear event handlers
288
- for (const event in eventHandlers) {
289
- eventHandlers[event] = [];
290
- }
291
- },
292
-
293
- on: (event: string, handler: Function) => {
294
- if (!eventHandlers[event]) {
295
- eventHandlers[event] = [];
296
- }
297
- eventHandlers[event].push(handler);
298
- return chip;
299
- },
300
-
301
- off: (event: string, handler: Function) => {
302
- if (eventHandlers[event]) {
303
- eventHandlers[event] = eventHandlers[event].filter(h => h !== handler);
304
- }
305
- return chip;
306
- },
307
-
308
- addClass: (...classes: string[]) => {
309
- classes.forEach(className => element.classList.add(className));
310
- return chip;
311
- }
312
- };
313
-
314
- return chip;
315
- };
316
-
317
- describe('Chip Component', () => {
318
- test('should create a chip element', () => {
319
- const chip = createMockChip();
320
- expect(chip.element).toBeDefined();
321
- expect(chip.element.tagName).toBe('DIV');
322
- expect(chip.element.className).toContain('mtrl-chip');
323
- });
324
-
325
- test('should apply variant classes', () => {
326
- const variants: ChipVariant[] = [
327
- CHIP_VARIANTS.FILLED,
328
- CHIP_VARIANTS.OUTLINED,
329
- CHIP_VARIANTS.ELEVATED,
330
- CHIP_VARIANTS.ASSIST,
331
- CHIP_VARIANTS.FILTER,
332
- CHIP_VARIANTS.INPUT,
333
- CHIP_VARIANTS.SUGGESTION
334
- ];
335
-
336
- variants.forEach(variant => {
337
- const chip = createMockChip({ variant });
338
- expect(chip.element.className).toContain(`mtrl-chip--${variant}`);
339
- });
340
- });
341
-
342
- test('should set initial text content', () => {
343
- const chip = createMockChip({ text: 'Test Chip' });
344
-
345
- const textElement = chip.element.querySelector('.mtrl-chip__text');
346
- expect(textElement).toBeDefined();
347
- expect(textElement?.textContent).toBe('Test Chip');
348
- expect(chip.getText()).toBe('Test Chip');
349
- });
350
-
351
- test('should set initial leading icon', () => {
352
- const iconHtml = '<svg><path></path></svg>';
353
- const chip = createMockChip({ leadingIcon: iconHtml });
354
-
355
- const iconElement = chip.element.querySelector('.mtrl-chip__icon--leading');
356
- expect(iconElement).toBeDefined();
357
- expect(iconElement?.innerHTML).toBe(iconHtml);
358
- expect(chip.getIcon()).toBe(iconHtml);
359
- });
360
-
361
- test('should use icon as alias for leadingIcon', () => {
362
- const iconHtml = '<svg><path></path></svg>';
363
- const chip = createMockChip({ icon: iconHtml });
364
-
365
- const iconElement = chip.element.querySelector('.mtrl-chip__icon--leading');
366
- expect(iconElement).toBeDefined();
367
- expect(iconElement?.innerHTML).toBe(iconHtml);
368
- expect(chip.getIcon()).toBe(iconHtml);
369
- });
370
-
371
- test('should set initial trailing icon', () => {
372
- const iconHtml = '<svg><path></path></svg>';
373
- const chip = createMockChip({ trailingIcon: iconHtml });
374
-
375
- const iconElement = chip.element.querySelector('.mtrl-chip__icon--trailing');
376
- expect(iconElement).toBeDefined();
377
- expect(iconElement?.innerHTML).toBe(iconHtml);
378
- });
379
-
380
- test('should set value attribute', () => {
381
- const chip = createMockChip({ value: 'chip-123' });
382
-
383
- expect(chip.element.getAttribute('data-value')).toBe('chip-123');
384
- expect(chip.getValue()).toBe('chip-123');
385
- });
386
-
387
- test('should create ripple element by default', () => {
388
- const chip = createMockChip();
389
-
390
- const rippleElement = chip.element.querySelector('.mtrl-chip__ripple');
391
- expect(rippleElement).toBeDefined();
392
- });
393
-
394
- test('should not create ripple element when disabled', () => {
395
- const chip = createMockChip({ ripple: false });
396
-
397
- const rippleElement = chip.element.querySelector('.mtrl-chip__ripple');
398
- expect(rippleElement).toBeNull();
399
- });
400
-
401
- test('should apply disabled state', () => {
402
- const chip = createMockChip({ disabled: true });
403
-
404
- expect(chip.element.className).toContain('mtrl-chip--disabled');
405
- expect(chip.element.getAttribute('aria-disabled')).toBe('true');
406
- expect(chip.isDisabled()).toBe(true);
407
- });
408
-
409
- test('should apply selected state', () => {
410
- const chip = createMockChip({ selected: true });
411
-
412
- expect(chip.element.className).toContain('mtrl-chip--selected');
413
- expect(chip.element.getAttribute('aria-selected')).toBe('true');
414
- expect(chip.isSelected()).toBe(true);
415
- });
416
-
417
- test('should be able to change text content', () => {
418
- const chip = createMockChip({ text: 'Initial Text' });
419
-
420
- expect(chip.getText()).toBe('Initial Text');
421
-
422
- chip.setText('Updated Text');
423
-
424
- expect(chip.getText()).toBe('Updated Text');
425
-
426
- const textElement = chip.element.querySelector('.mtrl-chip__text');
427
- expect(textElement?.textContent).toBe('Updated Text');
428
- });
429
-
430
- test('should be able to change leading icon', () => {
431
- const chip = createMockChip();
432
-
433
- const initialIconElement = chip.element.querySelector('.mtrl-chip__icon--leading');
434
- expect(initialIconElement).toBeNull();
435
-
436
- const iconHtml = '<svg><path></path></svg>';
437
- chip.setIcon(iconHtml);
438
-
439
- const updatedIconElement = chip.element.querySelector('.mtrl-chip__icon--leading');
440
- expect(updatedIconElement).toBeDefined();
441
- expect(updatedIconElement?.innerHTML).toBe(iconHtml);
442
- expect(chip.getIcon()).toBe(iconHtml);
443
- });
444
-
445
- test('should be able to change trailing icon', () => {
446
- const chip = createMockChip();
447
-
448
- const initialIconElement = chip.element.querySelector('.mtrl-chip__icon--trailing');
449
- expect(initialIconElement).toBeNull();
450
-
451
- const iconHtml = '<svg><path></path></svg>';
452
- chip.setTrailingIcon(iconHtml);
453
-
454
- const updatedIconElement = chip.element.querySelector('.mtrl-chip__icon--trailing');
455
- expect(updatedIconElement).toBeDefined();
456
- expect(updatedIconElement?.innerHTML).toBe(iconHtml);
457
- });
458
-
459
- test('should handle trailing icon click events', () => {
460
- let clickHandled = false;
461
-
462
- const chip = createMockChip({
463
- trailingIcon: '<svg></svg>',
464
- onTrailingIconClick: () => {
465
- clickHandled = true;
466
- }
467
- });
468
-
469
- const trailingIcon = chip.element.querySelector('.mtrl-chip__icon--trailing');
470
- expect(trailingIcon).toBeDefined();
471
-
472
- const clickEvent = new Event('click');
473
- clickEvent.stopPropagation = () => {};
474
- trailingIcon?.dispatchEvent(clickEvent);
475
-
476
- expect(clickHandled).toBe(true);
477
- });
478
-
479
- test('should be able to change selected state', () => {
480
- const chip = createMockChip();
481
-
482
- expect(chip.isSelected()).toBe(false);
483
-
484
- chip.setSelected(true);
485
-
486
- expect(chip.isSelected()).toBe(true);
487
- expect(chip.element.className).toContain('mtrl-chip--selected');
488
- expect(chip.element.getAttribute('aria-selected')).toBe('true');
489
-
490
- chip.setSelected(false);
491
-
492
- expect(chip.isSelected()).toBe(false);
493
- expect(chip.element.className).not.toContain('mtrl-chip--selected');
494
- expect(chip.element.getAttribute('aria-selected')).toBeNull();
495
- });
496
-
497
- test('should be able to toggle selected state', () => {
498
- const chip = createMockChip();
499
-
500
- expect(chip.isSelected()).toBe(false);
501
-
502
- chip.toggleSelected();
503
-
504
- expect(chip.isSelected()).toBe(true);
505
-
506
- chip.toggleSelected();
507
-
508
- expect(chip.isSelected()).toBe(false);
509
- });
510
-
511
- test('should be able to change disabled state', () => {
512
- const chip = createMockChip();
513
-
514
- expect(chip.isDisabled()).toBe(false);
515
-
516
- chip.disable();
517
-
518
- expect(chip.isDisabled()).toBe(true);
519
- expect(chip.element.className).toContain('mtrl-chip--disabled');
520
- expect(chip.element.getAttribute('aria-disabled')).toBe('true');
521
-
522
- chip.enable();
523
-
524
- expect(chip.isDisabled()).toBe(false);
525
- expect(chip.element.className).not.toContain('mtrl-chip--disabled');
526
- expect(chip.element.getAttribute('aria-disabled')).toBeNull();
527
- });
528
-
529
- test('should be able to change value', () => {
530
- const chip = createMockChip();
531
-
532
- expect(chip.getValue()).toBeNull();
533
-
534
- chip.setValue('new-value');
535
-
536
- expect(chip.getValue()).toBe('new-value');
537
- expect(chip.element.getAttribute('data-value')).toBe('new-value');
538
- });
539
-
540
- test('should add event listeners', () => {
541
- const chip = createMockChip();
542
- let eventFired = false;
543
-
544
- chip.on('click', () => {
545
- eventFired = true;
546
- });
547
-
548
- // Simulate click
549
- chip.element.dispatchEvent(new Event('click'));
550
-
551
- expect(eventFired).toBe(true);
552
- });
553
-
554
- test('should remove event listeners', () => {
555
- const chip = createMockChip();
556
- let count = 0;
557
-
558
- const handler = () => {
559
- count++;
560
- };
561
-
562
- chip.on('click', handler);
563
-
564
- // First click
565
- chip.element.dispatchEvent(new Event('click'));
566
- expect(count).toBe(1);
567
-
568
- // Remove listener
569
- chip.off('click', handler);
570
-
571
- // Second click
572
- chip.element.dispatchEvent(new Event('click'));
573
- expect(count).toBe(1); // Count should not increase
574
- });
575
-
576
- test('should add CSS classes', () => {
577
- const chip = createMockChip();
578
-
579
- chip.addClass('custom-class', 'special-chip');
580
-
581
- expect(chip.element.className).toContain('custom-class');
582
- expect(chip.element.className).toContain('special-chip');
583
- });
584
-
585
- test('should toggle selected state when clicked for filter chips', () => {
586
- const chip = createMockChip({ variant: CHIP_VARIANTS.FILTER });
587
-
588
- expect(chip.isSelected()).toBe(false);
589
-
590
- // Simulate click
591
- chip.element.dispatchEvent(new Event('click'));
592
-
593
- expect(chip.isSelected()).toBe(true);
594
-
595
- // Click again
596
- chip.element.dispatchEvent(new Event('click'));
597
-
598
- expect(chip.isSelected()).toBe(false);
599
- });
600
-
601
- test('should call onChange when selection changes', () => {
602
- let changeHandled = false;
603
- let selectedState = false;
604
-
605
- const chip = createMockChip({
606
- variant: CHIP_VARIANTS.FILTER,
607
- onChange: (c) => {
608
- changeHandled = true;
609
- selectedState = c.isSelected();
610
- }
611
- });
612
-
613
- // Simulate click
614
- chip.element.dispatchEvent(new Event('click'));
615
-
616
- expect(changeHandled).toBe(true);
617
- expect(selectedState).toBe(true);
618
- });
619
-
620
- test('should call onSelect when selected', () => {
621
- let selectHandled = false;
622
-
623
- const chip = createMockChip({
624
- onSelect: () => {
625
- selectHandled = true;
626
- }
627
- });
628
-
629
- chip.setSelected(true);
630
-
631
- expect(selectHandled).toBe(true);
632
- });
633
-
634
- test('should make any chip variant selectable with selectable flag', () => {
635
- const chip = createMockChip({
636
- variant: CHIP_VARIANTS.FILLED,
637
- selectable: true
638
- });
639
-
640
- expect(chip.isSelected()).toBe(false);
641
-
642
- // Simulate click
643
- chip.element.dispatchEvent(new Event('click'));
644
-
645
- expect(chip.isSelected()).toBe(true);
646
- });
647
-
648
- test('should not toggle selection when disabled', () => {
649
- const chip = createMockChip({
650
- variant: CHIP_VARIANTS.FILTER,
651
- disabled: true
652
- });
653
-
654
- expect(chip.isSelected()).toBe(false);
655
-
656
- // Simulate click
657
- chip.element.dispatchEvent(new Event('click'));
658
-
659
- expect(chip.isSelected()).toBe(false);
660
- });
661
-
662
- test('should be properly destroyed', () => {
663
- const chip = createMockChip();
664
- document.body.appendChild(chip.element);
665
-
666
- expect(document.body.contains(chip.element)).toBe(true);
667
-
668
- chip.destroy();
669
-
670
- expect(document.body.contains(chip.element)).toBe(false);
671
- });
672
-
673
- test('should handle basic chip set creation', () => {
674
- // Create a chip set container
675
- const chipSetElement = document.createElement('div');
676
- chipSetElement.className = 'mtrl-chip-set';
677
-
678
- // Add chips to the set
679
- const chip1 = createMockChip({ text: 'Chip 1' });
680
- const chip2 = createMockChip({ text: 'Chip 2', selected: true });
681
- const chip3 = createMockChip({ text: 'Chip 3' });
682
-
683
- chipSetElement.appendChild(chip1.element);
684
- chipSetElement.appendChild(chip2.element);
685
- chipSetElement.appendChild(chip3.element);
686
-
687
- // Check if all chips are in the set
688
- expect(chipSetElement.children.length).toBe(3);
689
- expect(chipSetElement.children[0].className).toContain('mtrl-chip');
690
- expect(chipSetElement.children[1].className).toContain('mtrl-chip--selected');
691
- });
692
- });