kempo-ui 0.1.6 → 0.2.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/.github/skills/highlight-code/SKILL.md +22 -6
- package/.github/skills/new-component/SKILL.md +68 -4
- package/AGENTS.md +11 -0
- package/bin/highlight_code.js +7 -15
- package/dist/components/Aside.js +326 -0
- package/dist/components/Dialog.js +1 -1
- package/dist/components/Dropdown.js +1 -1
- package/dist/components/Main.js +2 -2
- package/dist/components/Nav.js +27 -0
- package/dist/components/PhotoViewer.js +1 -1
- package/dist/components/ShadowComponent.js +1 -1
- package/dist/components/ThemeSwitcher.js +1 -0
- package/dist/components/Toast.js +1 -1
- package/docs/components/accordion.html +4 -3
- package/docs/components/aside-items.html +43 -0
- package/docs/components/aside-menu.html +43 -0
- package/docs/components/aside-push.html +26 -0
- package/docs/components/aside.html +567 -0
- package/docs/components/card.html +4 -3
- package/docs/components/color-picker.html +4 -3
- package/docs/components/content-slider.html +4 -3
- package/docs/components/dialog.html +4 -3
- package/docs/components/dropdown.html +4 -3
- package/docs/components/filter-list.html +4 -3
- package/docs/components/focus-capture.html +6 -5
- package/docs/components/html-editor.html +5 -4
- package/docs/components/hybrid-component.html +5 -4
- package/docs/components/icon.html +4 -3
- package/docs/components/import.html +5 -4
- package/docs/components/light-component.html +4 -3
- package/docs/components/nav.html +117 -0
- package/docs/components/photo-viewer.html +5 -4
- package/docs/components/resize.html +5 -4
- package/docs/components/shadow-component.html +4 -3
- package/docs/components/show-more.html +5 -4
- package/docs/components/sortable.html +4 -3
- package/docs/components/spinner.html +4 -3
- package/docs/components/split.html +5 -4
- package/docs/components/table.html +4 -3
- package/docs/components/tableControls.html +4 -3
- package/docs/components/tableCustomFields.html +4 -3
- package/docs/components/tableFetchRecords.html +5 -4
- package/docs/components/tableFieldSortHide.html +5 -4
- package/docs/components/tablePagination.html +4 -3
- package/docs/components/tableRecordEditing.html +4 -3
- package/docs/components/tableRecordFiltering.html +5 -4
- package/docs/components/tableRecordHiding.html +4 -3
- package/docs/components/tableRecordSearching.html +5 -4
- package/docs/components/tableRecordSelection.html +4 -3
- package/docs/components/tableRowControls.html +4 -3
- package/docs/components/tableServerSync.html +5 -4
- package/docs/components/tableSorting.html +5 -4
- package/docs/components/tabs.html +4 -3
- package/docs/components/tags.html +4 -3
- package/docs/components/theme-select.html +4 -3
- package/docs/components/theme-switcher.html +5 -4
- package/docs/components/timestamp.html +4 -3
- package/docs/components/toast.html +4 -3
- package/docs/components/toggle.html +5 -4
- package/docs/components/tree.html +5 -4
- package/docs/index.html +22 -21
- package/docs/nav-1.inc.html +18 -12
- package/docs/nav.inc.html +20 -13
- package/docs/nav.inc.js +1 -1
- package/docs/src/components/Aside.js +326 -0
- package/docs/src/components/Dialog.js +1 -1
- package/docs/src/components/Dropdown.js +1 -1
- package/docs/src/components/Main.js +2 -2
- package/docs/src/components/Nav.js +27 -0
- package/docs/src/components/PhotoViewer.js +1 -1
- package/docs/src/components/ShadowComponent.js +1 -1
- package/docs/src/components/ThemeSwitcher.js +1 -0
- package/docs/src/components/Toast.js +1 -1
- package/docs/utils/context.html +4 -3
- package/docs/utils/cookie.html +4 -3
- package/docs/utils/debounce.html +4 -3
- package/docs/utils/drag.html +4 -3
- package/docs/utils/elevation.html +156 -0
- package/docs/utils/formatTimestamp.html +4 -3
- package/docs/utils/object.html +4 -3
- package/docs/utils/propConverters.html +4 -3
- package/docs/utils/string.html +4 -3
- package/docs/utils/theme.html +4 -3
- package/docs/utils/toTitleCase.html +4 -3
- package/docs/utils/type.html +4 -3
- package/docs/utils/wait.html +4 -3
- package/llm.txt +247 -0
- package/package.json +3 -4
- package/src/components/Aside.js +726 -0
- package/src/components/Dialog.js +1 -1
- package/src/components/Dropdown.js +1 -1
- package/src/components/Main.js +34 -30
- package/src/components/Nav.js +53 -0
- package/src/components/PhotoViewer.js +1 -1
- package/src/components/ShadowComponent.js +1 -1
- package/src/components/ThemeSwitcher.js +1 -0
- package/src/components/Toast.js +1 -1
- package/tests/bin/get_icon.node-test.js +26 -0
- package/tests/bin/highlight_code.node-test.js +108 -0
- package/tests/bin/list_icons.node-test.js +26 -0
- package/tests/components/Aside.browser-test.js +778 -0
- package/tests/components/Main.browser-test.js +115 -283
- package/dist/components/SideMenu.js +0 -97
- package/dist/components/SidePanel.js +0 -236
- package/docs/components/side-menu.html +0 -189
- package/docs/components/side-panel-basic.html +0 -44
- package/docs/components/side-panel-dual.html +0 -70
- package/docs/components/side-panel-menu.html +0 -50
- package/docs/components/side-panel-persistent.html +0 -90
- package/docs/components/side-panel-scroll.html +0 -176
- package/docs/components/side-panel.html +0 -245
- package/docs/src/components/SideMenu.js +0 -97
- package/docs/src/components/SidePanel.js +0 -236
- package/scripts/update-kempo-css.js +0 -53
- package/src/components/SideMenu.js +0 -178
- package/src/components/SidePanel.js +0 -513
- package/tests/components/SideMenu.browser-test.js +0 -667
- package/tests/components/SidePanel.browser-test.js +0 -520
- package/tools/highlight.js +0 -51
|
@@ -0,0 +1,778 @@
|
|
|
1
|
+
import Aside from '../../src/components/Aside.js';
|
|
2
|
+
|
|
3
|
+
const createAside = async (options = {}) => {
|
|
4
|
+
const aside = document.createElement('k-aside');
|
|
5
|
+
if(options.state) aside.state = options.state;
|
|
6
|
+
if(options.side) aside.side = options.side;
|
|
7
|
+
if(options.main) aside.main = options.main;
|
|
8
|
+
if(options.overlayClose !== undefined) aside.overlayClose = options.overlayClose;
|
|
9
|
+
if(options.escClose !== undefined) aside.escClose = options.escClose;
|
|
10
|
+
document.body.appendChild(aside);
|
|
11
|
+
await aside.updateComplete;
|
|
12
|
+
return aside;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const cleanup = (...elements) => {
|
|
16
|
+
elements.forEach(el => {
|
|
17
|
+
if(el && el.parentNode) el.parentNode.removeChild(el);
|
|
18
|
+
});
|
|
19
|
+
document.body.classList.remove('no-scroll');
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default {
|
|
23
|
+
/*
|
|
24
|
+
Element Creation
|
|
25
|
+
*/
|
|
26
|
+
'should create aside element': async ({pass, fail}) => {
|
|
27
|
+
const aside = await createAside();
|
|
28
|
+
if(!(aside instanceof Aside)) {
|
|
29
|
+
cleanup(aside);
|
|
30
|
+
return fail('Element should be instance of Aside');
|
|
31
|
+
}
|
|
32
|
+
cleanup(aside);
|
|
33
|
+
pass('Aside element created correctly');
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
'should have shadow root': async ({pass, fail}) => {
|
|
37
|
+
const aside = await createAside();
|
|
38
|
+
if(!aside.shadowRoot) {
|
|
39
|
+
cleanup(aside);
|
|
40
|
+
return fail('Should have shadow root');
|
|
41
|
+
}
|
|
42
|
+
cleanup(aside);
|
|
43
|
+
pass('Has shadow root');
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
/*
|
|
47
|
+
Default Properties
|
|
48
|
+
*/
|
|
49
|
+
'should default state to expanded': async ({pass, fail}) => {
|
|
50
|
+
const aside = await createAside();
|
|
51
|
+
if(aside.state !== 'offscreen') {
|
|
52
|
+
cleanup(aside);
|
|
53
|
+
return fail(`Expected state "offscreen", got "${aside.state}"`);
|
|
54
|
+
}
|
|
55
|
+
cleanup(aside);
|
|
56
|
+
pass('Default state is offscreen');
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
'should default side to left': async ({pass, fail}) => {
|
|
60
|
+
const aside = await createAside();
|
|
61
|
+
if(aside.side !== 'left') {
|
|
62
|
+
cleanup(aside);
|
|
63
|
+
return fail(`Expected side "left", got "${aside.side}"`);
|
|
64
|
+
}
|
|
65
|
+
cleanup(aside);
|
|
66
|
+
pass('Default side is left');
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
'should default main to push': async ({pass, fail}) => {
|
|
70
|
+
const aside = await createAside();
|
|
71
|
+
if(aside.main !== 'overlay') {
|
|
72
|
+
cleanup(aside);
|
|
73
|
+
return fail(`Expected main "overlay", got "${aside.main}"`);
|
|
74
|
+
}
|
|
75
|
+
cleanup(aside);
|
|
76
|
+
pass('Default main is overlay');
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
'should default overlayClose to true': async ({pass, fail}) => {
|
|
80
|
+
const aside = await createAside();
|
|
81
|
+
if(aside.overlayClose !== true) {
|
|
82
|
+
cleanup(aside);
|
|
83
|
+
return fail(`Expected overlayClose true, got ${aside.overlayClose}`);
|
|
84
|
+
}
|
|
85
|
+
cleanup(aside);
|
|
86
|
+
pass('Default overlayClose is true');
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
'should default escClose to true': async ({pass, fail}) => {
|
|
90
|
+
const aside = await createAside();
|
|
91
|
+
if(aside.escClose !== true) {
|
|
92
|
+
cleanup(aside);
|
|
93
|
+
return fail(`Expected escClose true, got ${aside.escClose}`);
|
|
94
|
+
}
|
|
95
|
+
cleanup(aside);
|
|
96
|
+
pass('Default escClose is true');
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
/*
|
|
100
|
+
Attribute Reflection
|
|
101
|
+
*/
|
|
102
|
+
'should reflect state attribute': async ({pass, fail}) => {
|
|
103
|
+
const aside = await createAside();
|
|
104
|
+
if(aside.getAttribute('state') !== 'offscreen') {
|
|
105
|
+
cleanup(aside);
|
|
106
|
+
return fail('State attribute not reflected');
|
|
107
|
+
}
|
|
108
|
+
aside.state = 'expanded';
|
|
109
|
+
await aside.updateComplete;
|
|
110
|
+
if(aside.getAttribute('state') !== 'expanded') {
|
|
111
|
+
cleanup(aside);
|
|
112
|
+
return fail('State attribute not updated on change');
|
|
113
|
+
}
|
|
114
|
+
cleanup(aside);
|
|
115
|
+
pass('State attribute reflected');
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
'should reflect side attribute': async ({pass, fail}) => {
|
|
119
|
+
const aside = await createAside();
|
|
120
|
+
if(aside.getAttribute('side') !== 'left') {
|
|
121
|
+
cleanup(aside);
|
|
122
|
+
return fail('Side attribute not reflected');
|
|
123
|
+
}
|
|
124
|
+
aside.side = 'right';
|
|
125
|
+
await aside.updateComplete;
|
|
126
|
+
if(aside.getAttribute('side') !== 'right') {
|
|
127
|
+
cleanup(aside);
|
|
128
|
+
return fail('Side attribute not updated on change');
|
|
129
|
+
}
|
|
130
|
+
cleanup(aside);
|
|
131
|
+
pass('Side attribute reflected');
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
'should reflect main attribute': async ({pass, fail}) => {
|
|
135
|
+
const aside = await createAside();
|
|
136
|
+
if(aside.getAttribute('main') !== 'overlay') {
|
|
137
|
+
cleanup(aside);
|
|
138
|
+
return fail('Main attribute not reflected');
|
|
139
|
+
}
|
|
140
|
+
aside.main = 'push';
|
|
141
|
+
await aside.updateComplete;
|
|
142
|
+
if(aside.getAttribute('main') !== 'push') {
|
|
143
|
+
cleanup(aside);
|
|
144
|
+
return fail('Main attribute not updated on change');
|
|
145
|
+
}
|
|
146
|
+
cleanup(aside);
|
|
147
|
+
pass('Main attribute reflected');
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
/*
|
|
151
|
+
Methods
|
|
152
|
+
*/
|
|
153
|
+
'expand() should set state to expanded': async ({pass, fail}) => {
|
|
154
|
+
const aside = await createAside({ state: 'offscreen' });
|
|
155
|
+
aside.expand();
|
|
156
|
+
await aside.updateComplete;
|
|
157
|
+
if(aside.state !== 'expanded') {
|
|
158
|
+
cleanup(aside);
|
|
159
|
+
return fail(`Expected "expanded", got "${aside.state}"`);
|
|
160
|
+
}
|
|
161
|
+
cleanup(aside);
|
|
162
|
+
pass('expand() sets state to expanded');
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
'collapse() should set state to collapsed': async ({pass, fail}) => {
|
|
166
|
+
const aside = await createAside();
|
|
167
|
+
aside.collapse();
|
|
168
|
+
await aside.updateComplete;
|
|
169
|
+
if(aside.state !== 'collapsed') {
|
|
170
|
+
cleanup(aside);
|
|
171
|
+
return fail(`Expected "collapsed", got "${aside.state}"`);
|
|
172
|
+
}
|
|
173
|
+
cleanup(aside);
|
|
174
|
+
pass('collapse() sets state to collapsed');
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
'hide() should set state to offscreen': async ({pass, fail}) => {
|
|
178
|
+
const aside = await createAside();
|
|
179
|
+
aside.hide();
|
|
180
|
+
await aside.updateComplete;
|
|
181
|
+
if(aside.state !== 'offscreen') {
|
|
182
|
+
cleanup(aside);
|
|
183
|
+
return fail(`Expected "offscreen", got "${aside.state}"`);
|
|
184
|
+
}
|
|
185
|
+
cleanup(aside);
|
|
186
|
+
pass('hide() sets state to offscreen');
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
'toggle() should switch between expanded and offscreen': async ({pass, fail}) => {
|
|
190
|
+
const aside = await createAside({ state: 'offscreen' });
|
|
191
|
+
aside.toggle();
|
|
192
|
+
await aside.updateComplete;
|
|
193
|
+
if(aside.state !== 'expanded') {
|
|
194
|
+
cleanup(aside);
|
|
195
|
+
return fail(`Expected "expanded" after first toggle, got "${aside.state}"`);
|
|
196
|
+
}
|
|
197
|
+
aside.toggle();
|
|
198
|
+
await aside.updateComplete;
|
|
199
|
+
if(aside.state !== 'offscreen') {
|
|
200
|
+
cleanup(aside);
|
|
201
|
+
return fail(`Expected "offscreen" after second toggle, got "${aside.state}"`);
|
|
202
|
+
}
|
|
203
|
+
cleanup(aside);
|
|
204
|
+
pass('toggle() switches between expanded and offscreen');
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
/*
|
|
208
|
+
Events
|
|
209
|
+
*/
|
|
210
|
+
'should dispatch aside_state_change on element': async ({pass, fail}) => {
|
|
211
|
+
const aside = await createAside({ state: 'offscreen' });
|
|
212
|
+
let received = null;
|
|
213
|
+
aside.addEventListener('aside_state_change', e => { received = e.detail; });
|
|
214
|
+
aside.expand();
|
|
215
|
+
await aside.updateComplete;
|
|
216
|
+
if(!received) {
|
|
217
|
+
cleanup(aside);
|
|
218
|
+
return fail('Event not dispatched on element');
|
|
219
|
+
}
|
|
220
|
+
if(received.state !== 'expanded' || received.aside !== aside) {
|
|
221
|
+
cleanup(aside);
|
|
222
|
+
return fail('Event detail incorrect');
|
|
223
|
+
}
|
|
224
|
+
cleanup(aside);
|
|
225
|
+
pass('Dispatches aside_state_change on element');
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
'should dispatch aside_state_change on window': async ({pass, fail}) => {
|
|
229
|
+
const aside = await createAside({ state: 'offscreen' });
|
|
230
|
+
let received = null;
|
|
231
|
+
const handler = e => { received = e.detail; };
|
|
232
|
+
window.addEventListener('aside_state_change', handler);
|
|
233
|
+
aside.expand();
|
|
234
|
+
await aside.updateComplete;
|
|
235
|
+
window.removeEventListener('aside_state_change', handler);
|
|
236
|
+
if(!received) {
|
|
237
|
+
cleanup(aside);
|
|
238
|
+
return fail('Event not dispatched on window');
|
|
239
|
+
}
|
|
240
|
+
if(received.state !== 'expanded' || received.main !== 'overlay') {
|
|
241
|
+
cleanup(aside);
|
|
242
|
+
return fail('Window event detail incorrect');
|
|
243
|
+
}
|
|
244
|
+
cleanup(aside);
|
|
245
|
+
pass('Dispatches aside_state_change on window');
|
|
246
|
+
},
|
|
247
|
+
|
|
248
|
+
'event detail should include aside, state, main': async ({pass, fail}) => {
|
|
249
|
+
const aside = await createAside({ state: 'offscreen', main: 'overlay' });
|
|
250
|
+
let received = null;
|
|
251
|
+
aside.addEventListener('aside_state_change', e => { received = e.detail; });
|
|
252
|
+
aside.expand();
|
|
253
|
+
await aside.updateComplete;
|
|
254
|
+
if(!received || received.aside !== aside || received.state !== 'expanded' || received.main !== 'overlay') {
|
|
255
|
+
cleanup(aside);
|
|
256
|
+
return fail('Event detail should have aside, state, and main');
|
|
257
|
+
}
|
|
258
|
+
cleanup(aside);
|
|
259
|
+
pass('Event detail includes aside, state, main');
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
/*
|
|
263
|
+
Push Mode Rendering
|
|
264
|
+
*/
|
|
265
|
+
'push mode should render aside element': async ({pass, fail}) => {
|
|
266
|
+
const aside = await createAside({ main: 'push' });
|
|
267
|
+
const el = aside.shadowRoot.querySelector('aside');
|
|
268
|
+
if(!el) {
|
|
269
|
+
cleanup(aside);
|
|
270
|
+
return fail('Should render aside element in push mode');
|
|
271
|
+
}
|
|
272
|
+
cleanup(aside);
|
|
273
|
+
pass('Push mode renders aside element');
|
|
274
|
+
},
|
|
275
|
+
|
|
276
|
+
'push mode should not render focus capture': async ({pass, fail}) => {
|
|
277
|
+
const aside = await createAside({ main: 'push' });
|
|
278
|
+
const fc = aside.shadowRoot.querySelector('k-focus-capture');
|
|
279
|
+
if(fc) {
|
|
280
|
+
cleanup(aside);
|
|
281
|
+
return fail('Push mode should not have focus capture');
|
|
282
|
+
}
|
|
283
|
+
cleanup(aside);
|
|
284
|
+
pass('Push mode does not render focus capture');
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
/*
|
|
288
|
+
Overlay Mode Rendering
|
|
289
|
+
*/
|
|
290
|
+
'overlay mode should render focus capture': async ({pass, fail}) => {
|
|
291
|
+
const aside = await createAside({ main: 'overlay', state: 'offscreen' });
|
|
292
|
+
aside.expand();
|
|
293
|
+
await aside.updateComplete;
|
|
294
|
+
const fc = aside.shadowRoot.querySelector('k-focus-capture');
|
|
295
|
+
if(!fc) {
|
|
296
|
+
cleanup(aside);
|
|
297
|
+
return fail('Overlay mode should have focus capture');
|
|
298
|
+
}
|
|
299
|
+
cleanup(aside);
|
|
300
|
+
pass('Overlay mode renders focus capture');
|
|
301
|
+
},
|
|
302
|
+
|
|
303
|
+
'overlay mode should render overlay button': async ({pass, fail}) => {
|
|
304
|
+
const aside = await createAside({ main: 'overlay', state: 'offscreen' });
|
|
305
|
+
aside.expand();
|
|
306
|
+
await aside.updateComplete;
|
|
307
|
+
const btn = aside.shadowRoot.querySelector('#overlay-btn');
|
|
308
|
+
if(!btn) {
|
|
309
|
+
cleanup(aside);
|
|
310
|
+
return fail('Overlay mode should have overlay button');
|
|
311
|
+
}
|
|
312
|
+
cleanup(aside);
|
|
313
|
+
pass('Overlay mode renders overlay button');
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
'overlay mode should add no-scroll to body when expanded': async ({pass, fail}) => {
|
|
317
|
+
const aside = await createAside({ main: 'overlay', state: 'offscreen' });
|
|
318
|
+
aside.expand();
|
|
319
|
+
await aside.updateComplete;
|
|
320
|
+
if(!document.body.classList.contains('no-scroll')) {
|
|
321
|
+
cleanup(aside);
|
|
322
|
+
return fail('Should add no-scroll to body');
|
|
323
|
+
}
|
|
324
|
+
cleanup(aside);
|
|
325
|
+
pass('Overlay mode adds no-scroll when expanded');
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
'overlay mode should remove no-scroll when hidden': async ({pass, fail}) => {
|
|
329
|
+
const aside = await createAside({ main: 'overlay', state: 'offscreen' });
|
|
330
|
+
aside.expand();
|
|
331
|
+
await aside.updateComplete;
|
|
332
|
+
aside.hide();
|
|
333
|
+
await aside.updateComplete;
|
|
334
|
+
if(document.body.classList.contains('no-scroll')) {
|
|
335
|
+
cleanup(aside);
|
|
336
|
+
return fail('Should remove no-scroll from body');
|
|
337
|
+
}
|
|
338
|
+
cleanup(aside);
|
|
339
|
+
pass('Overlay mode removes no-scroll when hidden');
|
|
340
|
+
},
|
|
341
|
+
|
|
342
|
+
/*
|
|
343
|
+
Overlay Close Behavior
|
|
344
|
+
*/
|
|
345
|
+
'clicking overlay should close when overlayClose is true': async ({pass, fail}) => {
|
|
346
|
+
const aside = await createAside({ main: 'overlay', state: 'offscreen' });
|
|
347
|
+
aside.expand();
|
|
348
|
+
await aside.updateComplete;
|
|
349
|
+
const btn = aside.shadowRoot.querySelector('#overlay-btn');
|
|
350
|
+
btn.click();
|
|
351
|
+
await aside.updateComplete;
|
|
352
|
+
if(aside.state !== 'offscreen') {
|
|
353
|
+
cleanup(aside);
|
|
354
|
+
return fail('Should hide on overlay click');
|
|
355
|
+
}
|
|
356
|
+
cleanup(aside);
|
|
357
|
+
pass('Clicking overlay closes when overlayClose is true');
|
|
358
|
+
},
|
|
359
|
+
|
|
360
|
+
'clicking overlay should not close when overlayClose is false': async ({pass, fail}) => {
|
|
361
|
+
const aside = await createAside({ main: 'overlay', state: 'offscreen', overlayClose: false });
|
|
362
|
+
aside.expand();
|
|
363
|
+
await aside.updateComplete;
|
|
364
|
+
const btn = aside.shadowRoot.querySelector('#overlay-btn');
|
|
365
|
+
btn.click();
|
|
366
|
+
await aside.updateComplete;
|
|
367
|
+
if(aside.state !== 'expanded') {
|
|
368
|
+
cleanup(aside);
|
|
369
|
+
return fail('Should not hide on overlay click when overlayClose is false');
|
|
370
|
+
}
|
|
371
|
+
cleanup(aside);
|
|
372
|
+
pass('Clicking overlay does not close when overlayClose is false');
|
|
373
|
+
},
|
|
374
|
+
|
|
375
|
+
/*
|
|
376
|
+
ESC Key Close Behavior
|
|
377
|
+
*/
|
|
378
|
+
'ESC should close when escClose is true': async ({pass, fail}) => {
|
|
379
|
+
const aside = await createAside({ main: 'overlay', state: 'offscreen' });
|
|
380
|
+
aside.expand();
|
|
381
|
+
await aside.updateComplete;
|
|
382
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
|
|
383
|
+
await aside.updateComplete;
|
|
384
|
+
if(aside.state !== 'offscreen') {
|
|
385
|
+
cleanup(aside);
|
|
386
|
+
return fail('Should hide on ESC');
|
|
387
|
+
}
|
|
388
|
+
cleanup(aside);
|
|
389
|
+
pass('ESC closes when escClose is true');
|
|
390
|
+
},
|
|
391
|
+
|
|
392
|
+
'ESC should not close when escClose is false': async ({pass, fail}) => {
|
|
393
|
+
const aside = await createAside({ main: 'overlay', state: 'offscreen', escClose: false });
|
|
394
|
+
aside.expand();
|
|
395
|
+
await aside.updateComplete;
|
|
396
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
|
|
397
|
+
await aside.updateComplete;
|
|
398
|
+
if(aside.state !== 'expanded') {
|
|
399
|
+
cleanup(aside);
|
|
400
|
+
return fail('Should not hide on ESC when escClose is false');
|
|
401
|
+
}
|
|
402
|
+
cleanup(aside);
|
|
403
|
+
pass('ESC does not close when escClose is false');
|
|
404
|
+
},
|
|
405
|
+
|
|
406
|
+
/*
|
|
407
|
+
Side Configuration
|
|
408
|
+
*/
|
|
409
|
+
'should support right side': async ({pass, fail}) => {
|
|
410
|
+
const aside = await createAside({ side: 'right' });
|
|
411
|
+
if(aside.getAttribute('side') !== 'right') {
|
|
412
|
+
cleanup(aside);
|
|
413
|
+
return fail('Side attribute not set to right');
|
|
414
|
+
}
|
|
415
|
+
const el = aside.shadowRoot.querySelector('aside');
|
|
416
|
+
if(!el) {
|
|
417
|
+
cleanup(aside);
|
|
418
|
+
return fail('Should render aside element');
|
|
419
|
+
}
|
|
420
|
+
cleanup(aside);
|
|
421
|
+
pass('Supports right side');
|
|
422
|
+
},
|
|
423
|
+
|
|
424
|
+
/*
|
|
425
|
+
Cleanup on Disconnect
|
|
426
|
+
*/
|
|
427
|
+
'should remove no-scroll on disconnect': async ({pass, fail}) => {
|
|
428
|
+
const aside = await createAside({ main: 'overlay', state: 'offscreen' });
|
|
429
|
+
aside.expand();
|
|
430
|
+
await aside.updateComplete;
|
|
431
|
+
if(!document.body.classList.contains('no-scroll')) {
|
|
432
|
+
cleanup(aside);
|
|
433
|
+
return fail('Should have no-scroll before disconnect');
|
|
434
|
+
}
|
|
435
|
+
aside.parentNode.removeChild(aside);
|
|
436
|
+
if(document.body.classList.contains('no-scroll')) {
|
|
437
|
+
return fail('Should remove no-scroll on disconnect');
|
|
438
|
+
}
|
|
439
|
+
pass('Removes no-scroll on disconnect');
|
|
440
|
+
},
|
|
441
|
+
|
|
442
|
+
'should dispatch offscreen event on disconnect': async ({pass, fail}) => {
|
|
443
|
+
const aside = await createAside();
|
|
444
|
+
let received = null;
|
|
445
|
+
const handler = e => { received = e.detail; };
|
|
446
|
+
window.addEventListener('aside_state_change', handler);
|
|
447
|
+
aside.parentNode.removeChild(aside);
|
|
448
|
+
window.removeEventListener('aside_state_change', handler);
|
|
449
|
+
if(!received || received.state !== 'offscreen') {
|
|
450
|
+
return fail('Should dispatch offscreen event on disconnect');
|
|
451
|
+
}
|
|
452
|
+
pass('Dispatches offscreen event on disconnect');
|
|
453
|
+
},
|
|
454
|
+
|
|
455
|
+
/*
|
|
456
|
+
AsideItem
|
|
457
|
+
*/
|
|
458
|
+
'AsideItem: should create element': async ({pass, fail}) => {
|
|
459
|
+
const aside = await createAside({ main: 'push' });
|
|
460
|
+
const item = document.createElement('k-aside-item');
|
|
461
|
+
item.icon = 'cards';
|
|
462
|
+
item.href = '#test';
|
|
463
|
+
item.textContent = 'Test';
|
|
464
|
+
aside.appendChild(item);
|
|
465
|
+
await item.updateComplete;
|
|
466
|
+
if(!item.shadowRoot) {
|
|
467
|
+
cleanup(aside);
|
|
468
|
+
return fail('Should have shadow root');
|
|
469
|
+
}
|
|
470
|
+
cleanup(aside);
|
|
471
|
+
pass('AsideItem created');
|
|
472
|
+
},
|
|
473
|
+
|
|
474
|
+
'AsideItem: should default collapsed to false': async ({pass, fail}) => {
|
|
475
|
+
const aside = await createAside({ main: 'push' });
|
|
476
|
+
const item = document.createElement('k-aside-item');
|
|
477
|
+
aside.appendChild(item);
|
|
478
|
+
await item.updateComplete;
|
|
479
|
+
if(item.collapsed !== false) {
|
|
480
|
+
cleanup(aside);
|
|
481
|
+
return fail(`Expected collapsed false, got ${item.collapsed}`);
|
|
482
|
+
}
|
|
483
|
+
cleanup(aside);
|
|
484
|
+
pass('AsideItem defaults collapsed to false');
|
|
485
|
+
},
|
|
486
|
+
|
|
487
|
+
'AsideItem: should sync collapsed when aside collapses': async ({pass, fail}) => {
|
|
488
|
+
const aside = await createAside({ main: 'push' });
|
|
489
|
+
const item = document.createElement('k-aside-item');
|
|
490
|
+
aside.appendChild(item);
|
|
491
|
+
await item.updateComplete;
|
|
492
|
+
aside.collapse();
|
|
493
|
+
await aside.updateComplete;
|
|
494
|
+
await item.updateComplete;
|
|
495
|
+
if(!item.collapsed) {
|
|
496
|
+
cleanup(aside);
|
|
497
|
+
return fail('Item should be collapsed');
|
|
498
|
+
}
|
|
499
|
+
cleanup(aside);
|
|
500
|
+
pass('AsideItem syncs collapsed');
|
|
501
|
+
},
|
|
502
|
+
|
|
503
|
+
'AsideItem: should sync expanded when aside expands': async ({pass, fail}) => {
|
|
504
|
+
const aside = await createAside({ main: 'push', state: 'collapsed' });
|
|
505
|
+
const item = document.createElement('k-aside-item');
|
|
506
|
+
aside.appendChild(item);
|
|
507
|
+
await item.updateComplete;
|
|
508
|
+
aside.expand();
|
|
509
|
+
await aside.updateComplete;
|
|
510
|
+
await item.updateComplete;
|
|
511
|
+
if(item.collapsed) {
|
|
512
|
+
cleanup(aside);
|
|
513
|
+
return fail('Item should not be collapsed');
|
|
514
|
+
}
|
|
515
|
+
cleanup(aside);
|
|
516
|
+
pass('AsideItem syncs expanded');
|
|
517
|
+
},
|
|
518
|
+
|
|
519
|
+
'AsideItem: should render link with icon': async ({pass, fail}) => {
|
|
520
|
+
const aside = await createAside({ main: 'push' });
|
|
521
|
+
const item = document.createElement('k-aside-item');
|
|
522
|
+
item.icon = 'cards';
|
|
523
|
+
item.href = '#test';
|
|
524
|
+
item.textContent = 'Dashboard';
|
|
525
|
+
aside.appendChild(item);
|
|
526
|
+
await item.updateComplete;
|
|
527
|
+
const link = item.shadowRoot.querySelector('a');
|
|
528
|
+
const icon = item.shadowRoot.querySelector('k-icon');
|
|
529
|
+
if(!link || link.getAttribute('href') !== '#test') {
|
|
530
|
+
cleanup(aside);
|
|
531
|
+
return fail('Should render link with href');
|
|
532
|
+
}
|
|
533
|
+
if(!icon || icon.getAttribute('name') !== 'cards') {
|
|
534
|
+
cleanup(aside);
|
|
535
|
+
return fail('Should render icon');
|
|
536
|
+
}
|
|
537
|
+
cleanup(aside);
|
|
538
|
+
pass('AsideItem renders link with icon');
|
|
539
|
+
},
|
|
540
|
+
|
|
541
|
+
'AsideItem: should hide label when collapsed': async ({pass, fail}) => {
|
|
542
|
+
const aside = await createAside({ main: 'push' });
|
|
543
|
+
const item = document.createElement('k-aside-item');
|
|
544
|
+
item.icon = 'cards';
|
|
545
|
+
item.textContent = 'Dashboard';
|
|
546
|
+
aside.appendChild(item);
|
|
547
|
+
await item.updateComplete;
|
|
548
|
+
aside.collapse();
|
|
549
|
+
await aside.updateComplete;
|
|
550
|
+
await item.updateComplete;
|
|
551
|
+
const label = item.shadowRoot.querySelector('.label');
|
|
552
|
+
if(label) {
|
|
553
|
+
cleanup(aside);
|
|
554
|
+
return fail('Label should not render when collapsed');
|
|
555
|
+
}
|
|
556
|
+
cleanup(aside);
|
|
557
|
+
pass('AsideItem hides label when collapsed');
|
|
558
|
+
},
|
|
559
|
+
|
|
560
|
+
'AsideItem: clicking collapsed item should expand aside': async ({pass, fail}) => {
|
|
561
|
+
const aside = await createAside({ main: 'push', state: 'collapsed' });
|
|
562
|
+
const item = document.createElement('k-aside-item');
|
|
563
|
+
item.icon = 'cards';
|
|
564
|
+
aside.appendChild(item);
|
|
565
|
+
await item.updateComplete;
|
|
566
|
+
item.collapsed = true;
|
|
567
|
+
await item.updateComplete;
|
|
568
|
+
const link = item.shadowRoot.querySelector('a');
|
|
569
|
+
link.click();
|
|
570
|
+
await aside.updateComplete;
|
|
571
|
+
if(aside.state !== 'expanded') {
|
|
572
|
+
cleanup(aside);
|
|
573
|
+
return fail('Aside should expand on collapsed item click');
|
|
574
|
+
}
|
|
575
|
+
cleanup(aside);
|
|
576
|
+
pass('Clicking collapsed item expands aside');
|
|
577
|
+
},
|
|
578
|
+
|
|
579
|
+
'AsideItem: no-expand should prevent expand on click': async ({pass, fail}) => {
|
|
580
|
+
const aside = await createAside({ main: 'push', state: 'collapsed' });
|
|
581
|
+
const item = document.createElement('k-aside-item');
|
|
582
|
+
item.icon = 'cards';
|
|
583
|
+
item['no-expand'] = true;
|
|
584
|
+
aside.appendChild(item);
|
|
585
|
+
await item.updateComplete;
|
|
586
|
+
item.collapsed = true;
|
|
587
|
+
await item.updateComplete;
|
|
588
|
+
const link = item.shadowRoot.querySelector('a');
|
|
589
|
+
link.click();
|
|
590
|
+
await aside.updateComplete;
|
|
591
|
+
if(aside.state !== 'collapsed') {
|
|
592
|
+
cleanup(aside);
|
|
593
|
+
return fail('Aside should stay collapsed with no-expand');
|
|
594
|
+
}
|
|
595
|
+
cleanup(aside);
|
|
596
|
+
pass('no-expand prevents expand');
|
|
597
|
+
},
|
|
598
|
+
|
|
599
|
+
'AsideItem: hide-when-collapsed should hide item': async ({pass, fail}) => {
|
|
600
|
+
const aside = await createAside({ main: 'push' });
|
|
601
|
+
const item = document.createElement('k-aside-item');
|
|
602
|
+
item.setAttribute('hide-when-collapsed', '');
|
|
603
|
+
item.icon = 'cards';
|
|
604
|
+
aside.appendChild(item);
|
|
605
|
+
await item.updateComplete;
|
|
606
|
+
aside.collapse();
|
|
607
|
+
await aside.updateComplete;
|
|
608
|
+
await item.updateComplete;
|
|
609
|
+
await new Promise(r => requestAnimationFrame(r));
|
|
610
|
+
const display = getComputedStyle(item).display;
|
|
611
|
+
if(display !== 'none') {
|
|
612
|
+
cleanup(aside);
|
|
613
|
+
return fail(`Expected display none, got ${display}`);
|
|
614
|
+
}
|
|
615
|
+
cleanup(aside);
|
|
616
|
+
pass('hide-when-collapsed hides item');
|
|
617
|
+
},
|
|
618
|
+
|
|
619
|
+
/*
|
|
620
|
+
AsideLabel
|
|
621
|
+
*/
|
|
622
|
+
'AsideLabel: should render text when expanded': async ({pass, fail}) => {
|
|
623
|
+
const aside = await createAside({ main: 'push' });
|
|
624
|
+
const label = document.createElement('k-aside-label');
|
|
625
|
+
label.textContent = 'Projects';
|
|
626
|
+
aside.appendChild(label);
|
|
627
|
+
await label.updateComplete;
|
|
628
|
+
const div = label.shadowRoot.querySelector('.label');
|
|
629
|
+
if(!div) {
|
|
630
|
+
cleanup(aside);
|
|
631
|
+
return fail('Should render label div when expanded');
|
|
632
|
+
}
|
|
633
|
+
cleanup(aside);
|
|
634
|
+
pass('AsideLabel renders text when expanded');
|
|
635
|
+
},
|
|
636
|
+
|
|
637
|
+
'AsideLabel: should render hr when collapsed': async ({pass, fail}) => {
|
|
638
|
+
const aside = await createAside({ main: 'push' });
|
|
639
|
+
const label = document.createElement('k-aside-label');
|
|
640
|
+
label.textContent = 'Projects';
|
|
641
|
+
aside.appendChild(label);
|
|
642
|
+
await label.updateComplete;
|
|
643
|
+
aside.collapse();
|
|
644
|
+
await aside.updateComplete;
|
|
645
|
+
await label.updateComplete;
|
|
646
|
+
const hr = label.shadowRoot.querySelector('hr');
|
|
647
|
+
const div = label.shadowRoot.querySelector('.label');
|
|
648
|
+
if(!hr) {
|
|
649
|
+
cleanup(aside);
|
|
650
|
+
return fail('Should render hr when collapsed');
|
|
651
|
+
}
|
|
652
|
+
if(div) {
|
|
653
|
+
cleanup(aside);
|
|
654
|
+
return fail('Should not render label div when collapsed');
|
|
655
|
+
}
|
|
656
|
+
cleanup(aside);
|
|
657
|
+
pass('AsideLabel renders hr when collapsed');
|
|
658
|
+
},
|
|
659
|
+
|
|
660
|
+
/*
|
|
661
|
+
AsideMenu
|
|
662
|
+
*/
|
|
663
|
+
'AsideMenu: should create with properties': async ({pass, fail}) => {
|
|
664
|
+
const aside = await createAside({ main: 'push' });
|
|
665
|
+
const menu = document.createElement('k-aside-menu');
|
|
666
|
+
menu.icon = 'folder';
|
|
667
|
+
menu.label = 'Projects';
|
|
668
|
+
aside.appendChild(menu);
|
|
669
|
+
await menu.updateComplete;
|
|
670
|
+
if(menu.icon !== 'folder' || menu.label !== 'Projects' || menu.open !== false) {
|
|
671
|
+
cleanup(aside);
|
|
672
|
+
return fail('Properties not set correctly');
|
|
673
|
+
}
|
|
674
|
+
cleanup(aside);
|
|
675
|
+
pass('AsideMenu properties correct');
|
|
676
|
+
},
|
|
677
|
+
|
|
678
|
+
'AsideMenu: should toggle open on click': async ({pass, fail}) => {
|
|
679
|
+
const aside = await createAside({ main: 'push' });
|
|
680
|
+
const menu = document.createElement('k-aside-menu');
|
|
681
|
+
menu.icon = 'folder';
|
|
682
|
+
menu.label = 'Projects';
|
|
683
|
+
aside.appendChild(menu);
|
|
684
|
+
await menu.updateComplete;
|
|
685
|
+
const btn = menu.shadowRoot.querySelector('button');
|
|
686
|
+
btn.click();
|
|
687
|
+
await menu.updateComplete;
|
|
688
|
+
if(!menu.open) {
|
|
689
|
+
cleanup(aside);
|
|
690
|
+
return fail('Menu should be open after click');
|
|
691
|
+
}
|
|
692
|
+
btn.click();
|
|
693
|
+
await menu.updateComplete;
|
|
694
|
+
if(menu.open) {
|
|
695
|
+
cleanup(aside);
|
|
696
|
+
return fail('Menu should be closed after second click');
|
|
697
|
+
}
|
|
698
|
+
cleanup(aside);
|
|
699
|
+
pass('AsideMenu toggles open');
|
|
700
|
+
},
|
|
701
|
+
|
|
702
|
+
'AsideMenu: should close and collapse when aside collapses': async ({pass, fail}) => {
|
|
703
|
+
const aside = await createAside({ main: 'push' });
|
|
704
|
+
const menu = document.createElement('k-aside-menu');
|
|
705
|
+
menu.icon = 'folder';
|
|
706
|
+
menu.label = 'Projects';
|
|
707
|
+
aside.appendChild(menu);
|
|
708
|
+
await menu.updateComplete;
|
|
709
|
+
menu.open = true;
|
|
710
|
+
await menu.updateComplete;
|
|
711
|
+
aside.collapse();
|
|
712
|
+
await aside.updateComplete;
|
|
713
|
+
await menu.updateComplete;
|
|
714
|
+
if(!menu.collapsed || menu.open) {
|
|
715
|
+
cleanup(aside);
|
|
716
|
+
return fail('Menu should be collapsed and closed');
|
|
717
|
+
}
|
|
718
|
+
cleanup(aside);
|
|
719
|
+
pass('AsideMenu collapses and closes');
|
|
720
|
+
},
|
|
721
|
+
|
|
722
|
+
'AsideMenu: clicking collapsed menu should expand aside': async ({pass, fail}) => {
|
|
723
|
+
const aside = await createAside({ main: 'push', state: 'collapsed' });
|
|
724
|
+
const menu = document.createElement('k-aside-menu');
|
|
725
|
+
menu.icon = 'folder';
|
|
726
|
+
menu.label = 'Projects';
|
|
727
|
+
aside.appendChild(menu);
|
|
728
|
+
await menu.updateComplete;
|
|
729
|
+
menu.collapsed = true;
|
|
730
|
+
await menu.updateComplete;
|
|
731
|
+
const btn = menu.shadowRoot.querySelector('button');
|
|
732
|
+
btn.click();
|
|
733
|
+
await aside.updateComplete;
|
|
734
|
+
if(aside.state !== 'expanded') {
|
|
735
|
+
cleanup(aside);
|
|
736
|
+
return fail('Aside should expand on collapsed menu click');
|
|
737
|
+
}
|
|
738
|
+
cleanup(aside);
|
|
739
|
+
pass('Clicking collapsed menu expands aside');
|
|
740
|
+
},
|
|
741
|
+
|
|
742
|
+
/*
|
|
743
|
+
AsideSpacer
|
|
744
|
+
*/
|
|
745
|
+
'AsideSpacer: should render with flex 1': async ({pass, fail}) => {
|
|
746
|
+
const aside = await createAside({ main: 'push' });
|
|
747
|
+
const spacer = document.createElement('k-aside-spacer');
|
|
748
|
+
aside.appendChild(spacer);
|
|
749
|
+
await spacer.updateComplete;
|
|
750
|
+
const flex = getComputedStyle(spacer).flexGrow;
|
|
751
|
+
if(flex !== '1') {
|
|
752
|
+
cleanup(aside);
|
|
753
|
+
return fail(`Expected flex-grow 1, got ${flex}`);
|
|
754
|
+
}
|
|
755
|
+
cleanup(aside);
|
|
756
|
+
pass('AsideSpacer has flex-grow 1');
|
|
757
|
+
},
|
|
758
|
+
|
|
759
|
+
/*
|
|
760
|
+
Cleanup
|
|
761
|
+
*/
|
|
762
|
+
'AsideItem: should remove listener on disconnect': async ({pass, fail}) => {
|
|
763
|
+
const aside = await createAside({ main: 'push' });
|
|
764
|
+
const item = document.createElement('k-aside-item');
|
|
765
|
+
item.icon = 'cards';
|
|
766
|
+
aside.appendChild(item);
|
|
767
|
+
await item.updateComplete;
|
|
768
|
+
aside.removeChild(item);
|
|
769
|
+
aside.collapse();
|
|
770
|
+
await aside.updateComplete;
|
|
771
|
+
if(item.collapsed) {
|
|
772
|
+
cleanup(aside);
|
|
773
|
+
return fail('Disconnected item should not react to aside changes');
|
|
774
|
+
}
|
|
775
|
+
cleanup(aside);
|
|
776
|
+
pass('AsideItem removes listener on disconnect');
|
|
777
|
+
}
|
|
778
|
+
};
|