orgchart 6.0.0 → 7.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.
Files changed (35) hide show
  1. package/.github/workflows/publish.yml +11 -8
  2. package/README.md +47 -35
  3. package/README.zh-cn.md +73 -30
  4. package/README.zh-tw.md +73 -30
  5. package/demo/edit-chart.html +0 -4
  6. package/demo/edit-hybrid-chart.html +3 -4
  7. package/demo/index.html +5 -1
  8. package/demo/ondemand-loading-data.html +137 -80
  9. package/demo/options-compact-drag.html +82 -0
  10. package/demo/options-export-navigation.html +76 -0
  11. package/demo/options-horizontal-template.html +80 -0
  12. package/demo/options-hybrid-navigation.html +71 -0
  13. package/dist/css/orgchart.css +12 -1
  14. package/dist/css/orgchart.min.css +1 -1
  15. package/dist/js/orgchart.js +896 -523
  16. package/dist/js/orgchart.min.js +1 -1
  17. package/dist/js/orgchart.min.js.map +1 -1
  18. package/gulpfile.js +5 -1
  19. package/package.json +2 -1
  20. package/src/css/orgchart.css +12 -1
  21. package/src/js/orgchart.js +896 -523
  22. package/test/compatibility/README.md +123 -0
  23. package/test/compatibility/browser.tests.js +1279 -0
  24. package/test/compatibility/options.tests.js +628 -0
  25. package/test/cypress/e2e/demo-interactions.cy.js +3 -3
  26. package/test/cypress/e2e/demo-rendering.cy.js +62 -1
  27. package/test/cypress/e2e/drag-drop-hybrid-chart.cy.js +2 -1
  28. package/test/cypress/e2e/local-datasource.cy.js +16 -16
  29. package/test/cypress/e2e/ondemand-loading-data.cy.js +34 -10
  30. package/test/cypress/e2e/toggle-sibs-resp.cy.js +4 -4
  31. package/test/cypress/e2e/vertical-vevel.cy.js +5 -5
  32. package/test/integration/init-tests.js +30 -43
  33. package/test/integration/tests.js +21 -13
  34. package/test/unit/lifecycle-builders-export.tests.js +179 -118
  35. package/test/unit/tests.js +33 -34
@@ -17,6 +17,7 @@ describe('Demo Rendering and Configuration', () => {
17
17
 
18
18
  it('renders custom control icons', () => {
19
19
  visitChartDemo('custom-icons.html', 'Custom Icons Demo');
20
+ cy.get('#chart-container .nodes.vertical .node').filter(':has(.parentNodeSymbol)').first().trigger('mouseenter');
20
21
  cy.get('#chart-container .node .toggleBtn').should('have.class', 'fa-solid');
21
22
  });
22
23
 
@@ -32,7 +33,7 @@ describe('Demo Rendering and Configuration', () => {
32
33
 
33
34
  it('renders hybrid nodes from node data', () => {
34
35
  visitChartDemo('data-prop-hybrid.html', 'Hybrid Nodes Demo');
35
- cy.get('#chart-container .verticalNodes, #chart-container .toggleBtn').should('exist');
36
+ cy.get('#chart-container .nodes.vertical').should('exist');
36
37
  });
37
38
 
38
39
  it('renders the PNG export control', () => {
@@ -85,6 +86,66 @@ describe('Demo Rendering and Configuration', () => {
85
86
  cy.get('#chart-container .orgchart').should('have.class', 'r2l');
86
87
  });
87
88
 
89
+ it('combines hybrid layout, initial depth and minimap navigation', () => {
90
+ visitChartDemo('options-hybrid-navigation.html');
91
+ cy.get('#chart-container .node').should('have.length', 14);
92
+ cy.get('#chart-container .nodes.vertical').should('exist');
93
+ cy.get('#api').should('not.be.visible');
94
+ cy.get('#chart-container .orgchart-minimap-node').should('have.length.at.least', 1);
95
+ cy.get('#services').trigger('mouseenter');
96
+ cy.get('#services .toggleBtn').click({ force: true });
97
+ cy.get('#api').should('be.visible');
98
+ });
99
+
100
+ it('combines compact layout with constrained team assignments', () => {
101
+ visitChartDemo('options-compact-drag.html');
102
+ cy.get('#chart-container .node.compact').should('exist');
103
+ cy.get('#studio').trigger('mouseenter');
104
+ cy.get('#studio > .backToLooseSymbol').click();
105
+ cy.get('#engineering-team').trigger('mouseenter');
106
+ cy.get('#engineering-team > .backToLooseSymbol').click();
107
+ cy.get('#member-tie').should('have.attr', 'draggable', 'true');
108
+ cy.window().then((win) => {
109
+ const dataTransfer = new win.DataTransfer();
110
+ cy.get('#member-tie').then(($node) => {
111
+ $node[0].dispatchEvent(new win.DragEvent('dragstart', { bubbles: true, dataTransfer }));
112
+ });
113
+ cy.get('#product-team').should('have.class', 'allowedDrop');
114
+ cy.get('#member-bo').should('not.have.class', 'allowedDrop');
115
+ cy.get('#studio').should('not.have.class', 'allowedDrop');
116
+ cy.get('#product-team').then(($node) => {
117
+ $node[0].dispatchEvent(new win.DragEvent('drop', { bubbles: true, dataTransfer }));
118
+ });
119
+ });
120
+ cy.get('#product-team #member-tie').should('exist');
121
+ cy.get('#engineering-team #member-tie').should('not.exist');
122
+ cy.get('#assignment-status').should('have.text', 'Tie Hua / Product');
123
+ });
124
+
125
+ it('combines horizontal layout, custom fields, avatars and icons', () => {
126
+ visitChartDemo('options-horizontal-template.html');
127
+ cy.get('#chart-container .orgchart').should('have.class', 'l2r');
128
+ cy.get('#design-director').should('have.attr', 'data-office', 'Shanghai');
129
+ cy.get('#design-director .title').should('contain.text', 'Lao Lao');
130
+ cy.get('#design-director .content').should('have.text', 'Design director');
131
+ cy.get('#chart-container .avatar').should('have.length', 7);
132
+ cy.get('#experience-lead').trigger('mouseenter');
133
+ cy.get('#experience-lead > .edge').should('have.class', 'fa-solid');
134
+ });
135
+
136
+ it('combines bottom-to-top layout, custom fields and PDF export', () => {
137
+ visitChartDemo('options-export-navigation.html');
138
+ cy.get('#chart-container .orgchart').should('have.class', 'b2t').and('have.class', 'delivery-chart');
139
+ cy.get('#delivery-director .title').should('have.text', 'Lao Lao');
140
+ cy.get('#west-manager').should('have.attr', 'data-region', 'west');
141
+ cy.get('#east-manager').should('have.attr', 'data-region', 'east');
142
+ cy.get('.oc-export-btn').should('be.visible').and('have.text', 'Export PDF');
143
+ cy.window().then((win) => {
144
+ expect(win.html2canvas).to.be.a('function');
145
+ expect(win.jspdf.jsPDF).to.be.a('function');
146
+ });
147
+ });
148
+
88
149
  it('parses the unordered-list datasource', () => {
89
150
  visitChartDemo('ul-datasource.html', 'UL Datasource Demo');
90
151
  cy.contains('#chart-container .node .title', 'Lao Lao').should('exist');
@@ -15,8 +15,9 @@ describe('Hybrid Drag and Drop Chart', () => {
15
15
 
16
16
  cy.get(sanYue).should(($node) => {
17
17
  expect($node.siblings('.nodes').find('.node:contains("San Dan")')).to.exist;
18
- expect($node.find('.toggleBtn')).to.exist;
19
18
  });
19
+ cy.get(sanYue).find('.toggleBtn').should('not.exist');
20
+ cy.get(sanYue).trigger('mouseenter').find('.toggleBtn').should('exist');
20
21
  });
21
22
 
22
23
  });
@@ -14,67 +14,67 @@ describe('Local Datasource', () => {
14
14
  });
15
15
 
16
16
  it('bottomEdge of root node works well', () => {
17
- cy.get(laolao).find('.bottomEdge').click();
17
+ cy.get(laolao).trigger('mouseenter').find('.bottomEdge').click();
18
18
  cy.get(bomiao).get(sumiao).get(hongmiao).should('not.be.visible');
19
19
 
20
- cy.get(laolao).find('.bottomEdge').click();
20
+ cy.get(laolao).trigger('mouseenter').find('.bottomEdge').click();
21
21
  cy.get(bomiao).get(sumiao).get(hongmiao).should('be.visible');
22
22
  });
23
23
 
24
24
  it('topEdge of sumiao node works well', () => {
25
- cy.get(sumiao).find('.topEdge').click();
25
+ cy.get(sumiao).trigger('mouseenter').find('.topEdge').click();
26
26
  cy.get(laolao).get(bomiao).get(hongmiao).should('not.be.visible');
27
27
 
28
- cy.get(sumiao).find('.topEdge').click();
28
+ cy.get(sumiao).trigger('mouseenter').find('.topEdge').click();
29
29
  cy.get(laolao).should('be.visible');
30
30
  cy.get(bomiao).get(hongmiao).should('not.be.visible');
31
31
  });
32
32
 
33
33
  it('leftEdge of sumiao node works well', () => {
34
- cy.get(sumiao).find('.leftEdge').click();
34
+ cy.get(sumiao).trigger('mouseenter').find('.leftEdge').click();
35
35
  cy.get(bomiao).get(hongmiao).should('not.be.visible');
36
36
 
37
- cy.get(sumiao).find('.leftEdge').click();
37
+ cy.get(sumiao).trigger('mouseenter').find('.leftEdge').click();
38
38
  cy.get(bomiao).get(hongmiao).should('be.visible');
39
39
  });
40
40
 
41
41
  it('rightEdge of sumiao node works well', () => {
42
- cy.get(sumiao).find('.rightEdge').click();
42
+ cy.get(sumiao).trigger('mouseenter').find('.rightEdge').click();
43
43
  cy.get(bomiao).get(hongmiao).should('not.be.visible');
44
44
 
45
- cy.get(sumiao).find('.rightEdge').click();
45
+ cy.get(sumiao).trigger('mouseenter').find('.rightEdge').click();
46
46
  cy.get(bomiao).get(hongmiao).should('be.visible');
47
47
  });
48
48
 
49
49
  it('bottomEdge of sumiao node works well', () => {
50
- cy.get(sumiao).find('.bottomEdge').click();
50
+ cy.get(sumiao).trigger('mouseenter').find('.bottomEdge').click();
51
51
  cy.get(tiehua).get(heihei).get(pangpang).get(dandan).should('not.be.visible');
52
52
 
53
- cy.get(sumiao).find('.bottomEdge').click();
53
+ cy.get(sumiao).trigger('mouseenter').find('.bottomEdge').click();
54
54
  cy.get(tiehua).get(heihei).get(pangpang).should('be.visible');
55
55
  });
56
56
 
57
57
  it('topEdge of heihei node works well', () => {
58
- cy.get(heihei).find('.topEdge').click();
58
+ cy.get(heihei).trigger('mouseenter').find('.topEdge').click();
59
59
  cy.get(laolao).get(bomiao).get(sumiao).get(hongmiao).get(tiehua).get(pangpang).should('not.be.visible');
60
60
 
61
- cy.get(heihei).find('.topEdge').click();
61
+ cy.get(heihei).trigger('mouseenter').find('.topEdge').click();
62
62
  cy.get(sumiao).should('be.visible');
63
63
  cy.get(laolao).should('not.be.visible');
64
64
  });
65
65
 
66
66
  it('horizontalEdge of heihei node works well', () => {
67
- cy.get(heihei).find('.topEdge').click();
68
- cy.get(heihei).find('.leftEdge').click();
67
+ cy.get(heihei).trigger('mouseenter').find('.topEdge').click();
68
+ cy.get(heihei).trigger('mouseenter').find('.leftEdge').click();
69
69
  cy.get(sumiao).get(tiehua).get(pangpang).should('be.visible');
70
70
  cy.get(laolao).should('not.be.visible');
71
71
  });
72
72
 
73
73
  it('horizontalEdge of pangpang node works well', () => {
74
- cy.get(pangpang).find('.leftEdge').click();
74
+ cy.get(pangpang).trigger('mouseenter').find('.leftEdge').click();
75
75
  cy.get(tiehua).get(heihei).get(dandan).should('not.be.visible');
76
76
 
77
- cy.get(pangpang).find('.leftEdge').click();
77
+ cy.get(pangpang).trigger('mouseenter').find('.leftEdge').click();
78
78
  cy.get(tiehua).get(heihei).should('be.visible');
79
79
  });
80
80
 
@@ -1,35 +1,59 @@
1
1
  describe('Load data on-demand', () => {
2
2
 
3
- const sumiao = '.node:contains("Su Miao")';
4
- const heihei = '.node:contains("Hei Hei")';
3
+ const sumiao = '#n1';
4
+ const heihei = '#n3';
5
5
 
6
6
  beforeEach(() => {
7
7
  cy.visit('demo/ondemand-loading-data.html');
8
8
  });
9
9
 
10
10
  it('load the descendants of hehei', () => {
11
- cy.get(heihei).find('.bottomEdge').click();
11
+ cy.get(heihei).find('.bottomEdge, .request-control').should('not.exist');
12
+ cy.get(heihei).trigger('mouseenter').find('.request-children').click();
13
+ cy.get('#n5').should('exist');
14
+ cy.get(heihei).find('.request-control').should('not.exist');
12
15
  cy.get(heihei).should(($node) => {
13
16
  expect($node.siblings('.nodes').children('.hierarchy').length).to.equal(2);
14
17
  expect($node.siblings('.nodes').children('.hierarchy').eq(0).find('.node:contains("Er Dan")')).to.exist;
15
18
  });
19
+ cy.get(heihei).trigger('mouseenter').find('.bottomEdge').click();
20
+ cy.get('#n5').should('not.be.visible');
21
+ cy.get(heihei).find('.bottomEdge').click();
22
+ cy.get('#n5').should('be.visible');
16
23
  });
17
24
 
18
25
  it('load the ancestors of sumiao by expanding vertically', () => {
19
- cy.get(sumiao).find('.topEdge').click();
26
+ cy.get(sumiao).trigger('mouseenter').find('.request-parent').click();
27
+ cy.get('#n0').should('exist');
20
28
  cy.get(sumiao).should(($node) => {
21
29
  expect($node.closest('.nodes').siblings('.node:contains("Lao Lao")')).to.exist;
22
30
  expect($node.closest('.hierarchy').siblings().length).to.equal(7);
23
31
  });
24
32
  });
25
33
 
26
- it('load the ancestors of sumiao by expanding horizontally', () => {
27
- cy.get(sumiao).find('.leftEdge').click();
28
- cy.get(sumiao).should(($node) => {
29
- expect($node.closest('.nodes').siblings('.node:contains("Lao Lao")')).to.exist;
30
- expect($node.closest('.hierarchy').siblings().length).to.equal(7);
31
- });
34
+ it('shows both sibling loading buttons on hover without duplicating them', () => {
35
+ cy.get(sumiao).find('.request-siblings').should('not.exist');
36
+ cy.get(sumiao).trigger('mouseenter').trigger('mouseenter');
37
+ cy.get(sumiao).find('.request-siblings').should('have.length', 2);
38
+ cy.get(sumiao).find('.request-siblings-left .oci-chevron-left').should('be.visible');
39
+ cy.get(sumiao).find('.request-siblings-right .oci-chevron-right').should('be.visible');
40
+ cy.get(sumiao).trigger('mouseleave').find('.request-siblings').should('not.exist');
41
+ cy.get(sumiao).trigger('mouseenter').find('.request-siblings').should('have.length', 2);
42
+ });
32
43
 
44
+ ['left', 'right'].forEach((side) => {
45
+ it('load the ancestors of sumiao by expanding horizontally from the ' + side, () => {
46
+ cy.clock();
47
+ cy.get(sumiao).trigger('mouseenter').find('.request-siblings-' + side).click();
48
+ cy.get(sumiao).find('.request-siblings').should('have.length', 2).and('be.disabled');
49
+ cy.tick(1000);
50
+ cy.get('#n0').should('exist');
51
+ cy.get(sumiao).find('.request-control').should('not.exist');
52
+ cy.get(sumiao).should(($node) => {
53
+ expect($node.closest('.nodes').siblings('.node:contains("Lao Lao")')).to.exist;
54
+ expect($node.closest('.hierarchy').siblings().length).to.equal(7);
55
+ });
56
+ });
33
57
  });
34
58
 
35
59
  });
@@ -13,19 +13,19 @@ describe('Toggle sibling nodes respectively', () => {
13
13
  });
14
14
 
15
15
  it('toggle the left side sibling nodes of sumiao', () => {
16
- cy.get(sumiao).find('.leftEdge').click();
16
+ cy.get(sumiao).trigger('mouseenter').find('.leftEdge').click();
17
17
  cy.get(bomiao).get(renwu).get(lixin).should('not.be.visible');
18
18
 
19
- cy.get(sumiao).find('.leftEdge').click();
19
+ cy.get(sumiao).trigger('mouseenter').find('.leftEdge').click();
20
20
  cy.get(bomiao).should('be.visible');
21
21
  cy.get(renwu).get(lixin).should('not.be.visible');
22
22
  });
23
23
 
24
24
  it('toggle the right side sibling nodes of sumiao', () => {
25
- cy.get(sumiao).find('.rightEdge').click();
25
+ cy.get(sumiao).trigger('mouseenter').find('.rightEdge').click();
26
26
  cy.get(hongmiao).get(xingan).get(yidian).should('not.be.visible');
27
27
 
28
- cy.get(sumiao).find('.rightEdge').click();
28
+ cy.get(sumiao).trigger('mouseenter').find('.rightEdge').click();
29
29
  cy.get(hongmiao).should('be.visible');
30
30
  cy.get(xingan).get(yidian).should('not.be.visible');
31
31
  });
@@ -15,24 +15,24 @@ describe('Hybrid(horizontal + vertical) Chart', () => {
15
15
  const wudan = '.node:contains("Wu Dan")';
16
16
 
17
17
  it('toggle the vertical nodes', () => {
18
- cy.get(dandan).find('.toggleBtn').click({ force: true });
18
+ cy.get(dandan).trigger('mouseenter').find('.toggleBtn').click({ force: true });
19
19
  cy.get(dandan).find('.toggleBtn').should('have.class', 'oci-minus-square');
20
20
  cy.get(erdan).get(sandan).should('be.visible');
21
21
  cy.get(sidan).get(wudan).should('not.be.visible');
22
22
 
23
- cy.get(heihei).find('.toggleBtn').click({ force: true });
23
+ cy.get(heihei).trigger('mouseenter').find('.toggleBtn').click({ force: true });
24
24
  cy.get(heihei).find('.toggleBtn').should('have.class', 'oci-plus-square');
25
25
  cy.get(pangpang).get(dandan).get(erdan).get(sandan).should('not.be.visible');
26
26
 
27
- cy.get(heihei).find('.toggleBtn').click({ force: true });
27
+ cy.get(heihei).trigger('mouseenter').find('.toggleBtn').click({ force: true });
28
28
  cy.get(pangpang).get(dandan).should('be.visible');
29
29
  });
30
30
 
31
31
  it('toggle the vertical nodes by cicking horizontal nodes', () => {
32
- cy.get(sumiao).find('.bottomEdge').click();
32
+ cy.get(sumiao).trigger('mouseenter').find('.bottomEdge').click();
33
33
  cy.get(tiehua).get(heihei).get(pangpang).get(dandan).should('be.visible');
34
34
 
35
- cy.get(sumiao).find('.bottomEdge').click();
35
+ cy.get(sumiao).trigger('mouseenter').find('.bottomEdge').click();
36
36
  cy.get(tiehua).get(heihei).should('be.visible');
37
37
  cy.get(pangpang).get(dandan).should('not.be.visible');
38
38
  });
@@ -33,16 +33,11 @@ describe('orgchart -- integration tests', function () {
33
33
  ]
34
34
  };
35
35
 
36
- const fragment = '<div class="orgchart"><ul class="nodes"><li class="hierarchy">' +
37
- '<div id="n1" class="node"><div class="title"><i class="oci oci-menu parentNodeSymbol">' +
38
- '</i>Lao Lao</div><i class="edge verticalEdge bottomEdge oci"></i></div><ul class="nodes">' +
39
- '<li class="hierarchy"><div id="n2" data-parent="n1" class="node">' +
40
- '<div class="title">Bo Miao</div><i class="edge verticalEdge topEdge oci"></i>' +
41
- '<i class="edge horizontalEdge rightEdge oci"></i><i class="edge horizontalEdge leftEdge oci">' +
42
- '</i></div></li><li class="hierarchy"><div id="n3" data-parent="n1" class="node">' +
43
- '<div class="title">Su Miao</div><i class="edge verticalEdge topEdge oci"></i>' +
44
- '<i class="edge horizontalEdge rightEdge oci"></i><i class="edge horizontalEdge leftEdge oci">' +
45
- '</i></div></li></ul></li></ul></div>';
36
+ const fragment = '<div class="orgchart"><ul class="nodes"><li class="hierarchy"><div id="n1" class="node" tabindex="0"><div clas' +
37
+ 's="title"><i class="oci oci-menu parentNodeSymbol"></i>Lao Lao</div></div><ul class="nodes"><li class="hierarc' +
38
+ 'hy"><div id="n2" data-parent="n1" class="node" tabindex="0"><div class="title">Bo Miao</div></div></li><li cla' +
39
+ 'ss="hierarchy"><div id="n3" data-parent="n1" class="node" tabindex="0"><div class="title">Su Miao</div></div><' +
40
+ '/li></ul></li></ul></div>';
46
41
 
47
42
  let oc = {};
48
43
 
@@ -96,11 +91,10 @@ describe('orgchart -- integration tests', function () {
96
91
  });
97
92
 
98
93
  it('initialize chart with the given vertical level', function () {
99
- const fragment = '<li class="hierarchy"><div id="n1" class="node"><div class="title">' +
100
- '<i class="oci oci-menu parentNodeSymbol"></i>Lao Lao</div><i class="edge verticalEdge bottomEdge oci">' +
101
- '</i></div><ul class="nodes vertical"><li class="hierarchy"><div id="n2" data-parent="n1" class="node">' +
102
- '<div class="title">Bo Miao</div></div></li><li class="hierarchy"><div id="n3" data-parent="n1" class="node">' +
103
- '<div class="title">Su Miao</div></div></li></ul></li>';
94
+ const fragment = '<li class="hierarchy"><div id="n1" class="node" tabindex="0"><div class="title"><i class="oci oci-menu parentN' +
95
+ 'odeSymbol"></i>Lao Lao</div></div><ul class="nodes vertical"><li class="hierarchy"><div id="n2" data-parent="n' +
96
+ '1" class="node" tabindex="0"><div class="title">Bo Miao</div></div></li><li class="hierarchy"><div id="n3" dat' +
97
+ 'a-parent="n1" class="node" tabindex="0"><div class="title">Su Miao</div></div></li></ul></li>';
104
98
  oc = new OrgChart({
105
99
  chartContainer: '#chart-container',
106
100
  'data': ds,
@@ -125,16 +119,13 @@ describe('orgchart -- integration tests', function () {
125
119
  };
126
120
 
127
121
  it('verticalLevel=2 and visibleLevel=1', function () {
128
- const fragment = '<div class="orgchart"><ul class="nodes"><li class="hierarchy">' +
129
- '<div class="node"><div class="title"><i class="oci oci-menu parentNodeSymbol">' +
130
- '</i>Lao Lao</div><i class="edge verticalEdge bottomEdge oci"></i></div>' +
131
- '<ul class="nodes hidden vertical"><li class="hierarchy"><div class="node slide-up">' +
132
- '<div class="title">Bo Miao</div></div></li><li class="hierarchy">' +
133
- '<div class="node slide-up"><div class="title"><i class="oci oci-menu parentNodeSymbol"></i>Su Miao</div>' +
134
- '<i class="toggleBtn oci"></i></div><ul class="nodes hidden">' +
135
- '<li class="hierarchy"><div class="node slide-up"><div class="title">Tie Hua</div>' +
136
- '</div></li><li class="hierarchy"><div class="node slide-up">' +
137
- '<div class="title">Hei Hei</div></div></li></ul></li></ul></li></ul></div>';
122
+ const fragment = '<div class="orgchart"><ul class="nodes"><li class="hierarchy"><div class="node" tabindex="0"><div class="title' +
123
+ '"><i class="oci oci-menu parentNodeSymbol"></i>Lao Lao</div></div><ul class="nodes hidden vertical"><li class=' +
124
+ '"hierarchy"><div class="node slide-up" tabindex="0"><div class="title">Bo Miao</div></div></li><li class="hier' +
125
+ 'archy"><div class="node slide-up" tabindex="0"><div class="title"><i class="oci oci-menu parentNodeSymbol"></i' +
126
+ '>Su Miao</div></div><ul class="nodes hidden"><li class="hierarchy"><div class="node slide-up" tabindex="0"><di' +
127
+ 'v class="title">Tie Hua</div></div></li><li class="hierarchy"><div class="node slide-up" tabindex="0"><div cla' +
128
+ 'ss="title">Hei Hei</div></div></li></ul></li></ul></li></ul></div>';
138
129
  oc = new OrgChart({
139
130
  chartContainer: '#chart-container',
140
131
  'data': ds,
@@ -145,15 +136,13 @@ describe('orgchart -- integration tests', function () {
145
136
  });
146
137
 
147
138
  it('verticalLevel=2 and visibleLevel=2', function () {
148
- const fragment = '<div class="orgchart"><ul class="nodes"><li class="hierarchy">' +
149
- '<div class="node"><div class="title"><i class="oci oci-menu parentNodeSymbol"></i>Lao Lao</div>' +
150
- '<i class="edge verticalEdge bottomEdge oci"></i></div><ul class="nodes vertical">' +
151
- '<li class="hierarchy"><div class="node"><div class="title">Bo Miao</div></div></li>' +
152
- '<li class="hierarchy"><div class="node"><div class="title"><i class="oci oci-menu parentNodeSymbol"></i>Su Miao</div>' +
153
- '<i class="toggleBtn oci"></i></div><ul class="nodes hidden">' +
154
- '<li class="hierarchy"><div class="node slide-up"><div class="title">Tie Hua</div></div>' +
155
- '</li><li class="hierarchy"><div class="node slide-up"><div class="title">Hei Hei</div>' +
156
- '</div></li></ul></li></ul></li></ul></div>';
139
+ const fragment = '<div class="orgchart"><ul class="nodes"><li class="hierarchy"><div class="node" tabindex="0"><div class="title' +
140
+ '"><i class="oci oci-menu parentNodeSymbol"></i>Lao Lao</div></div><ul class="nodes vertical"><li class="hierar' +
141
+ 'chy"><div class="node" tabindex="0"><div class="title">Bo Miao</div></div></li><li class="hierarchy"><div clas' +
142
+ 's="node" tabindex="0"><div class="title"><i class="oci oci-menu parentNodeSymbol"></i>Su Miao</div></div><ul c' +
143
+ 'lass="nodes hidden"><li class="hierarchy"><div class="node slide-up" tabindex="0"><div class="title">Tie Hua</' +
144
+ 'div></div></li><li class="hierarchy"><div class="node slide-up" tabindex="0"><div class="title">Hei Hei</div><' +
145
+ '/div></li></ul></li></ul></li></ul></div>';
157
146
  oc = new OrgChart({
158
147
  chartContainer: '#chart-container',
159
148
  'data': ds,
@@ -164,15 +153,13 @@ describe('orgchart -- integration tests', function () {
164
153
  });
165
154
 
166
155
  it('verticalLevel=2 and visibleLevel=3', function () {
167
- const fragment = '<div class="orgchart"><ul class="nodes"><li class="hierarchy">' +
168
- '<div class="node"><div class="title"><i class="oci oci-menu parentNodeSymbol"></i>Lao Lao</div>' +
169
- '<i class="edge verticalEdge bottomEdge oci"></i></div><ul class="nodes vertical">' +
170
- '<li class="hierarchy"><div class="node"><div class="title">Bo Miao</div></div></li>' +
171
- '<li class="hierarchy"><div class="node"><div class="title"><i class="oci oci-menu parentNodeSymbol"></i>Su Miao</div>' +
172
- '<i class="toggleBtn oci"></i></div><ul class="nodes">' +
173
- '<li class="hierarchy"><div class="node"><div class="title">Tie Hua</div></div>' +
174
- '</li><li class="hierarchy"><div class="node"><div class="title">Hei Hei</div></div>' +
175
- '</li></ul></li></ul></li></ul></div>';
156
+ const fragment = '<div class="orgchart"><ul class="nodes"><li class="hierarchy"><div class="node" tabindex="0"><div class="title' +
157
+ '"><i class="oci oci-menu parentNodeSymbol"></i>Lao Lao</div></div><ul class="nodes vertical"><li class="hierar' +
158
+ 'chy"><div class="node" tabindex="0"><div class="title">Bo Miao</div></div></li><li class="hierarchy"><div clas' +
159
+ 's="node" tabindex="0"><div class="title"><i class="oci oci-menu parentNodeSymbol"></i>Su Miao</div></div><ul c' +
160
+ 'lass="nodes"><li class="hierarchy"><div class="node" tabindex="0"><div class="title">Tie Hua</div></div></li><' +
161
+ 'li class="hierarchy"><div class="node" tabindex="0"><div class="title">Hei Hei</div></div></li></ul></li></ul>' +
162
+ '</li></ul></div>';
176
163
  oc = new OrgChart({
177
164
  chartContainer: '#chart-container',
178
165
  'data': ds,
@@ -130,7 +130,7 @@ describe('orgchart -- integration tests', function () {
130
130
  $container = document.getElementById('chart-container');
131
131
  oc = new OrgChart({
132
132
  chartContainer: '#chart-container',
133
- 'data': ds
133
+ 'data': cloneValue(ds)
134
134
  }),
135
135
  $laolao = document.getElementById('n1'),
136
136
  $bomiao = document.getElementById('n2'),
@@ -177,7 +177,7 @@ describe('orgchart -- integration tests', function () {
177
177
  });
178
178
 
179
179
  it('addAncestors()', function () {
180
- oc.addAncestors({ 'name': 'Lao Ye', 'id': 'n0', 'relationship': '001' }, 'n0');
180
+ oc.addAncestors({ 'name': 'Lao Ye', 'id': 'n0' }, 'n0');
181
181
 
182
182
  should.exist(query('#n0'));
183
183
  query('#n0').closest('.hierarchy').should.not.equal(query('#n1').closest('.hierarchy'));
@@ -357,6 +357,7 @@ describe('orgchart -- integration tests', function () {
357
357
 
358
358
  describe('interaction flows', function () {
359
359
  it('dispatches hide/show children events through bottom-edge clicks', function () {
360
+ dispatchNativeEvent($sumiao, 'mouseenter', { bubbles: false });
360
361
  const bottomEdge = query('.bottomEdge', $sumiao);
361
362
  const firstChildNode = siblingElements($sumiao, '.nodes')[0].querySelector('.node');
362
363
  const events = [];
@@ -377,6 +378,7 @@ describe('orgchart -- integration tests', function () {
377
378
  });
378
379
 
379
380
  it('dispatches hide/show siblings events through horizontal-edge clicks', function () {
381
+ dispatchNativeEvent($sumiao, 'mouseenter', { bubbles: false });
380
382
  const leftEdge = query('.leftEdge', $sumiao);
381
383
  const firstSiblingNode = query('#n2');
382
384
  const events = [];
@@ -397,6 +399,7 @@ describe('orgchart -- integration tests', function () {
397
399
  });
398
400
 
399
401
  it('dispatches hide-parent and enters ancestor-collapsed state through top-edge click', function () {
402
+ dispatchNativeEvent($heihei, 'mouseenter', { bubbles: false });
400
403
  const topEdge = query('.topEdge', $heihei);
401
404
  const hideSpy = sinon.spy();
402
405
  const currentHierarchy = $heihei.closest('.hierarchy');
@@ -438,6 +441,7 @@ describe('orgchart -- integration tests', function () {
438
441
  });
439
442
 
440
443
  it('toggles children visibility through bottom-edge clicks and transition cleanup', function () {
444
+ dispatchNativeEvent($sumiao, 'mouseenter', { bubbles: false });
441
445
  const childNodesContainer = siblingElements($sumiao, '.nodes')[0];
442
446
  const bottomEdge = query('.bottomEdge', $sumiao);
443
447
  const childHierarchies = Array.from(childNodesContainer.children).filter(function (childEl) {
@@ -475,6 +479,7 @@ describe('orgchart -- integration tests', function () {
475
479
  });
476
480
 
477
481
  it('toggles sibling visibility through horizontal-edge clicks and transition cleanup', function () {
482
+ dispatchNativeEvent($sumiao, 'mouseenter', { bubbles: false });
478
483
  const leftEdge = query('.leftEdge', $sumiao);
479
484
  const siblingHierarchies = Array.from($sumiao.closest('.hierarchy').parentElement.children).filter(function (childEl) {
480
485
  return childEl !== $sumiao.closest('.hierarchy') && childEl.classList.contains('hierarchy');
@@ -508,27 +513,25 @@ describe('orgchart -- integration tests', function () {
508
513
  oc.getNodeState($sumiao, 'siblings').should.deep.equal({ exist: true, visible: true });
509
514
  });
510
515
 
511
- it('shows directional edge icons on hover and clears them on mouseleave', function () {
516
+ it('creates directional controls on hover and removes them on mouseleave', function () {
517
+ queryAll('.edge', $sumiao).length.should.equal(0);
518
+ dispatchNativeEvent($sumiao, 'mouseenter', { bubbles: false });
512
519
  const topEdge = query('.topEdge', $sumiao);
513
520
  const bottomEdge = query('.bottomEdge', $sumiao);
514
521
  const leftEdge = query('.leftEdge', $sumiao);
515
522
  const rightEdge = query('.rightEdge', $sumiao);
516
523
 
517
- dispatchNativeEvent($sumiao, 'mouseenter', { bubbles: false });
518
524
  topEdge.classList.contains(oc.options.icons.collapseToDown).should.be.true;
519
525
  bottomEdge.classList.contains(oc.options.icons.expandToUp).should.be.true;
520
526
  leftEdge.classList.contains(oc.options.icons.expandToRight).should.be.true;
521
527
  rightEdge.classList.contains(oc.options.icons.collapseToLeft).should.be.true;
522
528
 
523
529
  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;
530
+ queryAll('.edge', $sumiao).length.should.equal(0);
531
+ topEdge.isConnected.should.equal(false);
532
+ bottomEdge.isConnected.should.equal(false);
533
+ leftEdge.isConnected.should.equal(false);
534
+ rightEdge.isConnected.should.equal(false);
532
535
  });
533
536
  });
534
537
 
@@ -648,6 +651,7 @@ describe('orgchart -- integration tests', function () {
648
651
  visibleLevel: 2
649
652
  });
650
653
  hybridSumiao = query('#n3', hybridChart.chart);
654
+ dispatchNativeEvent(hybridSumiao, 'mouseenter', { bubbles: false });
651
655
  toggleBtn = query('.toggleBtn', hybridSumiao);
652
656
  descendantsWrapper = siblingElements(hybridSumiao, '.nodes')[0];
653
657
  childNodes = Array.from(descendantsWrapper.children).filter(function (childEl) {
@@ -852,7 +856,10 @@ describe('orgchart -- integration tests', function () {
852
856
  originalEvent: { currentTarget: dropNode }
853
857
  });
854
858
 
855
- query('#n4', hybridChart.chart).querySelector(':scope > .toggleBtn').should.exist;
859
+ const updatedDropNode = query('#n4', hybridChart.chart);
860
+ should.not.exist(updatedDropNode.querySelector(':scope > .toggleBtn'));
861
+ dispatchNativeEvent(updatedDropNode, 'mouseenter', { bubbles: false });
862
+ should.exist(updatedDropNode.querySelector(':scope > .toggleBtn'));
856
863
  query('#n3', hybridChart.chart).closest('.hierarchy').parentElement.previousElementSibling.id.should.equal('n4');
857
864
  } finally {
858
865
  global.JSONDigger = originalJSONDigger;
@@ -887,6 +894,7 @@ describe('orgchart -- integration tests', function () {
887
894
  }
888
895
  });
889
896
  compactNode = query('#n2', compactChart.chart);
897
+ dispatchNativeEvent(compactNode, 'mouseenter', { bubbles: false });
890
898
  backToLooseSymbol = query('.backToLooseSymbol', compactNode);
891
899
  backToCompactSymbol = query('.backToCompactSymbol', compactNode);
892
900