@omicronenergy/oscd-shell 0.0.13 → 0.0.15
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/dist/landing-page/landing-page.js +3 -2
- package/dist/landing-page/landing-page.js.map +1 -1
- package/dist/landing-page/landing-page.spec.js +4 -2
- package/dist/landing-page/landing-page.spec.js.map +1 -1
- package/dist/locales/de.d.ts +8 -0
- package/dist/locales/de.js +9 -0
- package/dist/locales/de.js.map +1 -1
- package/dist/menus/files-menu.js +4 -5
- package/dist/menus/files-menu.js.map +1 -1
- package/dist/menus/plugins-menu.d.ts +6 -3
- package/dist/menus/plugins-menu.js +40 -6
- package/dist/menus/plugins-menu.js.map +1 -1
- package/dist/menus/plugins-menu.spec.js +1 -0
- package/dist/menus/plugins-menu.spec.js.map +1 -1
- package/dist/oscd-shell-design-tokens.js +139 -64
- package/dist/oscd-shell-design-tokens.js.map +1 -1
- package/dist/oscd-shell.d.ts +31 -22
- package/dist/oscd-shell.js +136 -73
- package/dist/oscd-shell.js.map +1 -1
- package/dist/side-panel/build-tree-nodes.spec.d.ts +1 -0
- package/dist/side-panel/build-tree-nodes.spec.js +50 -0
- package/dist/side-panel/build-tree-nodes.spec.js.map +1 -0
- package/dist/side-panel/editor-plugins-panel.d.ts +79 -7
- package/dist/side-panel/editor-plugins-panel.js +829 -92
- package/dist/side-panel/editor-plugins-panel.js.map +1 -1
- package/dist/side-panel/editor-plugins-panel.spec.js +469 -24
- package/dist/side-panel/editor-plugins-panel.spec.js.map +1 -1
- package/dist/utils/plugin-utils.d.ts +29 -5
- package/dist/utils/plugin-utils.js +73 -14
- package/dist/utils/plugin-utils.js.map +1 -1
- package/dist/utils/plugin-utils.spec.js +113 -4
- package/dist/utils/plugin-utils.spec.js.map +1 -1
- package/dist/utils/testing/plugin-helpers.d.ts +7 -3
- package/dist/utils/testing/plugin-helpers.js +21 -9
- package/dist/utils/testing/plugin-helpers.js.map +1 -1
- package/package.json +37 -38
|
@@ -3,8 +3,30 @@ import '../oscd-shell.js';
|
|
|
3
3
|
import { createTestDocs } from '../utils/testing/test-doc-helpers.js';
|
|
4
4
|
import { sampleEditorPlugins } from '../utils/testing/plugin-helpers.js';
|
|
5
5
|
import { TestMenuPlugin1 } from '../utils/testing/test-plugins.js';
|
|
6
|
+
import { OscdOutlinedSearchField } from '@omicronenergy/oscd-ui/search-field/OscdOutlinedSearchField.js';
|
|
7
|
+
import sinon from 'sinon';
|
|
8
|
+
// A grouped editor fixture, used to exercise the collapsed rail's group
|
|
9
|
+
// flyout (rendered only when `editors` contains a `PluginGroup`).
|
|
10
|
+
const groupedEditorPlugins = [
|
|
11
|
+
{
|
|
12
|
+
name: 'Grouped Editors',
|
|
13
|
+
icon: 'folder',
|
|
14
|
+
plugins: [
|
|
15
|
+
{
|
|
16
|
+
name: 'Grouped Editor 1',
|
|
17
|
+
tagName: 'test-grouped-editor-1',
|
|
18
|
+
icon: 'coronavirus',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'Grouped Editor 2',
|
|
22
|
+
tagName: 'test-grouped-editor-2',
|
|
23
|
+
icon: 'coronavirus',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
];
|
|
6
28
|
const findPanelToggleButton = (pluginsMenu) => {
|
|
7
|
-
const toggleButton = pluginsMenu.shadowRoot?.querySelector('
|
|
29
|
+
const toggleButton = pluginsMenu.shadowRoot?.querySelector('.toggle-button');
|
|
8
30
|
expect(toggleButton).to.exist;
|
|
9
31
|
return toggleButton;
|
|
10
32
|
};
|
|
@@ -15,6 +37,27 @@ describe('editor-plugins-panel', () => {
|
|
|
15
37
|
let oscdShell;
|
|
16
38
|
let editorPluginsPanel;
|
|
17
39
|
let docs;
|
|
40
|
+
const extraShells = [];
|
|
41
|
+
const LS_KEYS = {
|
|
42
|
+
expanded: 'editor-plugins-panel:expanded',
|
|
43
|
+
expandedIds: 'editor-plugins-panel:expandedIds',
|
|
44
|
+
pinnedPluginIds: 'editor-plugins-panel:pinnedPluginIds',
|
|
45
|
+
pinnedExpanded: 'editor-plugins-panel:pinnedExpanded',
|
|
46
|
+
};
|
|
47
|
+
// Mounts a brand-new shell + panel, simulating a page reload. Any pre-seeded
|
|
48
|
+
// localStorage is therefore read by a freshly constructed panel.
|
|
49
|
+
const mountFreshPanel = async () => {
|
|
50
|
+
const shell = (await fixture(html `<oscd-shell
|
|
51
|
+
.docs=${docs}
|
|
52
|
+
docName=${Object.keys(docs)[0]}
|
|
53
|
+
></oscd-shell>`));
|
|
54
|
+
shell.plugins = { editor: sampleEditorPlugins };
|
|
55
|
+
const panel = shell.shadowRoot.querySelector('editor-plugins-panel');
|
|
56
|
+
await shell.updateComplete;
|
|
57
|
+
await panel.updateComplete;
|
|
58
|
+
extraShells.push(shell);
|
|
59
|
+
return panel;
|
|
60
|
+
};
|
|
18
61
|
beforeEach(async () => {
|
|
19
62
|
docs = createTestDocs(1);
|
|
20
63
|
oscdShell = (await fixture(html `<oscd-shell
|
|
@@ -33,7 +76,10 @@ describe('editor-plugins-panel', () => {
|
|
|
33
76
|
});
|
|
34
77
|
afterEach(() => {
|
|
35
78
|
oscdShell.remove();
|
|
36
|
-
|
|
79
|
+
while (extraShells.length) {
|
|
80
|
+
extraShells.pop().remove();
|
|
81
|
+
}
|
|
82
|
+
Object.values(LS_KEYS).forEach(key => localStorage.removeItem(key));
|
|
37
83
|
});
|
|
38
84
|
it('collapses on toggle button click when already expanded ', async () => {
|
|
39
85
|
const toggleButton = findPanelToggleButton(editorPluginsPanel);
|
|
@@ -43,12 +89,11 @@ describe('editor-plugins-panel', () => {
|
|
|
43
89
|
expect(isPanelExpanded(editorPluginsPanel)).to.be.false;
|
|
44
90
|
});
|
|
45
91
|
it('expands on toggle button click when already collapsed', async () => {
|
|
46
|
-
|
|
47
|
-
expect(isPanelExpanded(editorPluginsPanel)).to.be.true;
|
|
48
|
-
toggleButton.click();
|
|
92
|
+
findPanelToggleButton(editorPluginsPanel).click();
|
|
49
93
|
await editorPluginsPanel.updateComplete;
|
|
50
94
|
expect(isPanelExpanded(editorPluginsPanel)).to.be.false;
|
|
51
|
-
|
|
95
|
+
// The control is a different element in the collapsed rail, so re-query it.
|
|
96
|
+
findPanelToggleButton(editorPluginsPanel).click();
|
|
52
97
|
await editorPluginsPanel.updateComplete;
|
|
53
98
|
expect(isPanelExpanded(editorPluginsPanel)).to.be.true;
|
|
54
99
|
});
|
|
@@ -56,32 +101,432 @@ describe('editor-plugins-panel', () => {
|
|
|
56
101
|
expect(isPanelExpanded(editorPluginsPanel)).to.be.true;
|
|
57
102
|
});
|
|
58
103
|
it('uses value stored in localstorage initially', async () => {
|
|
59
|
-
localStorage.setItem(
|
|
60
|
-
const
|
|
61
|
-
.docs=${docs}
|
|
62
|
-
docName=${Object.keys(docs)[0]}
|
|
63
|
-
></oscd-shell>`));
|
|
64
|
-
oscdShell2.plugins = {
|
|
65
|
-
editor: sampleEditorPlugins,
|
|
66
|
-
};
|
|
67
|
-
const editorPluginsPanel2 = oscdShell2.shadowRoot.querySelector('editor-plugins-panel');
|
|
68
|
-
await oscdShell2.updateComplete;
|
|
69
|
-
await editorPluginsPanel2.updateComplete;
|
|
104
|
+
localStorage.setItem(LS_KEYS.expanded, JSON.stringify(false));
|
|
105
|
+
const editorPluginsPanel2 = await mountFreshPanel();
|
|
70
106
|
expect(isPanelExpanded(editorPluginsPanel2)).to.be.false;
|
|
71
|
-
oscdShell2.remove();
|
|
72
107
|
});
|
|
73
108
|
it('saves expanded/collapsed state (when toggled) in localStorage', async () => {
|
|
74
|
-
const toggleButton = findPanelToggleButton(editorPluginsPanel);
|
|
75
|
-
expect(localStorage.getItem('editorsPanel.expanded')).to.be.null;
|
|
76
109
|
expect(isPanelExpanded(editorPluginsPanel)).to.be.true;
|
|
77
|
-
|
|
110
|
+
findPanelToggleButton(editorPluginsPanel).click();
|
|
78
111
|
await editorPluginsPanel.updateComplete;
|
|
79
112
|
expect(isPanelExpanded(editorPluginsPanel)).to.be.false;
|
|
80
|
-
expect(localStorage.getItem(
|
|
81
|
-
|
|
113
|
+
expect(localStorage.getItem(LS_KEYS.expanded)).to.equal(JSON.stringify(false));
|
|
114
|
+
// Re-query: the collapsed rail renders a different toggle element.
|
|
115
|
+
findPanelToggleButton(editorPluginsPanel).click();
|
|
82
116
|
await editorPluginsPanel.updateComplete;
|
|
83
117
|
expect(isPanelExpanded(editorPluginsPanel)).to.be.true;
|
|
84
|
-
expect(localStorage.getItem(
|
|
118
|
+
expect(localStorage.getItem(LS_KEYS.expanded)).to.equal(JSON.stringify(true));
|
|
119
|
+
});
|
|
120
|
+
describe('restores persisted state on reload (fresh mount)', () => {
|
|
121
|
+
it('hydrates expandedIds from localStorage and does not clobber it', async () => {
|
|
122
|
+
const seeded = ['group:0:Communication', 'group:1:Advanced'];
|
|
123
|
+
localStorage.setItem(LS_KEYS.expandedIds, JSON.stringify(seeded));
|
|
124
|
+
const panel = await mountFreshPanel();
|
|
125
|
+
expect(panel.expandedIds).to.deep.equal(seeded);
|
|
126
|
+
expect(localStorage.getItem(LS_KEYS.expandedIds)).to.equal(JSON.stringify(seeded));
|
|
127
|
+
});
|
|
128
|
+
it('hydrates pinnedPluginIds from localStorage and does not clobber it', async () => {
|
|
129
|
+
const seeded = ['oscd-example-editor', 'oscd-other-editor'];
|
|
130
|
+
localStorage.setItem(LS_KEYS.pinnedPluginIds, JSON.stringify(seeded));
|
|
131
|
+
const panel = await mountFreshPanel();
|
|
132
|
+
expect(panel.pinnedPluginIds).to.deep.equal(seeded);
|
|
133
|
+
expect(localStorage.getItem(LS_KEYS.pinnedPluginIds)).to.equal(JSON.stringify(seeded));
|
|
134
|
+
});
|
|
135
|
+
it('hydrates pinnedExpanded from localStorage and does not clobber it', async () => {
|
|
136
|
+
const seeded = ['pinned'];
|
|
137
|
+
localStorage.setItem(LS_KEYS.pinnedExpanded, JSON.stringify(seeded));
|
|
138
|
+
const panel = await mountFreshPanel();
|
|
139
|
+
expect(panel.pinnedExpanded).to.deep.equal(seeded);
|
|
140
|
+
expect(localStorage.getItem(LS_KEYS.pinnedExpanded)).to.equal(JSON.stringify(seeded));
|
|
141
|
+
});
|
|
142
|
+
it('hydrates expanded (collapsed) state from localStorage and does not clobber it', async () => {
|
|
143
|
+
localStorage.setItem(LS_KEYS.expanded, JSON.stringify(false));
|
|
144
|
+
const panel = await mountFreshPanel();
|
|
145
|
+
expect(panel.expanded).to.be.false;
|
|
146
|
+
expect(localStorage.getItem(LS_KEYS.expanded)).to.equal(JSON.stringify(false));
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
const getSearchField = () => editorPluginsPanel.shadowRoot.querySelector('oscd-outlined-search-field');
|
|
150
|
+
const setSearch = async (value) => {
|
|
151
|
+
const field = getSearchField();
|
|
152
|
+
field.value = value;
|
|
153
|
+
field.dispatchEvent(new Event('input'));
|
|
154
|
+
await editorPluginsPanel.updateComplete;
|
|
155
|
+
};
|
|
156
|
+
it('filters editors by their (source) name when searching', async () => {
|
|
157
|
+
await setSearch('Plugin 2');
|
|
158
|
+
expect(editorPluginsPanel.editorTreeNodes).to.have.lengthOf(1);
|
|
159
|
+
expect(editorPluginsPanel.editorTreeNodes[0].name).to.equal('Test Editor Plugin 2');
|
|
160
|
+
});
|
|
161
|
+
it('filters editors by their localized label when a locale is set', async () => {
|
|
162
|
+
editorPluginsPanel.locale = 'de';
|
|
163
|
+
await editorPluginsPanel.updateComplete;
|
|
164
|
+
await setSearch('Erweiterung');
|
|
165
|
+
// Both sample editors share the German label "…Erweiterung" only on the
|
|
166
|
+
// first entry; searching the German term must still match it.
|
|
167
|
+
const names = editorPluginsPanel.editorTreeNodes.map(n => n.name);
|
|
168
|
+
expect(names).to.include('Test Editor Plugin');
|
|
169
|
+
});
|
|
170
|
+
it('highlights editor nodes with arrow navigation without selecting them', async () => {
|
|
171
|
+
const field = getSearchField();
|
|
172
|
+
field.dispatchEvent(new KeyboardEvent('keydown', {
|
|
173
|
+
key: 'ArrowDown',
|
|
174
|
+
bubbles: true,
|
|
175
|
+
composed: true,
|
|
176
|
+
}));
|
|
177
|
+
await editorPluginsPanel.updateComplete;
|
|
178
|
+
expect(editorPluginsPanel.focusedTree).to.equal('pinned');
|
|
179
|
+
expect(editorPluginsPanel.shadowRoot.querySelector('.pinned-tree')
|
|
180
|
+
.activeId).to.equal('pinned');
|
|
181
|
+
expect(editorPluginsPanel.selectedEditor).to.exist;
|
|
182
|
+
});
|
|
183
|
+
it('preserves the search query when the field is refocused', async () => {
|
|
184
|
+
await setSearch('Plugin 2');
|
|
185
|
+
const field = getSearchField();
|
|
186
|
+
editorPluginsPanel.focusSearch();
|
|
187
|
+
await editorPluginsPanel.updateComplete;
|
|
188
|
+
expect(field.value).to.equal('Plugin 2');
|
|
189
|
+
expect(editorPluginsPanel.editorTreeNodes).to.have.lengthOf(1);
|
|
190
|
+
});
|
|
191
|
+
it('hands search-field arrow navigation to the editor tree when searching', async () => {
|
|
192
|
+
await setSearch('Plugin');
|
|
193
|
+
const field = getSearchField();
|
|
194
|
+
field.dispatchEvent(new KeyboardEvent('keydown', {
|
|
195
|
+
key: 'ArrowDown',
|
|
196
|
+
bubbles: true,
|
|
197
|
+
composed: true,
|
|
198
|
+
}));
|
|
199
|
+
await editorPluginsPanel.updateComplete;
|
|
200
|
+
const editorsTree = editorPluginsPanel.shadowRoot.querySelector('.editors-tree');
|
|
201
|
+
expect(editorPluginsPanel.focusedTree).to.equal('editors');
|
|
202
|
+
expect(editorsTree.activeId).to.equal(editorsTree.getFirstNodeId());
|
|
203
|
+
});
|
|
204
|
+
it('starts search navigation at the last editor for ArrowUp', async () => {
|
|
205
|
+
await setSearch('Plugin');
|
|
206
|
+
const field = getSearchField();
|
|
207
|
+
field.dispatchEvent(new KeyboardEvent('keydown', {
|
|
208
|
+
key: 'ArrowUp',
|
|
209
|
+
bubbles: true,
|
|
210
|
+
composed: true,
|
|
211
|
+
}));
|
|
212
|
+
await editorPluginsPanel.updateComplete;
|
|
213
|
+
const editorsTree = editorPluginsPanel.shadowRoot.querySelector('.editors-tree');
|
|
214
|
+
expect(editorsTree.activeId).to.equal(editorsTree.getLastNodeId());
|
|
215
|
+
});
|
|
216
|
+
it('selects the only search result when Enter is pressed in the search field', async () => {
|
|
217
|
+
await setSearch('Plugin 2');
|
|
218
|
+
const field = getSearchField();
|
|
219
|
+
let selected;
|
|
220
|
+
editorPluginsPanel.addEventListener('editor-select', (event) => {
|
|
221
|
+
selected = event.detail.editor;
|
|
222
|
+
});
|
|
223
|
+
field.focus();
|
|
224
|
+
field.dispatchEvent(new KeyboardEvent('keydown', {
|
|
225
|
+
key: 'Enter',
|
|
226
|
+
bubbles: true,
|
|
227
|
+
composed: true,
|
|
228
|
+
}));
|
|
229
|
+
await editorPluginsPanel.updateComplete;
|
|
230
|
+
expect(selected).to.exist;
|
|
231
|
+
});
|
|
232
|
+
it('does nothing on search-field Enter when multiple results remain', async () => {
|
|
233
|
+
await setSearch('Plugin');
|
|
234
|
+
const field = getSearchField();
|
|
235
|
+
let selected = false;
|
|
236
|
+
editorPluginsPanel.addEventListener('editor-select', () => {
|
|
237
|
+
selected = true;
|
|
238
|
+
});
|
|
239
|
+
field.focus();
|
|
240
|
+
field.dispatchEvent(new KeyboardEvent('keydown', {
|
|
241
|
+
key: 'Enter',
|
|
242
|
+
bubbles: true,
|
|
243
|
+
composed: true,
|
|
244
|
+
}));
|
|
245
|
+
await editorPluginsPanel.updateComplete;
|
|
246
|
+
expect(selected).to.be.false;
|
|
247
|
+
});
|
|
248
|
+
it('updates the focused tree when the tree emits active-changed or receives focus', async () => {
|
|
249
|
+
const editorsTree = editorPluginsPanel.shadowRoot.querySelector('.editors-tree');
|
|
250
|
+
editorsTree.dispatchEvent(new Event('focusin', { bubbles: true }));
|
|
251
|
+
expect(editorPluginsPanel.focusedTree).to.equal('editors');
|
|
252
|
+
editorsTree.dispatchEvent(new CustomEvent('active-changed', {
|
|
253
|
+
detail: { activeId: editorsTree.activeId },
|
|
254
|
+
bubbles: true,
|
|
255
|
+
}));
|
|
256
|
+
expect(editorPluginsPanel.focusedTree).to.equal('editors');
|
|
257
|
+
});
|
|
258
|
+
it('does not focus a tree when the handoff has no active node', () => {
|
|
259
|
+
// @ts-expect-error focusTree is private; exercise its null-node guard.
|
|
260
|
+
expect(editorPluginsPanel.focusTree('editors', null)).to.be.false;
|
|
261
|
+
});
|
|
262
|
+
it('transfers focus from the pinned tree to the editor tree at its boundary', async () => {
|
|
263
|
+
const pinnedTree = editorPluginsPanel.shadowRoot.querySelector('.pinned-tree');
|
|
264
|
+
const editorsTree = editorPluginsPanel.shadowRoot.querySelector('.editors-tree');
|
|
265
|
+
pinnedTree.activeId = pinnedTree.getLastNodeId();
|
|
266
|
+
const focusSpy = sinon.spy(editorsTree, 'focus');
|
|
267
|
+
pinnedTree.dispatchEvent(new CustomEvent('navigation-boundary', {
|
|
268
|
+
detail: { direction: 'last' },
|
|
269
|
+
bubbles: true,
|
|
270
|
+
composed: true,
|
|
271
|
+
}));
|
|
272
|
+
await editorPluginsPanel.updateComplete;
|
|
273
|
+
expect(editorPluginsPanel.focusedTree).to.equal('editors');
|
|
274
|
+
expect(editorsTree.activeId).to.equal(editorsTree.getFirstNodeId());
|
|
275
|
+
expect(focusSpy.called).to.be.true;
|
|
276
|
+
focusSpy.restore();
|
|
277
|
+
});
|
|
278
|
+
it('transfers focus back to the pinned tree at the editor boundary', async () => {
|
|
279
|
+
const pinnedTree = editorPluginsPanel.shadowRoot.querySelector('.pinned-tree');
|
|
280
|
+
const editorsTree = editorPluginsPanel.shadowRoot.querySelector('.editors-tree');
|
|
281
|
+
editorPluginsPanel.focusedTree = 'editors';
|
|
282
|
+
editorsTree.activeId = editorsTree.getFirstNodeId();
|
|
283
|
+
editorsTree.dispatchEvent(new CustomEvent('navigation-boundary', {
|
|
284
|
+
detail: { direction: 'first' },
|
|
285
|
+
bubbles: true,
|
|
286
|
+
composed: true,
|
|
287
|
+
}));
|
|
288
|
+
await editorPluginsPanel.updateComplete;
|
|
289
|
+
expect(editorPluginsPanel.focusedTree).to.equal('pinned');
|
|
290
|
+
expect(pinnedTree.activeId).to.equal(pinnedTree.getLastNodeId());
|
|
291
|
+
});
|
|
292
|
+
it('does not transfer across the editor boundary while searching', async () => {
|
|
293
|
+
await setSearch('Plugin');
|
|
294
|
+
const editorsTree = editorPluginsPanel.shadowRoot.querySelector('.editors-tree');
|
|
295
|
+
editorPluginsPanel.focusedTree = 'editors';
|
|
296
|
+
editorsTree.dispatchEvent(new CustomEvent('navigation-boundary', {
|
|
297
|
+
detail: { direction: 'first' },
|
|
298
|
+
bubbles: true,
|
|
299
|
+
}));
|
|
300
|
+
await editorPluginsPanel.updateComplete;
|
|
301
|
+
expect(editorPluginsPanel.focusedTree).to.equal('editors');
|
|
302
|
+
});
|
|
303
|
+
it('pins and unpins an editor, persisting the ids to localStorage', async () => {
|
|
304
|
+
const tagName = oscdShell.plugins.editor[0].tagName;
|
|
305
|
+
editorPluginsPanel.togglePin(tagName);
|
|
306
|
+
await editorPluginsPanel.updateComplete;
|
|
307
|
+
expect(editorPluginsPanel.pinnedPluginIds).to.include(tagName);
|
|
308
|
+
expect(localStorage.getItem('editor-plugins-panel:pinnedPluginIds')).to.contain(tagName);
|
|
309
|
+
editorPluginsPanel.togglePin(tagName);
|
|
310
|
+
await editorPluginsPanel.updateComplete;
|
|
311
|
+
expect(editorPluginsPanel.pinnedPluginIds).to.not.include(tagName);
|
|
312
|
+
});
|
|
313
|
+
it('reflects the selected editor into the pinned tree selectedIds', async () => {
|
|
314
|
+
const editor = oscdShell.plugins.editor[0];
|
|
315
|
+
editorPluginsPanel.togglePin(editor.tagName);
|
|
316
|
+
editorPluginsPanel.selectedEditor = editor;
|
|
317
|
+
await editorPluginsPanel.updateComplete;
|
|
318
|
+
const pinnedTree = editorPluginsPanel.shadowRoot.querySelector('.tree-container oscd-tree:not(.editors-tree)');
|
|
319
|
+
expect(pinnedTree.selectedIds).to.deep.equal([editor.tagName]);
|
|
320
|
+
});
|
|
321
|
+
it('selects an editor chosen from the pinned tree', async () => {
|
|
322
|
+
const editor = oscdShell.plugins.editor[0];
|
|
323
|
+
editorPluginsPanel.togglePin(editor.tagName);
|
|
324
|
+
await editorPluginsPanel.updateComplete;
|
|
325
|
+
let selected;
|
|
326
|
+
editorPluginsPanel.addEventListener('editor-select', (event) => {
|
|
327
|
+
selected = event.detail.editor;
|
|
328
|
+
});
|
|
329
|
+
const pinnedTree = editorPluginsPanel.shadowRoot.querySelector('.tree-container oscd-tree:not(.editors-tree)');
|
|
330
|
+
pinnedTree.dispatchEvent(new CustomEvent('selected-ids-changed', {
|
|
331
|
+
detail: { selectedIds: [editor.tagName] },
|
|
332
|
+
}));
|
|
333
|
+
await editorPluginsPanel.updateComplete;
|
|
334
|
+
expect(selected?.tagName).to.equal(editor.tagName);
|
|
335
|
+
});
|
|
336
|
+
it('ignores an editor selection with no id', async () => {
|
|
337
|
+
let dispatched = false;
|
|
338
|
+
editorPluginsPanel.addEventListener('editor-select', () => {
|
|
339
|
+
dispatched = true;
|
|
340
|
+
});
|
|
341
|
+
editorPluginsPanel.selectEditor([]);
|
|
342
|
+
expect(dispatched).to.be.false;
|
|
343
|
+
});
|
|
344
|
+
it('ignores an unknown editor selection id', async () => {
|
|
345
|
+
let dispatched = false;
|
|
346
|
+
editorPluginsPanel.addEventListener('editor-select', () => {
|
|
347
|
+
dispatched = true;
|
|
348
|
+
});
|
|
349
|
+
editorPluginsPanel.selectEditor(['unknown-editor']);
|
|
350
|
+
expect(dispatched).to.be.false;
|
|
351
|
+
});
|
|
352
|
+
describe('transient search mode (collapsed rail)', () => {
|
|
353
|
+
const collapse = async (panel) => {
|
|
354
|
+
findPanelToggleButton(panel).click();
|
|
355
|
+
await panel.updateComplete;
|
|
356
|
+
expect(isPanelExpanded(panel)).to.be.false;
|
|
357
|
+
};
|
|
358
|
+
const findRailSearchButton = (panel) => panel.shadowRoot.querySelector('.rail oscd-icon-button.rail-item');
|
|
359
|
+
it('opens the panel without persisting `expanded` when the rail search icon is clicked', async () => {
|
|
360
|
+
await collapse(editorPluginsPanel);
|
|
361
|
+
expect(localStorage.getItem('editor-plugins-panel:expanded')).to.equal(JSON.stringify(false));
|
|
362
|
+
findRailSearchButton(editorPluginsPanel).click();
|
|
363
|
+
await editorPluginsPanel.updateComplete;
|
|
364
|
+
expect(editorPluginsPanel.hasAttribute('search-mode')).to.be.true;
|
|
365
|
+
expect(editorPluginsPanel.shadowRoot.querySelector('.tree-container')).to
|
|
366
|
+
.exist;
|
|
367
|
+
// Still collapsed as far as persisted state is concerned.
|
|
368
|
+
expect(editorPluginsPanel.expanded).to.be.false;
|
|
369
|
+
expect(localStorage.getItem('editor-plugins-panel:expanded')).to.equal(JSON.stringify(false));
|
|
370
|
+
});
|
|
371
|
+
it('focuses the search field on entering search mode', async () => {
|
|
372
|
+
await collapse(editorPluginsPanel);
|
|
373
|
+
const focusSpy = sinon.spy(OscdOutlinedSearchField.prototype, 'focus');
|
|
374
|
+
findRailSearchButton(editorPluginsPanel).click();
|
|
375
|
+
await editorPluginsPanel.updateComplete;
|
|
376
|
+
// The focus() call is chained off `updateComplete.then(...)`; await it
|
|
377
|
+
// again so that microtask has a chance to run.
|
|
378
|
+
await editorPluginsPanel.updateComplete;
|
|
379
|
+
expect(focusSpy.called).to.be.true;
|
|
380
|
+
focusSpy.restore();
|
|
381
|
+
});
|
|
382
|
+
it('exits search mode (and clears the query) on Escape', async () => {
|
|
383
|
+
await collapse(editorPluginsPanel);
|
|
384
|
+
findRailSearchButton(editorPluginsPanel).click();
|
|
385
|
+
await editorPluginsPanel.updateComplete;
|
|
386
|
+
await setSearch('Plugin 2');
|
|
387
|
+
expect(editorPluginsPanel.searchValue).to.equal('Plugin 2');
|
|
388
|
+
editorPluginsPanel.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
|
|
389
|
+
await editorPluginsPanel.updateComplete;
|
|
390
|
+
expect(editorPluginsPanel.hasAttribute('search-mode')).to.be.false;
|
|
391
|
+
expect(editorPluginsPanel.searchValue).to.equal('');
|
|
392
|
+
});
|
|
393
|
+
it('ignores Escape when not in search mode', async () => {
|
|
394
|
+
await collapse(editorPluginsPanel);
|
|
395
|
+
editorPluginsPanel.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
|
|
396
|
+
await editorPluginsPanel.updateComplete;
|
|
397
|
+
expect(isPanelExpanded(editorPluginsPanel)).to.be.false;
|
|
398
|
+
});
|
|
399
|
+
it('exits search mode on selecting an editor, without persisting expanded', async () => {
|
|
400
|
+
await collapse(editorPluginsPanel);
|
|
401
|
+
findRailSearchButton(editorPluginsPanel).click();
|
|
402
|
+
await editorPluginsPanel.updateComplete;
|
|
403
|
+
let selected;
|
|
404
|
+
editorPluginsPanel.addEventListener('editor-select', (event) => {
|
|
405
|
+
selected = event.detail.editor;
|
|
406
|
+
});
|
|
407
|
+
const tagName = oscdShell.plugins.editor[0].tagName;
|
|
408
|
+
editorPluginsPanel.selectEditor([tagName]);
|
|
409
|
+
await editorPluginsPanel.updateComplete;
|
|
410
|
+
expect(selected?.tagName).to.equal(tagName);
|
|
411
|
+
expect(editorPluginsPanel.hasAttribute('search-mode')).to.be.false;
|
|
412
|
+
expect(editorPluginsPanel.expanded).to.be.false;
|
|
413
|
+
});
|
|
414
|
+
it('selects a root editor from the collapsed rail', async () => {
|
|
415
|
+
await collapse(editorPluginsPanel);
|
|
416
|
+
const editor = oscdShell.plugins.editor[0];
|
|
417
|
+
const railEditors = editorPluginsPanel.shadowRoot.querySelectorAll('.rail > oscd-icon-button.rail-item');
|
|
418
|
+
const railEditor = railEditors[2];
|
|
419
|
+
expect(!!railEditor).to.be.true;
|
|
420
|
+
let selected;
|
|
421
|
+
editorPluginsPanel.addEventListener('editor-select', (event) => {
|
|
422
|
+
selected = event.detail.editor;
|
|
423
|
+
});
|
|
424
|
+
railEditor.dispatchEvent(new MouseEvent('click', { bubbles: true, composed: true }));
|
|
425
|
+
await editorPluginsPanel.updateComplete;
|
|
426
|
+
expect(selected?.tagName).to.equal(editor.tagName);
|
|
427
|
+
});
|
|
428
|
+
});
|
|
429
|
+
describe('pinned/editors tree expand-state persistence', () => {
|
|
430
|
+
it('persists the pinned tree expanded ids on `expanded-ids-changed`', async () => {
|
|
431
|
+
const tagName = oscdShell.plugins.editor[0].tagName;
|
|
432
|
+
editorPluginsPanel.togglePin(tagName);
|
|
433
|
+
await editorPluginsPanel.updateComplete;
|
|
434
|
+
const pinnedTree = editorPluginsPanel.shadowRoot.querySelector('.tree-container oscd-tree:not(.editors-tree)');
|
|
435
|
+
pinnedTree.dispatchEvent(new CustomEvent('expanded-ids-changed', {
|
|
436
|
+
detail: { expandedIds: ['pinned'] },
|
|
437
|
+
}));
|
|
438
|
+
await editorPluginsPanel.updateComplete;
|
|
439
|
+
expect(editorPluginsPanel.pinnedExpanded).to.deep.equal(['pinned']);
|
|
440
|
+
});
|
|
441
|
+
it('persists the editors tree expanded ids on `expanded-ids-changed`', async () => {
|
|
442
|
+
const editorsTree = editorPluginsPanel.shadowRoot.querySelector('.tree-container oscd-tree.editors-tree');
|
|
443
|
+
editorsTree.dispatchEvent(new CustomEvent('expanded-ids-changed', {
|
|
444
|
+
detail: { expandedIds: ['group:0:Communication'] },
|
|
445
|
+
}));
|
|
446
|
+
await editorPluginsPanel.updateComplete;
|
|
447
|
+
expect(editorPluginsPanel.expandedIds).to.deep.equal([
|
|
448
|
+
'group:0:Communication',
|
|
449
|
+
]);
|
|
450
|
+
});
|
|
451
|
+
});
|
|
452
|
+
describe('collapsed rail group flyout', () => {
|
|
453
|
+
let groupedShell;
|
|
454
|
+
let groupedPanel;
|
|
455
|
+
beforeEach(async () => {
|
|
456
|
+
groupedShell = (await fixture(html `<oscd-shell
|
|
457
|
+
.docs=${docs}
|
|
458
|
+
docName=${Object.keys(docs)[0]}
|
|
459
|
+
></oscd-shell>`));
|
|
460
|
+
groupedShell.plugins = { editor: groupedEditorPlugins };
|
|
461
|
+
groupedPanel = groupedShell.shadowRoot.querySelector('editor-plugins-panel');
|
|
462
|
+
await groupedShell.updateComplete;
|
|
463
|
+
await groupedPanel.updateComplete;
|
|
464
|
+
// Start from the collapsed rail, where the group flyout lives.
|
|
465
|
+
findPanelToggleButton(groupedPanel).click();
|
|
466
|
+
await groupedPanel.updateComplete;
|
|
467
|
+
extraShells.push(groupedShell);
|
|
468
|
+
});
|
|
469
|
+
const findGroupRailButton = () => groupedPanel.shadowRoot.querySelector('#rail-group-0');
|
|
470
|
+
const findGroupFlyoutMenu = () => groupedPanel.shadowRoot.querySelector('oscd-menu.rail-flyout[data-flyout="group-0"]');
|
|
471
|
+
it('shows a disabled placeholder in the empty pinned flyout', () => {
|
|
472
|
+
const pinnedFlyout = groupedPanel.shadowRoot.querySelector('oscd-menu.rail-flyout[data-flyout="pinned"]');
|
|
473
|
+
const placeholder = pinnedFlyout?.querySelector('oscd-menu-item');
|
|
474
|
+
expect(placeholder?.hasAttribute('disabled')).to.be.true;
|
|
475
|
+
expect(placeholder?.textContent).to.contain('Items you pin will appear here');
|
|
476
|
+
});
|
|
477
|
+
it('opens the group flyout menu on rail icon click', async () => {
|
|
478
|
+
const menu = findGroupFlyoutMenu();
|
|
479
|
+
expect(menu.open).to.be.false;
|
|
480
|
+
findGroupRailButton().click();
|
|
481
|
+
await groupedPanel.updateComplete;
|
|
482
|
+
expect(menu.open).to.be.true;
|
|
483
|
+
});
|
|
484
|
+
it('closes the group flyout menu on a second rail icon click', async () => {
|
|
485
|
+
findGroupRailButton().click();
|
|
486
|
+
await groupedPanel.updateComplete;
|
|
487
|
+
expect(findGroupFlyoutMenu().open).to.be.true;
|
|
488
|
+
findGroupRailButton().click();
|
|
489
|
+
await groupedPanel.updateComplete;
|
|
490
|
+
expect(findGroupFlyoutMenu().open).to.be.false;
|
|
491
|
+
});
|
|
492
|
+
it('does nothing when toggling a flyout with no matching anchor', () => {
|
|
493
|
+
expect(() =>
|
|
494
|
+
// @ts-expect-error toggleFlyout is private; exercised directly to
|
|
495
|
+
// cover the defensive "no matching anchor" guard.
|
|
496
|
+
groupedPanel.toggleFlyout('unknown-group')).to.not.throw();
|
|
497
|
+
});
|
|
498
|
+
it('dispatches editor-select when a flyout item is clicked', async () => {
|
|
499
|
+
findGroupRailButton().click();
|
|
500
|
+
await groupedPanel.updateComplete;
|
|
501
|
+
let selected;
|
|
502
|
+
groupedPanel.addEventListener('editor-select', (event) => {
|
|
503
|
+
selected = event.detail.editor;
|
|
504
|
+
});
|
|
505
|
+
const flyoutItem = findGroupFlyoutMenu().querySelector('oscd-menu-item');
|
|
506
|
+
flyoutItem.dispatchEvent(new Event('click', { bubbles: true }));
|
|
507
|
+
await groupedPanel.updateComplete;
|
|
508
|
+
expect(selected?.tagName).to.equal('test-grouped-editor-1');
|
|
509
|
+
});
|
|
510
|
+
it('toggles a group when its tree selection is committed', async () => {
|
|
511
|
+
findPanelToggleButton(groupedPanel).click();
|
|
512
|
+
await groupedPanel.updateComplete;
|
|
513
|
+
const editorsTree = groupedPanel.shadowRoot.querySelector('.editors-tree');
|
|
514
|
+
editorsTree.dispatchEvent(new CustomEvent('selected-ids-changed', {
|
|
515
|
+
detail: { selectedIds: ['group:0:Grouped Editors'] },
|
|
516
|
+
bubbles: true,
|
|
517
|
+
}));
|
|
518
|
+
await groupedPanel.updateComplete;
|
|
519
|
+
expect(editorsTree.expandedIds).to.include('group:0:Grouped Editors');
|
|
520
|
+
});
|
|
521
|
+
it('marks the rail group icon active when one of its plugins is selected', async () => {
|
|
522
|
+
groupedPanel.selectedEditor = {
|
|
523
|
+
name: 'Grouped Editor 1',
|
|
524
|
+
tagName: 'test-grouped-editor-1',
|
|
525
|
+
icon: 'coronavirus',
|
|
526
|
+
};
|
|
527
|
+
await groupedPanel.updateComplete;
|
|
528
|
+
expect(findGroupRailButton().classList.contains('active')).to.be.true;
|
|
529
|
+
});
|
|
85
530
|
});
|
|
86
531
|
});
|
|
87
532
|
//# sourceMappingURL=editor-plugins-panel.spec.js.map
|