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.
- package/.env +15 -0
- package/CONTRIBUTING.md +8 -8
- package/DOCS.md +3 -3
- package/README.md +43 -20
- package/TESTING.md +128 -18
- package/dist/index.js +14865 -0
- package/git-user-stats.js +545 -0
- package/index.ts +9 -67
- package/package.json +8 -3
- package/src/components/badge/api.ts +15 -1
- package/src/components/badge/badge.ts +43 -4
- package/src/components/badge/config.ts +40 -8
- package/src/components/badge/index.ts +64 -3
- package/src/components/badge/types.ts +175 -33
- package/src/components/button/api.ts +63 -1
- package/src/components/button/button.ts +39 -3
- package/src/components/button/config.ts +21 -4
- package/src/components/button/index.ts +26 -1
- package/src/components/button/types.ts +7 -1
- package/src/components/card/api.ts +78 -9
- package/src/components/card/card.ts +58 -3
- package/src/components/card/config.ts +41 -11
- package/src/components/card/features.ts +39 -12
- package/src/components/card/index.ts +84 -19
- package/src/components/card/types.ts +218 -29
- package/src/components/carousel/carousel.ts +92 -28
- package/src/components/carousel/constants.ts +107 -21
- package/src/components/carousel/index.ts +31 -13
- package/src/components/checkbox/checkbox.ts +83 -16
- package/src/components/checkbox/index.ts +43 -1
- package/src/components/checkbox/types.ts +219 -32
- package/src/components/chips/api.ts +194 -0
- package/src/components/{chip → chips/chip}/api.ts +42 -2
- package/src/components/chips/chip/chip.ts +131 -0
- package/src/components/{chip → chips/chip}/config.ts +3 -3
- package/src/components/chips/chip/index.ts +3 -0
- package/src/components/chips/chips.md +481 -0
- package/src/components/chips/chips.ts +75 -0
- package/src/components/chips/config.ts +109 -0
- package/src/components/chips/constants.ts +61 -0
- package/src/components/chips/features/chip-items.ts +33 -0
- package/src/components/chips/features/container.ts +77 -0
- package/src/components/chips/features/controller.ts +448 -0
- package/src/components/chips/features/index.ts +5 -0
- package/src/components/chips/features/label.ts +108 -0
- package/src/components/chips/index.ts +11 -0
- package/src/components/chips/schema.ts +61 -0
- package/src/components/{chip → chips}/types.ts +203 -92
- package/src/components/dialog/dialog.ts +99 -16
- package/src/components/dialog/index.ts +97 -1
- package/src/components/dialog/types.ts +375 -69
- package/src/components/divider/config.ts +90 -6
- package/src/components/divider/divider.ts +32 -2
- package/src/components/divider/features.ts +26 -0
- package/src/components/divider/index.ts +30 -0
- package/src/components/divider/types.ts +86 -9
- package/src/components/extended-fab/api.ts +53 -1
- package/src/components/extended-fab/config.ts +29 -1
- package/src/components/extended-fab/extended-fab.ts +28 -0
- package/src/components/extended-fab/index.ts +36 -0
- package/src/components/extended-fab/types.ts +458 -13
- package/src/components/fab/api.ts +42 -2
- package/src/components/fab/config.ts +29 -1
- package/src/components/fab/fab.ts +16 -2
- package/src/components/fab/index.ts +35 -0
- package/src/components/fab/types.ts +374 -10
- package/src/components/list/api.ts +12 -2
- package/src/components/list/config.ts +21 -0
- package/src/components/list/features.ts +6 -0
- package/src/components/list/index.ts +56 -1
- package/src/components/list/list-item.ts +46 -2
- package/src/components/list/list.ts +73 -2
- package/src/components/list/types.ts +172 -0
- package/src/components/list/utils.ts +26 -2
- package/src/components/menu/api.ts +217 -20
- package/src/components/menu/config.ts +27 -0
- package/src/components/menu/features/visibility.ts +55 -6
- package/src/components/menu/index.ts +64 -0
- package/src/components/menu/menu-item.ts +46 -3
- package/src/components/menu/menu.ts +77 -1
- package/src/components/menu/types.ts +404 -39
- package/src/components/sheet/config.ts +1 -2
- package/src/components/sheet/features/gestures.ts +1 -1
- package/src/components/sheet/features/position.ts +1 -2
- package/src/components/sheet/features/state.ts +1 -1
- package/src/components/sheet/index.ts +10 -2
- package/src/components/sheet/sheet.ts +1 -2
- package/src/components/sheet/types.ts +29 -1
- package/src/components/slider/api.ts +1 -1
- package/src/components/slider/config.ts +1 -1
- package/src/components/slider/features/controller.ts +1 -1
- package/src/components/slider/features/handlers.ts +1 -1
- package/src/components/slider/features/states.ts +1 -1
- package/src/components/slider/index.ts +12 -5
- package/src/components/slider/schema.ts +1 -1
- package/src/components/slider/types.ts +31 -0
- package/src/components/tabs/tab-api.ts +1 -1
- package/src/components/tabs/types.ts +1 -1
- package/src/components/tooltip/api.ts +6 -2
- package/src/components/tooltip/config.ts +9 -28
- package/src/components/tooltip/index.ts +10 -1
- package/src/components/tooltip/types.ts +38 -3
- package/src/index.ts +129 -31
- package/src/styles/abstract/_mixins.scss +23 -9
- package/src/styles/abstract/_variables.scss +14 -4
- package/src/styles/components/_card.scss +1 -1
- package/src/styles/components/_chip.scss +323 -113
- package/src/styles/components/_tabs.scss +1 -1
- package/CLAUDE.md +0 -33
- package/src/components/checkbox/constants.ts +0 -37
- package/src/components/chip/chip-set.ts +0 -225
- package/src/components/chip/chip.ts +0 -118
- package/src/components/chip/constants.ts +0 -28
- package/src/components/chip/index.ts +0 -12
- package/src/components/list/constants.ts +0 -116
- package/src/components/sheet/constants.ts +0 -20
- package/src/components/slider/constants.ts +0 -32
- package/src/components/tooltip/constants.ts +0 -27
- package/test/components/badge.test.ts +0 -545
- package/test/components/bottom-app-bar.test.ts +0 -303
- package/test/components/button.test.ts +0 -233
- package/test/components/card.test.ts +0 -560
- package/test/components/carousel.test.ts +0 -951
- package/test/components/checkbox.test.ts +0 -462
- package/test/components/chip.test.ts +0 -692
- package/test/components/datepicker.test.ts +0 -1124
- package/test/components/dialog.test.ts +0 -990
- package/test/components/divider.test.ts +0 -412
- package/test/components/extended-fab.test.ts +0 -672
- package/test/components/fab.test.ts +0 -561
- package/test/components/list.test.ts +0 -365
- package/test/components/menu.test.ts +0 -718
- package/test/components/navigation.test.ts +0 -186
- package/test/components/progress.test.ts +0 -567
- package/test/components/radios.test.ts +0 -699
- package/test/components/search.test.ts +0 -1135
- package/test/components/segmented-button.test.ts +0 -732
- package/test/components/sheet.test.ts +0 -641
- package/test/components/slider.test.ts +0 -1220
- package/test/components/snackbar.test.ts +0 -461
- package/test/components/switch.test.ts +0 -452
- package/test/components/tabs.test.ts +0 -1369
- package/test/components/textfield.test.ts +0 -400
- package/test/components/timepicker.test.ts +0 -592
- package/test/components/tooltip.test.ts +0 -630
- package/test/components/top-app-bar.test.ts +0 -566
- package/test/core/dom.attributes.test.ts +0 -148
- package/test/core/dom.classes.test.ts +0 -152
- package/test/core/dom.events.test.ts +0 -243
- package/test/core/emitter.test.ts +0 -141
- package/test/core/ripple.test.ts +0 -99
- package/test/core/state.store.test.ts +0 -189
- package/test/core/utils.normalize.test.ts +0 -61
- package/test/core/utils.object.test.ts +0 -120
- package/test/setup.js +0 -371
- package/test/setup.ts +0 -451
- package/tsconfig.json +0 -22
- package/typedoc.json +0 -28
- package/typedoc.simple.json +0 -14
|
@@ -1,990 +0,0 @@
|
|
|
1
|
-
// test/components/dialog.test.ts
|
|
2
|
-
import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
|
|
3
|
-
import { JSDOM } from 'jsdom';
|
|
4
|
-
import {
|
|
5
|
-
type DialogComponent,
|
|
6
|
-
type DialogConfig,
|
|
7
|
-
type DialogButton,
|
|
8
|
-
type DialogSize,
|
|
9
|
-
type DialogAnimation,
|
|
10
|
-
type DialogFooterAlignment,
|
|
11
|
-
type DialogEventType
|
|
12
|
-
} from '../../src/components/dialog/types';
|
|
13
|
-
|
|
14
|
-
// Setup jsdom environment
|
|
15
|
-
let dom: JSDOM;
|
|
16
|
-
let window: Window;
|
|
17
|
-
let document: Document;
|
|
18
|
-
let originalGlobalDocument: any;
|
|
19
|
-
let originalGlobalWindow: any;
|
|
20
|
-
|
|
21
|
-
beforeAll(() => {
|
|
22
|
-
// Create a new JSDOM instance
|
|
23
|
-
dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {
|
|
24
|
-
url: 'http://localhost/',
|
|
25
|
-
pretendToBeVisual: true
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// Get window and document from jsdom
|
|
29
|
-
window = dom.window;
|
|
30
|
-
document = window.document;
|
|
31
|
-
|
|
32
|
-
// Store original globals
|
|
33
|
-
originalGlobalDocument = global.document;
|
|
34
|
-
originalGlobalWindow = global.window;
|
|
35
|
-
|
|
36
|
-
// Set globals to use jsdom
|
|
37
|
-
global.document = document;
|
|
38
|
-
global.window = window;
|
|
39
|
-
global.Element = window.Element;
|
|
40
|
-
global.HTMLElement = window.HTMLElement;
|
|
41
|
-
global.HTMLButtonElement = window.HTMLButtonElement;
|
|
42
|
-
global.Event = window.Event;
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
afterAll(() => {
|
|
46
|
-
// Restore original globals
|
|
47
|
-
global.document = originalGlobalDocument;
|
|
48
|
-
global.window = originalGlobalWindow;
|
|
49
|
-
|
|
50
|
-
// Clean up jsdom
|
|
51
|
-
window.close();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// Mock dialog implementation
|
|
55
|
-
const createMockDialog = (config: DialogConfig = {}): DialogComponent => {
|
|
56
|
-
// Create main elements
|
|
57
|
-
const element = document.createElement('div');
|
|
58
|
-
element.className = 'mtrl-dialog';
|
|
59
|
-
|
|
60
|
-
const overlay = document.createElement('div');
|
|
61
|
-
overlay.className = 'mtrl-dialog-overlay';
|
|
62
|
-
|
|
63
|
-
// Default settings
|
|
64
|
-
const settings = {
|
|
65
|
-
title: config.title || '',
|
|
66
|
-
subtitle: config.subtitle || '',
|
|
67
|
-
content: config.content || '',
|
|
68
|
-
closeButton: config.closeButton !== undefined ? config.closeButton : true,
|
|
69
|
-
size: config.size || 'medium',
|
|
70
|
-
animation: config.animation || 'scale',
|
|
71
|
-
footerAlignment: config.footerAlignment || 'right',
|
|
72
|
-
closeOnOverlayClick: config.closeOnOverlayClick !== undefined ? config.closeOnOverlayClick : true,
|
|
73
|
-
closeOnEscape: config.closeOnEscape !== undefined ? config.closeOnEscape : true,
|
|
74
|
-
modal: config.modal !== undefined ? config.modal : true,
|
|
75
|
-
autofocus: config.autofocus !== undefined ? config.autofocus : true,
|
|
76
|
-
trapFocus: config.trapFocus !== undefined ? config.trapFocus : true,
|
|
77
|
-
divider: config.divider !== undefined ? config.divider : false,
|
|
78
|
-
zIndex: config.zIndex || 1000,
|
|
79
|
-
animationDuration: config.animationDuration || 300,
|
|
80
|
-
buttons: [...(config.buttons || [])]
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
// Set size class
|
|
84
|
-
if (settings.size) {
|
|
85
|
-
element.classList.add(`mtrl-dialog--${settings.size}`);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Set animation class
|
|
89
|
-
if (settings.animation) {
|
|
90
|
-
element.classList.add(`mtrl-dialog--animation-${settings.animation}`);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Create dialog structure
|
|
94
|
-
const container = document.createElement('div');
|
|
95
|
-
container.className = 'mtrl-dialog-container';
|
|
96
|
-
|
|
97
|
-
// Create dialog header
|
|
98
|
-
const header = document.createElement('div');
|
|
99
|
-
header.className = 'mtrl-dialog-header';
|
|
100
|
-
|
|
101
|
-
const titleElement = document.createElement('h2');
|
|
102
|
-
titleElement.className = 'mtrl-dialog-title';
|
|
103
|
-
titleElement.textContent = settings.title;
|
|
104
|
-
header.appendChild(titleElement);
|
|
105
|
-
|
|
106
|
-
if (settings.subtitle) {
|
|
107
|
-
const subtitleElement = document.createElement('h3');
|
|
108
|
-
subtitleElement.className = 'mtrl-dialog-subtitle';
|
|
109
|
-
subtitleElement.textContent = settings.subtitle;
|
|
110
|
-
header.appendChild(subtitleElement);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (settings.closeButton) {
|
|
114
|
-
const closeButton = document.createElement('button');
|
|
115
|
-
closeButton.className = 'mtrl-dialog-close';
|
|
116
|
-
closeButton.setAttribute('aria-label', 'Close dialog');
|
|
117
|
-
closeButton.innerHTML = '×';
|
|
118
|
-
closeButton.addEventListener('click', () => {
|
|
119
|
-
dialog.close();
|
|
120
|
-
});
|
|
121
|
-
header.appendChild(closeButton);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
container.appendChild(header);
|
|
125
|
-
|
|
126
|
-
// Add divider if configured
|
|
127
|
-
if (settings.divider) {
|
|
128
|
-
const divider = document.createElement('hr');
|
|
129
|
-
divider.className = 'mtrl-dialog-divider';
|
|
130
|
-
container.appendChild(divider);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Create dialog content
|
|
134
|
-
const content = document.createElement('div');
|
|
135
|
-
content.className = 'mtrl-dialog-content';
|
|
136
|
-
|
|
137
|
-
if (settings.content) {
|
|
138
|
-
if (settings.content.includes('<')) {
|
|
139
|
-
content.innerHTML = settings.content;
|
|
140
|
-
} else {
|
|
141
|
-
content.textContent = settings.content;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
container.appendChild(content);
|
|
146
|
-
|
|
147
|
-
// Create dialog footer with buttons
|
|
148
|
-
const footer = document.createElement('div');
|
|
149
|
-
footer.className = 'mtrl-dialog-footer';
|
|
150
|
-
footer.classList.add(`mtrl-dialog-footer--${settings.footerAlignment}`);
|
|
151
|
-
|
|
152
|
-
const buttons: DialogButton[] = [...settings.buttons];
|
|
153
|
-
|
|
154
|
-
buttons.forEach(button => {
|
|
155
|
-
const buttonElement = document.createElement('button');
|
|
156
|
-
buttonElement.className = 'mtrl-button';
|
|
157
|
-
|
|
158
|
-
if (button.variant) {
|
|
159
|
-
buttonElement.classList.add(`mtrl-button--${button.variant}`);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (button.color) {
|
|
163
|
-
buttonElement.classList.add(`mtrl-button--${button.color}`);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (button.size) {
|
|
167
|
-
buttonElement.classList.add(`mtrl-button--${button.size}`);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
buttonElement.textContent = button.text;
|
|
171
|
-
|
|
172
|
-
if (button.attrs) {
|
|
173
|
-
for (const [key, value] of Object.entries(button.attrs)) {
|
|
174
|
-
buttonElement.setAttribute(key, value);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
if (button.autofocus) {
|
|
179
|
-
buttonElement.setAttribute('autofocus', 'true');
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
buttonElement.addEventListener('click', (event) => {
|
|
183
|
-
let shouldClose = button.closeDialog !== false;
|
|
184
|
-
|
|
185
|
-
if (button.onClick) {
|
|
186
|
-
const result = button.onClick(event, dialog);
|
|
187
|
-
if (result === false) {
|
|
188
|
-
shouldClose = false;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (shouldClose) {
|
|
193
|
-
dialog.close();
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
footer.appendChild(buttonElement);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
container.appendChild(footer);
|
|
201
|
-
element.appendChild(container);
|
|
202
|
-
|
|
203
|
-
// Track open state
|
|
204
|
-
let isDialogOpen = config.open || false;
|
|
205
|
-
|
|
206
|
-
// Track event handlers
|
|
207
|
-
const eventHandlers: Record<string, Function[]> = {};
|
|
208
|
-
|
|
209
|
-
// Initialize event handlers from config
|
|
210
|
-
if (config.on) {
|
|
211
|
-
for (const [event, handler] of Object.entries(config.on)) {
|
|
212
|
-
if (!eventHandlers[event]) {
|
|
213
|
-
eventHandlers[event] = [];
|
|
214
|
-
}
|
|
215
|
-
eventHandlers[event].push(handler);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Create dialog event
|
|
220
|
-
const createDialogEvent = (type: string, originalEvent?: Event): any => {
|
|
221
|
-
let defaultPrevented = false;
|
|
222
|
-
|
|
223
|
-
return {
|
|
224
|
-
dialog,
|
|
225
|
-
originalEvent,
|
|
226
|
-
preventDefault: () => {
|
|
227
|
-
defaultPrevented = true;
|
|
228
|
-
},
|
|
229
|
-
defaultPrevented
|
|
230
|
-
};
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
// Emit event
|
|
234
|
-
const emit = (eventType: string, originalEvent?: Event) => {
|
|
235
|
-
if (eventHandlers[eventType]) {
|
|
236
|
-
const event = createDialogEvent(eventType, originalEvent);
|
|
237
|
-
for (const handler of eventHandlers[eventType]) {
|
|
238
|
-
handler(event);
|
|
239
|
-
}
|
|
240
|
-
return event.defaultPrevented;
|
|
241
|
-
}
|
|
242
|
-
return false;
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
// Set Z-index
|
|
246
|
-
overlay.style.zIndex = String(settings.zIndex);
|
|
247
|
-
element.style.zIndex = String(settings.zIndex + 1);
|
|
248
|
-
|
|
249
|
-
// Add class
|
|
250
|
-
if (config.class) {
|
|
251
|
-
const classes = config.class.split(' ');
|
|
252
|
-
classes.forEach(cls => element.classList.add(cls));
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Initial setup
|
|
256
|
-
if (isDialogOpen) {
|
|
257
|
-
overlay.classList.add('mtrl-dialog-overlay--open');
|
|
258
|
-
element.classList.add('mtrl-dialog--open');
|
|
259
|
-
} else {
|
|
260
|
-
overlay.style.display = 'none';
|
|
261
|
-
element.style.display = 'none';
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Handle overlay click
|
|
265
|
-
overlay.addEventListener('click', (event) => {
|
|
266
|
-
if (settings.closeOnOverlayClick && event.target === overlay) {
|
|
267
|
-
dialog.close();
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
// Handle escape key
|
|
272
|
-
const handleEscapeKey = (event: any) => {
|
|
273
|
-
if (settings.closeOnEscape && isDialogOpen && event.key === 'Escape') {
|
|
274
|
-
dialog.close();
|
|
275
|
-
}
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
document.addEventListener('keydown', handleEscapeKey);
|
|
279
|
-
|
|
280
|
-
// Create dialog component
|
|
281
|
-
const dialog: DialogComponent = {
|
|
282
|
-
element,
|
|
283
|
-
overlay,
|
|
284
|
-
|
|
285
|
-
open: () => {
|
|
286
|
-
if (!isDialogOpen) {
|
|
287
|
-
// Emit beforeopen event
|
|
288
|
-
if (emit('beforeopen')) {
|
|
289
|
-
return dialog; // Event was prevented
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
overlay.style.display = 'block';
|
|
293
|
-
element.style.display = 'block';
|
|
294
|
-
|
|
295
|
-
// For tests, we run immediately instead of setTimeout
|
|
296
|
-
overlay.classList.add('mtrl-dialog-overlay--open');
|
|
297
|
-
element.classList.add('mtrl-dialog--open');
|
|
298
|
-
|
|
299
|
-
isDialogOpen = true;
|
|
300
|
-
|
|
301
|
-
// Auto focus the first button or focusable element
|
|
302
|
-
if (settings.autofocus) {
|
|
303
|
-
const autofocusButton = element.querySelector('[autofocus]');
|
|
304
|
-
if (autofocusButton) {
|
|
305
|
-
(autofocusButton as HTMLElement).focus();
|
|
306
|
-
} else {
|
|
307
|
-
const firstFocusable = element.querySelector('button, [tabindex], a, input, select, textarea');
|
|
308
|
-
if (firstFocusable) {
|
|
309
|
-
(firstFocusable as HTMLElement).focus();
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// Emit afteropen event
|
|
315
|
-
emit('afteropen');
|
|
316
|
-
|
|
317
|
-
// Emit open event
|
|
318
|
-
emit('open');
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
return dialog;
|
|
322
|
-
},
|
|
323
|
-
|
|
324
|
-
close: () => {
|
|
325
|
-
if (isDialogOpen) {
|
|
326
|
-
// Emit beforeclose event
|
|
327
|
-
if (emit('beforeclose')) {
|
|
328
|
-
return dialog; // Event was prevented
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
overlay.classList.remove('mtrl-dialog-overlay--open');
|
|
332
|
-
element.classList.remove('mtrl-dialog--open');
|
|
333
|
-
|
|
334
|
-
// For tests, we run immediately instead of setTimeout
|
|
335
|
-
if (!isDialogOpen) {
|
|
336
|
-
overlay.style.display = 'none';
|
|
337
|
-
element.style.display = 'none';
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// Emit afterclose event
|
|
341
|
-
emit('afterclose');
|
|
342
|
-
|
|
343
|
-
isDialogOpen = false;
|
|
344
|
-
|
|
345
|
-
// Emit close event
|
|
346
|
-
emit('close');
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
return dialog;
|
|
350
|
-
},
|
|
351
|
-
|
|
352
|
-
toggle: (open?: boolean) => {
|
|
353
|
-
if (open === undefined) {
|
|
354
|
-
return isDialogOpen ? dialog.close() : dialog.open();
|
|
355
|
-
} else {
|
|
356
|
-
return open ? dialog.open() : dialog.close();
|
|
357
|
-
}
|
|
358
|
-
},
|
|
359
|
-
|
|
360
|
-
isOpen: () => isDialogOpen,
|
|
361
|
-
|
|
362
|
-
setTitle: (title: string) => {
|
|
363
|
-
settings.title = title;
|
|
364
|
-
const titleEl = element.querySelector('.mtrl-dialog-title');
|
|
365
|
-
if (titleEl) {
|
|
366
|
-
titleEl.textContent = title;
|
|
367
|
-
}
|
|
368
|
-
return dialog;
|
|
369
|
-
},
|
|
370
|
-
|
|
371
|
-
getTitle: () => settings.title,
|
|
372
|
-
|
|
373
|
-
setSubtitle: (subtitle: string) => {
|
|
374
|
-
settings.subtitle = subtitle;
|
|
375
|
-
|
|
376
|
-
let subtitleEl = element.querySelector('.mtrl-dialog-subtitle');
|
|
377
|
-
if (!subtitleEl && subtitle) {
|
|
378
|
-
// Create subtitle element if it doesn't exist
|
|
379
|
-
subtitleEl = document.createElement('h3');
|
|
380
|
-
subtitleEl.className = 'mtrl-dialog-subtitle';
|
|
381
|
-
const titleEl = element.querySelector('.mtrl-dialog-title');
|
|
382
|
-
if (titleEl?.parentNode) {
|
|
383
|
-
titleEl.parentNode.insertBefore(subtitleEl, titleEl.nextSibling);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
if (subtitleEl) {
|
|
388
|
-
if (subtitle) {
|
|
389
|
-
subtitleEl.textContent = subtitle;
|
|
390
|
-
} else {
|
|
391
|
-
subtitleEl.parentNode?.removeChild(subtitleEl);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
return dialog;
|
|
396
|
-
},
|
|
397
|
-
|
|
398
|
-
getSubtitle: () => settings.subtitle,
|
|
399
|
-
|
|
400
|
-
setContent: (content: string) => {
|
|
401
|
-
settings.content = content;
|
|
402
|
-
|
|
403
|
-
const contentEl = element.querySelector('.mtrl-dialog-content');
|
|
404
|
-
if (contentEl) {
|
|
405
|
-
if (content.includes('<')) {
|
|
406
|
-
contentEl.innerHTML = content;
|
|
407
|
-
} else {
|
|
408
|
-
contentEl.textContent = content;
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
return dialog;
|
|
413
|
-
},
|
|
414
|
-
|
|
415
|
-
getContent: () => settings.content,
|
|
416
|
-
|
|
417
|
-
addButton: (button: DialogButton) => {
|
|
418
|
-
settings.buttons.push(button);
|
|
419
|
-
|
|
420
|
-
const footerEl = element.querySelector('.mtrl-dialog-footer');
|
|
421
|
-
if (footerEl) {
|
|
422
|
-
const buttonElement = document.createElement('button');
|
|
423
|
-
buttonElement.className = 'mtrl-button';
|
|
424
|
-
|
|
425
|
-
if (button.variant) {
|
|
426
|
-
buttonElement.classList.add(`mtrl-button--${button.variant}`);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
if (button.color) {
|
|
430
|
-
buttonElement.classList.add(`mtrl-button--${button.color}`);
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
if (button.size) {
|
|
434
|
-
buttonElement.classList.add(`mtrl-button--${button.size}`);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
buttonElement.textContent = button.text;
|
|
438
|
-
|
|
439
|
-
if (button.attrs) {
|
|
440
|
-
for (const [key, value] of Object.entries(button.attrs)) {
|
|
441
|
-
buttonElement.setAttribute(key, value);
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
if (button.autofocus) {
|
|
446
|
-
buttonElement.setAttribute('autofocus', 'true');
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
buttonElement.addEventListener('click', (event) => {
|
|
450
|
-
let shouldClose = button.closeDialog !== false;
|
|
451
|
-
|
|
452
|
-
if (button.onClick) {
|
|
453
|
-
const result = button.onClick(event, dialog);
|
|
454
|
-
if (result === false) {
|
|
455
|
-
shouldClose = false;
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
if (shouldClose) {
|
|
460
|
-
dialog.close();
|
|
461
|
-
}
|
|
462
|
-
});
|
|
463
|
-
|
|
464
|
-
footerEl.appendChild(buttonElement);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
return dialog;
|
|
468
|
-
},
|
|
469
|
-
|
|
470
|
-
removeButton: (indexOrText: number | string) => {
|
|
471
|
-
const footerEl = element.querySelector('.mtrl-dialog-footer');
|
|
472
|
-
if (footerEl) {
|
|
473
|
-
if (typeof indexOrText === 'number') {
|
|
474
|
-
if (indexOrText >= 0 && indexOrText < settings.buttons.length) {
|
|
475
|
-
settings.buttons.splice(indexOrText, 1);
|
|
476
|
-
const buttons = footerEl.querySelectorAll('.mtrl-button');
|
|
477
|
-
if (indexOrText < buttons.length) {
|
|
478
|
-
footerEl.removeChild(buttons[indexOrText]);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
} else {
|
|
482
|
-
const buttonIndex = settings.buttons.findIndex(b => b.text === indexOrText);
|
|
483
|
-
if (buttonIndex !== -1) {
|
|
484
|
-
settings.buttons.splice(buttonIndex, 1);
|
|
485
|
-
const buttons = footerEl.querySelectorAll('.mtrl-button');
|
|
486
|
-
if (buttonIndex < buttons.length) {
|
|
487
|
-
footerEl.removeChild(buttons[buttonIndex]);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
return dialog;
|
|
494
|
-
},
|
|
495
|
-
|
|
496
|
-
getButtons: () => [...settings.buttons],
|
|
497
|
-
|
|
498
|
-
setFooterAlignment: (alignment: DialogFooterAlignment | string) => {
|
|
499
|
-
settings.footerAlignment = alignment;
|
|
500
|
-
|
|
501
|
-
const footerEl = element.querySelector('.mtrl-dialog-footer');
|
|
502
|
-
if (footerEl) {
|
|
503
|
-
footerEl.className = 'mtrl-dialog-footer';
|
|
504
|
-
footerEl.classList.add(`mtrl-dialog-footer--${alignment}`);
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
return dialog;
|
|
508
|
-
},
|
|
509
|
-
|
|
510
|
-
setSize: (size: DialogSize | string) => {
|
|
511
|
-
const prevSize = settings.size;
|
|
512
|
-
settings.size = size;
|
|
513
|
-
|
|
514
|
-
if (prevSize) {
|
|
515
|
-
element.classList.remove(`mtrl-dialog--${prevSize}`);
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
element.classList.add(`mtrl-dialog--${size}`);
|
|
519
|
-
|
|
520
|
-
return dialog;
|
|
521
|
-
},
|
|
522
|
-
|
|
523
|
-
on: (event: DialogEventType | string, handler: Function) => {
|
|
524
|
-
if (!eventHandlers[event]) {
|
|
525
|
-
eventHandlers[event] = [];
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
eventHandlers[event].push(handler);
|
|
529
|
-
return dialog;
|
|
530
|
-
},
|
|
531
|
-
|
|
532
|
-
off: (event: DialogEventType | string, handler: Function) => {
|
|
533
|
-
if (eventHandlers[event]) {
|
|
534
|
-
eventHandlers[event] = eventHandlers[event].filter(h => h !== handler);
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
return dialog;
|
|
538
|
-
},
|
|
539
|
-
|
|
540
|
-
getHeaderElement: () => element.querySelector('.mtrl-dialog-header'),
|
|
541
|
-
|
|
542
|
-
getContentElement: () => element.querySelector('.mtrl-dialog-content'),
|
|
543
|
-
|
|
544
|
-
getFooterElement: () => element.querySelector('.mtrl-dialog-footer'),
|
|
545
|
-
|
|
546
|
-
toggleDivider: (show: boolean) => {
|
|
547
|
-
settings.divider = show;
|
|
548
|
-
|
|
549
|
-
let dividerEl = element.querySelector('.mtrl-dialog-divider');
|
|
550
|
-
if (show && !dividerEl) {
|
|
551
|
-
dividerEl = document.createElement('hr');
|
|
552
|
-
dividerEl.className = 'mtrl-dialog-divider';
|
|
553
|
-
|
|
554
|
-
const headerEl = element.querySelector('.mtrl-dialog-header');
|
|
555
|
-
const contentEl = element.querySelector('.mtrl-dialog-content');
|
|
556
|
-
|
|
557
|
-
if (headerEl && contentEl && headerEl.parentNode) {
|
|
558
|
-
headerEl.parentNode.insertBefore(dividerEl, contentEl);
|
|
559
|
-
}
|
|
560
|
-
} else if (!show && dividerEl) {
|
|
561
|
-
dividerEl.parentNode?.removeChild(dividerEl);
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
return dialog;
|
|
565
|
-
},
|
|
566
|
-
|
|
567
|
-
hasDivider: () => settings.divider,
|
|
568
|
-
|
|
569
|
-
confirm: (options?: any) => {
|
|
570
|
-
// Simple mock implementation of confirm
|
|
571
|
-
return new Promise<boolean>(resolve => {
|
|
572
|
-
const confirmTitle = options?.title || 'Confirm';
|
|
573
|
-
const confirmMessage = options?.message || 'Are you sure?';
|
|
574
|
-
const confirmText = options?.confirmText || 'Yes';
|
|
575
|
-
const cancelText = options?.cancelText || 'No';
|
|
576
|
-
|
|
577
|
-
// In real implementation this would create a new dialog
|
|
578
|
-
// For testing purposes we just resolve immediately
|
|
579
|
-
const willConfirm = true; // simulate confirmation
|
|
580
|
-
resolve(willConfirm);
|
|
581
|
-
});
|
|
582
|
-
},
|
|
583
|
-
|
|
584
|
-
destroy: () => {
|
|
585
|
-
// Clean up event listeners
|
|
586
|
-
document.removeEventListener('keydown', handleEscapeKey);
|
|
587
|
-
|
|
588
|
-
// Remove from the DOM
|
|
589
|
-
if (overlay.parentNode) {
|
|
590
|
-
overlay.parentNode.removeChild(overlay);
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
if (element.parentNode) {
|
|
594
|
-
element.parentNode.removeChild(element);
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
// Clear event handlers
|
|
598
|
-
for (const event in eventHandlers) {
|
|
599
|
-
eventHandlers[event] = [];
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
};
|
|
603
|
-
|
|
604
|
-
return dialog;
|
|
605
|
-
};
|
|
606
|
-
|
|
607
|
-
describe('Dialog Component', () => {
|
|
608
|
-
test('should create a dialog element', () => {
|
|
609
|
-
const dialog = createMockDialog();
|
|
610
|
-
|
|
611
|
-
expect(dialog.element).toBeDefined();
|
|
612
|
-
expect(dialog.element.tagName).toBe('DIV');
|
|
613
|
-
expect(dialog.element.className).toContain('mtrl-dialog');
|
|
614
|
-
|
|
615
|
-
expect(dialog.overlay).toBeDefined();
|
|
616
|
-
expect(dialog.overlay.tagName).toBe('DIV');
|
|
617
|
-
expect(dialog.overlay.className).toContain('mtrl-dialog-overlay');
|
|
618
|
-
});
|
|
619
|
-
|
|
620
|
-
test('should set title and subtitle', () => {
|
|
621
|
-
const dialog = createMockDialog({
|
|
622
|
-
title: 'Dialog Title',
|
|
623
|
-
subtitle: 'Dialog Subtitle'
|
|
624
|
-
});
|
|
625
|
-
|
|
626
|
-
const titleElement = dialog.element.querySelector('.mtrl-dialog-title');
|
|
627
|
-
expect(titleElement).toBeDefined();
|
|
628
|
-
expect(titleElement?.textContent).toBe('Dialog Title');
|
|
629
|
-
|
|
630
|
-
const subtitleElement = dialog.element.querySelector('.mtrl-dialog-subtitle');
|
|
631
|
-
expect(subtitleElement).toBeDefined();
|
|
632
|
-
expect(subtitleElement?.textContent).toBe('Dialog Subtitle');
|
|
633
|
-
|
|
634
|
-
expect(dialog.getTitle()).toBe('Dialog Title');
|
|
635
|
-
expect(dialog.getSubtitle()).toBe('Dialog Subtitle');
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
test('should set content', () => {
|
|
639
|
-
const dialog = createMockDialog({
|
|
640
|
-
content: 'Dialog content text'
|
|
641
|
-
});
|
|
642
|
-
|
|
643
|
-
const contentElement = dialog.element.querySelector('.mtrl-dialog-content');
|
|
644
|
-
expect(contentElement).toBeDefined();
|
|
645
|
-
expect(contentElement?.textContent).toBe('Dialog content text');
|
|
646
|
-
|
|
647
|
-
expect(dialog.getContent()).toBe('Dialog content text');
|
|
648
|
-
});
|
|
649
|
-
|
|
650
|
-
test('should support HTML content', () => {
|
|
651
|
-
const dialog = createMockDialog({
|
|
652
|
-
content: '<p>Dialog <strong>HTML</strong> content</p>'
|
|
653
|
-
});
|
|
654
|
-
|
|
655
|
-
const contentElement = dialog.element.querySelector('.mtrl-dialog-content');
|
|
656
|
-
expect(contentElement).toBeDefined();
|
|
657
|
-
expect(contentElement?.innerHTML).toBe('<p>Dialog <strong>HTML</strong> content</p>');
|
|
658
|
-
|
|
659
|
-
const strongElement = contentElement?.querySelector('strong');
|
|
660
|
-
expect(strongElement).toBeDefined();
|
|
661
|
-
expect(strongElement?.textContent).toBe('HTML');
|
|
662
|
-
});
|
|
663
|
-
|
|
664
|
-
test('should apply size variants', () => {
|
|
665
|
-
const sizes: DialogSize[] = ['small', 'medium', 'large', 'fullwidth', 'fullscreen'];
|
|
666
|
-
|
|
667
|
-
sizes.forEach(size => {
|
|
668
|
-
const dialog = createMockDialog({ size });
|
|
669
|
-
expect(dialog.element.className).toContain(`mtrl-dialog--${size}`);
|
|
670
|
-
});
|
|
671
|
-
});
|
|
672
|
-
|
|
673
|
-
test('should apply animation variants', () => {
|
|
674
|
-
const animations: DialogAnimation[] = ['scale', 'slide-up', 'slide-down', 'fade'];
|
|
675
|
-
|
|
676
|
-
animations.forEach(animation => {
|
|
677
|
-
const dialog = createMockDialog({ animation });
|
|
678
|
-
expect(dialog.element.className).toContain(`mtrl-dialog--animation-${animation}`);
|
|
679
|
-
});
|
|
680
|
-
});
|
|
681
|
-
|
|
682
|
-
test('should create close button by default', () => {
|
|
683
|
-
const dialog = createMockDialog();
|
|
684
|
-
|
|
685
|
-
const closeButton = dialog.element.querySelector('.mtrl-dialog-close');
|
|
686
|
-
expect(closeButton).toBeDefined();
|
|
687
|
-
});
|
|
688
|
-
|
|
689
|
-
test('should hide close button when configured', () => {
|
|
690
|
-
const dialog = createMockDialog({
|
|
691
|
-
closeButton: false
|
|
692
|
-
});
|
|
693
|
-
|
|
694
|
-
const closeButton = dialog.element.querySelector('.mtrl-dialog-close');
|
|
695
|
-
expect(closeButton).toBeNull();
|
|
696
|
-
});
|
|
697
|
-
|
|
698
|
-
test('should create buttons in footer', () => {
|
|
699
|
-
const buttons: DialogButton[] = [
|
|
700
|
-
{ text: 'Cancel', variant: 'text' },
|
|
701
|
-
{ text: 'OK', variant: 'filled' }
|
|
702
|
-
];
|
|
703
|
-
|
|
704
|
-
const dialog = createMockDialog({ buttons });
|
|
705
|
-
|
|
706
|
-
const footerElement = dialog.element.querySelector('.mtrl-dialog-footer');
|
|
707
|
-
expect(footerElement).toBeDefined();
|
|
708
|
-
|
|
709
|
-
const buttonElements = dialog.element.querySelectorAll('.mtrl-button');
|
|
710
|
-
expect(buttonElements.length).toBe(2);
|
|
711
|
-
|
|
712
|
-
expect(buttonElements[0].textContent).toBe('Cancel');
|
|
713
|
-
expect(buttonElements[0].className).toContain('mtrl-button--text');
|
|
714
|
-
|
|
715
|
-
expect(buttonElements[1].textContent).toBe('OK');
|
|
716
|
-
expect(buttonElements[1].className).toContain('mtrl-button--filled');
|
|
717
|
-
});
|
|
718
|
-
|
|
719
|
-
test('should initially be closed by default', () => {
|
|
720
|
-
const dialog = createMockDialog();
|
|
721
|
-
|
|
722
|
-
expect(dialog.isOpen()).toBe(false);
|
|
723
|
-
expect(dialog.element.style.display).toBe('none');
|
|
724
|
-
expect(dialog.overlay.style.display).toBe('none');
|
|
725
|
-
});
|
|
726
|
-
|
|
727
|
-
test('should initially be open when configured', () => {
|
|
728
|
-
const dialog = createMockDialog({
|
|
729
|
-
open: true
|
|
730
|
-
});
|
|
731
|
-
|
|
732
|
-
expect(dialog.isOpen()).toBe(true);
|
|
733
|
-
expect(dialog.element.className).toContain('mtrl-dialog--open');
|
|
734
|
-
expect(dialog.overlay.className).toContain('mtrl-dialog-overlay--open');
|
|
735
|
-
});
|
|
736
|
-
|
|
737
|
-
test('should open and close the dialog', () => {
|
|
738
|
-
const dialog = createMockDialog();
|
|
739
|
-
|
|
740
|
-
expect(dialog.isOpen()).toBe(false);
|
|
741
|
-
|
|
742
|
-
dialog.open();
|
|
743
|
-
|
|
744
|
-
expect(dialog.isOpen()).toBe(true);
|
|
745
|
-
expect(dialog.element.style.display).toBe('block');
|
|
746
|
-
expect(dialog.overlay.style.display).toBe('block');
|
|
747
|
-
expect(dialog.element.className).toContain('mtrl-dialog--open');
|
|
748
|
-
|
|
749
|
-
dialog.close();
|
|
750
|
-
|
|
751
|
-
expect(dialog.isOpen()).toBe(false);
|
|
752
|
-
expect(dialog.element.className).not.toContain('mtrl-dialog--open');
|
|
753
|
-
});
|
|
754
|
-
|
|
755
|
-
test('should toggle the dialog', () => {
|
|
756
|
-
const dialog = createMockDialog();
|
|
757
|
-
|
|
758
|
-
expect(dialog.isOpen()).toBe(false);
|
|
759
|
-
|
|
760
|
-
dialog.toggle();
|
|
761
|
-
expect(dialog.isOpen()).toBe(true);
|
|
762
|
-
|
|
763
|
-
dialog.toggle();
|
|
764
|
-
expect(dialog.isOpen()).toBe(false);
|
|
765
|
-
|
|
766
|
-
dialog.toggle(true);
|
|
767
|
-
expect(dialog.isOpen()).toBe(true);
|
|
768
|
-
|
|
769
|
-
dialog.toggle(true); // Should remain open
|
|
770
|
-
expect(dialog.isOpen()).toBe(true);
|
|
771
|
-
|
|
772
|
-
dialog.toggle(false);
|
|
773
|
-
expect(dialog.isOpen()).toBe(false);
|
|
774
|
-
});
|
|
775
|
-
|
|
776
|
-
test('should update title dynamically', () => {
|
|
777
|
-
const dialog = createMockDialog({
|
|
778
|
-
title: 'Initial Title'
|
|
779
|
-
});
|
|
780
|
-
|
|
781
|
-
const titleElement = dialog.element.querySelector('.mtrl-dialog-title');
|
|
782
|
-
expect(titleElement?.textContent).toBe('Initial Title');
|
|
783
|
-
|
|
784
|
-
dialog.setTitle('Updated Title');
|
|
785
|
-
|
|
786
|
-
expect(titleElement?.textContent).toBe('Updated Title');
|
|
787
|
-
expect(dialog.getTitle()).toBe('Updated Title');
|
|
788
|
-
});
|
|
789
|
-
|
|
790
|
-
test('should update subtitle dynamically', () => {
|
|
791
|
-
const dialog = createMockDialog({
|
|
792
|
-
subtitle: 'Initial Subtitle'
|
|
793
|
-
});
|
|
794
|
-
|
|
795
|
-
const subtitleElement = dialog.element.querySelector('.mtrl-dialog-subtitle');
|
|
796
|
-
expect(subtitleElement?.textContent).toBe('Initial Subtitle');
|
|
797
|
-
|
|
798
|
-
dialog.setSubtitle('Updated Subtitle');
|
|
799
|
-
|
|
800
|
-
expect(subtitleElement?.textContent).toBe('Updated Subtitle');
|
|
801
|
-
expect(dialog.getSubtitle()).toBe('Updated Subtitle');
|
|
802
|
-
});
|
|
803
|
-
|
|
804
|
-
test('should update content dynamically', () => {
|
|
805
|
-
const dialog = createMockDialog({
|
|
806
|
-
content: 'Initial content'
|
|
807
|
-
});
|
|
808
|
-
|
|
809
|
-
const contentElement = dialog.element.querySelector('.mtrl-dialog-content');
|
|
810
|
-
expect(contentElement?.textContent).toBe('Initial content');
|
|
811
|
-
|
|
812
|
-
dialog.setContent('Updated content');
|
|
813
|
-
|
|
814
|
-
expect(contentElement?.textContent).toBe('Updated content');
|
|
815
|
-
expect(dialog.getContent()).toBe('Updated content');
|
|
816
|
-
});
|
|
817
|
-
|
|
818
|
-
test('should add buttons dynamically', () => {
|
|
819
|
-
const dialog = createMockDialog();
|
|
820
|
-
|
|
821
|
-
const footerElement = dialog.element.querySelector('.mtrl-dialog-footer');
|
|
822
|
-
expect(footerElement?.children.length).toBe(0);
|
|
823
|
-
|
|
824
|
-
dialog.addButton({ text: 'New Button', variant: 'text' });
|
|
825
|
-
|
|
826
|
-
expect(footerElement?.children.length).toBe(1);
|
|
827
|
-
expect(footerElement?.children[0].textContent).toBe('New Button');
|
|
828
|
-
expect(dialog.getButtons().length).toBe(1);
|
|
829
|
-
});
|
|
830
|
-
|
|
831
|
-
test('should remove buttons by index', () => {
|
|
832
|
-
const dialog = createMockDialog({
|
|
833
|
-
buttons: [
|
|
834
|
-
{ text: 'Button 1' },
|
|
835
|
-
{ text: 'Button 2' },
|
|
836
|
-
{ text: 'Button 3' }
|
|
837
|
-
]
|
|
838
|
-
});
|
|
839
|
-
|
|
840
|
-
const footerElement = dialog.element.querySelector('.mtrl-dialog-footer');
|
|
841
|
-
expect(footerElement?.children.length).toBe(3);
|
|
842
|
-
|
|
843
|
-
dialog.removeButton(1);
|
|
844
|
-
|
|
845
|
-
expect(footerElement?.children.length).toBe(2);
|
|
846
|
-
expect(footerElement?.children[0].textContent).toBe('Button 1');
|
|
847
|
-
expect(footerElement?.children[1].textContent).toBe('Button 3');
|
|
848
|
-
expect(dialog.getButtons().length).toBe(2);
|
|
849
|
-
});
|
|
850
|
-
|
|
851
|
-
test('should remove buttons by text', () => {
|
|
852
|
-
const dialog = createMockDialog({
|
|
853
|
-
buttons: [
|
|
854
|
-
{ text: 'Button 1' },
|
|
855
|
-
{ text: 'Button 2' },
|
|
856
|
-
{ text: 'Button 3' }
|
|
857
|
-
]
|
|
858
|
-
});
|
|
859
|
-
|
|
860
|
-
const footerElement = dialog.element.querySelector('.mtrl-dialog-footer');
|
|
861
|
-
expect(footerElement?.children.length).toBe(3);
|
|
862
|
-
|
|
863
|
-
dialog.removeButton('Button 2');
|
|
864
|
-
|
|
865
|
-
expect(footerElement?.children.length).toBe(2);
|
|
866
|
-
expect(footerElement?.children[0].textContent).toBe('Button 1');
|
|
867
|
-
expect(footerElement?.children[1].textContent).toBe('Button 3');
|
|
868
|
-
expect(dialog.getButtons().length).toBe(2);
|
|
869
|
-
});
|
|
870
|
-
|
|
871
|
-
test('should change footer alignment', () => {
|
|
872
|
-
const dialog = createMockDialog({
|
|
873
|
-
footerAlignment: 'right'
|
|
874
|
-
});
|
|
875
|
-
|
|
876
|
-
const footerElement = dialog.element.querySelector('.mtrl-dialog-footer');
|
|
877
|
-
expect(footerElement?.className).toContain('mtrl-dialog-footer--right');
|
|
878
|
-
|
|
879
|
-
dialog.setFooterAlignment('center');
|
|
880
|
-
|
|
881
|
-
expect(footerElement?.className).toContain('mtrl-dialog-footer--center');
|
|
882
|
-
expect(footerElement?.className).not.toContain('mtrl-dialog-footer--right');
|
|
883
|
-
});
|
|
884
|
-
|
|
885
|
-
test('should change dialog size', () => {
|
|
886
|
-
const dialog = createMockDialog({
|
|
887
|
-
size: 'medium'
|
|
888
|
-
});
|
|
889
|
-
|
|
890
|
-
expect(dialog.element.className).toContain('mtrl-dialog--medium');
|
|
891
|
-
|
|
892
|
-
dialog.setSize('large');
|
|
893
|
-
|
|
894
|
-
expect(dialog.element.className).toContain('mtrl-dialog--large');
|
|
895
|
-
expect(dialog.element.className).not.toContain('mtrl-dialog--medium');
|
|
896
|
-
});
|
|
897
|
-
|
|
898
|
-
test('should add and remove event listeners', () => {
|
|
899
|
-
const dialog = createMockDialog();
|
|
900
|
-
let eventCount = 0;
|
|
901
|
-
|
|
902
|
-
const handler = () => {
|
|
903
|
-
eventCount++;
|
|
904
|
-
};
|
|
905
|
-
|
|
906
|
-
dialog.on('open', handler);
|
|
907
|
-
|
|
908
|
-
dialog.open();
|
|
909
|
-
expect(eventCount).toBe(1);
|
|
910
|
-
|
|
911
|
-
dialog.close();
|
|
912
|
-
dialog.open();
|
|
913
|
-
expect(eventCount).toBe(2);
|
|
914
|
-
|
|
915
|
-
dialog.off('open', handler);
|
|
916
|
-
|
|
917
|
-
dialog.close();
|
|
918
|
-
dialog.open();
|
|
919
|
-
expect(eventCount).toBe(2); // Should not increment
|
|
920
|
-
});
|
|
921
|
-
|
|
922
|
-
test('should get dialog elements', () => {
|
|
923
|
-
const dialog = createMockDialog();
|
|
924
|
-
|
|
925
|
-
const headerElement = dialog.getHeaderElement();
|
|
926
|
-
expect(headerElement).toBeDefined();
|
|
927
|
-
expect(headerElement?.className).toBe('mtrl-dialog-header');
|
|
928
|
-
|
|
929
|
-
const contentElement = dialog.getContentElement();
|
|
930
|
-
expect(contentElement).toBeDefined();
|
|
931
|
-
expect(contentElement?.className).toBe('mtrl-dialog-content');
|
|
932
|
-
|
|
933
|
-
const footerElement = dialog.getFooterElement();
|
|
934
|
-
expect(footerElement).toBeDefined();
|
|
935
|
-
expect(footerElement?.className).toContain('mtrl-dialog-footer');
|
|
936
|
-
});
|
|
937
|
-
|
|
938
|
-
test('should toggle divider', () => {
|
|
939
|
-
const dialog = createMockDialog();
|
|
940
|
-
|
|
941
|
-
expect(dialog.hasDivider()).toBe(false);
|
|
942
|
-
expect(dialog.element.querySelector('.mtrl-dialog-divider')).toBeNull();
|
|
943
|
-
|
|
944
|
-
dialog.toggleDivider(true);
|
|
945
|
-
|
|
946
|
-
expect(dialog.hasDivider()).toBe(true);
|
|
947
|
-
expect(dialog.element.querySelector('.mtrl-dialog-divider')).toBeDefined();
|
|
948
|
-
|
|
949
|
-
dialog.toggleDivider(false);
|
|
950
|
-
|
|
951
|
-
expect(dialog.hasDivider()).toBe(false);
|
|
952
|
-
expect(dialog.element.querySelector('.mtrl-dialog-divider')).toBeNull();
|
|
953
|
-
});
|
|
954
|
-
|
|
955
|
-
test('should create with divider when configured', () => {
|
|
956
|
-
const dialog = createMockDialog({
|
|
957
|
-
divider: true
|
|
958
|
-
});
|
|
959
|
-
|
|
960
|
-
expect(dialog.hasDivider()).toBe(true);
|
|
961
|
-
expect(dialog.element.querySelector('.mtrl-dialog-divider')).toBeDefined();
|
|
962
|
-
});
|
|
963
|
-
|
|
964
|
-
test('should handle confirm method', async () => {
|
|
965
|
-
const dialog = createMockDialog();
|
|
966
|
-
|
|
967
|
-
const result = await dialog.confirm({
|
|
968
|
-
title: 'Confirm Title',
|
|
969
|
-
message: 'Confirm Message',
|
|
970
|
-
confirmText: 'Yes',
|
|
971
|
-
cancelText: 'No'
|
|
972
|
-
});
|
|
973
|
-
|
|
974
|
-
expect(result).toBe(true);
|
|
975
|
-
});
|
|
976
|
-
|
|
977
|
-
test('should be properly destroyed', () => {
|
|
978
|
-
const dialog = createMockDialog();
|
|
979
|
-
document.body.appendChild(dialog.element);
|
|
980
|
-
document.body.appendChild(dialog.overlay);
|
|
981
|
-
|
|
982
|
-
expect(document.body.contains(dialog.element)).toBe(true);
|
|
983
|
-
expect(document.body.contains(dialog.overlay)).toBe(true);
|
|
984
|
-
|
|
985
|
-
dialog.destroy();
|
|
986
|
-
|
|
987
|
-
expect(document.body.contains(dialog.element)).toBe(false);
|
|
988
|
-
expect(document.body.contains(dialog.overlay)).toBe(false);
|
|
989
|
-
});
|
|
990
|
-
});
|