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,365 +0,0 @@
1
- // test/components/list.test.ts
2
- import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
3
- import { JSDOM } from 'jsdom';
4
- import { LIST_TYPES } from '../../src/components/list/constants';
5
- import type { ListConfig, ListComponent, ListItemConfig } from '../../src/components/list/types';
6
-
7
- // IMPORTANT: Due to potential circular dependencies in the actual list component
8
- // we are using a mock implementation for tests.
9
-
10
- // Setup jsdom environment
11
- let dom: JSDOM;
12
- let window: Window;
13
- let document: Document;
14
- let originalGlobalDocument: any;
15
- let originalGlobalWindow: any;
16
-
17
- beforeAll(() => {
18
- // Create a new JSDOM instance
19
- dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {
20
- url: 'http://localhost/',
21
- pretendToBeVisual: true
22
- });
23
-
24
- // Get window and document from jsdom
25
- window = dom.window;
26
- document = window.document;
27
-
28
- // Store original globals
29
- originalGlobalDocument = global.document;
30
- originalGlobalWindow = global.window;
31
-
32
- // Set globals to use jsdom
33
- global.document = document;
34
- global.window = window;
35
- global.Element = window.Element;
36
- global.HTMLElement = window.HTMLElement;
37
- global.Event = window.Event;
38
- });
39
-
40
- afterAll(() => {
41
- // Restore original globals
42
- global.document = originalGlobalDocument;
43
- global.window = originalGlobalWindow;
44
-
45
- // Clean up jsdom
46
- window.close();
47
- });
48
-
49
- // Mock list component factory
50
- const createList = (config: ListConfig = {}): ListComponent => {
51
- // Set defaults
52
- const listConfig: ListConfig = {
53
- type: LIST_TYPES.DEFAULT,
54
- prefix: 'mtrl',
55
- items: [],
56
- ...config
57
- };
58
-
59
- // Create main element
60
- const element = document.createElement('div');
61
- element.className = `${listConfig.prefix}-list`;
62
-
63
- if (listConfig.class) {
64
- element.className += ` ${listConfig.class}`;
65
- }
66
-
67
- element.setAttribute('role', 'list');
68
- element.setAttribute('data-type', listConfig.type as string);
69
-
70
- // Create maps for items and selection
71
- const items = new Map();
72
- const selectedItems = new Set<string>();
73
-
74
- // Event handlers
75
- const eventHandlers: Record<string, Function[]> = {};
76
-
77
- // Create list items
78
- const createListItem = (item: ListItemConfig): HTMLElement => {
79
- const itemElement = document.createElement('div');
80
- itemElement.className = `${listConfig.prefix}-list-item`;
81
- itemElement.setAttribute('role', 'listitem');
82
- itemElement.setAttribute('data-id', item.id);
83
-
84
- // Create content
85
- if (item.headline) {
86
- const headline = document.createElement('div');
87
- headline.className = `${listConfig.prefix}-list-item-headline`;
88
- headline.textContent = item.headline;
89
- itemElement.appendChild(headline);
90
- }
91
-
92
- if (item.supportingText) {
93
- const supporting = document.createElement('div');
94
- supporting.className = `${listConfig.prefix}-list-item-supporting`;
95
- supporting.textContent = item.supportingText;
96
- itemElement.appendChild(supporting);
97
- }
98
-
99
- // Handle selection
100
- if (item.selected) {
101
- itemElement.setAttribute('aria-selected', 'true');
102
- selectedItems.add(item.id);
103
- } else {
104
- itemElement.setAttribute('aria-selected', 'false');
105
- }
106
-
107
- // Add to items map
108
- items.set(item.id, {
109
- element: itemElement,
110
- disabled: item.disabled || false,
111
- config: item
112
- });
113
-
114
- // Add click handler for selection
115
- itemElement.addEventListener('click', () => {
116
- if (listConfig.type === LIST_TYPES.SINGLE_SELECT) {
117
- // For single select, deselect all others
118
- items.forEach((item, id) => {
119
- if (id !== itemElement.dataset.id) {
120
- item.element.setAttribute('aria-selected', 'false');
121
- selectedItems.delete(id);
122
- }
123
- });
124
-
125
- // Toggle selection of clicked item
126
- if (itemElement.getAttribute('aria-selected') === 'true') {
127
- itemElement.setAttribute('aria-selected', 'false');
128
- selectedItems.delete(itemElement.dataset.id as string);
129
- } else {
130
- itemElement.setAttribute('aria-selected', 'true');
131
- selectedItems.add(itemElement.dataset.id as string);
132
- }
133
- } else if (listConfig.type === LIST_TYPES.MULTI_SELECT) {
134
- // For multi select, toggle selection
135
- if (itemElement.getAttribute('aria-selected') === 'true') {
136
- itemElement.setAttribute('aria-selected', 'false');
137
- selectedItems.delete(itemElement.dataset.id as string);
138
- } else {
139
- itemElement.setAttribute('aria-selected', 'true');
140
- selectedItems.add(itemElement.dataset.id as string);
141
- }
142
- }
143
-
144
- // Emit selection change event
145
- emit('selectionchange', {
146
- selected: Array.from(selectedItems),
147
- item: items.get(itemElement.dataset.id as string),
148
- type: listConfig.type
149
- });
150
- });
151
-
152
- return itemElement;
153
- };
154
-
155
- // Create initial items
156
- if (listConfig.items && listConfig.items.length > 0) {
157
- listConfig.items.forEach(item => {
158
- const itemElement = createListItem(item);
159
- element.appendChild(itemElement);
160
- });
161
- }
162
-
163
- // Event emitter function
164
- const emit = (event: string, data: any): void => {
165
- if (eventHandlers[event]) {
166
- eventHandlers[event].forEach(handler => handler(data));
167
- }
168
- };
169
-
170
- // Public API
171
- return {
172
- element,
173
- items,
174
- selectedItems,
175
-
176
- getSelected(): string[] {
177
- return Array.from(selectedItems);
178
- },
179
-
180
- setSelected(ids: string[]): void {
181
- // First, deselect all
182
- items.forEach((item, id) => {
183
- item.element.setAttribute('aria-selected', 'false');
184
- selectedItems.delete(id);
185
- });
186
-
187
- // Then select the specified items
188
- ids.forEach(id => {
189
- const item = items.get(id);
190
- if (item) {
191
- item.element.setAttribute('aria-selected', 'true');
192
- selectedItems.add(id);
193
- }
194
- });
195
-
196
- // Emit selection change event
197
- emit('selectionchange', {
198
- selected: Array.from(selectedItems),
199
- type: listConfig.type
200
- });
201
- },
202
-
203
- addItem(itemConfig: ListItemConfig): void {
204
- const itemElement = createListItem(itemConfig);
205
- element.appendChild(itemElement);
206
- },
207
-
208
- removeItem(id: string): void {
209
- const item = items.get(id);
210
- if (item) {
211
- element.removeChild(item.element);
212
- items.delete(id);
213
- selectedItems.delete(id);
214
- }
215
- },
216
-
217
- on(event: string, handler: Function): ListComponent {
218
- if (!eventHandlers[event]) {
219
- eventHandlers[event] = [];
220
- }
221
- eventHandlers[event].push(handler);
222
- return this;
223
- },
224
-
225
- off(event: string, handler: Function): ListComponent {
226
- if (eventHandlers[event]) {
227
- eventHandlers[event] = eventHandlers[event].filter(h => h !== handler);
228
- }
229
- return this;
230
- },
231
-
232
- enable(): ListComponent {
233
- element.removeAttribute('disabled');
234
- return this;
235
- },
236
-
237
- disable(): ListComponent {
238
- element.setAttribute('disabled', '');
239
- return this;
240
- },
241
-
242
- emit,
243
- prefix: listConfig.prefix,
244
-
245
- destroy(): void {
246
- // Clean up event handlers
247
- items.forEach(item => {
248
- item.element.removeEventListener('click', () => {});
249
- });
250
-
251
- // Clear maps
252
- items.clear();
253
- selectedItems.clear();
254
-
255
- // Remove from DOM
256
- if (element.parentNode) {
257
- element.parentNode.removeChild(element);
258
- }
259
- }
260
- };
261
- };
262
-
263
- describe('List Component', () => {
264
- test('should create a default list element', () => {
265
- const list = createList({
266
- items: [{ id: 'item1', headline: 'Item 1' }]
267
- });
268
-
269
- expect(list.element).toBeDefined();
270
- // Default type is "default" and role "list"
271
- expect(list.element.getAttribute('data-type')).toBe(LIST_TYPES.DEFAULT);
272
- expect(list.element.getAttribute('role')).toBe('list');
273
-
274
- // Check at least one list item exists
275
- const listItem = list.element.querySelector(`.${list.prefix}-list-item`);
276
- expect(listItem).not.toBeNull();
277
- });
278
-
279
- test('should support single select behavior', () => {
280
- const list = createList({
281
- type: LIST_TYPES.SINGLE_SELECT,
282
- items: [
283
- { id: 'item1', headline: 'Item 1' },
284
- { id: 'item2', headline: 'Item 2' }
285
- ]
286
- });
287
-
288
- // Simulate clicking on the first item
289
- const items = list.element.querySelectorAll(`.${list.prefix}-list-item`);
290
- const firstItem = items[0];
291
- firstItem.dispatchEvent(new Event('click'));
292
- expect(firstItem.getAttribute('aria-selected')).toBe('true');
293
-
294
- // Now click the second item; the first should be deselected
295
- const secondItem = items[1];
296
- secondItem.dispatchEvent(new Event('click'));
297
- expect(firstItem.getAttribute('aria-selected')).toBe('false');
298
- expect(secondItem.getAttribute('aria-selected')).toBe('true');
299
- });
300
-
301
- test('should support multi select behavior', () => {
302
- const list = createList({
303
- type: LIST_TYPES.MULTI_SELECT,
304
- items: [
305
- { id: 'item1', headline: 'Item 1' },
306
- { id: 'item2', headline: 'Item 2' }
307
- ]
308
- });
309
-
310
- const items = list.element.querySelectorAll(`.${list.prefix}-list-item`);
311
- const firstItem = items[0];
312
- const secondItem = items[1];
313
-
314
- // Click to select first item
315
- firstItem.dispatchEvent(new Event('click'));
316
- expect(firstItem.getAttribute('aria-selected')).toBe('true');
317
-
318
- // Click to select second item
319
- secondItem.dispatchEvent(new Event('click'));
320
- expect(secondItem.getAttribute('aria-selected')).toBe('true');
321
- expect(list.getSelected().length).toBe(2);
322
-
323
- // Click first item again to deselect it
324
- firstItem.dispatchEvent(new Event('click'));
325
- expect(firstItem.getAttribute('aria-selected')).toBe('false');
326
- expect(list.getSelected().length).toBe(1);
327
- });
328
-
329
- test('should set selected items via setSelected', () => {
330
- const list = createList({
331
- type: LIST_TYPES.MULTI_SELECT,
332
- items: [
333
- { id: 'item1', headline: 'Item 1' },
334
- { id: 'item2', headline: 'Item 2' },
335
- { id: 'item3', headline: 'Item 3' }
336
- ]
337
- });
338
-
339
- list.setSelected(['item2', 'item3']);
340
- const items = Array.from(
341
- list.element.querySelectorAll(`.${list.prefix}-list-item`)
342
- );
343
- const item2 = items.find(i => i.getAttribute('data-id') === 'item2');
344
- const item3 = items.find(i => i.getAttribute('data-id') === 'item3');
345
-
346
- expect(item2?.getAttribute('aria-selected')).toBe('true');
347
- expect(item3?.getAttribute('aria-selected')).toBe('true');
348
- expect(list.getSelected()).toEqual(expect.arrayContaining(['item2', 'item3']));
349
- });
350
-
351
- test('should add and remove items dynamically', () => {
352
- const list = createList({
353
- items: [{ id: 'item1', headline: 'Item 1' }]
354
- });
355
-
356
- const initialCount = list.element.querySelectorAll(`.${list.prefix}-list-item`).length;
357
- list.addItem({ id: 'item2', headline: 'Item 2' });
358
- const newCount = list.element.querySelectorAll(`.${list.prefix}-list-item`).length;
359
- expect(newCount).toBe(initialCount + 1);
360
-
361
- list.removeItem('item1');
362
- const finalCount = list.element.querySelectorAll(`.${list.prefix}-list-item`).length;
363
- expect(finalCount).toBe(newCount - 1);
364
- });
365
- });