@neovici/cosmoz-treenode-navigator 5.0.0 → 5.2.0
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/workflows/workflow.yml +6 -11
- package/CHANGELOG.md +28 -0
- package/cosmoz-treenode-navigator.js +146 -212
- package/cosmoz-treenode-navigator.styles.js +119 -0
- package/helpers.js +45 -172
- package/package.json +3 -3
- package/test/cosmoz-treenode-navigator.test.js +33 -31
|
@@ -12,22 +12,17 @@ jobs:
|
|
|
12
12
|
runs-on: ubuntu-latest
|
|
13
13
|
steps:
|
|
14
14
|
- name: Checkout
|
|
15
|
-
uses: actions/checkout@
|
|
16
|
-
|
|
17
|
-
- name: Use Node.js 14.x
|
|
18
|
-
uses: actions/setup-node@v1
|
|
15
|
+
uses: actions/checkout@v2
|
|
19
16
|
with:
|
|
20
|
-
|
|
17
|
+
fetch-depth: 0
|
|
21
18
|
|
|
22
|
-
- name:
|
|
23
|
-
|
|
24
|
-
uses: actions/cache@v2
|
|
19
|
+
- name: Use Node.js
|
|
20
|
+
uses: actions/setup-node@v2
|
|
25
21
|
with:
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
node-version: 16.x
|
|
23
|
+
cache: 'npm'
|
|
28
24
|
|
|
29
25
|
- name: Install
|
|
30
|
-
if: steps.node-cache.outputs.cache-hit != 'true'
|
|
31
26
|
run: npm ci
|
|
32
27
|
|
|
33
28
|
- name: Lint commit messages
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
# [5.2.0](https://github.com/neovici/cosmoz-treenode-navigator/compare/v5.1.0...v5.2.0) (2022-08-01)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **cosmoz-treenode-navigator:** improve performance ([ac12d74](https://github.com/neovici/cosmoz-treenode-navigator/commit/ac12d74345501e34cdccde6962563a24d6efed4f))
|
|
7
|
+
|
|
8
|
+
# [5.1.0](https://github.com/neovici/cosmoz-treenode-navigator/compare/v5.0.1...v5.1.0) (2022-07-28)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **ci:** worflow typo ([17b92aa](https://github.com/neovici/cosmoz-treenode-navigator/commit/17b92aa332f0411b6548729002fd9cf62a99d223))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
* **cosmoz-treenode-navigator:** add keyboard navigation ([26b6d42](https://github.com/neovici/cosmoz-treenode-navigator/commit/26b6d42d99737d0b48a7fbc21a8677117b1d8a9e))
|
|
19
|
+
* refactor with lit-virtualizer instead of listbox ([7b69ab4](https://github.com/neovici/cosmoz-treenode-navigator/commit/7b69ab4e0e049c9c7f8e03c3b781f02fab83eb18))
|
|
20
|
+
* support `Right` key ([28a4df6](https://github.com/neovici/cosmoz-treenode-navigator/commit/28a4df6d35bc62ac49aba81135891ce8fcacfa05))
|
|
21
|
+
|
|
22
|
+
## [5.0.1](https://github.com/neovici/cosmoz-treenode-navigator/compare/v5.0.0...v5.0.1) (2022-07-27)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Bug Fixes
|
|
26
|
+
|
|
27
|
+
* **search:** remove label ([950ed7c](https://github.com/neovici/cosmoz-treenode-navigator/commit/950ed7cef3ab282f5fea8925dae121c771723eab))
|
|
28
|
+
|
|
1
29
|
# [5.0.0](https://github.com/neovici/cosmoz-treenode-navigator/compare/v4.0.0...v5.0.0) (2022-07-19)
|
|
2
30
|
|
|
3
31
|
|
|
@@ -1,25 +1,21 @@
|
|
|
1
1
|
import { component, useEffect, useState, useMemo, useCallback } from 'haunted';
|
|
2
2
|
import { html } from 'lit-html';
|
|
3
3
|
import { when } from 'lit-html/directives/when.js';
|
|
4
|
-
|
|
4
|
+
import { guard } from 'lit-html/directives/guard.js';
|
|
5
|
+
import { virtualize } from '@lit-labs/virtualizer/virtualize.js';
|
|
5
6
|
import '@neovici/cosmoz-input';
|
|
6
|
-
import '@neovici/cosmoz-
|
|
7
|
+
import { useMeta } from '@neovici/cosmoz-utils/hooks/use-meta';
|
|
7
8
|
import { notifyProperty } from '@neovici/cosmoz-utils/hooks/use-notify-property';
|
|
8
9
|
|
|
9
10
|
import {
|
|
11
|
+
getParentPath,
|
|
10
12
|
computeDataPlane,
|
|
11
13
|
computeRowClass,
|
|
12
|
-
computeSearching,
|
|
13
|
-
getNodeName,
|
|
14
|
-
hasChildren,
|
|
15
|
-
nodeStyles,
|
|
16
|
-
normalizeNodes,
|
|
17
14
|
onNodeDblClicked,
|
|
18
|
-
renderLevel,
|
|
19
|
-
showGlobalSearchBtn
|
|
20
15
|
} from './helpers';
|
|
16
|
+
import styles from './cosmoz-treenode-navigator.styles';
|
|
21
17
|
|
|
22
|
-
const TreenodeNavigator = host => {
|
|
18
|
+
const TreenodeNavigator = (host) => {
|
|
23
19
|
const {
|
|
24
20
|
/**
|
|
25
21
|
* The main node structure
|
|
@@ -38,16 +34,12 @@ const TreenodeNavigator = host => {
|
|
|
38
34
|
* Minimum length of searchValue to trigger a search
|
|
39
35
|
*/
|
|
40
36
|
searchMinLength,
|
|
41
|
-
nodePath
|
|
37
|
+
nodePath,
|
|
42
38
|
} = host,
|
|
43
39
|
/**
|
|
44
40
|
* The path of the opened node
|
|
45
41
|
*/
|
|
46
42
|
[openNodePath, setOpenNodePath] = useState(''),
|
|
47
|
-
/**
|
|
48
|
-
* The nodes on the path of the opened node
|
|
49
|
-
*/
|
|
50
|
-
[nodesOnOpenNodePath, setNodesOnOpenNodePath] = useState([]),
|
|
51
43
|
/**
|
|
52
44
|
* The highlighted (focused) node
|
|
53
45
|
* This is the node which is currently selected in the list
|
|
@@ -57,90 +49,41 @@ const TreenodeNavigator = host => {
|
|
|
57
49
|
* The search string
|
|
58
50
|
*/
|
|
59
51
|
[searchValue, setSearchValue] = useState(''),
|
|
52
|
+
search = useMemo(
|
|
53
|
+
() => (searchValue?.length > searchMinLength && searchValue) || undefined,
|
|
54
|
+
[searchValue, searchMinLength]
|
|
55
|
+
),
|
|
60
56
|
/**
|
|
61
57
|
* The currently displayed node list
|
|
62
58
|
*/
|
|
63
|
-
dataPlane = useMemo(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
renderLevel(openNodePath, tree),
|
|
68
|
-
tree
|
|
69
|
-
);
|
|
70
|
-
}, [tree, openNodePath, highlightedNode, searchValue]),
|
|
59
|
+
dataPlane = useMemo(
|
|
60
|
+
() => computeDataPlane(tree, search, openNodePath),
|
|
61
|
+
[tree, search, openNodePath]
|
|
62
|
+
),
|
|
71
63
|
/**
|
|
72
64
|
* Opens a node (renderLevel) based on a given path
|
|
73
65
|
* @param {object} clickedNode - The clicked node
|
|
74
66
|
* @return {undefined}
|
|
75
67
|
*/
|
|
76
|
-
openNode = useCallback(
|
|
77
|
-
|
|
78
|
-
setOpenNodePath(clickedNode
|
|
79
|
-
setSearchValue('');
|
|
80
|
-
}
|
|
81
|
-
if (!clickedNode) {
|
|
82
|
-
setOpenNodePath('');
|
|
68
|
+
openNode = useCallback(
|
|
69
|
+
(clickedNode) => {
|
|
70
|
+
setOpenNodePath(clickedNode?.pathLocator || '');
|
|
83
71
|
setSearchValue('');
|
|
84
|
-
setHighlightedNode(
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Returns true, if the path of a node should be visible in the view
|
|
89
|
-
* @param {Number} index - The node's current index in the list
|
|
90
|
-
* @param {Object} parentSectionName - The nodes current parent section name
|
|
91
|
-
* @return {Boolean} - If the path should be visible
|
|
92
|
-
*/
|
|
93
|
-
renderSection = (index, parentSectionName) => {
|
|
94
|
-
const _dataPlane = computeDataPlane(
|
|
95
|
-
computeSearching(searchValue, searchMinLength),
|
|
96
|
-
searchValue,
|
|
97
|
-
renderLevel(openNodePath, tree),
|
|
98
|
-
tree
|
|
99
|
-
);
|
|
100
|
-
if (
|
|
101
|
-
!computeSearching(searchValue, searchMinLength) ||
|
|
102
|
-
index == null ||
|
|
103
|
-
_dataPlane == null ||
|
|
104
|
-
index >= _dataPlane.length ||
|
|
105
|
-
parentSectionName == null
|
|
106
|
-
) {
|
|
107
|
-
return false;
|
|
108
|
-
}
|
|
109
|
-
if (index === 0) {
|
|
110
|
-
return true;
|
|
111
|
-
}
|
|
112
|
-
const prevItem = _dataPlane[index - 1];
|
|
113
|
-
return prevItem.parentSectionName !== parentSectionName;
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
useEffect(() => {
|
|
117
|
-
if (!openNodePath) {
|
|
118
|
-
notifyProperty(host, 'highlightedNodePath', '');
|
|
119
|
-
notifyProperty(host, 'nodesOnNodePath', []);
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
setNodesOnOpenNodePath(
|
|
123
|
-
normalizeNodes(tree.getPathNodes(openNodePath).filter(item => item))
|
|
72
|
+
setHighlightedNode();
|
|
73
|
+
},
|
|
74
|
+
[tree]
|
|
124
75
|
);
|
|
125
|
-
}, [openNodePath]);
|
|
126
76
|
|
|
127
77
|
useEffect(() => {
|
|
128
78
|
if (!openNodePath) {
|
|
129
79
|
notifyProperty(host, 'highlightedNodePath', '');
|
|
130
|
-
|
|
80
|
+
notifyProperty(host, 'nodesOnNodePath', []);
|
|
131
81
|
}
|
|
132
|
-
setNodesOnOpenNodePath(
|
|
133
|
-
normalizeNodes(tree.getPathNodes(openNodePath).filter(item => item))
|
|
134
|
-
);
|
|
135
82
|
}, [openNodePath]);
|
|
136
83
|
|
|
137
84
|
useEffect(() => {
|
|
138
85
|
if (nodePath !== undefined) {
|
|
139
|
-
host.dispatchEvent(
|
|
140
|
-
new CustomEvent('select-node', {
|
|
141
|
-
detail: {}
|
|
142
|
-
})
|
|
143
|
-
);
|
|
86
|
+
host.dispatchEvent(new CustomEvent('select-node'));
|
|
144
87
|
}
|
|
145
88
|
}, [nodePath]);
|
|
146
89
|
|
|
@@ -148,77 +91,113 @@ const TreenodeNavigator = host => {
|
|
|
148
91
|
notifyProperty(
|
|
149
92
|
host,
|
|
150
93
|
'highlightedNodePath',
|
|
151
|
-
|
|
94
|
+
highlightedNode?.pathLocator || ''
|
|
152
95
|
);
|
|
153
96
|
}, [highlightedNode]);
|
|
154
97
|
|
|
155
|
-
|
|
156
|
-
<style>
|
|
157
|
-
:host {
|
|
158
|
-
--cosmoz-treenode-navigator-select-node-icon-color: var(
|
|
159
|
-
--primary-color,
|
|
160
|
-
white
|
|
161
|
-
);
|
|
162
|
-
--cosmoz-treenode-navigator-list-item-focused-color: #f0f8ff;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
#header {
|
|
166
|
-
margin: 0 16px;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
#header a {
|
|
170
|
-
text-decoration: none;
|
|
171
|
-
color: inherit;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
.icon {
|
|
175
|
-
display: inline-block;
|
|
176
|
-
position: relative;
|
|
177
|
-
padding: 8px;
|
|
178
|
-
outline: none;
|
|
179
|
-
user-select: none;
|
|
180
|
-
cursor: pointer;
|
|
181
|
-
z-index: 0;
|
|
182
|
-
line-height: 1;
|
|
183
|
-
width: 40px;
|
|
184
|
-
height: 40px;
|
|
185
|
-
box-sizing: border-box;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
.path {
|
|
189
|
-
display: flex;
|
|
190
|
-
align-items: center;
|
|
191
|
-
flex-wrap: wrap;
|
|
192
|
-
}
|
|
98
|
+
const meta = useMeta({ dataPlane, highlightedNode, openNode });
|
|
193
99
|
|
|
194
|
-
|
|
195
|
-
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
// eslint-disable-next-line max-lines-per-function
|
|
102
|
+
const handler = (e) => {
|
|
103
|
+
if ((e.ctrlKey && e.altKey) || e.defaultPrevented) {
|
|
104
|
+
return;
|
|
196
105
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
106
|
+
const { dataPlane: items, highlightedNode: node, openNode: open } = meta,
|
|
107
|
+
getIndex = () =>
|
|
108
|
+
items.findIndex((i) => i.pathLocator === node?.pathLocator);
|
|
109
|
+
|
|
110
|
+
switch (e.key) {
|
|
111
|
+
case 'Up':
|
|
112
|
+
case 'ArrowUp':
|
|
113
|
+
{
|
|
114
|
+
const idx = getIndex();
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
setHighlightedNode(items[Math.max(idx - 1, 0)]);
|
|
117
|
+
}
|
|
118
|
+
break;
|
|
119
|
+
case 'Down':
|
|
120
|
+
case 'ArrowDown':
|
|
121
|
+
{
|
|
122
|
+
const idx = getIndex();
|
|
123
|
+
if (idx < items.length - 1) {
|
|
124
|
+
e.preventDefault();
|
|
125
|
+
setHighlightedNode(items[idx + 1]);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
break;
|
|
129
|
+
|
|
130
|
+
case 'Enter':
|
|
131
|
+
if (node) {
|
|
132
|
+
e.preventDefault();
|
|
133
|
+
host.dispatchEvent(new CustomEvent('select-node'));
|
|
134
|
+
}
|
|
135
|
+
break;
|
|
136
|
+
|
|
137
|
+
case 'Right':
|
|
138
|
+
case 'ArrowRight':
|
|
139
|
+
if (node) {
|
|
140
|
+
e.preventDefault();
|
|
141
|
+
open(node);
|
|
142
|
+
}
|
|
143
|
+
break;
|
|
144
|
+
default:
|
|
145
|
+
break;
|
|
200
146
|
}
|
|
147
|
+
};
|
|
201
148
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
149
|
+
document.addEventListener('keydown', handler, true);
|
|
150
|
+
return () => document.removeEventListener('keydown', handler, true);
|
|
151
|
+
}, [meta]);
|
|
152
|
+
|
|
153
|
+
const renderItem = (node, index) => html` <div class="item">
|
|
154
|
+
${((parentPath) =>
|
|
155
|
+
when(
|
|
156
|
+
search &&
|
|
157
|
+
(index === 0 ||
|
|
158
|
+
parentPath !== getParentPath(tree, dataPlane[index - 1])),
|
|
159
|
+
() =>
|
|
160
|
+
html`
|
|
161
|
+
<div class="section">
|
|
162
|
+
${tree.getPathString(parentPath, tree.searchProperty)}
|
|
163
|
+
</div>
|
|
164
|
+
`
|
|
165
|
+
))(getParentPath(tree, node))}
|
|
166
|
+
<div
|
|
167
|
+
class=${computeRowClass('node', node, highlightedNode)}
|
|
168
|
+
@click=${() => setHighlightedNode(node)}
|
|
169
|
+
@dblclick=${(e) => onNodeDblClicked(e, host)}
|
|
170
|
+
>
|
|
171
|
+
<div class="name">${node[tree.searchProperty]}</div>
|
|
172
|
+
${when(
|
|
173
|
+
tree.hasChildren(node),
|
|
174
|
+
() => html`
|
|
175
|
+
<span class="icon" @click=${() => openNode(node)}>
|
|
176
|
+
<svg
|
|
177
|
+
viewBox="0 0 24 24"
|
|
178
|
+
preserveAspectRatio="xMidYMid meet"
|
|
179
|
+
focusable="false"
|
|
180
|
+
style="pointer-events: none; display: block; width: 100%; height: 100%;"
|
|
181
|
+
>
|
|
182
|
+
<g>
|
|
183
|
+
<path
|
|
184
|
+
d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"
|
|
185
|
+
></path>
|
|
186
|
+
</g>
|
|
187
|
+
</svg>
|
|
188
|
+
</span>
|
|
189
|
+
`
|
|
190
|
+
)}
|
|
191
|
+
</div>
|
|
192
|
+
</div>`;
|
|
209
193
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
cursor: pointer;
|
|
214
|
-
margin: 0 0.29em;
|
|
215
|
-
padding: 0.7em 0.57em;
|
|
216
|
-
text-transform: uppercase;
|
|
217
|
-
}
|
|
194
|
+
return html`
|
|
195
|
+
<style>
|
|
196
|
+
${styles}
|
|
218
197
|
</style>
|
|
219
|
-
<div
|
|
198
|
+
<div class="header">
|
|
220
199
|
<h3 class="path">
|
|
221
|
-
<span class="icon" @click=${
|
|
200
|
+
<span class="icon" @click=${() => openNode()}>
|
|
222
201
|
<svg
|
|
223
202
|
viewBox="0 0 24 24"
|
|
224
203
|
preserveAspectRatio="xMidYMid meet"
|
|
@@ -228,84 +207,39 @@ const TreenodeNavigator = host => {
|
|
|
228
207
|
<g><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"></path></g>
|
|
229
208
|
</svg>
|
|
230
209
|
</span>
|
|
231
|
-
${
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
210
|
+
${guard([tree, openNodePath], () =>
|
|
211
|
+
((openNodePath && tree.getPathNodes(openNodePath)) || []).map(
|
|
212
|
+
(node) => html`
|
|
213
|
+
<span class="slash">/</span>
|
|
214
|
+
<span class="pointer" tabindex="0" @click=${() => openNode(node)}
|
|
215
|
+
>${node[tree.searchProperty]}</span
|
|
216
|
+
>
|
|
217
|
+
`
|
|
218
|
+
)
|
|
219
|
+
)}
|
|
237
220
|
</h3>
|
|
238
221
|
<cosmoz-input
|
|
239
222
|
tabindex="0"
|
|
240
|
-
.value=${
|
|
241
|
-
.placeholder
|
|
242
|
-
|
|
243
|
-
@input="${ ({ target }) => setSearchValue(target.value) }"
|
|
223
|
+
.value=${searchValue}
|
|
224
|
+
.placeholder=${searchPlaceholder}
|
|
225
|
+
@input=${(e) => setSearchValue(e.target.value)}
|
|
244
226
|
/>
|
|
245
227
|
</div>
|
|
246
|
-
${
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
${ when(
|
|
259
|
-
renderSection(index, node.parentSectionName),
|
|
260
|
-
() => html`
|
|
261
|
-
<div class="section">${ node.parentSectionName }</div>
|
|
262
|
-
`
|
|
263
|
-
) }
|
|
264
|
-
<div
|
|
265
|
-
class="${ computeRowClass(
|
|
266
|
-
'node-item pointer',
|
|
267
|
-
node,
|
|
268
|
-
highlightedNode
|
|
269
|
-
) }"
|
|
270
|
-
@click="${ () => setHighlightedNode(node) }"
|
|
271
|
-
@dblclick="${ e => onNodeDblClicked(e, host) }"
|
|
272
|
-
>
|
|
273
|
-
<div style="flex: auto">${ node.name }</div>
|
|
274
|
-
${ when(
|
|
275
|
-
hasChildren(node),
|
|
276
|
-
() => html`
|
|
277
|
-
<span class="icon" @click="${ () => openNode(node) }">
|
|
278
|
-
<svg
|
|
279
|
-
viewBox="0 0 24 24"
|
|
280
|
-
preserveAspectRatio="xMidYMid meet"
|
|
281
|
-
focusable="false"
|
|
282
|
-
style="pointer-events: none; display: block; width: 100%; height: 100%;"
|
|
283
|
-
>
|
|
284
|
-
<g>
|
|
285
|
-
<path
|
|
286
|
-
d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"
|
|
287
|
-
></path>
|
|
288
|
-
</g>
|
|
289
|
-
</svg>
|
|
290
|
-
</span>
|
|
291
|
-
`
|
|
292
|
-
) }
|
|
293
|
-
</div>
|
|
294
|
-
</div>
|
|
295
|
-
` }
|
|
296
|
-
</cosmoz-listbox>`
|
|
297
|
-
) }
|
|
298
|
-
${ when(
|
|
299
|
-
showGlobalSearchBtn(
|
|
300
|
-
computeSearching(searchValue, searchMinLength),
|
|
301
|
-
openNodePath
|
|
302
|
-
),
|
|
303
|
-
() => html`
|
|
304
|
-
<button class="btn-ghost" @click="${ () => setOpenNodePath('') }">
|
|
305
|
-
${ searchGlobalPlaceholder }
|
|
228
|
+
${when(
|
|
229
|
+
tree,
|
|
230
|
+
() => html` <div class="items">
|
|
231
|
+
<div virtualizer-sizer></div>
|
|
232
|
+
${virtualize({ items: dataPlane, renderItem, scroller: true })}
|
|
233
|
+
</div>`
|
|
234
|
+
)}
|
|
235
|
+
${when(
|
|
236
|
+
search && openNodePath,
|
|
237
|
+
() => html`
|
|
238
|
+
<button class="btn-ghost" @click=${() => setOpenNodePath('')}>
|
|
239
|
+
${searchGlobalPlaceholder}
|
|
306
240
|
</button>
|
|
307
241
|
`
|
|
308
|
-
|
|
242
|
+
)}
|
|
309
243
|
`;
|
|
310
244
|
};
|
|
311
245
|
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { tagged as css } from '@neovici/cosmoz-utils';
|
|
2
|
+
|
|
3
|
+
export default css`
|
|
4
|
+
:host {
|
|
5
|
+
--cosmoz-treenode-navigator-select-node-icon-color: var(
|
|
6
|
+
--primary-color,
|
|
7
|
+
white
|
|
8
|
+
);
|
|
9
|
+
--cosmoz-treenode-navigator-list-item-focused-color: #f0f8ff;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.header {
|
|
13
|
+
margin: 0 16px;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.header a {
|
|
17
|
+
text-decoration: none;
|
|
18
|
+
color: inherit;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.icon {
|
|
22
|
+
display: inline-block;
|
|
23
|
+
position: relative;
|
|
24
|
+
padding: 8px;
|
|
25
|
+
outline: none;
|
|
26
|
+
user-select: none;
|
|
27
|
+
cursor: pointer;
|
|
28
|
+
z-index: 0;
|
|
29
|
+
line-height: 1;
|
|
30
|
+
width: 40px;
|
|
31
|
+
height: 40px;
|
|
32
|
+
box-sizing: border-box;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.path {
|
|
36
|
+
display: flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
flex-wrap: wrap;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.slash {
|
|
42
|
+
margin: 0 2px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.pointer {
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.items {
|
|
49
|
+
height: var(--cosmoz-treenode-navigator-list-height, 50vh);
|
|
50
|
+
width: 100%;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.section {
|
|
54
|
+
background-color: #f5f5f5;
|
|
55
|
+
padding: 5px;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.pointer {
|
|
59
|
+
cursor: pointer;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.item {
|
|
63
|
+
width: 100%;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.node {
|
|
67
|
+
align-items: center;
|
|
68
|
+
display: flex;
|
|
69
|
+
font-family: 'Roboto', 'Noto', sans-serif;
|
|
70
|
+
font-size: 16px;
|
|
71
|
+
font-weight: 400;
|
|
72
|
+
height: 40px;
|
|
73
|
+
line-height: 24px;
|
|
74
|
+
padding: 6px 12px;
|
|
75
|
+
cursor: pointer;
|
|
76
|
+
}
|
|
77
|
+
.name {
|
|
78
|
+
flex: auto;
|
|
79
|
+
}
|
|
80
|
+
.icon {
|
|
81
|
+
box-sizing: border-box;
|
|
82
|
+
cursor: pointer;
|
|
83
|
+
display: inline-block;
|
|
84
|
+
height: 40px;
|
|
85
|
+
line-height: 1;
|
|
86
|
+
outline: none;
|
|
87
|
+
padding: 8px;
|
|
88
|
+
position: relative;
|
|
89
|
+
user-select: none;
|
|
90
|
+
width: 40px;
|
|
91
|
+
z-index: 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.node.selected {
|
|
95
|
+
background-color: var(
|
|
96
|
+
--cosmoz-listbox-active-color,
|
|
97
|
+
var(--cosmoz-selection-color, rgba(58, 145, 226, 0.1))
|
|
98
|
+
);
|
|
99
|
+
transition: background-color 0.2s ease-out;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.node.selected .icon svg {
|
|
103
|
+
fill: var(--cosmoz-treenode-navigator-select-node-icon-color);
|
|
104
|
+
transition: color 0.8s ease-out;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.btn-ghost {
|
|
108
|
+
background: transparent;
|
|
109
|
+
border: none;
|
|
110
|
+
cursor: pointer;
|
|
111
|
+
margin: 0 20px;
|
|
112
|
+
padding: 0.7em 0.57em;
|
|
113
|
+
width: calc(100% - 50px);
|
|
114
|
+
border: solid 1px #a4abae;
|
|
115
|
+
border-radius: 16px;
|
|
116
|
+
font-size: 14px;
|
|
117
|
+
font-family: inherit;
|
|
118
|
+
}
|
|
119
|
+
`;
|
package/helpers.js
CHANGED
|
@@ -1,93 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
* @param {Object} node - The node
|
|
9
|
-
* @return {Boolean} - True if node has children
|
|
10
|
-
*/
|
|
11
|
-
hasChildren = node => {
|
|
12
|
-
return tree.hasChildren(node);
|
|
13
|
-
},
|
|
14
|
-
/**
|
|
15
|
-
* Returns true, if a search string is eligable to trigger a search
|
|
16
|
-
* @param {String} value - The search string
|
|
17
|
-
* @param {Number} searchMinLength - The minimum length of value to be valid
|
|
18
|
-
* @return {Boolean} - If a search should be triggered
|
|
19
|
-
*/
|
|
20
|
-
computeSearching = (value, searchMinLength) => {
|
|
21
|
-
return value && value.length >= searchMinLength && value !== '';
|
|
22
|
-
},
|
|
23
|
-
/**
|
|
24
|
-
* Normalizes and returns an Array of nodes
|
|
25
|
-
* with the properties name, path, parentSectionName, children
|
|
26
|
-
* @param {Array} nodes - The input nodes
|
|
27
|
-
* @return {Array} - The normalized nodes
|
|
28
|
-
*/
|
|
29
|
-
normalizeNodes = nodes => {
|
|
30
|
-
if (!Array.isArray(nodes)) {
|
|
31
|
-
return [];
|
|
32
|
-
}
|
|
33
|
-
return nodes.map(node => {
|
|
34
|
-
if (!node) {
|
|
35
|
-
return node;
|
|
36
|
-
}
|
|
37
|
-
const path = node.pathLocator || node.path,
|
|
38
|
-
pathLocatorSeparator = tree.pathLocatorSeparator,
|
|
39
|
-
parentPath = path.includes(pathLocatorSeparator) ? path.substring(0, path.lastIndexOf(pathLocatorSeparator)) : path;
|
|
40
|
-
return {
|
|
41
|
-
name: node[tree.searchProperty],
|
|
42
|
-
path,
|
|
43
|
-
children: node[tree.childProperty],
|
|
44
|
-
parentSectionName: tree.getPathString(parentPath, tree.searchProperty),
|
|
45
|
-
id: node.id,
|
|
46
|
-
pathLocator: node.pathLocator
|
|
47
|
-
};
|
|
48
|
-
});
|
|
49
|
-
},
|
|
50
|
-
/**
|
|
51
|
-
* Returns the found nodes based on a search string and a given tree to be searched
|
|
52
|
-
* @param {Boolean} searching - If true, a search should be executed
|
|
53
|
-
* @param {String} searchString - The search string
|
|
54
|
-
* @param {Array} renderedLevel - The node list on which the search should be executed
|
|
55
|
-
* @param {Tree} tree - The main tree
|
|
56
|
-
* @return {Array} - The found nodes
|
|
57
|
-
*/
|
|
58
|
-
computeDataPlane = (searching, searchString, renderedLevel, tree) => {
|
|
59
|
-
if (searching && tree) {
|
|
60
|
-
const results = tree.searchNodes(searchString, renderedLevel, false);
|
|
61
|
-
return normalizeNodes(results);
|
|
62
|
-
}
|
|
63
|
-
return renderedLevel;
|
|
1
|
+
export const //
|
|
2
|
+
getParentPath = (tree, node) => {
|
|
3
|
+
const path = node.pathLocator || node.path,
|
|
4
|
+
pathLocatorSeparator = tree.pathLocatorSeparator;
|
|
5
|
+
return path.includes(pathLocatorSeparator)
|
|
6
|
+
? path.substring(0, path.lastIndexOf(pathLocatorSeparator))
|
|
7
|
+
: path;
|
|
64
8
|
},
|
|
65
9
|
/**
|
|
66
10
|
* Returns a node array with the children of a node on the given path
|
|
67
11
|
* If the node doesn't have children, the node gets returned
|
|
68
|
-
* @param {String} pathLocator - The separated address parts of a node
|
|
69
12
|
* @param {Tree} tree - The main tree
|
|
13
|
+
* @param {String} pathLocator - The separated address parts of a node
|
|
70
14
|
* @return {Array} - Nodes
|
|
71
15
|
*/
|
|
72
|
-
renderLevel = (
|
|
16
|
+
renderLevel = (tree, pathLocator) => {
|
|
73
17
|
if (!tree) {
|
|
74
18
|
return;
|
|
75
19
|
}
|
|
76
20
|
const node = tree.getNodeByPathLocator(pathLocator),
|
|
77
21
|
children = tree.getChildren(node),
|
|
78
|
-
level =
|
|
22
|
+
level = children.length > 0 ? children : node,
|
|
23
|
+
searchProperty = tree.searchProperty,
|
|
79
24
|
sortFunc = (a, b) => {
|
|
80
25
|
// First sort based on "folder" status (containing children)
|
|
81
|
-
if (hasChildren(a)) {
|
|
82
|
-
if (!hasChildren(b)) {
|
|
26
|
+
if (tree.hasChildren(a)) {
|
|
27
|
+
if (!tree.hasChildren(b)) {
|
|
83
28
|
return -1;
|
|
84
29
|
}
|
|
85
|
-
} else if (hasChildren(b)) {
|
|
30
|
+
} else if (tree.hasChildren(b)) {
|
|
86
31
|
return 1;
|
|
87
32
|
}
|
|
88
33
|
// Then sort on searchProperty
|
|
89
|
-
const val1 = a[
|
|
90
|
-
val2 = b[
|
|
34
|
+
const val1 = a[searchProperty],
|
|
35
|
+
val2 = b[searchProperty];
|
|
91
36
|
|
|
92
37
|
if (val1 > val2) {
|
|
93
38
|
return 1;
|
|
@@ -97,8 +42,21 @@ const tree = new DefaultTree(basicTree),
|
|
|
97
42
|
}
|
|
98
43
|
return 0;
|
|
99
44
|
};
|
|
100
|
-
return
|
|
101
|
-
|
|
45
|
+
return level.sort(sortFunc);
|
|
46
|
+
},
|
|
47
|
+
/**
|
|
48
|
+
* Returns the found nodes based on a search string and a given tree to be searched
|
|
49
|
+
* @param {Tree} tree - The main tree
|
|
50
|
+
* @param {String|undefined} search - The search string
|
|
51
|
+
* @param {String} pathLocator - The separated address parts of a node
|
|
52
|
+
* @return {Array} - The found nodes
|
|
53
|
+
*/
|
|
54
|
+
computeDataPlane = (tree, search, pathLocator) => {
|
|
55
|
+
if (!tree) {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
const nodes = renderLevel(tree, pathLocator);
|
|
59
|
+
return search ? tree.searchNodes(search, nodes, false) : nodes;
|
|
102
60
|
},
|
|
103
61
|
/**
|
|
104
62
|
* Returns the classes of a row based its selection state
|
|
@@ -110,9 +68,10 @@ const tree = new DefaultTree(basicTree),
|
|
|
110
68
|
computeRowClass = (classes, node, highlightedNode) => {
|
|
111
69
|
let selected = false;
|
|
112
70
|
if (highlightedNode) {
|
|
113
|
-
selected =
|
|
114
|
-
|
|
115
|
-
|
|
71
|
+
selected =
|
|
72
|
+
node === highlightedNode ||
|
|
73
|
+
(node.id && node.id === highlightedNode.id) ||
|
|
74
|
+
node.pathLocator === highlightedNode.pathLocator;
|
|
116
75
|
}
|
|
117
76
|
return selected ? classes + ' selected' : classes;
|
|
118
77
|
},
|
|
@@ -123,28 +82,13 @@ const tree = new DefaultTree(basicTree),
|
|
|
123
82
|
* @return {undefined}
|
|
124
83
|
*/
|
|
125
84
|
onNodeDblClicked = (event, host) => {
|
|
126
|
-
host.dispatchEvent(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
* Returns the name of a given node
|
|
134
|
-
* @param {Object} node - The node
|
|
135
|
-
* @return {String} - The name
|
|
136
|
-
*/
|
|
137
|
-
getNodeName = node => {
|
|
138
|
-
return node[tree.searchProperty];
|
|
139
|
-
},
|
|
140
|
-
/**
|
|
141
|
-
* Returns true, if the button should be visible
|
|
142
|
-
* @param {Boolean} searching - If a search is currently executed
|
|
143
|
-
* @param {String} openNodeLevelPath - The open node level
|
|
144
|
-
* @return {Boolean} - The visibility of the button
|
|
145
|
-
*/
|
|
146
|
-
showGlobalSearchBtn = (searching, openNodeLevelPath) => {
|
|
147
|
-
return searching && openNodeLevelPath !== '';
|
|
85
|
+
host.dispatchEvent(
|
|
86
|
+
new CustomEvent('node-dblclicked', {
|
|
87
|
+
detail: {
|
|
88
|
+
model: event.model,
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
);
|
|
148
92
|
},
|
|
149
93
|
/**
|
|
150
94
|
* Returns the nodes on a path specified by a given path locator
|
|
@@ -156,7 +100,7 @@ const tree = new DefaultTree(basicTree),
|
|
|
156
100
|
if (!tree || !pathLocator) {
|
|
157
101
|
return [];
|
|
158
102
|
}
|
|
159
|
-
return
|
|
103
|
+
return tree.getPathNodes(pathLocator);
|
|
160
104
|
},
|
|
161
105
|
/**
|
|
162
106
|
* Returns a node based on a given path locator.
|
|
@@ -175,78 +119,7 @@ const tree = new DefaultTree(basicTree),
|
|
|
175
119
|
let nodes;
|
|
176
120
|
|
|
177
121
|
if (!node) {
|
|
178
|
-
nodes = tree.getPathNodes(pathLocator).filter(n => n != null);
|
|
122
|
+
nodes = tree.getPathNodes(pathLocator).filter((n) => n != null);
|
|
179
123
|
}
|
|
180
124
|
return nodes && nodes.length > 0 ? nodes.pop() : node;
|
|
181
|
-
}
|
|
182
|
-
nodeStyles = css`
|
|
183
|
-
.section {
|
|
184
|
-
background-color: #f5f5f5;
|
|
185
|
-
padding: 5px;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
.pointer {
|
|
189
|
-
cursor: pointer;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
.item {
|
|
193
|
-
padding: 0;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
.item[data-index] {
|
|
197
|
-
background: transparent;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
.node-item {
|
|
201
|
-
align-items: center;
|
|
202
|
-
display: flex;
|
|
203
|
-
font-family: 'Roboto', 'Noto', sans-serif;
|
|
204
|
-
font-size: 16px;
|
|
205
|
-
font-weight: 400;
|
|
206
|
-
height: 40px;
|
|
207
|
-
line-height: 24px;
|
|
208
|
-
padding: 6px 12px;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
.node-item-wrapper {
|
|
212
|
-
width: 100%;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
.icon {
|
|
216
|
-
box-sizing: border-box;
|
|
217
|
-
cursor: pointer;
|
|
218
|
-
display: inline-block;
|
|
219
|
-
height: 40px;
|
|
220
|
-
line-height: 1;
|
|
221
|
-
outline: none;
|
|
222
|
-
padding: 8px;
|
|
223
|
-
position: relative;
|
|
224
|
-
user-select: none;
|
|
225
|
-
width: 40px;
|
|
226
|
-
z-index: 0;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
.node-item.selected {
|
|
230
|
-
background-color: var(--cosmoz-listbox-active-color, var(--cosmoz-selection-color, rgba(58, 145, 226, 0.1)));
|
|
231
|
-
transition: background-color 0.2s ease-out;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
.node-item.selected .icon svg {
|
|
235
|
-
fill: var(--cosmoz-treenode-navigator-select-node-icon-color);
|
|
236
|
-
transition: color 0.8s ease-out;
|
|
237
|
-
}`;
|
|
238
|
-
|
|
239
|
-
export {
|
|
240
|
-
hasChildren,
|
|
241
|
-
computeSearching,
|
|
242
|
-
normalizeNodes,
|
|
243
|
-
renderLevel,
|
|
244
|
-
computeDataPlane,
|
|
245
|
-
computeRowClass,
|
|
246
|
-
onNodeDblClicked,
|
|
247
|
-
getNodeName,
|
|
248
|
-
showGlobalSearchBtn,
|
|
249
|
-
getTreePathParts,
|
|
250
|
-
getNode,
|
|
251
|
-
nodeStyles
|
|
252
|
-
};
|
|
125
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neovici/cosmoz-treenode-navigator",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.0",
|
|
4
4
|
"description": "A Polymer component that lets you navigate and search through hierarchically structured data-nodes and select one of them.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"polymer",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
}
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@
|
|
57
|
+
"@lit-labs/virtualizer": "^0.7.1",
|
|
58
58
|
"@neovici/cosmoz-dialog": "^3.0.0",
|
|
59
59
|
"@neovici/cosmoz-i18next": "^3.2.0",
|
|
60
60
|
"@neovici/cosmoz-input": "^3.0.0",
|
|
@@ -80,9 +80,9 @@
|
|
|
80
80
|
"@polymer/test-fixture": "^4.0.2",
|
|
81
81
|
"@semantic-release/changelog": "^6.0.0",
|
|
82
82
|
"@semantic-release/git": "^10.0.0",
|
|
83
|
-
"@webcomponents/webcomponentsjs": "^2.0.0",
|
|
84
83
|
"@web/dev-server": "^0.1.25",
|
|
85
84
|
"@web/test-runner": "^0.13.19",
|
|
85
|
+
"@webcomponents/webcomponentsjs": "^2.0.0",
|
|
86
86
|
"husky": "^8.0.0",
|
|
87
87
|
"semantic-release": "^19.0.3"
|
|
88
88
|
}
|
|
@@ -2,18 +2,17 @@ import { assert, fixture, html } from '@open-wc/testing';
|
|
|
2
2
|
import { DefaultTree } from '@neovici/cosmoz-tree/cosmoz-default-tree';
|
|
3
3
|
import basicTree from './data/basicTree.js';
|
|
4
4
|
import '../cosmoz-treenode-navigator';
|
|
5
|
-
import { computeDataPlane
|
|
5
|
+
import { computeDataPlane } from '../helpers';
|
|
6
6
|
|
|
7
7
|
suite('cosmoz-treenode-navigator', () => {
|
|
8
8
|
let navigator;
|
|
9
9
|
|
|
10
10
|
setup(async () => {
|
|
11
|
-
navigator = await fixture(html`
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
>`);
|
|
11
|
+
navigator = await fixture(html` <cosmoz-treenode-navigator
|
|
12
|
+
.tree=${new DefaultTree(basicTree)}
|
|
13
|
+
.searchPlaceholder="${'Custom search placeholder'}"
|
|
14
|
+
.searchMinLength="${2}"
|
|
15
|
+
></cosmoz-treenode-navigator>`);
|
|
17
16
|
});
|
|
18
17
|
|
|
19
18
|
test('instantiating the element', () => {
|
|
@@ -27,10 +26,9 @@ suite('cosmoz-treenode-navigator', () => {
|
|
|
27
26
|
navigator.openNodePath = '1000';
|
|
28
27
|
|
|
29
28
|
const dataPlane = computeDataPlane(
|
|
30
|
-
|
|
29
|
+
navigator.tree,
|
|
31
30
|
navigator.searchValue,
|
|
32
|
-
|
|
33
|
-
navigator.tree
|
|
31
|
+
navigator.openNodePath
|
|
34
32
|
);
|
|
35
33
|
// then I can see the contents of the node
|
|
36
34
|
// and it is ordered folder-first, then alphabetically
|
|
@@ -41,28 +39,35 @@ suite('cosmoz-treenode-navigator', () => {
|
|
|
41
39
|
|
|
42
40
|
test('set proper search placeholder', () => {
|
|
43
41
|
assert.equal(
|
|
44
|
-
navigator.shadowRoot
|
|
45
|
-
.
|
|
46
|
-
.querySelector('input').placeholder,
|
|
47
|
-
'Custom search placeholder'
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
test('compute search if value is bigger than searchMinLength', () => {
|
|
51
|
-
assert.equal(computeSearching('long string', navigator.searchMinLength), true);
|
|
52
|
-
assert.equal(computeSearching('a', navigator.searchMinLength), false);
|
|
42
|
+
navigator.shadowRoot
|
|
43
|
+
.querySelector('cosmoz-input')
|
|
44
|
+
.shadowRoot.querySelector('input').placeholder,
|
|
45
|
+
'Custom search placeholder'
|
|
46
|
+
);
|
|
53
47
|
});
|
|
54
48
|
|
|
55
49
|
test('should check if node has children', () => {
|
|
56
50
|
const node = {
|
|
57
51
|
name: 'D:',
|
|
58
52
|
path: '1000',
|
|
59
|
-
children: {
|
|
53
|
+
children: {
|
|
54
|
+
1001: {
|
|
55
|
+
name: 'Data',
|
|
56
|
+
pathLocator: '1000.1001',
|
|
57
|
+
children: { 1002: { name: 'John', pathLocator: '1000.1001.1002' } },
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
60
|
parentSectionName: 'D:',
|
|
61
|
-
pathLocator: '1000'
|
|
61
|
+
pathLocator: '1000',
|
|
62
62
|
};
|
|
63
|
-
assert.equal(hasChildren(node), true);
|
|
64
|
-
const node2 = {
|
|
65
|
-
|
|
63
|
+
assert.equal(navigator.tree.hasChildren(node), true);
|
|
64
|
+
const node2 = {
|
|
65
|
+
name: 'Git',
|
|
66
|
+
path: '1.5.7',
|
|
67
|
+
parentSectionName: 'C:/Program Files',
|
|
68
|
+
pathLocator: '1.5.7',
|
|
69
|
+
};
|
|
70
|
+
assert.equal(navigator.tree.hasChildren(node2), false);
|
|
66
71
|
});
|
|
67
72
|
|
|
68
73
|
test('match search string', () => {
|
|
@@ -71,16 +76,14 @@ suite('cosmoz-treenode-navigator', () => {
|
|
|
71
76
|
navigator.searchValue = 'John';
|
|
72
77
|
|
|
73
78
|
const dataPlane = computeDataPlane(
|
|
74
|
-
|
|
79
|
+
navigator.tree,
|
|
75
80
|
navigator.searchValue,
|
|
76
|
-
|
|
77
|
-
navigator.tree
|
|
81
|
+
'1000'
|
|
78
82
|
);
|
|
79
83
|
|
|
80
84
|
//then I can see only 1 item matching 'John'
|
|
81
85
|
assert.lengthOf(dataPlane, 1);
|
|
82
86
|
assert.equal(dataPlane[0].name, 'John');
|
|
83
|
-
|
|
84
87
|
});
|
|
85
88
|
|
|
86
89
|
test('not match search string', () => {
|
|
@@ -88,10 +91,9 @@ suite('cosmoz-treenode-navigator', () => {
|
|
|
88
91
|
// when I search for 'NONEXISTING'
|
|
89
92
|
navigator.searchValue = 'NONEXISTING';
|
|
90
93
|
const dataPlane = computeDataPlane(
|
|
91
|
-
|
|
94
|
+
navigator.tree,
|
|
92
95
|
navigator.searchValue,
|
|
93
|
-
|
|
94
|
-
navigator.tree
|
|
96
|
+
'1000'
|
|
95
97
|
);
|
|
96
98
|
|
|
97
99
|
//then I can see that is no contents
|