mtrl 0.2.9 → 0.3.1
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.
- package/CLAUDE.md +33 -0
- package/package.json +3 -1
- package/src/components/button/button.ts +34 -5
- package/src/components/navigation/index.ts +4 -1
- package/src/components/navigation/system/core.ts +302 -0
- package/src/components/navigation/system/events.ts +240 -0
- package/src/components/navigation/system/index.ts +184 -0
- package/src/components/navigation/system/mobile.ts +278 -0
- package/src/components/navigation/system/state.ts +77 -0
- package/src/components/navigation/system/types.ts +364 -0
- package/src/components/navigation/types.ts +33 -0
- package/src/components/slider/config.ts +2 -2
- package/src/components/slider/features/controller.ts +1 -25
- package/src/components/slider/features/handlers.ts +0 -1
- package/src/components/slider/features/range.ts +7 -7
- package/src/components/slider/{structure.ts → schema.ts} +2 -13
- package/src/components/slider/slider.ts +3 -2
- package/src/components/snackbar/index.ts +7 -1
- package/src/components/snackbar/types.ts +25 -0
- package/src/components/switch/api.ts +16 -0
- package/src/components/switch/config.ts +1 -18
- package/src/components/switch/features.ts +198 -0
- package/src/components/switch/index.ts +6 -1
- package/src/components/switch/switch.ts +3 -3
- package/src/components/switch/types.ts +27 -2
- package/src/components/textfield/index.ts +7 -1
- package/src/components/textfield/types.ts +36 -0
- package/src/core/composition/features/dom.ts +26 -14
- package/src/core/composition/features/icon.ts +18 -18
- package/src/core/composition/features/index.ts +3 -2
- package/src/core/composition/features/label.ts +16 -17
- package/src/core/composition/features/layout.ts +47 -0
- package/src/core/composition/index.ts +4 -4
- package/src/core/layout/README.md +350 -0
- package/src/core/layout/array.ts +181 -0
- package/src/core/layout/create.ts +55 -0
- package/src/core/layout/index.ts +26 -0
- package/src/core/layout/object.ts +124 -0
- package/src/core/layout/processor.ts +58 -0
- package/src/core/layout/result.ts +85 -0
- package/src/core/layout/types.ts +125 -0
- package/src/core/layout/utils.ts +136 -0
- package/src/styles/abstract/_variables.scss +28 -0
- package/src/styles/components/_switch.scss +133 -69
- package/src/styles/components/_textfield.scss +9 -16
- package/test/components/badge.test.ts +545 -0
- package/test/components/bottom-app-bar.test.ts +303 -0
- package/test/components/button.test.ts +233 -0
- package/test/components/card.test.ts +560 -0
- package/test/components/carousel.test.ts +951 -0
- package/test/components/checkbox.test.ts +462 -0
- package/test/components/chip.test.ts +692 -0
- package/test/components/datepicker.test.ts +1124 -0
- package/test/components/dialog.test.ts +990 -0
- package/test/components/divider.test.ts +412 -0
- package/test/components/extended-fab.test.ts +672 -0
- package/test/components/fab.test.ts +561 -0
- package/test/components/list.test.ts +365 -0
- package/test/components/menu.test.ts +718 -0
- package/test/components/navigation.test.ts +186 -0
- package/test/components/progress.test.ts +567 -0
- package/test/components/radios.test.ts +699 -0
- package/test/components/search.test.ts +1135 -0
- package/test/components/segmented-button.test.ts +732 -0
- package/test/components/sheet.test.ts +641 -0
- package/test/components/slider.test.ts +1220 -0
- package/test/components/snackbar.test.ts +461 -0
- package/test/components/switch.test.ts +452 -0
- package/test/components/tabs.test.ts +1369 -0
- package/test/components/textfield.test.ts +400 -0
- package/test/components/timepicker.test.ts +592 -0
- package/test/components/tooltip.test.ts +630 -0
- package/test/components/top-app-bar.test.ts +566 -0
- package/test/core/dom.attributes.test.ts +148 -0
- package/test/core/dom.classes.test.ts +152 -0
- package/test/core/dom.events.test.ts +243 -0
- package/test/core/emitter.test.ts +141 -0
- package/test/core/ripple.test.ts +99 -0
- package/test/core/state.store.test.ts +189 -0
- package/test/core/utils.normalize.test.ts +61 -0
- package/test/core/utils.object.test.ts +120 -0
- package/test/setup.ts +451 -0
- package/tsconfig.json +2 -2
- package/src/components/navigation/system-types.ts +0 -124
- package/src/components/navigation/system.ts +0 -776
- package/src/components/snackbar/constants.ts +0 -26
- package/src/core/composition/features/structure.ts +0 -22
- package/src/core/layout/index.js +0 -95
- package/src/core/structure.ts +0 -288
- package/test/components/button.test.js +0 -170
- package/test/components/checkbox.test.js +0 -238
- package/test/components/list.test.js +0 -105
- package/test/components/menu.test.js +0 -385
- package/test/components/navigation.test.js +0 -227
- package/test/components/snackbar.test.js +0 -234
- package/test/components/switch.test.js +0 -186
- package/test/components/textfield.test.js +0 -314
- package/test/core/emitter.test.js +0 -141
- package/test/core/ripple.test.js +0 -66
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
// test/components/progress.test.ts
|
|
2
|
+
import { describe, test, expect } from 'bun:test';
|
|
3
|
+
import {
|
|
4
|
+
type ProgressComponent,
|
|
5
|
+
type ProgressConfig,
|
|
6
|
+
type ProgressVariant,
|
|
7
|
+
type ProgressEvent
|
|
8
|
+
} from '../../src/components/progress/types';
|
|
9
|
+
|
|
10
|
+
// Constants for progress variants
|
|
11
|
+
const PROGRESS_VARIANTS = {
|
|
12
|
+
LINEAR: 'linear',
|
|
13
|
+
CIRCULAR: 'circular'
|
|
14
|
+
} as const;
|
|
15
|
+
|
|
16
|
+
// Constants for progress events
|
|
17
|
+
const PROGRESS_EVENTS = {
|
|
18
|
+
CHANGE: 'change',
|
|
19
|
+
COMPLETE: 'complete'
|
|
20
|
+
} as const;
|
|
21
|
+
|
|
22
|
+
// Mock progress component implementation
|
|
23
|
+
const createMockProgress = (config: ProgressConfig = {}): ProgressComponent => {
|
|
24
|
+
// Create main elements
|
|
25
|
+
const element = document.createElement('div');
|
|
26
|
+
element.className = 'mtrl-progress';
|
|
27
|
+
|
|
28
|
+
const trackElement = document.createElement('div');
|
|
29
|
+
trackElement.className = 'mtrl-progress-track';
|
|
30
|
+
|
|
31
|
+
const indicatorElement = document.createElement('div');
|
|
32
|
+
indicatorElement.className = 'mtrl-progress-indicator';
|
|
33
|
+
|
|
34
|
+
// Default settings
|
|
35
|
+
const settings = {
|
|
36
|
+
variant: config.variant || PROGRESS_VARIANTS.LINEAR,
|
|
37
|
+
value: config.value !== undefined ? config.value : 0,
|
|
38
|
+
max: config.max !== undefined ? config.max : 100,
|
|
39
|
+
buffer: config.buffer !== undefined ? config.buffer : 0,
|
|
40
|
+
disabled: config.disabled || false,
|
|
41
|
+
indeterminate: config.indeterminate || false,
|
|
42
|
+
showLabel: config.showLabel || false,
|
|
43
|
+
labelFormatter: config.labelFormatter || ((value, max) => `${Math.round((value/max) * 100)}%`),
|
|
44
|
+
componentName: 'progress',
|
|
45
|
+
prefix: config.prefix || 'mtrl'
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Apply variant class
|
|
49
|
+
element.classList.add(`mtrl-progress--${settings.variant}`);
|
|
50
|
+
|
|
51
|
+
// Apply disabled state
|
|
52
|
+
if (settings.disabled) {
|
|
53
|
+
element.classList.add('mtrl-progress--disabled');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Apply indeterminate state
|
|
57
|
+
if (settings.indeterminate) {
|
|
58
|
+
element.classList.add('mtrl-progress--indeterminate');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Apply additional classes
|
|
62
|
+
if (config.class) {
|
|
63
|
+
const classes = config.class.split(' ');
|
|
64
|
+
classes.forEach(className => element.classList.add(className));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Create buffer element for linear variant
|
|
68
|
+
let bufferElement: HTMLElement | undefined;
|
|
69
|
+
if (settings.variant === PROGRESS_VARIANTS.LINEAR) {
|
|
70
|
+
bufferElement = document.createElement('div');
|
|
71
|
+
bufferElement.className = 'mtrl-progress-buffer';
|
|
72
|
+
trackElement.appendChild(bufferElement);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Create label element if enabled
|
|
76
|
+
let labelElement: HTMLElement | undefined;
|
|
77
|
+
if (settings.showLabel) {
|
|
78
|
+
labelElement = document.createElement('div');
|
|
79
|
+
labelElement.className = 'mtrl-progress-label';
|
|
80
|
+
labelElement.textContent = settings.labelFormatter(settings.value, settings.max);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Assemble elements
|
|
84
|
+
trackElement.appendChild(indicatorElement);
|
|
85
|
+
element.appendChild(trackElement);
|
|
86
|
+
|
|
87
|
+
if (labelElement) {
|
|
88
|
+
element.appendChild(labelElement);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Track event handlers
|
|
92
|
+
const eventHandlers: Record<string, Function[]> = {};
|
|
93
|
+
|
|
94
|
+
// Update visuals based on current value
|
|
95
|
+
const updateVisuals = () => {
|
|
96
|
+
if (!settings.indeterminate) {
|
|
97
|
+
const percentage = (settings.value / settings.max) * 100;
|
|
98
|
+
|
|
99
|
+
if (settings.variant === PROGRESS_VARIANTS.LINEAR) {
|
|
100
|
+
indicatorElement.style.width = `${percentage}%`;
|
|
101
|
+
|
|
102
|
+
if (bufferElement) {
|
|
103
|
+
const bufferPercentage = (settings.buffer / settings.max) * 100;
|
|
104
|
+
bufferElement.style.width = `${bufferPercentage}%`;
|
|
105
|
+
}
|
|
106
|
+
} else if (settings.variant === PROGRESS_VARIANTS.CIRCULAR) {
|
|
107
|
+
// For circular progress, update stroke-dashoffset or similar property
|
|
108
|
+
const circumference = 100; // Simplified for testing
|
|
109
|
+
const offset = circumference - (percentage / 100 * circumference);
|
|
110
|
+
indicatorElement.style.setProperty('--progress-offset', `${offset}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Update label if present
|
|
114
|
+
if (labelElement) {
|
|
115
|
+
labelElement.textContent = settings.labelFormatter(settings.value, settings.max);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// Initialize visuals
|
|
121
|
+
updateVisuals();
|
|
122
|
+
|
|
123
|
+
// Emit an event
|
|
124
|
+
const emit = (event: string, data?: any) => {
|
|
125
|
+
if (eventHandlers[event]) {
|
|
126
|
+
eventHandlers[event].forEach(handler => handler(data));
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// Create the progress component
|
|
131
|
+
const progress: ProgressComponent = {
|
|
132
|
+
element,
|
|
133
|
+
trackElement,
|
|
134
|
+
indicatorElement,
|
|
135
|
+
bufferElement,
|
|
136
|
+
labelElement,
|
|
137
|
+
|
|
138
|
+
getClass: (name: string) => {
|
|
139
|
+
const prefix = settings.prefix;
|
|
140
|
+
return name ? `${prefix}-${name}` : `${prefix}-progress`;
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
setValue: (value: number) => {
|
|
144
|
+
// Ensure value is within bounds
|
|
145
|
+
value = Math.max(0, Math.min(value, settings.max));
|
|
146
|
+
|
|
147
|
+
const oldValue = settings.value;
|
|
148
|
+
settings.value = value;
|
|
149
|
+
|
|
150
|
+
// Update visuals
|
|
151
|
+
updateVisuals();
|
|
152
|
+
|
|
153
|
+
// Emit change event
|
|
154
|
+
emit(PROGRESS_EVENTS.CHANGE, { value, oldValue });
|
|
155
|
+
|
|
156
|
+
// Emit complete event if reached 100%
|
|
157
|
+
if (value === settings.max && oldValue !== settings.max) {
|
|
158
|
+
emit(PROGRESS_EVENTS.COMPLETE, { value });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return progress;
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
getValue: () => settings.value,
|
|
165
|
+
|
|
166
|
+
setBuffer: (value: number) => {
|
|
167
|
+
// Ensure buffer is within bounds
|
|
168
|
+
value = Math.max(0, Math.min(value, settings.max));
|
|
169
|
+
settings.buffer = value;
|
|
170
|
+
|
|
171
|
+
// Update visuals
|
|
172
|
+
updateVisuals();
|
|
173
|
+
|
|
174
|
+
return progress;
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
getBuffer: () => settings.buffer,
|
|
178
|
+
|
|
179
|
+
enable: () => {
|
|
180
|
+
progress.disabled.enable();
|
|
181
|
+
return progress;
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
disable: () => {
|
|
185
|
+
progress.disabled.disable();
|
|
186
|
+
return progress;
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
isDisabled: () => progress.disabled.isDisabled(),
|
|
190
|
+
|
|
191
|
+
showLabel: () => {
|
|
192
|
+
settings.showLabel = true;
|
|
193
|
+
|
|
194
|
+
if (!labelElement) {
|
|
195
|
+
labelElement = document.createElement('div');
|
|
196
|
+
labelElement.className = 'mtrl-progress-label';
|
|
197
|
+
labelElement.textContent = settings.labelFormatter(settings.value, settings.max);
|
|
198
|
+
element.appendChild(labelElement);
|
|
199
|
+
progress.labelElement = labelElement;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return progress;
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
hideLabel: () => {
|
|
206
|
+
settings.showLabel = false;
|
|
207
|
+
|
|
208
|
+
if (labelElement) {
|
|
209
|
+
element.removeChild(labelElement);
|
|
210
|
+
labelElement = undefined;
|
|
211
|
+
progress.labelElement = undefined;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return progress;
|
|
215
|
+
},
|
|
216
|
+
|
|
217
|
+
setLabelFormatter: (formatter: (value: number, max: number) => string) => {
|
|
218
|
+
settings.labelFormatter = formatter;
|
|
219
|
+
|
|
220
|
+
if (labelElement) {
|
|
221
|
+
labelElement.textContent = formatter(settings.value, settings.max);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return progress;
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
setIndeterminate: (indeterminate: boolean) => {
|
|
228
|
+
settings.indeterminate = indeterminate;
|
|
229
|
+
|
|
230
|
+
if (indeterminate) {
|
|
231
|
+
element.classList.add('mtrl-progress--indeterminate');
|
|
232
|
+
} else {
|
|
233
|
+
element.classList.remove('mtrl-progress--indeterminate');
|
|
234
|
+
updateVisuals();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return progress;
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
isIndeterminate: () => settings.indeterminate,
|
|
241
|
+
|
|
242
|
+
on: (event: string, handler: Function) => {
|
|
243
|
+
if (!eventHandlers[event]) {
|
|
244
|
+
eventHandlers[event] = [];
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
eventHandlers[event].push(handler);
|
|
248
|
+
return progress;
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
off: (event: string, handler: Function) => {
|
|
252
|
+
if (eventHandlers[event]) {
|
|
253
|
+
eventHandlers[event] = eventHandlers[event].filter(h => h !== handler);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return progress;
|
|
257
|
+
},
|
|
258
|
+
|
|
259
|
+
destroy: () => {
|
|
260
|
+
// Remove element from DOM if it has a parent
|
|
261
|
+
if (element.parentNode) {
|
|
262
|
+
element.parentNode.removeChild(element);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Clear event handlers
|
|
266
|
+
for (const event in eventHandlers) {
|
|
267
|
+
eventHandlers[event] = [];
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
addClass: (...classes: string[]) => {
|
|
272
|
+
classes.forEach(className => element.classList.add(className));
|
|
273
|
+
return progress;
|
|
274
|
+
},
|
|
275
|
+
|
|
276
|
+
disabled: {
|
|
277
|
+
enable: () => {
|
|
278
|
+
settings.disabled = false;
|
|
279
|
+
element.classList.remove('mtrl-progress--disabled');
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
disable: () => {
|
|
283
|
+
settings.disabled = true;
|
|
284
|
+
element.classList.add('mtrl-progress--disabled');
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
isDisabled: () => settings.disabled
|
|
288
|
+
},
|
|
289
|
+
|
|
290
|
+
lifecycle: {
|
|
291
|
+
destroy: () => {
|
|
292
|
+
progress.destroy();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
return progress;
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
describe('Progress Component', () => {
|
|
301
|
+
test('should create a progress element', () => {
|
|
302
|
+
const progress = createMockProgress();
|
|
303
|
+
|
|
304
|
+
expect(progress.element).toBeDefined();
|
|
305
|
+
expect(progress.element.tagName).toBe('DIV');
|
|
306
|
+
expect(progress.element.className).toContain('mtrl-progress');
|
|
307
|
+
|
|
308
|
+
expect(progress.trackElement).toBeDefined();
|
|
309
|
+
expect(progress.trackElement.className).toContain('mtrl-progress-track');
|
|
310
|
+
|
|
311
|
+
expect(progress.indicatorElement).toBeDefined();
|
|
312
|
+
expect(progress.indicatorElement.className).toContain('mtrl-progress-indicator');
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test('should apply linear variant by default', () => {
|
|
316
|
+
const progress = createMockProgress();
|
|
317
|
+
|
|
318
|
+
expect(progress.element.className).toContain('mtrl-progress--linear');
|
|
319
|
+
expect(progress.bufferElement).toBeDefined();
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
test('should apply circular variant when specified', () => {
|
|
323
|
+
const progress = createMockProgress({
|
|
324
|
+
variant: PROGRESS_VARIANTS.CIRCULAR
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
expect(progress.element.className).toContain('mtrl-progress--circular');
|
|
328
|
+
expect(progress.bufferElement).toBeUndefined();
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test('should set initial value', () => {
|
|
332
|
+
const progress = createMockProgress({
|
|
333
|
+
value: 25
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
expect(progress.getValue()).toBe(25);
|
|
337
|
+
expect(progress.indicatorElement.style.width).toBe('25%');
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
test('should set initial buffer value', () => {
|
|
341
|
+
const progress = createMockProgress({
|
|
342
|
+
buffer: 50
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
expect(progress.getBuffer()).toBe(50);
|
|
346
|
+
expect(progress.bufferElement?.style.width).toBe('50%');
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
test('should apply disabled state', () => {
|
|
350
|
+
const progress = createMockProgress({
|
|
351
|
+
disabled: true
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
expect(progress.element.className).toContain('mtrl-progress--disabled');
|
|
355
|
+
expect(progress.isDisabled()).toBe(true);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
test('should apply indeterminate state', () => {
|
|
359
|
+
const progress = createMockProgress({
|
|
360
|
+
indeterminate: true
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
expect(progress.element.className).toContain('mtrl-progress--indeterminate');
|
|
364
|
+
expect(progress.isIndeterminate()).toBe(true);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
test('should show label when configured', () => {
|
|
368
|
+
const progress = createMockProgress({
|
|
369
|
+
showLabel: true,
|
|
370
|
+
value: 75
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
expect(progress.labelElement).toBeDefined();
|
|
374
|
+
expect(progress.labelElement?.textContent).toBe('75%');
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
test('should use custom label formatter', () => {
|
|
378
|
+
const formatter = (value: number, max: number) => `${value} of ${max}`;
|
|
379
|
+
|
|
380
|
+
const progress = createMockProgress({
|
|
381
|
+
showLabel: true,
|
|
382
|
+
value: 50,
|
|
383
|
+
max: 200,
|
|
384
|
+
labelFormatter: formatter
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
expect(progress.labelElement?.textContent).toBe('50 of 200');
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
test('should update value and appearance', () => {
|
|
391
|
+
const progress = createMockProgress({
|
|
392
|
+
value: 0
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
expect(progress.getValue()).toBe(0);
|
|
396
|
+
expect(progress.indicatorElement.style.width).toBe('0%');
|
|
397
|
+
|
|
398
|
+
progress.setValue(60);
|
|
399
|
+
|
|
400
|
+
expect(progress.getValue()).toBe(60);
|
|
401
|
+
expect(progress.indicatorElement.style.width).toBe('60%');
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
test('should update buffer value', () => {
|
|
405
|
+
const progress = createMockProgress();
|
|
406
|
+
|
|
407
|
+
expect(progress.getBuffer()).toBe(0);
|
|
408
|
+
|
|
409
|
+
progress.setBuffer(75);
|
|
410
|
+
|
|
411
|
+
expect(progress.getBuffer()).toBe(75);
|
|
412
|
+
expect(progress.bufferElement?.style.width).toBe('75%');
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
test('should constrain values within bounds', () => {
|
|
416
|
+
const progress = createMockProgress({
|
|
417
|
+
max: 100
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
progress.setValue(-10);
|
|
421
|
+
expect(progress.getValue()).toBe(0);
|
|
422
|
+
|
|
423
|
+
progress.setValue(150);
|
|
424
|
+
expect(progress.getValue()).toBe(100);
|
|
425
|
+
|
|
426
|
+
progress.setBuffer(-20);
|
|
427
|
+
expect(progress.getBuffer()).toBe(0);
|
|
428
|
+
|
|
429
|
+
progress.setBuffer(200);
|
|
430
|
+
expect(progress.getBuffer()).toBe(100);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
test('should toggle disabled state', () => {
|
|
434
|
+
const progress = createMockProgress();
|
|
435
|
+
|
|
436
|
+
expect(progress.isDisabled()).toBe(false);
|
|
437
|
+
|
|
438
|
+
progress.disable();
|
|
439
|
+
|
|
440
|
+
expect(progress.isDisabled()).toBe(true);
|
|
441
|
+
expect(progress.element.className).toContain('mtrl-progress--disabled');
|
|
442
|
+
|
|
443
|
+
progress.enable();
|
|
444
|
+
|
|
445
|
+
expect(progress.isDisabled()).toBe(false);
|
|
446
|
+
expect(progress.element.className).not.toContain('mtrl-progress--disabled');
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
test('should toggle indeterminate state', () => {
|
|
450
|
+
const progress = createMockProgress();
|
|
451
|
+
|
|
452
|
+
expect(progress.isIndeterminate()).toBe(false);
|
|
453
|
+
|
|
454
|
+
progress.setIndeterminate(true);
|
|
455
|
+
|
|
456
|
+
expect(progress.isIndeterminate()).toBe(true);
|
|
457
|
+
expect(progress.element.className).toContain('mtrl-progress--indeterminate');
|
|
458
|
+
|
|
459
|
+
progress.setIndeterminate(false);
|
|
460
|
+
|
|
461
|
+
expect(progress.isIndeterminate()).toBe(false);
|
|
462
|
+
expect(progress.element.className).not.toContain('mtrl-progress--indeterminate');
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
test('should show and hide label', () => {
|
|
466
|
+
const progress = createMockProgress();
|
|
467
|
+
|
|
468
|
+
expect(progress.labelElement).toBeUndefined();
|
|
469
|
+
|
|
470
|
+
progress.showLabel();
|
|
471
|
+
|
|
472
|
+
expect(progress.labelElement).toBeDefined();
|
|
473
|
+
expect(progress.element.contains(progress.labelElement)).toBe(true);
|
|
474
|
+
|
|
475
|
+
progress.hideLabel();
|
|
476
|
+
|
|
477
|
+
expect(progress.labelElement).toBeUndefined();
|
|
478
|
+
expect(progress.element.querySelector('.mtrl-progress-label')).toBeNull();
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
test('should update label formatter', () => {
|
|
482
|
+
const progress = createMockProgress({
|
|
483
|
+
showLabel: true,
|
|
484
|
+
value: 50
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
expect(progress.labelElement?.textContent).toBe('50%');
|
|
488
|
+
|
|
489
|
+
progress.setLabelFormatter((value) => `${value} units`);
|
|
490
|
+
|
|
491
|
+
expect(progress.labelElement?.textContent).toBe('50 units');
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
test('should emit change event when value changes', () => {
|
|
495
|
+
const progress = createMockProgress();
|
|
496
|
+
let changeEventFired = false;
|
|
497
|
+
let eventData = null;
|
|
498
|
+
|
|
499
|
+
progress.on(PROGRESS_EVENTS.CHANGE, (data) => {
|
|
500
|
+
changeEventFired = true;
|
|
501
|
+
eventData = data;
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
progress.setValue(50);
|
|
505
|
+
|
|
506
|
+
expect(changeEventFired).toBe(true);
|
|
507
|
+
expect(eventData).toEqual({ value: 50, oldValue: 0 });
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
test('should emit complete event when reaching max', () => {
|
|
511
|
+
const progress = createMockProgress({
|
|
512
|
+
value: 50,
|
|
513
|
+
max: 100
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
let completeEventFired = false;
|
|
517
|
+
|
|
518
|
+
progress.on(PROGRESS_EVENTS.COMPLETE, () => {
|
|
519
|
+
completeEventFired = true;
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
progress.setValue(99);
|
|
523
|
+
expect(completeEventFired).toBe(false);
|
|
524
|
+
|
|
525
|
+
progress.setValue(100);
|
|
526
|
+
expect(completeEventFired).toBe(true);
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
test('should remove event listeners', () => {
|
|
530
|
+
const progress = createMockProgress();
|
|
531
|
+
let eventCount = 0;
|
|
532
|
+
|
|
533
|
+
const handler = () => {
|
|
534
|
+
eventCount++;
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
progress.on(PROGRESS_EVENTS.CHANGE, handler);
|
|
538
|
+
|
|
539
|
+
progress.setValue(10);
|
|
540
|
+
expect(eventCount).toBe(1);
|
|
541
|
+
|
|
542
|
+
progress.off(PROGRESS_EVENTS.CHANGE, handler);
|
|
543
|
+
|
|
544
|
+
progress.setValue(20);
|
|
545
|
+
expect(eventCount).toBe(1); // Count should not increase
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
test('should add CSS classes', () => {
|
|
549
|
+
const progress = createMockProgress();
|
|
550
|
+
|
|
551
|
+
progress.addClass('custom-class', 'special-progress');
|
|
552
|
+
|
|
553
|
+
expect(progress.element.className).toContain('custom-class');
|
|
554
|
+
expect(progress.element.className).toContain('special-progress');
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
test('should be properly destroyed', () => {
|
|
558
|
+
const progress = createMockProgress();
|
|
559
|
+
document.body.appendChild(progress.element);
|
|
560
|
+
|
|
561
|
+
expect(document.body.contains(progress.element)).toBe(true);
|
|
562
|
+
|
|
563
|
+
progress.destroy();
|
|
564
|
+
|
|
565
|
+
expect(document.body.contains(progress.element)).toBe(false);
|
|
566
|
+
});
|
|
567
|
+
});
|