neo.mjs 6.15.3 → 6.15.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/apps/ServiceWorker.mjs +2 -2
  2. package/apps/portal/view/Viewport.mjs +73 -10
  3. package/apps/portal/view/ViewportModel.mjs +29 -0
  4. package/apps/portal/view/learn/MainContainer.mjs +7 -10
  5. package/apps/portal/view/learn/MainContainerController.mjs +1 -1
  6. package/apps/portal/view/learn/PageSectionsContainer.mjs +51 -0
  7. package/examples/ServiceWorker.mjs +2 -2
  8. package/package.json +4 -4
  9. package/resources/data/deck/learnneo/pages/2023-10-14T19-25-08-153Z.md +0 -2
  10. package/resources/data/deck/learnneo/pages/ComponentsAndContainers.md +44 -11
  11. package/resources/data/deck/learnneo/pages/Config.md +0 -2
  12. package/resources/data/deck/learnneo/pages/CustomComponents.md +45 -0
  13. package/resources/data/deck/learnneo/pages/DescribingTheUI.md +0 -2
  14. package/resources/data/deck/learnneo/pages/Earthquakes.md +0 -2
  15. package/resources/data/deck/learnneo/pages/Events.md +0 -2
  16. package/resources/data/deck/learnneo/pages/Extending.md +0 -1
  17. package/resources/data/deck/learnneo/pages/References.md +0 -2
  18. package/resources/data/deck/learnneo/pages/Setup.md +0 -2
  19. package/resources/data/deck/learnneo/pages/TestLivePreview.md +1 -2
  20. package/resources/data/deck/learnneo/pages/Welcome.md +60 -0
  21. package/resources/data/deck/learnneo/pages/WhyNeo-Multi-Threaded.md +2 -0
  22. package/resources/data/deck/learnneo/pages/WhyNeo-Multi-Window.md +3 -3
  23. package/resources/data/deck/learnneo/pages/WhyNeo-Speed.md +34 -12
  24. package/resources/data/deck/learnneo/tree.json +3 -2
  25. package/resources/scss/src/apps/portal/learn/ContentView.scss +25 -20
  26. package/resources/scss/src/apps/portal/learn/LivePreview.scss +27 -6
  27. package/resources/scss/src/apps/portal/learn/MainContainer.scss +3 -33
  28. package/resources/scss/src/apps/portal/learn/PageContainer.scss +11 -6
  29. package/resources/scss/src/apps/portal/learn/PageSectionsContainer.scss +14 -0
  30. package/resources/scss/src/apps/portal/learn/PageSectionsList.scss +5 -6
  31. package/resources/scss/src/component/wrapper/MonacoEditor.scss +3 -0
  32. package/src/DefaultConfig.mjs +2 -2
  33. package/src/component/Base.mjs +0 -1
  34. package/src/component/wrapper/MonacoEditor.mjs +5 -0
  35. package/src/core/Base.mjs +24 -4
  36. package/src/form/field/ComboBox.mjs +2 -0
  37. package/src/list/Base.mjs +12 -5
  38. package/src/main/addon/IntersectionObserver.mjs +29 -5
  39. package/src/main/addon/Navigator.mjs +207 -155
  40. package/test/components/app.mjs +1 -0
  41. package/test/components/files/component/Base.mjs +88 -0
  42. package/test/components/siesta.js +1 -0
  43. package/apps/portal/view/learn/PageSectionsPanel.mjs +0 -59
  44. package/resources/scss/src/apps/portal/learn/PageSectionsPanel.scss +0 -40
@@ -37,81 +37,42 @@ class Navigator extends Base {
37
37
  }
38
38
 
39
39
  /**
40
- * Sets up keyboard based navigation within the passed element id.
41
- *
42
- * When navigation occurs from one navigable element to another, the `navigate` event
43
- * will be fired.
44
- *
45
- * Note that if focus is expected to enter the subject, the navigable elements
46
- * designated by the `selector` must be focusable in some way. So if not using natively
47
- * focusable elements, they must have `tabIndex="-1"`.
48
- *
49
- * Upon navigation, the `aria-activedescendant` property is automatically updated
50
- * on the `eventSource` element (which defaults to the subject element, but may be external)
51
- *
52
- * Pressing `Enter` when an item is active clicks that item.
53
- *
54
- * if `autoClick` is set to `true` in the data, simply navigating to an element will click it.
55
- * @param {*} data
56
- * @param {String} data.id The element id to navigate in.
57
- * @param {String} [data.eventSource] Optional - the element id to read keystrokes from.
58
- * defaults to the main element id. Select field uses this. Focus remains in the field's
59
- * `<input>` element while navigating its dropdown.
60
- * @param {String} data.selector A CSS selector which identifies the navigable elements.
61
- * @param {String} data.activeCls A CSS class to add to the currently active navigable element.
62
- * @param {Boolean} data.wrap Pass as `true` to have navigation wrap from first to last and vice versa.
63
- * @param {Boolean} [data.autoClick=false] Pass as `true` to have navigation click the target navigated to.
64
- * TabPanels will use this on their tab toolbar.
40
+ * @param {HTMLElement} el
65
41
  */
66
- subscribe(data) {
67
- const
68
- me = this,
69
- subject = data.subject = DomAccess.getElement(data.id),
70
- eventSource = data.eventSource = data.eventSource ? DomAccess.getElement(data.eventSource) : subject;
71
-
72
- subject.$navigator = data;
73
-
74
- if (!data.activeCls) {
75
- data.activeCls = 'neo-navigator-active-item'
42
+ clickItem(el) {
43
+ // The element knows how to click itself.
44
+ if (typeof el.click === 'function') {
45
+ el.click();
76
46
  }
47
+ // It operates through a listener, so needs an event firing into it.
48
+ else {
49
+ const
50
+ rect = el.getBoundingClientRect(),
51
+ clientX = rect.x + (rect.width / 2),
52
+ clientY = rect.y + (rect.height / 2);
77
53
 
78
- // Ensure that only *one* of the child focusables is actually tabbable.
79
- // We use arrow keys for internal navigation. TAB must move out.
80
- me.fixItemFocusability(data);
81
-
82
- // Finds a focusable item starting from a descendant el within one of our selector items
83
- data.findFocusable = el => DomUtils.closest(el, el =>
84
- // We're looking for an element that is focusable
85
- DomUtils.isFocusable(el) &&
86
- // And within our subject element
87
- (subject.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_CONTAINED_BY) &&
88
- // And within an element that matches our selector
89
- el.closest(data.selector)
90
- );
91
-
92
- // TreeWalker so that we can easily move between navigable elements within the target.
93
- data.treeWalker = document.createTreeWalker(subject, NodeFilter.SHOW_ELEMENT, node => me.navigateNodeFilter(node, data));
94
-
95
- // We have to know when the DOM mutates in case the active item is removed.
96
- (data.targetMutationMonitor = new MutationObserver(e => me.navigateTargetChildListChange(e, data))).observe(subject, {
97
- childList : true,
98
- subtree : true
99
- });
100
-
101
- eventSource.addEventListener('keydown', data.l1 = e => me.navigateKeyDownHandler(e, data));
102
- subject.addEventListener('mousedown', data.l2 = e => me.navigateMouseDownHandler(e, data));
103
- subject.addEventListener('click', data.l3 = e => me.navigateClickHandler(e, data));
104
- subject.addEventListener('focusin', data.l4 = e => me.navigateFocusInHandler(e, data));
105
- subject.addEventListener('focusout', data.l5 = e => me.navigateFocusOutHandler(e, data));
54
+ el.dispatchEvent(new MouseEvent('click', {
55
+ bubbles : true,
56
+ altKey : Neo.altKeyDown,
57
+ ctrlKey : Neo.controlKeyDown,
58
+ metaKey : Neo.metaKeyDown,
59
+ shiftKey : Neo.shiftKeyDown,
60
+ clientX,
61
+ clientY
62
+ }))
63
+ }
106
64
  }
107
65
 
108
- // The navigables we are dealing with, if they are focusable must *not* be tabbable.
109
- // Only *one* must be tabbable, so that tabbing into the subject element goes to the
110
- // one active element.
111
- //
112
- // Tabbing *from* that must exit the subject element.
113
- //
114
- // So we must ensure that all the focusable elements except the first are not tabbable.
66
+ /**
67
+ * The navigables we are dealing with, if they are focusable must *not* be tabbable.
68
+ * Only *one* must be tabbable, so that tabbing into the subject element goes to the
69
+ * one active element.
70
+ *
71
+ * Tabbing *from* that must exit the subject element.
72
+ *
73
+ * So we must ensure that all the focusable elements except the first are not tabbable.
74
+ * @param {Object} data
75
+ */
115
76
  fixItemFocusability(data) {
116
77
  // If the key events are being read from an external element, then that will always contain
117
78
  // focus, so we have nothing to do here. The navigable items wil be inert and not
@@ -141,35 +102,25 @@ class Navigator extends Base {
141
102
  }
142
103
  }
143
104
 
144
- unsubscribe(data) {
145
- const target = DomAccess.getElement(data.id);
146
-
147
- data = target?.$navigator;
148
- if (data) {
149
- delete target.$navigator;
150
- data.targetMutationMonitor.disconnect(target);
151
- data.eventSource.removeEventListener('keydown', data.l1);
152
- target.removeEventListener('mousedown', data.l2);
153
- target.removeEventListener('click', data.l3);
154
- target.removeEventListener('focusin', data.l4);
155
- target.removeEventListener('focusout', data.l5);
156
- }
157
- }
158
-
159
- // This is called if mutations take place within the subject element.
160
- // We have to keep things in order if the list items change.
161
- navigateTargetChildListChange(mutations, data) {
162
- this.fixItemFocusability(data);
163
-
164
- // Active item gone.
165
- // Try to activate the item at the same index;
166
- if (data.activeItem && !data.subject.contains(data.activeItem)) {
167
- const allItems = data.subject.querySelectorAll(data.selector);
105
+ /**
106
+ * @param {MouseEvent} e
107
+ * @param {Object} data
108
+ */
109
+ navigateClickHandler(e, data) {
110
+ const target = e.target.closest(data.selector);
168
111
 
169
- allItems.length && this.navigateTo(allItems[Math.max(Math.min(data.activeIndex, allItems.length - 1), 0)], data);
112
+ // If there was a focusable under the mouse, mousedown will have focused it and and we
113
+ // will have respond to that in navigateFocusInHandler.
114
+ // If not, we navigate programmatically.
115
+ if (target && !data.findFocusable(target)) {
116
+ this.navigateTo(target, data);
170
117
  }
171
118
  }
172
119
 
120
+ /**
121
+ * @param {FocusEvent} e
122
+ * @param {Object} data
123
+ */
173
124
  navigateFocusInHandler(e, data) {
174
125
  const
175
126
  target = e.target.closest(data.selector),
@@ -189,6 +140,10 @@ class Navigator extends Base {
189
140
  }
190
141
  }
191
142
 
143
+ /**
144
+ * @param {FocusEvent} e
145
+ * @param {Object} data
146
+ */
192
147
  navigateFocusOutHandler(e, data) {
193
148
  const { target } = e;
194
149
 
@@ -201,28 +156,35 @@ class Navigator extends Base {
201
156
  }
202
157
  }
203
158
 
204
- navigateClickHandler(e, data) {
205
- const target = e.target.closest(data.selector);
159
+ /**
160
+ * @param {Number} direction
161
+ * @param {Object} data
162
+ */
163
+ navigateGetAdjacent(direction=1, data) {
164
+ const { treeWalker } = data;
206
165
 
207
- // If there was a focusable under the mouse, mousedown will have focused it and and we
208
- // will have respond to that in navigateFocusInHandler.
209
- // If not, we navigate programmatically.
210
- if (target && !data.findFocusable(target)) {
211
- this.navigateTo(target, data);
212
- }
213
- }
166
+ // Walk forwards or backwards to the next or previous node which matches our selector
167
+ treeWalker.currentNode = this.navigatorGetActiveItem(data) || data.subject;
168
+ treeWalker[direction < 0 ? 'previousNode' : 'nextNode']();
214
169
 
215
- navigateMouseDownHandler(e, data) {
216
- const target = e.target.closest(data.selector);
170
+ // Found a target in the requested direction
171
+ if (treeWalker.currentNode) {
172
+ if (treeWalker.currentNode !== data.activeItem) {
173
+ return treeWalker.currentNode;
174
+ }
175
+ }
176
+ // Could not find target in requested direction, then wrap if configured to do so
177
+ else if (data.wrap !== false) {
178
+ const allItems = data.subject.querySelector(data.selector);
217
179
 
218
- // If there is a focusable under the mouse, it will take focus, and we respond to that in navigateFocusInHandler.
219
- // If not, we have to programmatically activate on click, but we must not draw focus away from
220
- // where it is, so preventDefault
221
- if (target && !data.findFocusable(target)) {
222
- e.preventDefault();
180
+ return allItems[direction === 1 ? 0 : allItems.length - 1];
223
181
  }
224
182
  }
225
183
 
184
+ /**
185
+ * @param {KeyboardEvent} keyEvent
186
+ * @param {Object} data
187
+ */
226
188
  navigateKeyDownHandler(keyEvent, data) {
227
189
  const
228
190
  me = this,
@@ -290,27 +252,46 @@ class Navigator extends Base {
290
252
  }
291
253
  }
292
254
 
293
- clickItem(el) {
294
- // The element knows how to click itself.
295
- if (typeof el.click === 'function') {
296
- el.click();
255
+ /**
256
+ * @param {MouseEvent} e
257
+ * @param {Object} data
258
+ */
259
+ navigateMouseDownHandler(e, data) {
260
+ const target = e.target.closest(data.selector);
261
+
262
+ // If there is a focusable under the mouse, it will take focus, and we respond to that in navigateFocusInHandler.
263
+ // If not, we have to programmatically activate on click, but we must not draw focus away from
264
+ // where it is, so preventDefault
265
+ if (target && !data.findFocusable(target)) {
266
+ e.preventDefault();
297
267
  }
298
- // It operates through a listener, so needs an event firing into it.
299
- else {
300
- const
301
- rect = el.getBoundingClientRect(),
302
- clientX = rect.x + (rect.width / 2),
303
- clientY = rect.y + (rect.height / 2);
268
+ }
304
269
 
305
- el.dispatchEvent(new MouseEvent('click', {
306
- bubbles : true,
307
- altKey : Neo.altKeyDown,
308
- ctrlKey : Neo.controlKeyDown,
309
- metaKey : Neo.metaKeyDown,
310
- shiftKey : Neo.shiftKeyDown,
311
- clientX,
312
- clientY
313
- }));
270
+ /**
271
+ * @param {HTMLElement} node
272
+ * @param {Object} data
273
+ */
274
+ navigateNodeFilter(node, data) {
275
+ return node.offsetParent && node.matches?.(data.selector) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
276
+ }
277
+
278
+ /**
279
+ * This is called if mutations take place within the subject element.
280
+ * We have to keep things in order if the list items change.
281
+ * @param {MutationRecord[]} mutations
282
+ * @param {Object} data
283
+ */
284
+ navigateTargetChildListChange(mutations, data) {
285
+ this.fixItemFocusability(data);
286
+
287
+ if (data.keepFocusIndex) {
288
+ // Active item gone.
289
+ // Try to activate the item at the same index;
290
+ if (data.activeItem && !data.subject.contains(data.activeItem)) {
291
+ const allItems = data.subject.querySelectorAll(data.selector);
292
+
293
+ allItems.length && this.navigateTo(allItems[Math.max(Math.min(data.activeIndex, allItems.length - 1), 0)], data);
294
+ }
314
295
  }
315
296
  }
316
297
 
@@ -362,6 +343,25 @@ class Navigator extends Base {
362
343
  }
363
344
  }
364
345
 
346
+ /**
347
+ * @param {Object} data
348
+ * @returns {HTMLElement|null}
349
+ */
350
+ navigatorGetActiveItem(data) {
351
+ let activeItem = data.activeItem && DomAccess.getElement(data.activeItem.id) || null;
352
+
353
+ if (!activeItem && ('activeIndex' in data)) {
354
+ const allItems = data.subject.querySelectorAll(data.selector);
355
+
356
+ activeItem = allItems[Math.max(Math.min(data.activeIndex, allItems.length - 1), 0)];
357
+ }
358
+ return activeItem;
359
+ }
360
+
361
+ /**
362
+ * @param {HTMLElement} newActiveElement
363
+ * @param {Object} data
364
+ */
365
365
  setActiveItem(newActiveElement, data) {
366
366
  const allItems = Array.from(data.subject.querySelectorAll(data.selector));
367
367
 
@@ -411,40 +411,92 @@ class Navigator extends Base {
411
411
  }
412
412
  }
413
413
 
414
- navigateGetAdjacent(direction = 1, data) {
415
- const { treeWalker } = data;
414
+ /**
415
+ * Sets up keyboard based navigation within the passed element id.
416
+ *
417
+ * When navigation occurs from one navigable element to another, the `navigate` event
418
+ * will be fired.
419
+ *
420
+ * Note that if focus is expected to enter the subject, the navigable elements
421
+ * designated by the `selector` must be focusable in some way. So if not using natively
422
+ * focusable elements, they must have `tabIndex="-1"`.
423
+ *
424
+ * Upon navigation, the `aria-activedescendant` property is automatically updated
425
+ * on the `eventSource` element (which defaults to the subject element, but may be external)
426
+ *
427
+ * Pressing `Enter` when an item is active clicks that item.
428
+ *
429
+ * if `autoClick` is set to `true` in the data, simply navigating to an element will click it.
430
+ * @param {*} data
431
+ * @param {String} data.id The element id to navigate in.
432
+ * @param {String} [data.eventSource] Optional - the element id to read keystrokes from.
433
+ * defaults to the main element id. Select field uses this. Focus remains in the field's
434
+ * `<input>` element while navigating its dropdown.
435
+ * @param {String} data.selector A CSS selector which identifies the navigable elements.
436
+ * @param {String} data.activeCls A CSS class to add to the currently active navigable element.
437
+ * @param {Boolean} data.wrap Pass as `true` to have navigation wrap from first to last and vice versa.
438
+ * @param {Boolean} [data.autoClick=false] Pass as `true` to have navigation click the target navigated to.
439
+ * TabPanels will use this on their tab toolbar.
440
+ */
441
+ subscribe(data) {
442
+ const
443
+ me = this,
444
+ subject = data.subject = DomAccess.getElement(data.id),
445
+ eventSource = data.eventSource = data.eventSource ? DomAccess.getElement(data.eventSource) : subject;
416
446
 
417
- // Walk forwards or backwards to the next or previous node which matches our selector
418
- treeWalker.currentNode = this.navigatorGetActiveItem(data) || data.subject;
419
- treeWalker[direction < 0 ? 'previousNode' : 'nextNode']();
447
+ subject.$navigator = data;
420
448
 
421
- // Found a target in the requested direction
422
- if (treeWalker.currentNode) {
423
- if (treeWalker.currentNode !== data.activeItem) {
424
- return treeWalker.currentNode;
425
- }
449
+ if (!data.activeCls) {
450
+ data.activeCls = 'neo-navigator-active-item'
426
451
  }
427
- // Could not find target in requested direction, then wrap if configured to do so
428
- else if (data.wrap !== false) {
429
- const allItems = data.subject.querySelector(data.selector);
430
452
 
431
- return allItems[direction === 1 ? 0 : allItems.length - 1];
432
- }
433
- }
453
+ // Ensure that only *one* of the child focusables is actually tabbable.
454
+ // We use arrow keys for internal navigation. TAB must move out.
455
+ me.fixItemFocusability(data);
434
456
 
435
- navigatorGetActiveItem(data) {
436
- let activeItem = data.activeItem && DomAccess.getElement(data.activeItem.id);
457
+ // Finds a focusable item starting from a descendant el within one of our selector items
458
+ data.findFocusable = el => DomUtils.closest(el, el =>
459
+ // We're looking for an element that is focusable
460
+ DomUtils.isFocusable(el) &&
461
+ // And within our subject element
462
+ (subject.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_CONTAINED_BY) &&
463
+ // And within an element that matches our selector
464
+ el.closest(data.selector)
465
+ );
437
466
 
438
- if (!activeItem && ('activeIndex' in data)) {
439
- const allItems = data.subject.querySelectorAll(data.selector);
467
+ // TreeWalker so that we can easily move between navigable elements within the target.
468
+ data.treeWalker = document.createTreeWalker(subject, NodeFilter.SHOW_ELEMENT, node => me.navigateNodeFilter(node, data));
440
469
 
441
- activeItem = allItems[Math.max(Math.min(data.activeIndex, allItems.length - 1), 0)];
442
- }
443
- return activeItem;
470
+ // We have to know when the DOM mutates in case the active item is removed.
471
+ (data.targetMutationMonitor = new MutationObserver(e => me.navigateTargetChildListChange(e, data))).observe(subject, {
472
+ childList : true,
473
+ subtree : true
474
+ });
475
+
476
+ eventSource.addEventListener('keydown', data.l1 = e => me.navigateKeyDownHandler(e, data));
477
+ subject.addEventListener('mousedown', data.l2 = e => me.navigateMouseDownHandler(e, data));
478
+ subject.addEventListener('click', data.l3 = e => me.navigateClickHandler(e, data));
479
+ subject.addEventListener('focusin', data.l4 = e => me.navigateFocusInHandler(e, data));
480
+ subject.addEventListener('focusout', data.l5 = e => me.navigateFocusOutHandler(e, data));
444
481
  }
445
482
 
446
- navigateNodeFilter(node, data) {
447
- return node.offsetParent && node.matches?.(data.selector) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
483
+ /**
484
+ * @param {Object} data
485
+ */
486
+ unsubscribe(data) {
487
+ const target = DomAccess.getElement(data.id);
488
+
489
+ data = target?.$navigator;
490
+
491
+ if (data) {
492
+ delete target.$navigator;
493
+ data.targetMutationMonitor.disconnect();
494
+ data.eventSource.removeEventListener('keydown', data.l1);
495
+ target.removeEventListener('mousedown', data.l2);
496
+ target.removeEventListener('click', data.l3);
497
+ target.removeEventListener('focusin', data.l4);
498
+ target.removeEventListener('focusout', data.l5);
499
+ }
448
500
  }
449
501
  }
450
502
 
@@ -5,5 +5,6 @@ import '../../src/button/Base.mjs';
5
5
  import '../../src/component/DateSelector.mjs';
6
6
  import '../../src/form/field/ComboBox.mjs';
7
7
  import '../../src/list/Chip.mjs';
8
+ import '../../src/toolbar/Base.mjs';
8
9
 
9
10
  export const onStart = () => Neo.app({name: 'AppEmpty'})
@@ -0,0 +1,88 @@
1
+ StartTest(t => {
2
+ t.it('Checking colliding style updates', async t => {
3
+ const containerId = await Neo.worker.App.createNeoInstance({
4
+ ntype : 'container',
5
+ height: 250,
6
+ style : {backgroundColor: 'red'},
7
+ width : 300
8
+ });
9
+
10
+ t.is(containerId, 'neo-container-1');
11
+
12
+ await t.waitForSelector('.neo-container');
13
+ t.diag('Container got rendered.');
14
+
15
+ const componentId = await Neo.worker.App.createNeoInstance({
16
+ ntype : 'component',
17
+ height : 150,
18
+ parentId: containerId,
19
+ style : {backgroundColor: 'blue'},
20
+ width : 150
21
+ });
22
+
23
+ t.is(componentId, 'neo-component-1');
24
+ t.diag('Component got rendered.');
25
+
26
+ t.diag('Child update before parent update');
27
+ Neo.worker.App.setConfigs({id: componentId, style: {backgroundColor: 'green'}});
28
+ Neo.worker.App.setConfigs({id: containerId, style: {backgroundColor: 'orange'}});
29
+
30
+ await t.waitFor(100);
31
+
32
+ t.is(document.getElementById(componentId).style.backgroundColor, 'green');
33
+ t.is(document.getElementById(containerId).style.backgroundColor, 'orange');
34
+
35
+ t.diag('Parent update before child update');
36
+ Neo.worker.App.setConfigs({id: containerId, style: {backgroundColor: 'pink'}});
37
+ Neo.worker.App.setConfigs({id: componentId, style: {backgroundColor: 'purple'}});
38
+
39
+ await t.waitFor(100);
40
+
41
+ t.is(document.getElementById(containerId).style.backgroundColor, 'pink');
42
+ t.is(document.getElementById(componentId).style.backgroundColor, 'purple');
43
+
44
+ await Neo.worker.App.destroyNeoInstance(containerId)
45
+ });
46
+
47
+ t.it('Checking colliding vdom updates', async t => {
48
+ const toolbarId = await Neo.worker.App.createNeoInstance({
49
+ ntype : 'toolbar',
50
+ height: 200,
51
+ width : 300
52
+ });
53
+
54
+ t.is(toolbarId, 'neo-toolbar-1');
55
+
56
+ await t.waitForSelector('.neo-toolbar');
57
+ t.diag('Toolbar got rendered.');
58
+
59
+ const buttonId = await Neo.worker.App.createNeoInstance({
60
+ ntype : 'button',
61
+ parentId: toolbarId,
62
+ text : 'hello'
63
+ });
64
+
65
+ t.is(buttonId, 'neo-button-1');
66
+
67
+ await t.waitForSelector('.neo-button');
68
+ t.diag('Button got rendered.');
69
+
70
+ t.diag('Child update before parent update');
71
+ Neo.worker.App.setConfigs({id: buttonId, text : 'world'});
72
+ Neo.worker.App.setConfigs({id: toolbarId, height: 300});
73
+
74
+ await t.waitFor(100);
75
+
76
+ t.is(document.getElementById(toolbarId).style.height, '300px');
77
+ t.is(document.getElementById(buttonId).firstChild.innerHTML, 'world');
78
+
79
+ t.diag('Parent update before child update');
80
+ Neo.worker.App.setConfigs({id: toolbarId, height: 200});
81
+ Neo.worker.App.setConfigs({id: buttonId, text : 'hello'});
82
+
83
+ await t.waitFor(100);
84
+
85
+ t.is(document.getElementById(toolbarId).style.height, '200px');
86
+ t.is(document.getElementById(buttonId).firstChild.innerHTML, 'hello');
87
+ });
88
+ });
@@ -75,6 +75,7 @@ project.plan({
75
75
  }, {
76
76
  group: 'component',
77
77
  items: [
78
+ 'files/component/Base.mjs',
78
79
  'files/component/DateSelector.mjs'
79
80
  ]
80
81
  }, {
@@ -1,59 +0,0 @@
1
- import PageSectionsList from './PageSectionsList.mjs';
2
- import Panel from '../../../../src/container/Panel.mjs';
3
-
4
- /**
5
- * @class Portal.view.learn.PageSectionsPanel
6
- * @extends Neo.container.Panel
7
- */
8
- class PageSectionsPanel extends Panel {
9
- static config = {
10
- /**
11
- * @member {String} className='Portal.view.learn.PageSectionsPanel'
12
- * @protected
13
- */
14
- className: 'Portal.view.learn.PageSectionsPanel',
15
- /**
16
- * @member {String[]} cls=['portal-page-sections-panel']
17
- */
18
- cls: ['portal-page-sections-panel'],
19
- /**
20
- * @member {Object[]} headers
21
- */
22
- // headers: [{
23
- // dock : 'top',
24
- // items: [{
25
- // ntype: 'label',
26
- // text : 'On this page'
27
- // }, '->', {
28
- // iconCls: 'fas fa-chevron-right',
29
- // ui : 'secondary',
30
- // tooltip: 'Collapse Sections'
31
- // }]
32
- // }],
33
- /**
34
- * @member {Object[]} items
35
- */
36
- items: [{
37
- vdom:
38
- { cn: [
39
- { tag: 'h3', html: 'On this page' },
40
- ]}
41
-
42
- },{
43
- module : PageSectionsList,
44
- reference: 'list'
45
- }]
46
- }
47
-
48
- /**
49
- * Convenience shortcut
50
- * @member {Portal.view.learn.PageSectionsList} list
51
- */
52
- get list() {
53
- return this.getReference('list')
54
- }
55
- }
56
-
57
- Neo.setupClass(PageSectionsPanel);
58
-
59
- export default PageSectionsPanel;
@@ -1,40 +0,0 @@
1
- .portal-page-sections-panel.neo-panel {
2
- border: none; // reset the default 1px
3
-
4
-
5
- background-color: transparent;
6
- display: block;
7
- width: 250px;
8
- flex-shrink: 0;
9
- z-index: 1;
10
- position: fixed;
11
- top: 80px;
12
- right: 0px;
13
- bottom: 0px;
14
-
15
- @media only screen and (max-width: 1100px) {
16
- display: none;
17
- }
18
-
19
- h3 {
20
- margin-left: 16px;
21
- }
22
-
23
- .neo-panel-header-toolbar {
24
- border : none; // reset the default 1px
25
- border-bottom: 1px solid #f2f2f2;
26
-
27
- .neo-button {
28
- // todo: we need styling for toolbar buttons within neo-theme-neo-light
29
- border-radius: 4px;
30
- height : inherit;
31
- min-width : inherit;
32
- padding : 5px 12px;
33
-
34
- .neo-button-glyph {
35
- color: var(--panel-header-text-color);
36
- }
37
- }
38
- }
39
-
40
- }