neo.mjs 6.10.9 → 6.10.11
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/apps/ServiceWorker.mjs +2 -2
- package/apps/portal/view/learn/ContentTreeList.mjs +24 -12
- package/apps/portal/view/learn/LivePreview.mjs +28 -11
- package/buildScripts/createAppMinimal.mjs +391 -0
- package/examples/ServiceWorker.mjs +2 -2
- package/examples/button/base/neo-config.json +2 -1
- package/examples/list/chip/neo-config.json +1 -2
- package/package.json +72 -70
- package/resources/data/deck/learnneo/data/theBeatles.json +22 -0
- package/resources/data/deck/learnneo/p/2023-10-14T19-25-08-153Z.md +29 -20
- package/resources/data/deck/learnneo/p/ComponentModels.md +116 -1
- package/resources/data/deck/learnneo/p/Config.md +157 -0
- package/resources/data/deck/learnneo/p/DescribingTheUI.md +67 -1
- package/resources/data/deck/learnneo/p/Earthquakes.md +214 -0
- package/resources/data/deck/learnneo/p/Events.md +142 -1
- package/resources/data/deck/learnneo/p/Extending.md +116 -1
- package/resources/data/deck/learnneo/p/References.md +126 -0
- package/resources/data/deck/learnneo/p/TestLivePreview.md +28 -6
- package/resources/data/deck/learnneo/t.json +5 -6
- package/resources/data/deck/training/p/2022-12-27T21-55-30-948Z.md +1 -1
- package/resources/data/deck/training/p/2022-12-27T22-23-55-083Z.md +1 -1
- package/resources/data/deck/training/p/2022-12-29T16-00-13-223Z.md +1 -1
- package/resources/data/deck/training/p/2022-12-29T18-34-25-826Z.md +1 -1
- package/resources/data/deck/training/p/2022-12-29T18-36-56-893Z.md +1 -1
- package/resources/data/deck/training/p/2022-12-31T18-43-56-338Z.md +1 -1
- package/resources/data/deck/training/p/2022-12-31T18-51-50-682Z.md +1 -1
- package/resources/data/deck/training/p/2022-12-31T18-54-04-176Z.md +1 -1
- package/resources/data/deck/training/p/2023-01-01T17-49-18-429Z.md +1 -1
- package/resources/data/deck/training/p/2023-01-01T21-23-17-716Z.md +1 -1
- package/resources/data/deck/training/p/2023-01-06T23-21-31-685Z.md +1 -1
- package/resources/data/deck/training/p/2023-01-06T23-34-13-897Z.md +2 -2
- package/resources/data/deck/training/p/2023-01-06T23-46-36-687Z.md +1 -1
- package/resources/data/deck/training/p/2023-01-08T01-24-21-088Z.md +1 -1
- package/resources/data/deck/training/p/2023-01-08T02-11-26-333Z.md +2 -2
- package/resources/data/deck/training/p/2023-01-14T00-40-27-784Z.md +2 -2
- package/resources/data/deck/training/p/2023-07-31T00-37-21-927Z.md +2 -2
- package/resources/data/deck/training/p/2023-10-14T19-25-08-153Z.md +3 -3
- package/resources/scss/src/apps/newwebsite/Viewport.scss +32 -0
- package/resources/scss/src/apps/portal/learn/ContentView.scss +20 -4
- package/resources/scss/src/apps/portal/learn/LivePreview.scss +8 -0
- package/resources/scss/src/component/Base.scss +13 -4
- package/resources/scss/src/form/field/Select.scss +2 -5
- package/resources/scss/src/form/field/Text.scss +0 -1
- package/resources/scss/src/list/Base.scss +47 -2
- package/resources/scss/src/list/Chip.scss +10 -4
- package/resources/scss/theme-dark/list/Base.scss +11 -10
- package/resources/scss/theme-light/list/Base.scss +11 -10
- package/resources/scss/theme-neo-light/design-tokens/Components.scss +3 -0
- package/resources/scss/theme-neo-light/list/Base.scss +1 -0
- package/src/DefaultConfig.mjs +3 -3
- package/src/collection/Base.mjs +4 -0
- package/src/component/Base.mjs +7 -0
- package/src/container/Base.mjs +6 -12
- package/src/core/Base.mjs +5 -2
- package/src/data/Model.mjs +7 -0
- package/src/data/RecordFactory.mjs +5 -4
- package/src/form/field/Base.mjs +11 -0
- package/src/form/field/Picker.mjs +0 -1
- package/src/form/field/Select.mjs +208 -257
- package/src/form/field/Text.mjs +3 -3
- package/src/form/field/trigger/Base.mjs +5 -6
- package/src/layout/Flexbox.mjs +23 -31
- package/src/layout/HBox.mjs +1 -1
- package/src/layout/VBox.mjs +1 -1
- package/src/list/Base.mjs +64 -31
- package/src/main/DomAccess.mjs +55 -28
- package/src/main/DomEvents.mjs +2 -1
- package/src/main/DomUtils.mjs +66 -0
- package/src/main/addon/Navigator.mjs +332 -0
- package/src/manager/DomEvent.mjs +2 -1
- package/src/selection/ListModel.mjs +46 -82
- package/src/selection/Model.mjs +56 -33
- package/src/util/Array.mjs +5 -2
- package/src/util/Function.mjs +31 -0
- package/src/util/String.mjs +9 -0
- package/src/vdom/Helper.mjs +1 -2
- package/test/components/app.mjs +4 -3
- package/test/components/files/component/ChipList.mjs +125 -0
- package/test/components/files/form/field/Select.mjs +177 -2
- package/test/components/siesta.js +34 -1
package/src/util/Array.mjs
CHANGED
@@ -29,6 +29,7 @@ class NeoArray extends Base {
|
|
29
29
|
arr.push(item);
|
30
30
|
}
|
31
31
|
});
|
32
|
+
return arr;
|
32
33
|
}
|
33
34
|
|
34
35
|
/**
|
@@ -97,6 +98,7 @@ class NeoArray extends Base {
|
|
97
98
|
|
98
99
|
index > -1 && arr.splice(index, 1);
|
99
100
|
});
|
101
|
+
return arr;
|
100
102
|
}
|
101
103
|
|
102
104
|
/**
|
@@ -108,7 +110,7 @@ class NeoArray extends Base {
|
|
108
110
|
*/
|
109
111
|
static removeAdd(arr, removeItems, addItems) {
|
110
112
|
this.remove(arr, removeItems);
|
111
|
-
this.add(arr, addItems);
|
113
|
+
return this.add(arr, addItems);
|
112
114
|
}
|
113
115
|
|
114
116
|
/**
|
@@ -118,7 +120,7 @@ class NeoArray extends Base {
|
|
118
120
|
* @param {Boolean} [add]
|
119
121
|
*/
|
120
122
|
static toggle(arr, item, add = !this.hasItem(arr, item)) {
|
121
|
-
this[add ? 'add' : 'remove'](arr, item);
|
123
|
+
return this[add ? 'add' : 'remove'](arr, item);
|
122
124
|
}
|
123
125
|
|
124
126
|
/**
|
@@ -149,6 +151,7 @@ class NeoArray extends Base {
|
|
149
151
|
arr.unshift(item);
|
150
152
|
}
|
151
153
|
});
|
154
|
+
return arr;
|
152
155
|
}
|
153
156
|
}
|
154
157
|
|
package/src/util/Function.mjs
CHANGED
@@ -121,3 +121,34 @@ export function throttle(callback, scope, delay=300) {
|
|
121
121
|
}
|
122
122
|
}
|
123
123
|
}
|
124
|
+
|
125
|
+
/**
|
126
|
+
* @param {Function} callback
|
127
|
+
* @param {Neo.core.Base} scope
|
128
|
+
* @param {Number} delay=300
|
129
|
+
* @returns {Function}
|
130
|
+
*/
|
131
|
+
export function buffer(callback, scope, delay=300) {
|
132
|
+
let timeoutId;
|
133
|
+
|
134
|
+
const wrapper = function(...args) {
|
135
|
+
// callback invocation comes "delay" ms after the last call to wrapper
|
136
|
+
// so cancel any pending invocation.
|
137
|
+
clearTimeout(timeoutId);
|
138
|
+
|
139
|
+
wrapper.isPending = true;
|
140
|
+
|
141
|
+
timeoutId = setTimeout(() => {
|
142
|
+
timeoutId = 0;
|
143
|
+
wrapper.isPending = false;
|
144
|
+
callback.apply(scope, args);
|
145
|
+
}, delay);
|
146
|
+
};
|
147
|
+
|
148
|
+
wrapper.cancel = () => {
|
149
|
+
wrapper.isPending = false;
|
150
|
+
clearTimeout(timeoutId);
|
151
|
+
};
|
152
|
+
|
153
|
+
return wrapper;
|
154
|
+
}
|
package/src/util/String.mjs
CHANGED
@@ -81,6 +81,15 @@ class StringUtil extends Base {
|
|
81
81
|
|
82
82
|
return value.replace(me.entityPattern, me.getCharFromEntity.bind(me));
|
83
83
|
}
|
84
|
+
|
85
|
+
/**
|
86
|
+
* Returns the passed string with the first letter uncapitalized.
|
87
|
+
* @param {Strinhg} value
|
88
|
+
* @returns
|
89
|
+
*/
|
90
|
+
static uncapitalize(value) {
|
91
|
+
return value && value[0].toLowerCase() + value.substring(1)
|
92
|
+
}
|
84
93
|
}
|
85
94
|
|
86
95
|
Neo.applyClassConfig(StringUtil);
|
package/src/vdom/Helper.mjs
CHANGED
@@ -580,8 +580,7 @@ class Helper extends Base {
|
|
580
580
|
string += ` ${key}`;
|
581
581
|
}
|
582
582
|
} else if (key !== 'removeDom') {
|
583
|
-
|
584
|
-
string += ` ${key}="${value.replaceAll('"', '"')}"`;
|
583
|
+
string += ` ${key}="${value?.replaceAll?.('"', '"') ?? value}"`;
|
585
584
|
}
|
586
585
|
});
|
587
586
|
|
package/test/components/app.mjs
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
// Important: You need to import all classes which you want to use inside tests here
|
2
2
|
// (excluding base classes (prototypes) of already imported classes)
|
3
3
|
|
4
|
-
import
|
5
|
-
import
|
6
|
-
import
|
4
|
+
import '../../src/button/Base.mjs';
|
5
|
+
import '../../src/component/DateSelector.mjs';
|
6
|
+
import '../../src/form/field/Select.mjs';
|
7
|
+
import '../../src/list/Chip.mjs'
|
7
8
|
|
8
9
|
export const onStart = () => Neo.app({name: 'AppEmpty'})
|
@@ -0,0 +1,125 @@
|
|
1
|
+
StartTest(t => {
|
2
|
+
let testId;
|
3
|
+
|
4
|
+
async function setup(config = {}) {
|
5
|
+
testId = await Neo.worker.App.createNeoInstance(Neo.merge({
|
6
|
+
ntype : 'chip-list',
|
7
|
+
displayField : 'firstname',
|
8
|
+
width : 300,
|
9
|
+
role : 'listbox',
|
10
|
+
itemRole : 'option',
|
11
|
+
navigator : {
|
12
|
+
wrap : true
|
13
|
+
},
|
14
|
+
store : {
|
15
|
+
keyProperty: 'githubId',
|
16
|
+
model: {
|
17
|
+
fields: [{
|
18
|
+
name: 'country',
|
19
|
+
type: 'String'
|
20
|
+
}, {
|
21
|
+
name: 'firstname',
|
22
|
+
type: 'String'
|
23
|
+
}, {
|
24
|
+
name: 'githubId',
|
25
|
+
type: 'String'
|
26
|
+
}, {
|
27
|
+
name: 'lastname',
|
28
|
+
type: 'String'
|
29
|
+
}]
|
30
|
+
},
|
31
|
+
|
32
|
+
data: [{
|
33
|
+
country : 'Germany',
|
34
|
+
firstname: 'Tobias',
|
35
|
+
githubId : 'tobiu',
|
36
|
+
lastname : 'Uhlig'
|
37
|
+
}, {
|
38
|
+
country : 'USA',
|
39
|
+
firstname: 'Rich',
|
40
|
+
githubId : 'rwaters',
|
41
|
+
lastname : 'Waters'
|
42
|
+
}, {
|
43
|
+
country : 'Germany',
|
44
|
+
firstname: 'Nils',
|
45
|
+
githubId : 'mrsunshine',
|
46
|
+
lastname : 'Dehl'
|
47
|
+
}, {
|
48
|
+
country : 'USA',
|
49
|
+
firstname: 'Gerard',
|
50
|
+
githubId : 'camtnbikerrwc',
|
51
|
+
lastname : 'Horan'
|
52
|
+
}, {
|
53
|
+
country : 'Slovakia',
|
54
|
+
firstname: 'Jozef',
|
55
|
+
githubId : 'jsakalos',
|
56
|
+
lastname : 'Sakalos'
|
57
|
+
}, {
|
58
|
+
country : 'Germany',
|
59
|
+
firstname: 'Bastian',
|
60
|
+
githubId : 'bhaustein',
|
61
|
+
lastname : 'Haustein'
|
62
|
+
}]
|
63
|
+
}
|
64
|
+
}, config));
|
65
|
+
|
66
|
+
|
67
|
+
return testId;
|
68
|
+
}
|
69
|
+
|
70
|
+
// Clear the eway for each test
|
71
|
+
t.beforeEach(async t => {
|
72
|
+
if (testId) {
|
73
|
+
await Neo.worker.App.destroyNeoInstance(testId);
|
74
|
+
testId = null;
|
75
|
+
}
|
76
|
+
});
|
77
|
+
|
78
|
+
t.it('Sanity', async t => {
|
79
|
+
await setup({ });
|
80
|
+
|
81
|
+
// Click on the *item*, *not* the focusable chip.
|
82
|
+
// We are testing that the Navigator directs focus to the focusable heart of the
|
83
|
+
// item - the Chip - which will then recieve focus and cause item activation.
|
84
|
+
await t.click('.neo-list-item', null, null, null, ['100%-1', 1]);
|
85
|
+
|
86
|
+
// That should select and activate the clicked item.
|
87
|
+
// And focus the chip inside it.
|
88
|
+
await t.waitForSelector('.neo-list-item.neo-navigator-active-item.neo-selected:nth-child(1) .neo-chip:focus');
|
89
|
+
t.selectorCountIs('.neo-list-item.neo-navigator-active-item', 1);
|
90
|
+
|
91
|
+
await t.type(null, '[END]');
|
92
|
+
|
93
|
+
// That should select and activate the last item.
|
94
|
+
// And focus the chip inside it.
|
95
|
+
await t.waitForSelector('.neo-list-item.neo-navigator-active-item:not(.neo-selected):nth-child(6) .neo-chip:focus');
|
96
|
+
|
97
|
+
// Item 1 is still the only one selected, and it's not focused
|
98
|
+
t.selectorExists('.neo-list-item.neo-selected:nth-child(1) .neo-chip:not(:focus)');
|
99
|
+
t.selectorCountIs('.neo-list-item.neo-selected', 1);
|
100
|
+
t.selectorCountIs('.neo-list-item.neo-navigator-active-item', 1);
|
101
|
+
|
102
|
+
await t.type(null, '[ENTER]');
|
103
|
+
|
104
|
+
// Item 6 is now the only one selected
|
105
|
+
await t.waitForSelector('.neo-list-item.neo-navigator-active-item.neo-selected:nth-child(6) .neo-chip:focus');
|
106
|
+
t.selectorCountIs('.neo-list-item.neo-selected', 1);
|
107
|
+
t.selectorCountIs('.neo-list-item.neo-navigator-active-item', 1);
|
108
|
+
|
109
|
+
await t.type(null, '[DOWN]');
|
110
|
+
|
111
|
+
// That should select and activate the first item.
|
112
|
+
// And focus the chip inside it.
|
113
|
+
await t.waitForSelector('.neo-list-item.neo-navigator-active-item:not(.neo-selected):nth-child(1) .neo-chip:focus');
|
114
|
+
t.selectorCountIs('.neo-list-item.neo-selected', 1);
|
115
|
+
t.selectorCountIs('.neo-list-item.neo-navigator-active-item', 1);
|
116
|
+
|
117
|
+
await t.type(null, '[UP]');
|
118
|
+
|
119
|
+
// Item 6 is now the only one selected
|
120
|
+
await t.waitForSelector('.neo-list-item.neo-navigator-active-item.neo-selected:nth-child(6) .neo-chip:focus');
|
121
|
+
t.selectorCountIs('.neo-list-item.neo-selected', 1);
|
122
|
+
t.selectorCountIs('.neo-list-item.neo-navigator-active-item', 1);
|
123
|
+
});
|
124
|
+
|
125
|
+
});
|
@@ -1,6 +1,8 @@
|
|
1
1
|
StartTest(t => {
|
2
|
-
|
3
|
-
|
2
|
+
let testId, inputField;
|
3
|
+
|
4
|
+
async function setup(config = {}) {
|
5
|
+
testId = await Neo.worker.App.createNeoInstance(Neo.merge({
|
4
6
|
ntype : 'selectfield',
|
5
7
|
labelPosition: 'inline',
|
6
8
|
labelText : 'US States',
|
@@ -22,6 +24,179 @@ StartTest(t => {
|
|
22
24
|
}]
|
23
25
|
}
|
24
26
|
}
|
27
|
+
}, config))
|
28
|
+
|
29
|
+
// Wait for input element to be present
|
30
|
+
await t.waitForSelector(`#${testId} input.neo-textfield-input:not(.neo-typeahead-input)`);
|
31
|
+
|
32
|
+
// Grab input element
|
33
|
+
inputField = t.query(`#${testId} input.neo-textfield-input:not(.neo-typeahead-input)`)[0]
|
34
|
+
|
35
|
+
return testId;
|
36
|
+
}
|
37
|
+
|
38
|
+
// Clear the eway for each test
|
39
|
+
t.beforeEach(async t => {
|
40
|
+
if (testId) {
|
41
|
+
await Neo.worker.App.destroyNeoInstance(testId);
|
42
|
+
testId = null;
|
43
|
+
}
|
44
|
+
});
|
45
|
+
|
46
|
+
t.it('Editable', async t => {
|
47
|
+
await setup({
|
48
|
+
editable : false
|
25
49
|
});
|
50
|
+
const blurEl = document.createElement('input');
|
51
|
+
document.body.appendChild(blurEl);
|
52
|
+
|
53
|
+
await t.waitFor(() => inputField.getAttribute('readonly'));
|
54
|
+
|
55
|
+
let blurCount = 0;
|
56
|
+
inputField.addEventListener('blur', () => blurCount++);
|
57
|
+
|
58
|
+
t.hasAttributeValue(inputField, 'role', 'combobox');
|
59
|
+
t.hasAttributeValue(inputField, 'aria-haspopup', 'listbox');
|
60
|
+
t.hasAttributeValue(inputField, 'aria-expanded', 'false');
|
61
|
+
|
62
|
+
await t.click(inputField);
|
63
|
+
|
64
|
+
await t.waitForSelector('.neo-picker-container');
|
65
|
+
|
66
|
+
t.hasAttributeValue(inputField, 'aria-expanded', 'true');
|
67
|
+
|
68
|
+
// Roles correct
|
69
|
+
t.hasAttributeValue('.neo-picker-container .neo-list', 'role', 'listbox');
|
70
|
+
t.selectorCountIs('.neo-picker-container .neo-list .neo-list-item[role="option"]', 59);
|
71
|
+
|
72
|
+
// Nothing selected
|
73
|
+
t.hasAttributeValue(inputField, 'aria-activedescendant', '');
|
74
|
+
|
75
|
+
// Should activate the first list item. editable : false means we can still be focused
|
76
|
+
// and select values, just that the filter input is read-only.
|
77
|
+
await t.type(null, '[DOWN]');
|
78
|
+
|
79
|
+
await t.waitForSelector('input.neo-textfield-input:not(:disabled)[aria-activedescendant="neo-list-1__AL"]');
|
80
|
+
|
81
|
+
t.hasAttributeValue(inputField, 'aria-activedescendant', 'neo-list-1__AL');
|
82
|
+
|
83
|
+
// Select that first item.
|
84
|
+
await t.type(null, '[ENTER]');
|
85
|
+
|
86
|
+
await t.waitForSelectorNotFound('.neo-picker-container:visible');
|
87
|
+
|
88
|
+
t.is(inputField.value, 'Alabama');
|
89
|
+
|
90
|
+
// Focus nebver leaves the input field
|
91
|
+
t.is(blurCount, 0);
|
92
|
+
|
93
|
+
await t.type(null, '[TAB]');
|
94
|
+
|
95
|
+
// Value still correct after blur
|
96
|
+
t.is(inputField.value, 'Alabama');
|
97
|
+
blurEl.remove();
|
98
|
+
|
99
|
+
// Now focus has left
|
100
|
+
t.is(blurCount, 1);
|
101
|
+
});
|
102
|
+
|
103
|
+
t.iit('Keyboard navigation', async t => {
|
104
|
+
await setup();
|
105
|
+
const blurEl = document.createElement('input');
|
106
|
+
document.body.appendChild(blurEl);
|
107
|
+
|
108
|
+
await t.click(inputField);
|
109
|
+
await t.type(null, '[DOWN]');
|
110
|
+
|
111
|
+
// Picker Must show with Alabama activated
|
112
|
+
await t.waitForSelector('.neo-list-item.neo-navigator-active-item:contains("Alabama")');
|
113
|
+
|
114
|
+
await t.type(null, '[END]');
|
115
|
+
|
116
|
+
// Picker should go to end
|
117
|
+
await t.waitForSelector('.neo-list-item.neo-navigator-active-item:contains("Wyoming")');
|
118
|
+
|
119
|
+
await t.type(null, '[UP]');
|
120
|
+
|
121
|
+
await t.waitForSelector('.neo-list-item.neo-navigator-active-item:contains("Wisconsin")');
|
122
|
+
|
123
|
+
await t.type(null, '[DOWN]');
|
124
|
+
|
125
|
+
await t.waitForSelector('.neo-list-item.neo-navigator-active-item:contains("Wyoming")');
|
126
|
+
|
127
|
+
await t.type(null, '[ENTER]');
|
128
|
+
|
129
|
+
await t.waitForSelectorNotFound('.neo-picker-container:visible');
|
130
|
+
|
131
|
+
t.is(inputField.value, 'Wyoming');
|
132
|
+
|
133
|
+
await t.type(null, '[DOWN]');
|
134
|
+
|
135
|
+
// Picker Must show with Wyoming activated
|
136
|
+
await t.waitForSelector('.neo-list-item.neo-navigator-active-item:contains("Wyoming")');
|
137
|
+
|
138
|
+
await t.type(null, '[UP]');
|
139
|
+
|
140
|
+
await t.waitForSelector('.neo-list-item.neo-navigator-active-item:contains("Wisconsin")');
|
141
|
+
|
142
|
+
await t.type(null, '[ENTER]');
|
143
|
+
|
144
|
+
await t.waitForSelectorNotFound('.neo-picker-container:visible');
|
145
|
+
|
146
|
+
t.is(inputField.value, 'Wisconsin');
|
147
|
+
|
148
|
+
await t.type(null, '[DOWN]');
|
149
|
+
|
150
|
+
// Picker Must show with Wisconsin activated
|
151
|
+
await t.waitForSelector('.neo-list-item.neo-navigator-active-item:contains("Wisconsin")');
|
152
|
+
|
153
|
+
// We're single select, so only one list item must be selected
|
154
|
+
t.selectorCountIs('[aria-selected="true"]', 1);
|
155
|
+
|
156
|
+
await t.type(null, '[TAB]');
|
157
|
+
|
158
|
+
// Value still correct after blur
|
159
|
+
t.is(inputField.value, 'Wisconsin');
|
160
|
+
blurEl.remove();
|
161
|
+
});
|
162
|
+
|
163
|
+
t.it('Type to filter', async t => {
|
164
|
+
await setup();
|
165
|
+
const blurEl = document.createElement('input');
|
166
|
+
document.body.appendChild(blurEl);
|
167
|
+
|
168
|
+
await t.click(inputField);
|
169
|
+
|
170
|
+
await t.type(null, 'Mar');
|
171
|
+
|
172
|
+
// Picker Must show with Marshall Islands activated
|
173
|
+
await t.waitForSelector('.neo-list-item.neo-navigator-active-item:contains("Marshall Islands")');
|
174
|
+
|
175
|
+
// Matches three states
|
176
|
+
t.selectorCountIs('.neo-picker-container .neo-list-item', 3);
|
177
|
+
|
178
|
+
// Only one is selected
|
179
|
+
t.selectorCountIs('.neo-list-item.neo-navigator-active-item', 1);
|
180
|
+
|
181
|
+
await t.type(null, '[DOWN]');
|
182
|
+
|
183
|
+
// Picker Must show with Maryland activated
|
184
|
+
await t.waitForSelector('.neo-list-item.neo-navigator-active-item:contains("Maryland")');
|
185
|
+
|
186
|
+
// Matches three states
|
187
|
+
t.selectorCountIs('.neo-picker-container .neo-list-item', 3);
|
188
|
+
|
189
|
+
// Only one is selected
|
190
|
+
t.selectorCountIs('.neo-list-item.neo-navigator-active-item', 1);
|
191
|
+
|
192
|
+
// Blur without selecting a value
|
193
|
+
await t.type(null, '[TAB]');
|
194
|
+
|
195
|
+
await t.waitFor(100)
|
196
|
+
|
197
|
+
// Inputs must have been cleared. Both typeahead and filter.
|
198
|
+
t.isDeeply(t.query(`#${testId} input`).map(i => i.value), ['', '']);
|
199
|
+
|
200
|
+
blurEl.remove();
|
26
201
|
});
|
27
202
|
});
|
@@ -31,6 +31,38 @@ project.configure({
|
|
31
31
|
this.SUPER(...arguments);
|
32
32
|
this.SUPER(t => t.waitFor(50));
|
33
33
|
}
|
34
|
+
},
|
35
|
+
|
36
|
+
methods : {
|
37
|
+
async waitForSelectorCount(selector, root, count) {
|
38
|
+
if (typeof root === 'number') {
|
39
|
+
count = root;
|
40
|
+
root = undefined;
|
41
|
+
}
|
42
|
+
return this.waitFor(() => this.query(selector, root).length === count);
|
43
|
+
},
|
44
|
+
|
45
|
+
async waitForDomMutation(root = this.global.document.body) {
|
46
|
+
root = this.normalizeElement(root);
|
47
|
+
|
48
|
+
if (root) {
|
49
|
+
return new Promise(resolve => {
|
50
|
+
const m = new MutationObserver(() => {
|
51
|
+
m.disconnect();
|
52
|
+
resolve();
|
53
|
+
});
|
54
|
+
m.observe(root, {
|
55
|
+
subtree : true,
|
56
|
+
childList : true,
|
57
|
+
attributes : true,
|
58
|
+
characterData : true
|
59
|
+
});
|
60
|
+
setTimeout(() => {
|
61
|
+
m.disconnect();
|
62
|
+
}, this.timeout)
|
63
|
+
});
|
64
|
+
}
|
65
|
+
}
|
34
66
|
}
|
35
67
|
})
|
36
68
|
});
|
@@ -45,7 +77,8 @@ project.plan(
|
|
45
77
|
{
|
46
78
|
group: 'component',
|
47
79
|
items: [
|
48
|
-
'files/component/DateSelector.mjs'
|
80
|
+
'files/component/DateSelector.mjs',
|
81
|
+
'files/component/ChipList.mjs'
|
49
82
|
]
|
50
83
|
},
|
51
84
|
{
|