orgchart 5.0.0 → 6.0.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/publish.yml +32 -0
- package/README.md +172 -93
- package/README.zh-cn.md +163 -74
- package/README.zh-tw.md +163 -74
- package/bower.json +6 -9
- package/demo/10000-nodes.html +139 -0
- package/demo/ajax-datasource.html +46 -45
- package/demo/bottom2top.html +12 -9
- package/demo/center-on-selected-node.html +21 -17
- package/demo/color-coded.html +12 -9
- package/demo/css/style.css +28 -0
- package/demo/custom-icons.html +12 -9
- package/demo/custom-template.html +12 -9
- package/demo/data-prop-compact.html +11 -8
- package/demo/data-prop-hybrid.html +79 -50
- package/demo/default-collapsed.html +12 -9
- package/demo/drag-drop-hybrid-chart.html +17 -14
- package/demo/drag-drop.html +21 -16
- package/demo/edit-chart.html +148 -94
- package/demo/edit-hybrid-chart.html +178 -135
- package/demo/expand-to-given-depth.html +30 -22
- package/demo/export-chart-pdf.html +12 -9
- package/demo/export-chart-with-pictures.html +19 -12
- package/demo/export-chart.html +12 -9
- package/demo/family-tree.html +14 -11
- package/demo/familytree-custom-properties.html +108 -0
- package/demo/filter-node.html +104 -57
- package/demo/get-hierarchy.html +17 -12
- package/demo/get-related-nodes.html +27 -17
- package/demo/index.html +5 -2
- package/demo/integrate-map.html +32 -27
- package/demo/left2right.html +12 -9
- package/demo/level-offset.html +16 -15
- package/demo/link-node.html +12 -9
- package/demo/local-datasource.html +11 -8
- package/demo/minimap.html +224 -0
- package/demo/nodes-of-different-widths.html +11 -8
- package/demo/ondemand-loading-data.html +88 -91
- package/demo/option-createNode.html +28 -17
- package/demo/pan-zoom.html +20 -11
- package/demo/reload-data.html +17 -14
- package/demo/report-path.html +59 -29
- package/demo/responsive-design.html +14 -11
- package/demo/right2left.html +12 -9
- package/demo/toggle-sibs-resp.html +12 -9
- package/demo/ul-datasource.html +11 -8
- package/demo/vertical-level.html +12 -9
- package/dist/css/{jquery.orgchart.css → orgchart.css} +143 -80
- package/dist/css/orgchart.min.css +1 -0
- package/dist/js/orgchart.js +4101 -0
- package/dist/js/orgchart.min.js +2 -0
- package/dist/js/orgchart.min.js.map +1 -0
- package/gulpfile.js +37 -26
- package/package.json +32 -32
- package/src/css/{jquery.orgchart.css → orgchart.css} +143 -80
- package/src/js/orgchart.js +4101 -0
- package/test/cypress/e2e/demo-interactions.cy.js +91 -0
- package/test/cypress/e2e/demo-rendering.cy.js +100 -0
- package/test/cypress/e2e/drag-drop-hybrid-chart.cy.js +22 -0
- package/test/cypress/e2e/edit-chart.cy.js +20 -0
- package/test/cypress/e2e/level-offset.cy.js +21 -0
- package/test/cypress/e2e/minimap.cy.js +66 -0
- package/test/cypress/e2e/pan-zoom.cy.js +165 -0
- package/test/cypress/e2e/report-path.cy.js +20 -0
- package/test/cypress/e2e/vertical-vevel.cy.js +5 -3
- package/test/e2e/local-datasource/test.js +1 -1
- package/test/integration/init-tests.js +168 -62
- package/test/integration/tests.js +1034 -56
- package/test/unit/lifecycle-builders-export.tests.js +1426 -0
- package/test/unit/minimap.tests.js +210 -0
- package/test/unit/tests.js +2372 -252
- package/dist/css/jquery.orgchart.min.css +0 -1
- package/dist/js/jquery.orgchart.js +0 -1741
- package/dist/js/jquery.orgchart.min.js +0 -5
- package/dist/js/jquery.orgchart.min.js.map +0 -1
- package/src/js/jquery.orgchart.js +0 -1741
|
@@ -1,16 +1,63 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
const chai = require('chai');
|
|
2
|
+
const sinon = require('sinon');
|
|
3
|
+
const sinonChai = require('sinon-chai').default;
|
|
4
|
+
const should = chai.should();
|
|
5
5
|
chai.use(sinonChai);
|
|
6
6
|
require('jsdom-global')();
|
|
7
|
-
|
|
8
|
-
require('../../src/js/
|
|
7
|
+
process.env.ORGCHART_TEST = '1';
|
|
8
|
+
const OrgChart = require('../../src/js/orgchart');
|
|
9
|
+
const getState = OrgChart.__testing__.getState;
|
|
10
|
+
const setState = OrgChart.__testing__.setState;
|
|
11
|
+
|
|
12
|
+
function query(selector, root) {
|
|
13
|
+
return (root || document).querySelector(selector);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function queryAll(selector, root) {
|
|
17
|
+
return Array.from((root || document).querySelectorAll(selector));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function siblingElements(element, selector) {
|
|
21
|
+
if (!element || !element.parentElement) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
return Array.from(element.parentElement.children).filter(function (childEl) {
|
|
25
|
+
return childEl !== element && (!selector || childEl.matches(selector));
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function cloneValue(value) {
|
|
30
|
+
return JSON.parse(JSON.stringify(value));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function dispatchNativeEvent(element, type, options) {
|
|
34
|
+
const eventOptions = Object.assign({ bubbles: true }, options);
|
|
35
|
+
let event;
|
|
36
|
+
|
|
37
|
+
if (/click|mouse/.test(type)) {
|
|
38
|
+
event = new window.MouseEvent(type, eventOptions);
|
|
39
|
+
} else {
|
|
40
|
+
event = new window.Event(type, eventOptions);
|
|
41
|
+
}
|
|
42
|
+
element.dispatchEvent(event);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function dispatchEventWithProperties(element, type, properties, options) {
|
|
46
|
+
const event = new window.Event(type, Object.assign({ bubbles: true }, options));
|
|
47
|
+
|
|
48
|
+
Object.keys(properties || {}).forEach(function (key) {
|
|
49
|
+
Object.defineProperty(event, key, {
|
|
50
|
+
configurable: true,
|
|
51
|
+
value: properties[key]
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
element.dispatchEvent(event);
|
|
55
|
+
}
|
|
9
56
|
|
|
10
57
|
describe('orgchart -- integration tests', function () {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
ds = {
|
|
58
|
+
let $container;
|
|
59
|
+
|
|
60
|
+
const ds = {
|
|
14
61
|
'id': 'n1',
|
|
15
62
|
'name': 'Lao Lao',
|
|
16
63
|
'children': [
|
|
@@ -36,9 +83,11 @@ describe('orgchart -- integration tests', function () {
|
|
|
36
83
|
},
|
|
37
84
|
{ 'id': 'n4', 'name': 'Hong Miao' },
|
|
38
85
|
]
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
let oc = {};
|
|
89
|
+
|
|
90
|
+
const hierarchy = {
|
|
42
91
|
id: 'n1',
|
|
43
92
|
children: [
|
|
44
93
|
{ id: 'n2' },
|
|
@@ -63,91 +112,1020 @@ describe('orgchart -- integration tests', function () {
|
|
|
63
112
|
},
|
|
64
113
|
{ id: 'n4' }
|
|
65
114
|
]
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
$
|
|
69
|
-
$
|
|
70
|
-
$
|
|
71
|
-
$
|
|
72
|
-
$
|
|
73
|
-
$
|
|
74
|
-
$
|
|
75
|
-
$
|
|
76
|
-
$
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
let $laolao;
|
|
118
|
+
let $bomiao;
|
|
119
|
+
let $sumiao;
|
|
120
|
+
let $hongmiao;
|
|
121
|
+
let $chunmiao;
|
|
122
|
+
let $tiehua;
|
|
123
|
+
let $heihei;
|
|
124
|
+
let $pangpang;
|
|
125
|
+
let $dandan;
|
|
126
|
+
let $erdan;
|
|
77
127
|
|
|
78
128
|
beforeEach(function () {
|
|
79
|
-
|
|
129
|
+
document.body.innerHTML = '<div id="chart-container"></div>';
|
|
130
|
+
$container = document.getElementById('chart-container');
|
|
131
|
+
oc = new OrgChart({
|
|
132
|
+
chartContainer: '#chart-container',
|
|
80
133
|
'data': ds
|
|
81
134
|
}),
|
|
82
|
-
$laolao =
|
|
83
|
-
$bomiao =
|
|
84
|
-
$sumiao =
|
|
85
|
-
$hongmiao =
|
|
86
|
-
$tiehua =
|
|
87
|
-
$heihei =
|
|
88
|
-
$pangpang =
|
|
89
|
-
$dandan =
|
|
90
|
-
$erdan =
|
|
91
|
-
$sandan =
|
|
92
|
-
});
|
|
93
|
-
|
|
135
|
+
$laolao = document.getElementById('n1'),
|
|
136
|
+
$bomiao = document.getElementById('n2'),
|
|
137
|
+
$sumiao = document.getElementById('n3'),
|
|
138
|
+
$hongmiao = document.getElementById('n4'),
|
|
139
|
+
$tiehua = document.getElementById('n5'),
|
|
140
|
+
$heihei = document.getElementById('n6'),
|
|
141
|
+
$pangpang = document.getElementById('n7'),
|
|
142
|
+
$dandan = document.getElementById('n8'),
|
|
143
|
+
$erdan = document.getElementById('n9'),
|
|
144
|
+
$sandan = document.getElementById('n10');
|
|
145
|
+
});
|
|
146
|
+
|
|
94
147
|
afterEach(function () {
|
|
95
148
|
$laolao = $bomiao = $sumiao = $hongmiao = $chunmiao = $tiehua = $heihei = $pangpang = $dandan = $erdan = $sandan = null;
|
|
96
|
-
$container.
|
|
149
|
+
$container.innerHTML = '';
|
|
97
150
|
});
|
|
98
151
|
|
|
99
152
|
it('addParent()', function () {
|
|
153
|
+
const expectedHierarchy = {
|
|
154
|
+
id: 'n0',
|
|
155
|
+
children: [cloneValue(hierarchy)]
|
|
156
|
+
};
|
|
157
|
+
|
|
100
158
|
oc.addParent($laolao, { 'name': 'Lao Ye', 'id': 'n0' });
|
|
101
|
-
|
|
102
|
-
|
|
159
|
+
|
|
160
|
+
siblingElements($laolao.closest('.nodes'), '.node').should.lengthOf(1);
|
|
161
|
+
query('.node', oc.chart).should.equal(query('#n0'));
|
|
162
|
+
oc.getHierarchy().should.deep.equal(expectedHierarchy);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('inserts a parent between a non-root node and its existing parent', function () {
|
|
166
|
+
const expectedHierarchy = cloneValue(hierarchy);
|
|
167
|
+
|
|
168
|
+
expectedHierarchy.children[0] = {
|
|
169
|
+
id: 'n0',
|
|
170
|
+
children: [expectedHierarchy.children[0]]
|
|
171
|
+
};
|
|
172
|
+
oc.addParent($bomiao, { 'name': 'Lao Ye', 'id': 'n0' });
|
|
173
|
+
|
|
174
|
+
query('#n0').closest('.hierarchy').parentElement.parentElement.querySelector(':scope > .node').should.equal(query('#n1'));
|
|
175
|
+
query('#n2').closest('.hierarchy').parentElement.previousElementSibling.should.equal(query('#n0'));
|
|
176
|
+
oc.getHierarchy().should.deep.equal(expectedHierarchy);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('addAncestors()', function () {
|
|
180
|
+
oc.addAncestors({ 'name': 'Lao Ye', 'id': 'n0', 'relationship': '001' }, 'n0');
|
|
181
|
+
|
|
182
|
+
should.exist(query('#n0'));
|
|
183
|
+
query('#n0').closest('.hierarchy').should.not.equal(query('#n1').closest('.hierarchy'));
|
|
184
|
+
oc.getHierarchy().should.deep.equal(hierarchy);
|
|
103
185
|
});
|
|
104
186
|
|
|
105
187
|
describe('addChildren()', function () {
|
|
106
188
|
it('Add child nodes to the leaf node', function () {
|
|
189
|
+
let siblingNodesElement;
|
|
190
|
+
const expectedHierarchy = cloneValue(hierarchy);
|
|
191
|
+
|
|
192
|
+
expectedHierarchy.children[0].children = [{ id: 'n11' }];
|
|
193
|
+
|
|
107
194
|
oc.addChildren($bomiao, [{'name': 'Li Xin', 'id': 'n11' }]);
|
|
108
|
-
$bomiao
|
|
109
|
-
$bomiao
|
|
110
|
-
|
|
195
|
+
siblingNodesElement = siblingElements($bomiao, '.nodes')[0];
|
|
196
|
+
siblingElements($bomiao, '.nodes').should.lengthOf(1);
|
|
197
|
+
queryAll('.hierarchy', siblingNodesElement).should.lengthOf(1);
|
|
198
|
+
query('.node', siblingNodesElement).id.should.equal('n11');
|
|
199
|
+
oc.getHierarchy().should.deep.equal(expectedHierarchy);
|
|
111
200
|
});
|
|
112
201
|
|
|
113
202
|
it('Add child nodes to the un-leaf node', function () {
|
|
203
|
+
let siblingNodesElements;
|
|
204
|
+
let hierarchyElements;
|
|
205
|
+
const expectedHierarchy = cloneValue(hierarchy);
|
|
206
|
+
|
|
207
|
+
expectedHierarchy.children[1].children.push({ id: 'n11' });
|
|
208
|
+
|
|
114
209
|
oc.addChildren($sumiao, [{'name': 'Li Xin', 'id': 'n11' }]);
|
|
115
|
-
$sumiao
|
|
116
|
-
|
|
210
|
+
siblingNodesElements = siblingElements($sumiao, '.nodes');
|
|
211
|
+
hierarchyElements = siblingNodesElements.flatMap(function (nodesElement) {
|
|
212
|
+
return Array.from(nodesElement.children).filter(function (childEl) {
|
|
213
|
+
return childEl.classList && childEl.classList.contains('hierarchy');
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
hierarchyElements.should.lengthOf(4);
|
|
217
|
+
query('.node', hierarchyElements[hierarchyElements.length - 1]).id.should.equal('n11');
|
|
218
|
+
oc.getHierarchy().should.deep.equal(expectedHierarchy);
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
describe('addDescendants()', function () {
|
|
223
|
+
it('Add descendant nodes from a native parent node', function () {
|
|
224
|
+
let siblingNodesElement;
|
|
225
|
+
const expectedHierarchy = cloneValue(hierarchy);
|
|
226
|
+
|
|
227
|
+
expectedHierarchy.children[0].children = [{ id: 'n11' }];
|
|
228
|
+
|
|
229
|
+
oc.addDescendants([{ 'name': 'Li Xin', 'id': 'n11' }], $bomiao);
|
|
230
|
+
|
|
231
|
+
siblingNodesElement = siblingElements($bomiao, '.nodes')[0];
|
|
232
|
+
siblingElements($bomiao, '.nodes').should.lengthOf(1);
|
|
233
|
+
queryAll('.hierarchy', siblingNodesElement).should.lengthOf(1);
|
|
234
|
+
query('.node', siblingNodesElement).id.should.equal('n11');
|
|
235
|
+
oc.getHierarchy().should.deep.equal(expectedHierarchy);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('Preserves descendant insertion order and nested descendant structure', function () {
|
|
239
|
+
const expectedHierarchy = cloneValue(hierarchy);
|
|
240
|
+
|
|
241
|
+
expectedHierarchy.children[0].children = [
|
|
242
|
+
{ id: 'n11', children: [{ id: 'n12' }] },
|
|
243
|
+
{ id: 'n13' }
|
|
244
|
+
];
|
|
245
|
+
|
|
246
|
+
oc.addDescendants([
|
|
247
|
+
{
|
|
248
|
+
'name': 'Li Xin',
|
|
249
|
+
'id': 'n11',
|
|
250
|
+
'children': [
|
|
251
|
+
{ 'name': 'Xiao Xin', 'id': 'n12' }
|
|
252
|
+
]
|
|
253
|
+
},
|
|
254
|
+
{ 'name': 'Li Na', 'id': 'n13' }
|
|
255
|
+
], $bomiao);
|
|
256
|
+
|
|
257
|
+
Array.from($bomiao.nextElementSibling.children).filter(function (childEl) {
|
|
258
|
+
return childEl.classList && childEl.classList.contains('hierarchy');
|
|
259
|
+
}).should.lengthOf(2);
|
|
260
|
+
queryAll('#n12', $bomiao.nextElementSibling).should.lengthOf(1);
|
|
261
|
+
oc.getHierarchy().should.deep.equal(expectedHierarchy);
|
|
117
262
|
});
|
|
118
263
|
});
|
|
119
264
|
|
|
120
265
|
describe('addSiblings()', function () {
|
|
121
266
|
it('Just add sibling nodes', function () {
|
|
267
|
+
const expectedHierarchy = cloneValue(hierarchy);
|
|
268
|
+
const rootHierarchyElement = $laolao.closest('.hierarchy');
|
|
269
|
+
const rootChildrenContainer = Array.from(rootHierarchyElement.children).find(function (childEl) {
|
|
270
|
+
return childEl.classList && childEl.classList.contains('nodes');
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
expectedHierarchy.children.push({ id: 'n11' });
|
|
274
|
+
|
|
122
275
|
oc.addSiblings($sumiao, [{'name': 'Li Xin', 'id': 'n11' }]);
|
|
123
|
-
|
|
124
|
-
|
|
276
|
+
Array.from(rootChildrenContainer.children).filter(function (childEl) {
|
|
277
|
+
return childEl.classList && childEl.classList.contains('hierarchy');
|
|
278
|
+
}).should.lengthOf(4);
|
|
279
|
+
should.exist(query('#n11', rootChildrenContainer));
|
|
280
|
+
oc.getHierarchy().should.deep.equal(expectedHierarchy);
|
|
125
281
|
});
|
|
126
282
|
|
|
127
283
|
it('Add sibling nodes as well as parent node', function () {
|
|
284
|
+
let hierarchySiblings;
|
|
285
|
+
const expectedHierarchy = {
|
|
286
|
+
id: 'n0',
|
|
287
|
+
children: [{ id: 'n11' }, cloneValue(hierarchy)]
|
|
288
|
+
};
|
|
289
|
+
|
|
128
290
|
oc.addSiblings($laolao, { 'name': 'Lao Ye', 'id': 'n0', 'children': [{'name': 'Li Xin', 'id': 'n11' }] });
|
|
129
|
-
$laolao.closest('.nodes')
|
|
130
|
-
|
|
131
|
-
$laolao.closest('.hierarchy')
|
|
132
|
-
|
|
291
|
+
siblingElements($laolao.closest('.nodes'), '.node').should.lengthOf(1);
|
|
292
|
+
query('.node', oc.chart).should.equal(query('#n0'));
|
|
293
|
+
hierarchySiblings = siblingElements($laolao.closest('.hierarchy'));
|
|
294
|
+
hierarchySiblings.should.lengthOf(1);
|
|
295
|
+
query('.node', hierarchySiblings[0]).id.should.equal('n11');
|
|
296
|
+
oc.getHierarchy().should.deep.equal(expectedHierarchy);
|
|
133
297
|
});
|
|
134
298
|
});
|
|
135
299
|
|
|
136
300
|
describe('removeNodes()', function () {
|
|
137
301
|
it('Remove leaf node', function () {
|
|
302
|
+
const expectedHierarchy = cloneValue(hierarchy);
|
|
303
|
+
|
|
304
|
+
delete expectedHierarchy.children[1].children[0].children;
|
|
305
|
+
|
|
138
306
|
oc.removeNodes($dandan);
|
|
139
|
-
$tiehua
|
|
307
|
+
siblingElements($tiehua, '.nodes').should.lengthOf(0);
|
|
308
|
+
should.not.exist(query('.bottomEdge', $tiehua));
|
|
309
|
+
oc.getHierarchy().should.deep.equal(expectedHierarchy);
|
|
140
310
|
});
|
|
311
|
+
|
|
141
312
|
it('Remove parent node', function () {
|
|
313
|
+
const siblingNodesElement = siblingElements($sumiao, '.nodes')[0];
|
|
314
|
+
const expectedHierarchy = cloneValue(hierarchy);
|
|
315
|
+
|
|
316
|
+
expectedHierarchy.children[1].children.splice(0, 1);
|
|
317
|
+
|
|
142
318
|
oc.removeNodes($tiehua);
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
319
|
+
Array.from(siblingNodesElement.children).filter(function (childEl) {
|
|
320
|
+
return childEl.classList && childEl.classList.contains('hierarchy');
|
|
321
|
+
}).should.lengthOf(2);
|
|
322
|
+
should.not.exist(query('#n5'));
|
|
323
|
+
should.not.exist(query('#n8'));
|
|
324
|
+
oc.getHierarchy().should.deep.equal(expectedHierarchy);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('Remove a root child node and preserve sibling order', function () {
|
|
328
|
+
const expectedHierarchy = cloneValue(hierarchy);
|
|
329
|
+
const rootHierarchyElement = $laolao.closest('.hierarchy');
|
|
330
|
+
const rootChildrenContainer = Array.from(rootHierarchyElement.children).find(function (childEl) {
|
|
331
|
+
return childEl.classList && childEl.classList.contains('nodes');
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
expectedHierarchy.children.splice(0, 1);
|
|
335
|
+
|
|
336
|
+
oc.removeNodes($bomiao);
|
|
337
|
+
|
|
338
|
+
should.not.exist(query('#n2'));
|
|
339
|
+
oc.getHierarchy().should.deep.equal(expectedHierarchy);
|
|
340
|
+
Array.from(query(':scope > .nodes', oc.chart).children).filter(function (childEl) {
|
|
341
|
+
return childEl.classList && childEl.classList.contains('hierarchy');
|
|
342
|
+
}).map(function (hierarchyEl) {
|
|
343
|
+
return query('.node', hierarchyEl).id;
|
|
344
|
+
}).should.deep.equal(['n1']);
|
|
345
|
+
Array.from(rootChildrenContainer.children).filter(function (childEl) {
|
|
346
|
+
return childEl.classList && childEl.classList.contains('hierarchy');
|
|
347
|
+
}).map(function (hierarchyEl) {
|
|
348
|
+
return query('.node', hierarchyEl).id;
|
|
349
|
+
}).should.deep.equal(['n3', 'n4']);
|
|
146
350
|
});
|
|
351
|
+
|
|
147
352
|
it('Remove root node', function () {
|
|
148
353
|
oc.removeNodes($laolao);
|
|
149
|
-
|
|
354
|
+
$container.children.length.should.equal(0);
|
|
150
355
|
});
|
|
151
356
|
});
|
|
152
357
|
|
|
153
|
-
|
|
358
|
+
describe('interaction flows', function () {
|
|
359
|
+
it('dispatches hide/show children events through bottom-edge clicks', function () {
|
|
360
|
+
const bottomEdge = query('.bottomEdge', $sumiao);
|
|
361
|
+
const firstChildNode = siblingElements($sumiao, '.nodes')[0].querySelector('.node');
|
|
362
|
+
const events = [];
|
|
363
|
+
|
|
364
|
+
$sumiao.addEventListener('hide-children.orgchart', function (event) {
|
|
365
|
+
events.push(event.type);
|
|
366
|
+
});
|
|
367
|
+
$sumiao.addEventListener('show-children.orgchart', function (event) {
|
|
368
|
+
events.push(event.type);
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
dispatchNativeEvent(bottomEdge, 'click');
|
|
372
|
+
dispatchNativeEvent(firstChildNode, 'transitionend');
|
|
373
|
+
dispatchNativeEvent(bottomEdge, 'click');
|
|
374
|
+
dispatchNativeEvent(firstChildNode, 'transitionend');
|
|
375
|
+
|
|
376
|
+
events.should.deep.equal(['hide-children.orgchart', 'show-children.orgchart']);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it('dispatches hide/show siblings events through horizontal-edge clicks', function () {
|
|
380
|
+
const leftEdge = query('.leftEdge', $sumiao);
|
|
381
|
+
const firstSiblingNode = query('#n2');
|
|
382
|
+
const events = [];
|
|
383
|
+
|
|
384
|
+
$sumiao.addEventListener('hide-siblings.orgchart', function (event) {
|
|
385
|
+
events.push(event.type);
|
|
386
|
+
});
|
|
387
|
+
$sumiao.addEventListener('show-siblings.orgchart', function (event) {
|
|
388
|
+
events.push(event.type);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
dispatchNativeEvent(leftEdge, 'click');
|
|
392
|
+
dispatchNativeEvent(firstSiblingNode, 'transitionend');
|
|
393
|
+
dispatchNativeEvent(leftEdge, 'click');
|
|
394
|
+
dispatchNativeEvent(firstSiblingNode, 'transitionend');
|
|
395
|
+
|
|
396
|
+
events.should.deep.equal(['hide-siblings.orgchart', 'show-siblings.orgchart']);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it('dispatches hide-parent and enters ancestor-collapsed state through top-edge click', function () {
|
|
400
|
+
const topEdge = query('.topEdge', $heihei);
|
|
401
|
+
const hideSpy = sinon.spy();
|
|
402
|
+
const currentHierarchy = $heihei.closest('.hierarchy');
|
|
403
|
+
const leftSiblingHierarchy = currentHierarchy.previousElementSibling;
|
|
404
|
+
const rightSiblingHierarchy = currentHierarchy.nextElementSibling;
|
|
405
|
+
|
|
406
|
+
$heihei.addEventListener('hide-parent.orgchart', hideSpy);
|
|
407
|
+
|
|
408
|
+
dispatchNativeEvent(topEdge, 'click');
|
|
409
|
+
|
|
410
|
+
hideSpy.should.have.been.calledOnce;
|
|
411
|
+
hideSpy.firstCall.args[0].type.should.equal('hide-parent.orgchart');
|
|
412
|
+
currentHierarchy.classList.contains('isAncestorsCollapsed').should.be.true;
|
|
413
|
+
currentHierarchy.classList.contains('isSiblingsCollapsed').should.be.true;
|
|
414
|
+
leftSiblingHierarchy.classList.contains('isCollapsedSibling').should.be.true;
|
|
415
|
+
rightSiblingHierarchy.classList.contains('isCollapsedSibling').should.be.true;
|
|
416
|
+
$sumiao.classList.contains('sliding').should.be.true;
|
|
417
|
+
$sumiao.classList.contains('slide-down').should.be.true;
|
|
418
|
+
$laolao.classList.contains('sliding').should.be.true;
|
|
419
|
+
$laolao.classList.contains('slide-down').should.be.true;
|
|
420
|
+
|
|
421
|
+
dispatchNativeEvent($sumiao, 'transitionend');
|
|
422
|
+
dispatchNativeEvent($laolao, 'transitionend');
|
|
423
|
+
|
|
424
|
+
$sumiao.classList.contains('sliding').should.be.false;
|
|
425
|
+
$laolao.classList.contains('sliding').should.be.false;
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('focuses the clicked node and clears focus when clicking chart background', function () {
|
|
429
|
+
dispatchNativeEvent($sumiao, 'click');
|
|
430
|
+
$sumiao.classList.contains('focused').should.be.true;
|
|
431
|
+
|
|
432
|
+
dispatchNativeEvent($hongmiao, 'click');
|
|
433
|
+
$sumiao.classList.contains('focused').should.be.false;
|
|
434
|
+
$hongmiao.classList.contains('focused').should.be.true;
|
|
435
|
+
|
|
436
|
+
dispatchNativeEvent(oc.chart, 'click');
|
|
437
|
+
queryAll('.node.focused', oc.chart).should.have.lengthOf(0);
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
it('toggles children visibility through bottom-edge clicks and transition cleanup', function () {
|
|
441
|
+
const childNodesContainer = siblingElements($sumiao, '.nodes')[0];
|
|
442
|
+
const bottomEdge = query('.bottomEdge', $sumiao);
|
|
443
|
+
const childHierarchies = Array.from(childNodesContainer.children).filter(function (childEl) {
|
|
444
|
+
return childEl.classList && childEl.classList.contains('hierarchy');
|
|
445
|
+
});
|
|
446
|
+
const visibleChildNodes = childHierarchies.map(function (hierarchyEl) {
|
|
447
|
+
return query('.node', hierarchyEl);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
oc.getNodeState($sumiao, 'children').should.deep.equal({ exist: true, visible: true });
|
|
451
|
+
|
|
452
|
+
dispatchNativeEvent(bottomEdge, 'click');
|
|
453
|
+
childNodesContainer.classList.contains('hidden').should.be.false;
|
|
454
|
+
childHierarchies.forEach(function (hierarchyEl) {
|
|
455
|
+
hierarchyEl.classList.contains('isCollapsedDescendant').should.be.true;
|
|
456
|
+
});
|
|
457
|
+
visibleChildNodes.forEach(function (childNodeEl) {
|
|
458
|
+
childNodeEl.classList.contains('sliding').should.be.true;
|
|
459
|
+
childNodeEl.classList.contains('slide-up').should.be.true;
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
dispatchNativeEvent(visibleChildNodes[0], 'transitionend');
|
|
463
|
+
childNodesContainer.classList.contains('hidden').should.be.true;
|
|
464
|
+
oc.getNodeState($sumiao, 'children').should.deep.equal({ exist: true, visible: false });
|
|
465
|
+
|
|
466
|
+
dispatchNativeEvent(bottomEdge, 'click');
|
|
467
|
+
childNodesContainer.classList.contains('hidden').should.be.false;
|
|
468
|
+
visibleChildNodes.forEach(function (childNodeEl) {
|
|
469
|
+
childNodeEl.classList.contains('sliding').should.be.true;
|
|
470
|
+
childNodeEl.classList.contains('slide-up').should.be.false;
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
dispatchNativeEvent(visibleChildNodes[0], 'transitionend');
|
|
474
|
+
oc.getNodeState($sumiao, 'children').should.deep.equal({ exist: true, visible: true });
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
it('toggles sibling visibility through horizontal-edge clicks and transition cleanup', function () {
|
|
478
|
+
const leftEdge = query('.leftEdge', $sumiao);
|
|
479
|
+
const siblingHierarchies = Array.from($sumiao.closest('.hierarchy').parentElement.children).filter(function (childEl) {
|
|
480
|
+
return childEl !== $sumiao.closest('.hierarchy') && childEl.classList.contains('hierarchy');
|
|
481
|
+
});
|
|
482
|
+
const siblingNodes = siblingHierarchies.map(function (hierarchyEl) {
|
|
483
|
+
return query('.node', hierarchyEl);
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
oc.getNodeState($sumiao, 'siblings').should.deep.equal({ exist: true, visible: true });
|
|
487
|
+
|
|
488
|
+
dispatchNativeEvent(leftEdge, 'click');
|
|
489
|
+
siblingNodes.forEach(function (siblingNodeEl) {
|
|
490
|
+
siblingNodeEl.classList.contains('sliding').should.be.true;
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
dispatchNativeEvent(siblingNodes[0], 'transitionend');
|
|
494
|
+
siblingHierarchies.forEach(function (siblingHierarchyEl) {
|
|
495
|
+
siblingHierarchyEl.classList.contains('hidden').should.be.true;
|
|
496
|
+
});
|
|
497
|
+
oc.getNodeState($sumiao, 'siblings').should.deep.equal({ exist: true, visible: false });
|
|
498
|
+
|
|
499
|
+
dispatchNativeEvent(leftEdge, 'click');
|
|
500
|
+
siblingHierarchies.forEach(function (siblingHierarchyEl) {
|
|
501
|
+
siblingHierarchyEl.classList.contains('hidden').should.be.false;
|
|
502
|
+
});
|
|
503
|
+
siblingNodes.forEach(function (siblingNodeEl) {
|
|
504
|
+
siblingNodeEl.classList.contains('sliding').should.be.true;
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
dispatchNativeEvent(siblingNodes[0], 'transitionend');
|
|
508
|
+
oc.getNodeState($sumiao, 'siblings').should.deep.equal({ exist: true, visible: true });
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
it('shows directional edge icons on hover and clears them on mouseleave', function () {
|
|
512
|
+
const topEdge = query('.topEdge', $sumiao);
|
|
513
|
+
const bottomEdge = query('.bottomEdge', $sumiao);
|
|
514
|
+
const leftEdge = query('.leftEdge', $sumiao);
|
|
515
|
+
const rightEdge = query('.rightEdge', $sumiao);
|
|
516
|
+
|
|
517
|
+
dispatchNativeEvent($sumiao, 'mouseenter', { bubbles: false });
|
|
518
|
+
topEdge.classList.contains(oc.options.icons.collapseToDown).should.be.true;
|
|
519
|
+
bottomEdge.classList.contains(oc.options.icons.expandToUp).should.be.true;
|
|
520
|
+
leftEdge.classList.contains(oc.options.icons.expandToRight).should.be.true;
|
|
521
|
+
rightEdge.classList.contains(oc.options.icons.collapseToLeft).should.be.true;
|
|
522
|
+
|
|
523
|
+
dispatchNativeEvent($sumiao, 'mouseleave', { bubbles: false });
|
|
524
|
+
topEdge.classList.contains(oc.options.icons.expandToUp).should.be.false;
|
|
525
|
+
topEdge.classList.contains(oc.options.icons.collapseToDown).should.be.false;
|
|
526
|
+
bottomEdge.classList.contains(oc.options.icons.expandToUp).should.be.false;
|
|
527
|
+
bottomEdge.classList.contains(oc.options.icons.collapseToDown).should.be.false;
|
|
528
|
+
leftEdge.classList.contains(oc.options.icons.expandToRight).should.be.false;
|
|
529
|
+
leftEdge.classList.contains(oc.options.icons.collapseToLeft).should.be.false;
|
|
530
|
+
rightEdge.classList.contains(oc.options.icons.expandToRight).should.be.false;
|
|
531
|
+
rightEdge.classList.contains(oc.options.icons.collapseToLeft).should.be.false;
|
|
532
|
+
});
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
describe('query methods', function () {
|
|
536
|
+
it('returns current parent, children, and siblings as native nodes', function () {
|
|
537
|
+
oc.getParent($sumiao).should.equal($laolao);
|
|
538
|
+
oc.getChildren($sumiao).map(function (nodeEl) { return nodeEl.id; }).should.deep.equal(['n5', 'n6', 'n7']);
|
|
539
|
+
oc.getSiblings($sumiao).map(function (nodeEl) { return nodeEl.id; }).should.deep.equal(['n2', 'n4']);
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
it('updates relationship queries after tree mutations', function () {
|
|
543
|
+
oc.addChildren($bomiao, [{ name: 'Li Xin', id: 'n11' }]);
|
|
544
|
+
oc.addSiblings($sumiao, [{ name: 'Li Na', id: 'n12' }]);
|
|
545
|
+
|
|
546
|
+
should.exist(query('#n11'));
|
|
547
|
+
should.exist(query('#n12'));
|
|
548
|
+
oc.getChildren($bomiao).map(function (nodeEl) { return nodeEl.id; }).should.deep.equal(['n11']);
|
|
549
|
+
oc.getSiblings($sumiao).map(function (nodeEl) { return nodeEl.id; }).should.deep.equal(['n2', 'n4', 'n12']);
|
|
550
|
+
oc.getParent(query('#n11')).should.equal($bomiao);
|
|
551
|
+
oc.getParent(query('#n12')).should.equal($laolao);
|
|
552
|
+
});
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
describe('runtime option flows', function () {
|
|
556
|
+
it('rebuilds the rendered hierarchy through setOptions({ data })', function () {
|
|
557
|
+
const replacementData = {
|
|
558
|
+
id: 'r1',
|
|
559
|
+
name: 'Replacement Root',
|
|
560
|
+
children: [{ id: 'r2', name: 'Replacement Child' }]
|
|
561
|
+
};
|
|
562
|
+
const previousChart = oc.chart;
|
|
563
|
+
|
|
564
|
+
oc.setOptions({ data: replacementData });
|
|
565
|
+
|
|
566
|
+
oc.chart.should.not.equal(previousChart);
|
|
567
|
+
should.not.exist(query('#n1', $container));
|
|
568
|
+
query('#r1', oc.chart).querySelector('.title').textContent.should.equal('Replacement Root');
|
|
569
|
+
oc.getChildren(query('#r1', oc.chart)).map(function (nodeEl) { return nodeEl.id; }).should.deep.equal(['r2']);
|
|
570
|
+
oc.getHierarchy().should.deep.equal({ id: 'r1', children: [{ id: 'r2' }] });
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
it('moves the chart through the minimap viewport and cleans up when disabled', function () {
|
|
574
|
+
const minimapChart = new OrgChart({
|
|
575
|
+
chartContainer: '#chart-container',
|
|
576
|
+
data: ds,
|
|
577
|
+
minimap: true
|
|
578
|
+
});
|
|
579
|
+
const minimapElement = query('.orgchart-minimap', $container);
|
|
580
|
+
const minimapStage = query('.orgchart-minimap-stage', minimapElement);
|
|
581
|
+
const minimapViewport = query('.orgchart-minimap-viewport', minimapElement);
|
|
582
|
+
const containerRectStub = sinon.stub(minimapChart.chartContainer, 'getBoundingClientRect').returns({
|
|
583
|
+
left: 0, top: 0, right: 400, bottom: 240, width: 400, height: 240
|
|
584
|
+
});
|
|
585
|
+
const chartRectStub = sinon.stub(minimapChart.chart, 'getBoundingClientRect').returns({
|
|
586
|
+
left: -100, top: -50, right: 900, bottom: 550, width: 1000, height: 600
|
|
587
|
+
});
|
|
588
|
+
const stageRectStub = sinon.stub(minimapStage, 'getBoundingClientRect').returns({
|
|
589
|
+
left: 0, top: 0, right: 180, bottom: 104, width: 180, height: 104
|
|
590
|
+
});
|
|
591
|
+
const viewportRectStub = sinon.stub(minimapViewport, 'getBoundingClientRect').returns({
|
|
592
|
+
left: 20, top: 10, right: 89, bottom: 52, width: 69, height: 42
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
Object.defineProperty(minimapStage, 'clientWidth', { configurable: true, value: 180 });
|
|
596
|
+
Object.defineProperty(minimapStage, 'clientHeight', { configurable: true, value: 104 });
|
|
597
|
+
minimapChart.updateMinimap(true);
|
|
598
|
+
|
|
599
|
+
dispatchEventWithProperties(minimapViewport, 'mousedown', {
|
|
600
|
+
clientX: 20,
|
|
601
|
+
clientY: 10,
|
|
602
|
+
preventDefault: function () {}
|
|
603
|
+
});
|
|
604
|
+
dispatchEventWithProperties(document, 'mousemove', { clientX: 40, clientY: 30 });
|
|
605
|
+
dispatchNativeEvent(document, 'mouseup');
|
|
606
|
+
|
|
607
|
+
minimapChart.chart.style.transform.should.not.equal('');
|
|
608
|
+
should.equal(getState(minimapChart.chart, 'minimapDragState'), null);
|
|
609
|
+
|
|
610
|
+
minimapChart.setOptions('minimap', false);
|
|
611
|
+
|
|
612
|
+
should.not.exist(query('.orgchart-minimap', $container));
|
|
613
|
+
$container.style.position.should.equal('');
|
|
614
|
+
containerRectStub.restore();
|
|
615
|
+
chartRectStub.restore();
|
|
616
|
+
stageRectStub.restore();
|
|
617
|
+
viewportRectStub.restore();
|
|
618
|
+
});
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
describe('hybrid vertical flows', function () {
|
|
622
|
+
it('expands and collapses vertical descendants through toggle button clicks', function () {
|
|
623
|
+
let hybridChart;
|
|
624
|
+
let hybridSumiao;
|
|
625
|
+
let toggleBtn;
|
|
626
|
+
let descendantsWrapper;
|
|
627
|
+
let childNodes;
|
|
628
|
+
|
|
629
|
+
$container.innerHTML = '';
|
|
630
|
+
hybridChart = new OrgChart({
|
|
631
|
+
chartContainer: '#chart-container',
|
|
632
|
+
data: {
|
|
633
|
+
id: 'n1',
|
|
634
|
+
name: 'Lao Lao',
|
|
635
|
+
children: [
|
|
636
|
+
{ id: 'n2', name: 'Bo Miao' },
|
|
637
|
+
{
|
|
638
|
+
id: 'n3',
|
|
639
|
+
name: 'Su Miao',
|
|
640
|
+
children: [
|
|
641
|
+
{ id: 'n5', name: 'Tie Hua' },
|
|
642
|
+
{ id: 'n6', name: 'Hei Hei' }
|
|
643
|
+
]
|
|
644
|
+
}
|
|
645
|
+
]
|
|
646
|
+
},
|
|
647
|
+
verticalLevel: 2,
|
|
648
|
+
visibleLevel: 2
|
|
649
|
+
});
|
|
650
|
+
hybridSumiao = query('#n3', hybridChart.chart);
|
|
651
|
+
toggleBtn = query('.toggleBtn', hybridSumiao);
|
|
652
|
+
descendantsWrapper = siblingElements(hybridSumiao, '.nodes')[0];
|
|
653
|
+
childNodes = Array.from(descendantsWrapper.children).filter(function (childEl) {
|
|
654
|
+
return childEl.classList && childEl.classList.contains('hierarchy');
|
|
655
|
+
}).map(function (hierarchyEl) {
|
|
656
|
+
return query('.node', hierarchyEl);
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
should.exist(toggleBtn);
|
|
660
|
+
descendantsWrapper.classList.contains('hidden').should.be.true;
|
|
661
|
+
childNodes.forEach(function (childNodeEl) {
|
|
662
|
+
childNodeEl.classList.contains('slide-up').should.be.true;
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
dispatchNativeEvent(toggleBtn, 'click');
|
|
666
|
+
descendantsWrapper.classList.contains('hidden').should.be.false;
|
|
667
|
+
childNodes.forEach(function (childNodeEl) {
|
|
668
|
+
childNodeEl.classList.contains('sliding').should.be.true;
|
|
669
|
+
childNodeEl.classList.contains('slide-up').should.be.false;
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
dispatchNativeEvent(childNodes[0], 'transitionend');
|
|
673
|
+
childNodes.forEach(function (childNodeEl) {
|
|
674
|
+
childNodeEl.classList.contains('sliding').should.be.false;
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
dispatchNativeEvent(toggleBtn, 'click');
|
|
678
|
+
descendantsWrapper.classList.contains('hidden').should.be.false;
|
|
679
|
+
childNodes.forEach(function (childNodeEl) {
|
|
680
|
+
childNodeEl.classList.contains('sliding').should.be.true;
|
|
681
|
+
childNodeEl.classList.contains('slide-up').should.be.true;
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
dispatchNativeEvent(childNodes[0], 'transitionend');
|
|
685
|
+
descendantsWrapper.classList.contains('hidden').should.be.true;
|
|
686
|
+
childNodes.forEach(function (childNodeEl) {
|
|
687
|
+
childNodeEl.classList.contains('sliding').should.be.false;
|
|
688
|
+
});
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
describe('drag and drop flows', function () {
|
|
693
|
+
it('filters allowed drop zones through the public dropCriteria option', function () {
|
|
694
|
+
const draggableChart = new OrgChart({
|
|
695
|
+
chartContainer: '#chart-container',
|
|
696
|
+
data: ds,
|
|
697
|
+
draggable: true,
|
|
698
|
+
dropCriteria: function (draggedNode, draggedParentNode, dropNode) {
|
|
699
|
+
return draggedNode.id === 'n2' && draggedParentNode.id === 'n1' && dropNode.id === 'n4';
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
const draggableBomiao = query('#n2', draggableChart.chart);
|
|
703
|
+
|
|
704
|
+
dispatchEventWithProperties(draggableBomiao, 'dragstart', {
|
|
705
|
+
dataTransfer: {
|
|
706
|
+
setData: function () {}
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
queryAll('.allowedDrop', draggableChart.chart).map(function (nodeEl) {
|
|
711
|
+
return nodeEl.id;
|
|
712
|
+
}).should.deep.equal(['n4']);
|
|
713
|
+
|
|
714
|
+
dispatchNativeEvent(draggableBomiao, 'dragend');
|
|
715
|
+
queryAll('.allowedDrop', draggableChart.chart).should.have.lengthOf(0);
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
it('dispatches otherdropped for an external dragged item', async function () {
|
|
719
|
+
const draggableChart = new OrgChart({
|
|
720
|
+
chartContainer: '#chart-container',
|
|
721
|
+
data: ds,
|
|
722
|
+
draggable: true
|
|
723
|
+
});
|
|
724
|
+
const draggableBomiao = query('#n2', draggableChart.chart);
|
|
725
|
+
const foreignItem = document.createElement('div');
|
|
726
|
+
const dropSpy = sinon.spy();
|
|
727
|
+
|
|
728
|
+
foreignItem.className = 'foreign-item';
|
|
729
|
+
draggableChart.chart.addEventListener('otherdropped.orgchart', dropSpy);
|
|
730
|
+
|
|
731
|
+
dispatchEventWithProperties(draggableBomiao, 'dragstart', {
|
|
732
|
+
dataTransfer: {
|
|
733
|
+
setData: function () {}
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
setState(draggableChart.chart, 'dragged', foreignItem);
|
|
737
|
+
|
|
738
|
+
await draggableChart.dropHandler({
|
|
739
|
+
dropZone: draggableBomiao,
|
|
740
|
+
originalEvent: {
|
|
741
|
+
currentTarget: draggableBomiao
|
|
742
|
+
}
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
dropSpy.should.have.been.calledOnce;
|
|
746
|
+
dropSpy.firstCall.args[0].type.should.equal('otherdropped.orgchart');
|
|
747
|
+
dropSpy.firstCall.args[0].detail.draggedItem.should.equal(foreignItem);
|
|
748
|
+
dropSpy.firstCall.args[0].detail.dropZone.should.equal(draggableBomiao);
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
it('marks allowed drop zones on native dragstart and clears them on dragend', function () {
|
|
752
|
+
const draggableChart = new OrgChart({
|
|
753
|
+
chartContainer: '#chart-container',
|
|
754
|
+
data: ds,
|
|
755
|
+
draggable: true
|
|
756
|
+
});
|
|
757
|
+
const draggableBomiao = query('#n2', draggableChart.chart);
|
|
758
|
+
const draggableHongmiao = query('#n4', draggableChart.chart);
|
|
759
|
+
|
|
760
|
+
dispatchEventWithProperties(draggableBomiao, 'dragstart', {
|
|
761
|
+
dataTransfer: {
|
|
762
|
+
setData: function () {}
|
|
763
|
+
}
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
draggableHongmiao.classList.contains('allowedDrop').should.be.true;
|
|
767
|
+
queryAll('.allowedDrop', draggableChart.chart).length.should.be.above(0);
|
|
768
|
+
|
|
769
|
+
dispatchNativeEvent(draggableBomiao, 'dragend');
|
|
770
|
+
|
|
771
|
+
queryAll('.allowedDrop', draggableChart.chart).should.have.lengthOf(0);
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
it('dispatches nodedrop and moves the dragged node through native drag/drop events', async function () {
|
|
775
|
+
const originalJSONDigger = global.JSONDigger;
|
|
776
|
+
const draggableChart = new OrgChart({
|
|
777
|
+
chartContainer: '#chart-container',
|
|
778
|
+
data: ds,
|
|
779
|
+
draggable: true
|
|
780
|
+
});
|
|
781
|
+
const draggableBomiao = query('#n2', draggableChart.chart);
|
|
782
|
+
const draggableHongmiao = query('#n4', draggableChart.chart);
|
|
783
|
+
const rootChildrenContainer = siblingElements(query('#n1', draggableChart.chart), '.nodes')[0];
|
|
784
|
+
const dropSpy = sinon.spy();
|
|
785
|
+
|
|
786
|
+
global.JSONDigger = function () {
|
|
787
|
+
return {
|
|
788
|
+
findOneNode: function () {
|
|
789
|
+
return null;
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
};
|
|
793
|
+
draggableChart.chart.addEventListener('nodedrop.orgchart', dropSpy);
|
|
794
|
+
|
|
795
|
+
dispatchEventWithProperties(draggableBomiao, 'dragstart', {
|
|
796
|
+
dataTransfer: {
|
|
797
|
+
setData: function () {}
|
|
798
|
+
}
|
|
799
|
+
});
|
|
800
|
+
draggableHongmiao.classList.add('allowedDrop');
|
|
801
|
+
|
|
802
|
+
await draggableChart.dropHandler({
|
|
803
|
+
dropZone: draggableHongmiao,
|
|
804
|
+
originalEvent: {
|
|
805
|
+
currentTarget: draggableHongmiao
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
dropSpy.should.have.been.calledOnce;
|
|
810
|
+
dropSpy.firstCall.args[0].type.should.equal('nodedrop.orgchart');
|
|
811
|
+
dropSpy.firstCall.args[0].detail.draggedNode.should.equal(draggableBomiao);
|
|
812
|
+
dropSpy.firstCall.args[0].detail.dropZone.should.equal(draggableHongmiao);
|
|
813
|
+
queryAll('#n2', siblingElements(draggableHongmiao, '.nodes')[0]).should.have.lengthOf(1);
|
|
814
|
+
Array.from(rootChildrenContainer.children).filter(function (childEl) {
|
|
815
|
+
return childEl.classList && childEl.classList.contains('hierarchy');
|
|
816
|
+
}).map(function (hierarchyEl) {
|
|
817
|
+
return query('.node', hierarchyEl).id;
|
|
818
|
+
}).should.deep.equal(['n3', 'n4']);
|
|
819
|
+
|
|
820
|
+
global.JSONDigger = originalJSONDigger;
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
it('adds a vertical toggle when hybrid drag and drop gives a leaf node children', async function () {
|
|
824
|
+
const originalJSONDigger = global.JSONDigger;
|
|
825
|
+
const hybridData = {
|
|
826
|
+
id: 'n1',
|
|
827
|
+
name: 'Root',
|
|
828
|
+
children: [
|
|
829
|
+
{
|
|
830
|
+
id: 'n2',
|
|
831
|
+
name: 'Source Parent',
|
|
832
|
+
children: [{ id: 'n3', name: 'Dragged Node' }]
|
|
833
|
+
},
|
|
834
|
+
{ id: 'n4', name: 'Drop Node' }
|
|
835
|
+
]
|
|
836
|
+
};
|
|
837
|
+
const hybridChart = new OrgChart({
|
|
838
|
+
chartContainer: '#chart-container',
|
|
839
|
+
data: hybridData,
|
|
840
|
+
draggable: true,
|
|
841
|
+
verticalLevel: 2
|
|
842
|
+
});
|
|
843
|
+
const draggedNode = query('#n3', hybridChart.chart);
|
|
844
|
+
const dropNode = query('#n4', hybridChart.chart);
|
|
845
|
+
|
|
846
|
+
global.JSONDigger = require('json-digger');
|
|
847
|
+
try {
|
|
848
|
+
setState(hybridChart.chart, 'dragged', draggedNode);
|
|
849
|
+
dropNode.classList.add('allowedDrop');
|
|
850
|
+
await hybridChart.dropHandler({
|
|
851
|
+
dropZone: dropNode,
|
|
852
|
+
originalEvent: { currentTarget: dropNode }
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
query('#n4', hybridChart.chart).querySelector(':scope > .toggleBtn').should.exist;
|
|
856
|
+
query('#n3', hybridChart.chart).closest('.hierarchy').parentElement.previousElementSibling.id.should.equal('n4');
|
|
857
|
+
} finally {
|
|
858
|
+
global.JSONDigger = originalJSONDigger;
|
|
859
|
+
}
|
|
860
|
+
});
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
describe('compact mode flows', function () {
|
|
864
|
+
it('toggles loose and compact modes through compact-node controls', function () {
|
|
865
|
+
let compactChart;
|
|
866
|
+
let compactNode;
|
|
867
|
+
let backToLooseSymbol;
|
|
868
|
+
let backToCompactSymbol;
|
|
869
|
+
|
|
870
|
+
$container.innerHTML = '';
|
|
871
|
+
compactChart = new OrgChart({
|
|
872
|
+
chartContainer: '#chart-container',
|
|
873
|
+
data: {
|
|
874
|
+
id: 'n1',
|
|
875
|
+
name: 'Lao Lao',
|
|
876
|
+
children: [
|
|
877
|
+
{
|
|
878
|
+
id: 'n2',
|
|
879
|
+
name: 'Bo Miao',
|
|
880
|
+
compact: true,
|
|
881
|
+
children: [
|
|
882
|
+
{ id: 'n3', name: 'Su Miao' },
|
|
883
|
+
{ id: 'n4', name: 'Hong Miao' }
|
|
884
|
+
]
|
|
885
|
+
}
|
|
886
|
+
]
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
compactNode = query('#n2', compactChart.chart);
|
|
890
|
+
backToLooseSymbol = query('.backToLooseSymbol', compactNode);
|
|
891
|
+
backToCompactSymbol = query('.backToCompactSymbol', compactNode);
|
|
892
|
+
|
|
893
|
+
should.exist(backToLooseSymbol);
|
|
894
|
+
should.exist(backToCompactSymbol);
|
|
895
|
+
compactNode.classList.contains('looseMode').should.be.false;
|
|
896
|
+
backToCompactSymbol.classList.contains('hidden').should.be.true;
|
|
897
|
+
|
|
898
|
+
dispatchNativeEvent(backToLooseSymbol, 'click');
|
|
899
|
+
|
|
900
|
+
compactNode.classList.contains('looseMode').should.be.true;
|
|
901
|
+
backToLooseSymbol.classList.contains('hidden').should.be.true;
|
|
902
|
+
backToCompactSymbol.classList.contains('hidden').should.be.false;
|
|
903
|
+
|
|
904
|
+
dispatchNativeEvent(backToCompactSymbol, 'click');
|
|
905
|
+
|
|
906
|
+
compactNode.classList.contains('looseMode').should.be.false;
|
|
907
|
+
backToLooseSymbol.classList.contains('hidden').should.be.false;
|
|
908
|
+
backToCompactSymbol.classList.contains('hidden').should.be.true;
|
|
909
|
+
});
|
|
910
|
+
});
|
|
911
|
+
|
|
912
|
+
describe('export flows', function () {
|
|
913
|
+
it('invokes export() through the rendered export button click', function () {
|
|
914
|
+
let exportChart;
|
|
915
|
+
let exportButton;
|
|
916
|
+
let exportStub;
|
|
917
|
+
|
|
918
|
+
$container.innerHTML = '';
|
|
919
|
+
Array.from(document.querySelectorAll('.oc-export-btn')).forEach(function (buttonEl) {
|
|
920
|
+
buttonEl.remove();
|
|
921
|
+
});
|
|
922
|
+
exportChart = new OrgChart({
|
|
923
|
+
chartContainer: '#chart-container',
|
|
924
|
+
data: ds,
|
|
925
|
+
exportButton: true
|
|
926
|
+
});
|
|
927
|
+
exportButton = document.querySelector('.oc-export-btn');
|
|
928
|
+
exportStub = sinon.stub(exportChart, 'export');
|
|
929
|
+
|
|
930
|
+
should.exist(exportButton);
|
|
931
|
+
|
|
932
|
+
dispatchNativeEvent(exportButton, 'click');
|
|
933
|
+
|
|
934
|
+
exportStub.should.have.been.calledOnce;
|
|
935
|
+
exportButton.remove();
|
|
936
|
+
exportStub.restore();
|
|
937
|
+
});
|
|
938
|
+
});
|
|
939
|
+
|
|
940
|
+
describe('pan and zoom binding flows', function () {
|
|
941
|
+
it('pans through native mousedown, mousemove, and mouseup bindings', function () {
|
|
942
|
+
let panChart;
|
|
943
|
+
let moveEvent;
|
|
944
|
+
|
|
945
|
+
$container.innerHTML = '';
|
|
946
|
+
panChart = new OrgChart({
|
|
947
|
+
chartContainer: '#chart-container',
|
|
948
|
+
data: ds,
|
|
949
|
+
pan: true
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
dispatchEventWithProperties(panChart.chart, 'mousedown', {
|
|
953
|
+
target: panChart.chart,
|
|
954
|
+
pageX: 10,
|
|
955
|
+
pageY: 20
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
getState(panChart.chart, 'panning').should.equal(true);
|
|
959
|
+
moveEvent = new window.Event('mousemove', { bubbles: true });
|
|
960
|
+
Object.defineProperty(moveEvent, 'pageX', { configurable: true, value: 30 });
|
|
961
|
+
Object.defineProperty(moveEvent, 'pageY', { configurable: true, value: 45 });
|
|
962
|
+
panChart.chart.dispatchEvent(moveEvent);
|
|
963
|
+
panChart.chart.style.transform.should.equal('matrix(1, 0, 0, 1, 20, 25)');
|
|
964
|
+
|
|
965
|
+
dispatchNativeEvent(document, 'mouseup');
|
|
966
|
+
|
|
967
|
+
getState(panChart.chart, 'panning').should.equal(false);
|
|
968
|
+
panChart.chart.style.cursor.should.equal('default');
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
it('zooms through the bound wheel listener on the chart container', function () {
|
|
972
|
+
let zoomChart;
|
|
973
|
+
|
|
974
|
+
$container.innerHTML = '';
|
|
975
|
+
zoomChart = new OrgChart({
|
|
976
|
+
chartContainer: '#chart-container',
|
|
977
|
+
data: ds,
|
|
978
|
+
zoom: true
|
|
979
|
+
});
|
|
980
|
+
zoomChart.chart.getBoundingClientRect = function () {
|
|
981
|
+
return { left: 0, top: 0 };
|
|
982
|
+
};
|
|
983
|
+
zoomChart.chartContainer.getBoundingClientRect = function () {
|
|
984
|
+
return {
|
|
985
|
+
left: 0,
|
|
986
|
+
top: 0,
|
|
987
|
+
right: 200,
|
|
988
|
+
bottom: 100,
|
|
989
|
+
width: 200,
|
|
990
|
+
height: 100
|
|
991
|
+
};
|
|
992
|
+
};
|
|
993
|
+
|
|
994
|
+
dispatchEventWithProperties(zoomChart.chartContainer, 'wheel', {
|
|
995
|
+
deltaY: -1,
|
|
996
|
+
clientX: 60,
|
|
997
|
+
clientY: 45,
|
|
998
|
+
preventDefault: function () {}
|
|
999
|
+
});
|
|
1000
|
+
|
|
1001
|
+
zoomChart.chart.style.transform.should.equal('matrix(1.2, 0, 0, 1.2, -12, -9)');
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
it('pinch-zooms through bound touchstart, touchmove, and touchend listeners', function () {
|
|
1005
|
+
let zoomChart;
|
|
1006
|
+
|
|
1007
|
+
$container.innerHTML = '';
|
|
1008
|
+
zoomChart = new OrgChart({
|
|
1009
|
+
chartContainer: '#chart-container',
|
|
1010
|
+
data: ds,
|
|
1011
|
+
zoom: true
|
|
1012
|
+
});
|
|
1013
|
+
zoomChart.chart.getBoundingClientRect = function () {
|
|
1014
|
+
return { left: 0, top: 0 };
|
|
1015
|
+
};
|
|
1016
|
+
zoomChart.chartContainer.getBoundingClientRect = function () {
|
|
1017
|
+
return {
|
|
1018
|
+
left: 0,
|
|
1019
|
+
top: 0,
|
|
1020
|
+
right: 200,
|
|
1021
|
+
bottom: 100,
|
|
1022
|
+
width: 200,
|
|
1023
|
+
height: 100
|
|
1024
|
+
};
|
|
1025
|
+
};
|
|
1026
|
+
|
|
1027
|
+
dispatchEventWithProperties(zoomChart.chartContainer, 'touchstart', {
|
|
1028
|
+
touches: [
|
|
1029
|
+
{ clientX: 40, clientY: 40 },
|
|
1030
|
+
{ clientX: 80, clientY: 40 }
|
|
1031
|
+
]
|
|
1032
|
+
});
|
|
1033
|
+
dispatchEventWithProperties(document, 'touchmove', {
|
|
1034
|
+
touches: [
|
|
1035
|
+
{ clientX: 30, clientY: 40 },
|
|
1036
|
+
{ clientX: 90, clientY: 40 }
|
|
1037
|
+
]
|
|
1038
|
+
});
|
|
1039
|
+
dispatchNativeEvent(document, 'touchend');
|
|
1040
|
+
|
|
1041
|
+
zoomChart.chart.style.transform.should.equal('matrix(1.2, 0, 0, 1.2, -12, -8)');
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1044
|
+
it('toggles pan bindings through setOptions()', function () {
|
|
1045
|
+
let panChart;
|
|
1046
|
+
|
|
1047
|
+
$container.innerHTML = '';
|
|
1048
|
+
panChart = new OrgChart({
|
|
1049
|
+
chartContainer: '#chart-container',
|
|
1050
|
+
data: ds
|
|
1051
|
+
});
|
|
1052
|
+
setState(panChart.chart, 'panning', false);
|
|
1053
|
+
|
|
1054
|
+
dispatchEventWithProperties(panChart.chart, 'mousedown', {
|
|
1055
|
+
target: panChart.chart,
|
|
1056
|
+
pageX: 10,
|
|
1057
|
+
pageY: 20
|
|
1058
|
+
});
|
|
1059
|
+
getState(panChart.chart, 'panning').should.equal(false);
|
|
1060
|
+
|
|
1061
|
+
panChart.setOptions({ pan: true });
|
|
1062
|
+
dispatchEventWithProperties(panChart.chart, 'mousedown', {
|
|
1063
|
+
target: panChart.chart,
|
|
1064
|
+
pageX: 10,
|
|
1065
|
+
pageY: 20
|
|
1066
|
+
});
|
|
1067
|
+
getState(panChart.chart, 'panning').should.equal(true);
|
|
1068
|
+
|
|
1069
|
+
dispatchNativeEvent(document, 'mouseup');
|
|
1070
|
+
panChart.setOptions({ pan: false });
|
|
1071
|
+
setState(panChart.chart, 'panning', false);
|
|
1072
|
+
|
|
1073
|
+
dispatchEventWithProperties(panChart.chart, 'mousedown', {
|
|
1074
|
+
target: panChart.chart,
|
|
1075
|
+
pageX: 30,
|
|
1076
|
+
pageY: 40
|
|
1077
|
+
});
|
|
1078
|
+
getState(panChart.chart, 'panning').should.equal(false);
|
|
1079
|
+
});
|
|
1080
|
+
|
|
1081
|
+
it('toggles zoom bindings through setOptions()', function () {
|
|
1082
|
+
let zoomChart;
|
|
1083
|
+
|
|
1084
|
+
$container.innerHTML = '';
|
|
1085
|
+
zoomChart = new OrgChart({
|
|
1086
|
+
chartContainer: '#chart-container',
|
|
1087
|
+
data: ds
|
|
1088
|
+
});
|
|
1089
|
+
zoomChart.chart.getBoundingClientRect = function () {
|
|
1090
|
+
return { left: 0, top: 0 };
|
|
1091
|
+
};
|
|
1092
|
+
zoomChart.chartContainer.getBoundingClientRect = function () {
|
|
1093
|
+
return {
|
|
1094
|
+
left: 0,
|
|
1095
|
+
top: 0,
|
|
1096
|
+
right: 200,
|
|
1097
|
+
bottom: 100,
|
|
1098
|
+
width: 200,
|
|
1099
|
+
height: 100
|
|
1100
|
+
};
|
|
1101
|
+
};
|
|
1102
|
+
|
|
1103
|
+
dispatchEventWithProperties(zoomChart.chartContainer, 'wheel', {
|
|
1104
|
+
deltaY: -1,
|
|
1105
|
+
clientX: 60,
|
|
1106
|
+
clientY: 45,
|
|
1107
|
+
preventDefault: function () {}
|
|
1108
|
+
});
|
|
1109
|
+
zoomChart.chart.style.transform.should.equal('');
|
|
1110
|
+
|
|
1111
|
+
zoomChart.setOptions({ zoom: true });
|
|
1112
|
+
dispatchEventWithProperties(zoomChart.chartContainer, 'wheel', {
|
|
1113
|
+
deltaY: -1,
|
|
1114
|
+
clientX: 60,
|
|
1115
|
+
clientY: 45,
|
|
1116
|
+
preventDefault: function () {}
|
|
1117
|
+
});
|
|
1118
|
+
zoomChart.chart.style.transform.should.equal('matrix(1.2, 0, 0, 1.2, -12, -9)');
|
|
1119
|
+
|
|
1120
|
+
zoomChart.setOptions({ zoom: false });
|
|
1121
|
+
zoomChart.chart.style.transform = 'none';
|
|
1122
|
+
dispatchEventWithProperties(zoomChart.chartContainer, 'wheel', {
|
|
1123
|
+
deltaY: -1,
|
|
1124
|
+
clientX: 60,
|
|
1125
|
+
clientY: 45,
|
|
1126
|
+
preventDefault: function () {}
|
|
1127
|
+
});
|
|
1128
|
+
zoomChart.chart.style.transform.should.equal('none');
|
|
1129
|
+
});
|
|
1130
|
+
});
|
|
1131
|
+
});
|