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.
Files changed (76) hide show
  1. package/.github/workflows/publish.yml +32 -0
  2. package/README.md +172 -93
  3. package/README.zh-cn.md +163 -74
  4. package/README.zh-tw.md +163 -74
  5. package/bower.json +6 -9
  6. package/demo/10000-nodes.html +139 -0
  7. package/demo/ajax-datasource.html +46 -45
  8. package/demo/bottom2top.html +12 -9
  9. package/demo/center-on-selected-node.html +21 -17
  10. package/demo/color-coded.html +12 -9
  11. package/demo/css/style.css +28 -0
  12. package/demo/custom-icons.html +12 -9
  13. package/demo/custom-template.html +12 -9
  14. package/demo/data-prop-compact.html +11 -8
  15. package/demo/data-prop-hybrid.html +79 -50
  16. package/demo/default-collapsed.html +12 -9
  17. package/demo/drag-drop-hybrid-chart.html +17 -14
  18. package/demo/drag-drop.html +21 -16
  19. package/demo/edit-chart.html +148 -94
  20. package/demo/edit-hybrid-chart.html +178 -135
  21. package/demo/expand-to-given-depth.html +30 -22
  22. package/demo/export-chart-pdf.html +12 -9
  23. package/demo/export-chart-with-pictures.html +19 -12
  24. package/demo/export-chart.html +12 -9
  25. package/demo/family-tree.html +14 -11
  26. package/demo/familytree-custom-properties.html +108 -0
  27. package/demo/filter-node.html +104 -57
  28. package/demo/get-hierarchy.html +17 -12
  29. package/demo/get-related-nodes.html +27 -17
  30. package/demo/index.html +5 -2
  31. package/demo/integrate-map.html +32 -27
  32. package/demo/left2right.html +12 -9
  33. package/demo/level-offset.html +16 -15
  34. package/demo/link-node.html +12 -9
  35. package/demo/local-datasource.html +11 -8
  36. package/demo/minimap.html +224 -0
  37. package/demo/nodes-of-different-widths.html +11 -8
  38. package/demo/ondemand-loading-data.html +88 -91
  39. package/demo/option-createNode.html +28 -17
  40. package/demo/pan-zoom.html +20 -11
  41. package/demo/reload-data.html +17 -14
  42. package/demo/report-path.html +59 -29
  43. package/demo/responsive-design.html +14 -11
  44. package/demo/right2left.html +12 -9
  45. package/demo/toggle-sibs-resp.html +12 -9
  46. package/demo/ul-datasource.html +11 -8
  47. package/demo/vertical-level.html +12 -9
  48. package/dist/css/{jquery.orgchart.css → orgchart.css} +143 -80
  49. package/dist/css/orgchart.min.css +1 -0
  50. package/dist/js/orgchart.js +4101 -0
  51. package/dist/js/orgchart.min.js +2 -0
  52. package/dist/js/orgchart.min.js.map +1 -0
  53. package/gulpfile.js +37 -26
  54. package/package.json +32 -32
  55. package/src/css/{jquery.orgchart.css → orgchart.css} +143 -80
  56. package/src/js/orgchart.js +4101 -0
  57. package/test/cypress/e2e/demo-interactions.cy.js +91 -0
  58. package/test/cypress/e2e/demo-rendering.cy.js +100 -0
  59. package/test/cypress/e2e/drag-drop-hybrid-chart.cy.js +22 -0
  60. package/test/cypress/e2e/edit-chart.cy.js +20 -0
  61. package/test/cypress/e2e/level-offset.cy.js +21 -0
  62. package/test/cypress/e2e/minimap.cy.js +66 -0
  63. package/test/cypress/e2e/pan-zoom.cy.js +165 -0
  64. package/test/cypress/e2e/report-path.cy.js +20 -0
  65. package/test/cypress/e2e/vertical-vevel.cy.js +5 -3
  66. package/test/e2e/local-datasource/test.js +1 -1
  67. package/test/integration/init-tests.js +168 -62
  68. package/test/integration/tests.js +1034 -56
  69. package/test/unit/lifecycle-builders-export.tests.js +1426 -0
  70. package/test/unit/minimap.tests.js +210 -0
  71. package/test/unit/tests.js +2372 -252
  72. package/dist/css/jquery.orgchart.min.css +0 -1
  73. package/dist/js/jquery.orgchart.js +0 -1741
  74. package/dist/js/jquery.orgchart.min.js +0 -5
  75. package/dist/js/jquery.orgchart.min.js.map +0 -1
  76. package/src/js/jquery.orgchart.js +0 -1741
@@ -0,0 +1,91 @@
1
+ describe('Demo Interactions', () => {
2
+
3
+ it('loads the ajax datasource after showing its loading state', () => {
4
+ cy.visit('demo/ajax-datasource.html');
5
+ cy.get('#chart-container .spinner').should('exist');
6
+ cy.get('#chart-container .node .title', { timeout: 6000 }).should('contain.text', 'Lao Lao');
7
+ cy.get('#chart-container .spinner').should('not.exist');
8
+ });
9
+
10
+ it('centers the selected node', () => {
11
+ cy.visit('demo/center-on-selected-node.html');
12
+ cy.get('#chart-container .orgchart').invoke('css', 'transform').then((beforeTransform) => {
13
+ cy.contains('#chart-container .node .title', 'Su Miao').closest('.node').click();
14
+ cy.get('#chart-container .orgchart').should('not.have.css', 'transform', beforeTransform);
15
+ });
16
+ });
17
+
18
+ it('starts configured branches collapsed and expands them', () => {
19
+ cy.visit('demo/default-collapsed.html');
20
+ cy.contains('#chart-container .node .title', 'Hei Hei').closest('.node').then(($node) => {
21
+ expect($node[0].nextElementSibling).to.have.class('hidden');
22
+ });
23
+
24
+ cy.contains('#chart-container .node .title', 'Hei Hei').closest('.node').find('.bottomEdge').click();
25
+ cy.contains('#chart-container .node .title', 'Dan Dan').should('be.visible');
26
+ });
27
+
28
+ it('adds a sibling in the editable hybrid chart', () => {
29
+ cy.visit('demo/edit-hybrid-chart.html');
30
+ cy.contains('#chart-container .node .title', 'Football').closest('.node').click();
31
+ cy.get('#new-nodelist .new-node').type('Futsal');
32
+ cy.get('#rd-sibling').click();
33
+ cy.get('#btn-add-nodes').click();
34
+ cy.contains('#chart-container .node .title', 'Futsal').should('exist');
35
+ });
36
+
37
+ it('expands a selected branch to the configured depth', () => {
38
+ cy.visit('demo/expand-to-given-depth.html');
39
+ cy.contains('#chart-container .node .title', 'Su Miao').closest('.node').find('.bottomEdge').click();
40
+ cy.contains('#chart-container .node .title', 'Dan Zai').should('be.visible');
41
+ cy.contains('#chart-container .node .title', 'Er Dan Zai').should('not.be.visible');
42
+ cy.contains('#chart-container .node .title', 'AAA').should('not.be.visible');
43
+ });
44
+
45
+ it('filters matching nodes and clears the filter result', () => {
46
+ cy.visit('demo/filter-node.html');
47
+ cy.get('#key-word').type('dan');
48
+ cy.get('#btn-filter-node').click();
49
+ cy.get('#chart-container .node.matched').should('have.length.at.least', 1);
50
+ cy.get('#chart-container .orgchart').should('have.class', 'noncollapsable');
51
+ cy.get('#btn-cancel').click();
52
+ cy.get('#chart-container .node.matched').should('not.exist');
53
+ cy.get('#chart-container .orgchart').should('not.have.class', 'noncollapsable');
54
+ });
55
+
56
+ it('exports the current unordered-list hierarchy as JSON', () => {
57
+ cy.visit('demo/get-hierarchy.html');
58
+ cy.get('#btn-export-hier').click();
59
+ cy.get('pre').should('contain.text', 'n1').and('contain.text', 'n7');
60
+ });
61
+
62
+ it('highlights and clears related nodes on hover', () => {
63
+ cy.visit('demo/get-related-nodes.html');
64
+ cy.contains('#chart-container .node .title', 'Hei Hei').closest('.node').trigger('mouseenter');
65
+ cy.get('#chart-container .highlight-parent').should('exist');
66
+ cy.get('#chart-container .highlight-siblings').should('exist');
67
+ cy.get('#chart-container .highlight-children').should('exist');
68
+ cy.contains('#chart-container .node .title', 'Hei Hei').closest('.node').trigger('mouseleave');
69
+ cy.get('#chart-container .highlight-parent, #chart-container .highlight-siblings, #chart-container .highlight-children').should('not.exist');
70
+ });
71
+
72
+ it('toggles the custom secondary menu created for each node', () => {
73
+ cy.visit('demo/option-createNode.html');
74
+ cy.contains('#chart-container .node .title', 'Lao Lao').closest('.node').find('.second-menu-icon').click({ force: true });
75
+ cy.contains('#chart-container .node .title', 'Lao Lao').closest('.node').find('.second-menu').should('have.css', 'display', 'block');
76
+ });
77
+
78
+ it('switches to a hybrid layout on a narrow viewport', () => {
79
+ cy.viewport(500, 720);
80
+ cy.visit('demo/responsive-design.html');
81
+ cy.window().trigger('resize');
82
+ cy.get('#chart-container .verticalNodes, #chart-container .toggleBtn').should('exist');
83
+ });
84
+
85
+ it('renders all 10,000 generated nodes', () => {
86
+ cy.visit('demo/10000-nodes.html');
87
+ cy.get('#status', { timeout: 30000 }).should('contain.text', 'Initialization completed');
88
+ cy.get('#chart-container .node').should('have.length', 10000);
89
+ });
90
+
91
+ });
@@ -0,0 +1,100 @@
1
+ describe('Demo Rendering and Configuration', () => {
2
+
3
+ const visitChartDemo = (page) => {
4
+ cy.visit('demo/' + page);
5
+ cy.get('#chart-container .orgchart').should('exist');
6
+ };
7
+
8
+ it('renders the bottom-to-top chart', () => {
9
+ visitChartDemo('bottom2top.html', 'Bottom to Top Demo');
10
+ cy.get('#chart-container .orgchart').should('have.class', 'b2t');
11
+ });
12
+
13
+ it('renders color-coded node levels', () => {
14
+ visitChartDemo('color-coded.html', 'Color Coded Demo');
15
+ cy.get('#chart-container .node').filter('.middle-level, .product-dept, .rd-dept').should('exist');
16
+ });
17
+
18
+ it('renders custom control icons', () => {
19
+ visitChartDemo('custom-icons.html', 'Custom Icons Demo');
20
+ cy.get('#chart-container .node .toggleBtn').should('have.class', 'fa-solid');
21
+ });
22
+
23
+ it('renders the custom node template', () => {
24
+ visitChartDemo('custom-template.html', 'Custom Template Demo');
25
+ cy.get('#chart-container .node .office').should('exist');
26
+ });
27
+
28
+ it('renders compact nodes from node data', () => {
29
+ visitChartDemo('data-prop-compact.html', 'Compact Nodes Demo');
30
+ cy.get('#chart-container .node.compact').should('exist');
31
+ });
32
+
33
+ it('renders hybrid nodes from node data', () => {
34
+ visitChartDemo('data-prop-hybrid.html', 'Hybrid Nodes Demo');
35
+ cy.get('#chart-container .verticalNodes, #chart-container .toggleBtn').should('exist');
36
+ });
37
+
38
+ it('renders the PNG export control', () => {
39
+ visitChartDemo('export-chart.html', 'Export Chart Demo');
40
+ cy.get('.oc-export-btn').should('be.visible');
41
+ });
42
+
43
+ it('renders the PDF export control', () => {
44
+ visitChartDemo('export-chart-pdf.html', 'Export PDF Demo');
45
+ cy.get('.oc-export-btn').should('be.visible');
46
+ });
47
+
48
+ it('renders the picture export control and avatars', () => {
49
+ visitChartDemo('export-chart-with-pictures.html', 'Export Chart with Pictures Demo');
50
+ cy.get('.oc-export-btn').should('be.visible');
51
+ cy.get('#chart-container .avatar').should('exist');
52
+ });
53
+
54
+ it('renders the family-tree relationship layout', () => {
55
+ visitChartDemo('family-tree.html', 'Family Tree Demo');
56
+ cy.get('#chart-container .node').should('have.length.at.least', 3);
57
+ });
58
+
59
+ it('renders family-tree custom properties', () => {
60
+ visitChartDemo('familytree-custom-properties.html', 'Family Tree Custom Properties Demo');
61
+ cy.get('#chart-container .node').should('have.length.at.least', 3);
62
+ });
63
+
64
+ it('renders the left-to-right chart', () => {
65
+ visitChartDemo('left2right.html', 'Left to Right Demo');
66
+ cy.get('#chart-container .orgchart').should('have.class', 'l2r');
67
+ });
68
+
69
+ it('renders link nodes', () => {
70
+ visitChartDemo('link-node.html', 'Link Nodes Demo');
71
+ cy.get('#chart-container .node').should('have.length.at.least', 2);
72
+ });
73
+
74
+ it('renders nodes with different widths', () => {
75
+ visitChartDemo('nodes-of-different-widths.html', 'Nodes of Different Widths Demo');
76
+ cy.get('#chart-container .node').then(($nodes) => {
77
+ const widths = Array.from($nodes, (node) => node.getBoundingClientRect().width);
78
+
79
+ expect(new Set(widths).size).to.be.greaterThan(1);
80
+ });
81
+ });
82
+
83
+ it('renders the right-to-left chart', () => {
84
+ visitChartDemo('right2left.html', 'Right to Left Demo');
85
+ cy.get('#chart-container .orgchart').should('have.class', 'r2l');
86
+ });
87
+
88
+ it('parses the unordered-list datasource', () => {
89
+ visitChartDemo('ul-datasource.html', 'UL Datasource Demo');
90
+ cy.contains('#chart-container .node .title', 'Lao Lao').should('exist');
91
+ });
92
+
93
+ it('renders the map integration page shell without requiring map tiles', () => {
94
+ cy.visit('demo/integrate-map.html');
95
+ cy.contains('h1', 'Map Integration Demo').should('be.visible');
96
+ cy.get('#map').should('exist');
97
+ cy.get('#chart-container').should('exist');
98
+ });
99
+
100
+ });
@@ -0,0 +1,22 @@
1
+ describe('Hybrid Drag and Drop Chart', () => {
2
+
3
+ const sanDan = '.node:contains("San Dan")';
4
+ const sanYue = '.node:contains("San Yue")';
5
+
6
+ beforeEach(() => {
7
+ cy.visit('demo/drag-drop-hybrid-chart.html');
8
+ });
9
+
10
+ it('recomputes vertical relationships after moving a nested branch', () => {
11
+ const dataTransfer = new DataTransfer();
12
+
13
+ cy.get(sanDan).trigger('dragstart', { dataTransfer, force: true });
14
+ cy.get(sanYue).trigger('drop', { dataTransfer, force: true });
15
+
16
+ cy.get(sanYue).should(($node) => {
17
+ expect($node.siblings('.nodes').find('.node:contains("San Dan")')).to.exist;
18
+ expect($node.find('.toggleBtn')).to.exist;
19
+ });
20
+ });
21
+
22
+ });
@@ -31,6 +31,26 @@ describe('Edit Chart', () => {
31
31
  });
32
32
  });
33
33
 
34
+ it('add parent to a non-root node without changing its branch', () => {
35
+ cy.get(football).click();
36
+ cy.get(newNodes).eq(0).type('Indoor sports');
37
+ cy.get(parentRel).click();
38
+ cy.get(addBtn).click();
39
+
40
+ cy.get(ballgame).should(($root) => {
41
+ const rootChildNodes = $root[0].nextElementSibling;
42
+ const rootChildren = Array.from(rootChildNodes.children);
43
+ const localParentHierarchy = rootChildren.find((hierarchy) =>
44
+ hierarchy.querySelector(':scope > .node > .title').textContent === 'Indoor sports'
45
+ );
46
+
47
+ expect(rootChildren).to.have.length(3);
48
+ expect(localParentHierarchy.querySelector(':scope > .nodes > .hierarchy > .node > .title').textContent).to.equal('Football');
49
+ expect(rootChildren.map((hierarchy) => hierarchy.querySelector(':scope > .node > .title').textContent))
50
+ .to.include.members(['Indoor sports', 'Basketball', 'Volleyball']);
51
+ });
52
+ });
53
+
34
54
  it('add child nodes -- Futsal and Beach football', () => {
35
55
  cy.get(football).click();
36
56
  cy.get(newNodes).eq(0).type('Futsal');
@@ -0,0 +1,21 @@
1
+ describe('Level Offset', () => {
2
+
3
+ beforeEach(() => {
4
+ cy.visit('demo/level-offset.html');
5
+ });
6
+
7
+ it('applies the configured offset to the node and connector variables', () => {
8
+ cy.contains('.node .title', 'Hong Miao')
9
+ .closest('.node')
10
+ .should('have.css', 'margin-top', '140px')
11
+ .then(($node) => {
12
+ const nodeStyle = $node[0].style;
13
+
14
+ expect(nodeStyle.getPropertyValue('--top')).to.equal('-151px');
15
+ expect(nodeStyle.getPropertyValue('--height')).to.equal('149px');
16
+ expect(nodeStyle.getPropertyValue('--top-cross-point')).to.equal('-153px');
17
+ expect(nodeStyle.getPropertyValue('--height-cross-point')).to.equal('151px');
18
+ });
19
+ });
20
+
21
+ });
@@ -0,0 +1,66 @@
1
+ describe('Minimap', () => {
2
+
3
+ const chart = '.orgchart';
4
+ const minimap = '.orgchart-minimap';
5
+ const minimapViewport = '.orgchart-minimap-viewport';
6
+ const parseMatrixScale = (transformValue) => {
7
+ const matrixMatch = /matrix\(([^)]+)\)/.exec(transformValue || '');
8
+
9
+ return matrixMatch ? Number.parseFloat(matrixMatch[1].split(',')[0]) : 1;
10
+ };
11
+
12
+ beforeEach(() => {
13
+ cy.visit('demo/minimap.html');
14
+ });
15
+
16
+ it('renders an overview and moves the chart when its viewport is dragged', () => {
17
+ cy.get(minimap).should('be.visible');
18
+ cy.get('.orgchart-minimap-node').should('have.length.at.least', 30);
19
+ cy.get(minimapViewport).should('be.visible');
20
+
21
+ cy.get(chart).invoke('css', 'transform').then((beforeTransform) => {
22
+ cy.get(minimapViewport).then(($viewport) => {
23
+ const viewport = $viewport[0];
24
+ const rect = viewport.getBoundingClientRect();
25
+ const startX = rect.left + (rect.width / 2);
26
+ const startY = rect.top + (rect.height / 2);
27
+ const win = viewport.ownerDocument.defaultView;
28
+
29
+ viewport.dispatchEvent(new win.MouseEvent('mousedown', {
30
+ bubbles: true,
31
+ cancelable: true,
32
+ clientX: startX,
33
+ clientY: startY
34
+ }));
35
+ viewport.ownerDocument.dispatchEvent(new win.MouseEvent('mousemove', {
36
+ bubbles: true,
37
+ clientX: startX + 20,
38
+ clientY: startY + 20
39
+ }));
40
+ viewport.ownerDocument.dispatchEvent(new win.MouseEvent('mouseup', { bubbles: true }));
41
+ });
42
+
43
+ cy.get(chart).should('not.have.css', 'transform', beforeTransform);
44
+ });
45
+ });
46
+
47
+ it('zooms the main chart from the minimap wheel control', () => {
48
+ cy.get(chart).invoke('css', 'transform').then((beforeTransform) => {
49
+ cy.get(minimap).then(($minimap) => {
50
+ const minimapElement = $minimap[0];
51
+ const win = minimapElement.ownerDocument.defaultView;
52
+
53
+ minimapElement.dispatchEvent(new win.WheelEvent('wheel', {
54
+ bubbles: true,
55
+ cancelable: true,
56
+ deltaY: -1
57
+ }));
58
+ });
59
+
60
+ cy.get(chart).invoke('css', 'transform').then((afterTransform) => {
61
+ expect(parseMatrixScale(afterTransform)).to.be.greaterThan(parseMatrixScale(beforeTransform));
62
+ });
63
+ });
64
+ });
65
+
66
+ });
@@ -5,6 +5,70 @@ describe('Pan & Zoom', () => {
5
5
  });
6
6
 
7
7
  const chart = '.orgchart';
8
+ const chartContainer = '#chart-container';
9
+ const pinchAnchorTolerance = 5;
10
+ const dispatchTouchEvent = (win, target, type, touches) => {
11
+ const touchLikePoints = touches.map((touch, index) => ({
12
+ identifier: index,
13
+ target,
14
+ clientX: touch.clientX,
15
+ clientY: touch.clientY,
16
+ pageX: touch.clientX,
17
+ pageY: touch.clientY,
18
+ screenX: touch.clientX,
19
+ screenY: touch.clientY
20
+ }));
21
+ const touchEvent = new win.Event(type, {
22
+ bubbles: true,
23
+ cancelable: true
24
+ });
25
+
26
+ Object.defineProperty(touchEvent, 'touches', {
27
+ value: type === 'touchend' ? [] : touchLikePoints,
28
+ configurable: true
29
+ });
30
+ Object.defineProperty(touchEvent, 'targetTouches', {
31
+ value: type === 'touchend' ? [] : touchLikePoints,
32
+ configurable: true
33
+ });
34
+ Object.defineProperty(touchEvent, 'changedTouches', {
35
+ value: touchLikePoints,
36
+ configurable: true
37
+ });
38
+
39
+ target.dispatchEvent(touchEvent);
40
+ };
41
+ const parseMatrixScale = (transformValue) => {
42
+ const matrixMatch = /matrix\(([^)]+)\)/.exec(transformValue || '');
43
+
44
+ if (!matrixMatch) {
45
+ return 1;
46
+ }
47
+
48
+ return Number.parseFloat(matrixMatch[1].split(',')[0]);
49
+ };
50
+ const pinchAtPoint = (win, target, center, startGap, endGap) => {
51
+ const initialTouches = [
52
+ { clientX: center.x - (startGap / 2), clientY: center.y },
53
+ { clientX: center.x + (startGap / 2), clientY: center.y }
54
+ ];
55
+ const movedTouches = [
56
+ { clientX: center.x - (endGap / 2), clientY: center.y },
57
+ { clientX: center.x + (endGap / 2), clientY: center.y }
58
+ ];
59
+
60
+ dispatchTouchEvent(win, target, 'touchstart', initialTouches);
61
+ dispatchTouchEvent(win, win.document, 'touchmove', movedTouches);
62
+ dispatchTouchEvent(win, win.document, 'touchend', movedTouches);
63
+ };
64
+ const getNodeCenter = ($node) => {
65
+ const rect = $node[0].getBoundingClientRect();
66
+
67
+ return {
68
+ x: rect.left + (rect.width / 2),
69
+ y: rect.top + (rect.height / 2)
70
+ };
71
+ };
8
72
 
9
73
  it('drag the chart to the top', () => {
10
74
  cy.get(chart)
@@ -38,4 +102,105 @@ describe('Pan & Zoom', () => {
38
102
  .should('have.css', 'transform', 'matrix(1, 0, 0, 1, 100, 0)');
39
103
  });
40
104
 
105
+ it('zooms around the node under the mouse pointer', () => {
106
+ cy.contains('.node .title', 'Lao Lao')
107
+ .closest('.node')
108
+ .then(($node) => {
109
+ const beforeCenter = getNodeCenter($node);
110
+
111
+ cy.get(chartContainer).then(($container) => {
112
+ const wheelEvent = new $container[0].ownerDocument.defaultView.WheelEvent('wheel', {
113
+ deltaY: -1,
114
+ clientX: beforeCenter.x,
115
+ clientY: beforeCenter.y,
116
+ bubbles: true,
117
+ cancelable: true
118
+ });
119
+
120
+ $container[0].dispatchEvent(wheelEvent);
121
+ });
122
+
123
+ cy.contains('.node .title', 'Lao Lao')
124
+ .closest('.node')
125
+ .then(($zoomedNode) => {
126
+ const afterCenter = getNodeCenter($zoomedNode);
127
+
128
+ expect(Math.abs(afterCenter.x - beforeCenter.x)).to.be.lessThan(1);
129
+ expect(Math.abs(afterCenter.y - beforeCenter.y)).to.be.lessThan(1);
130
+ });
131
+ });
132
+ });
133
+
134
+ it('ignores wheel zoom when the pointer is outside the chart container bounds', () => {
135
+ cy.get(chart).invoke('css', 'transform').then((beforeTransform) => {
136
+ cy.get(chartContainer).then(($container) => {
137
+ const rect = $container[0].getBoundingClientRect();
138
+
139
+ cy.get(chartContainer).trigger('wheel', {
140
+ eventConstructor: 'WheelEvent',
141
+ deltaY: -1,
142
+ clientX: rect.left - 5,
143
+ clientY: rect.top + 10,
144
+ bubbles: true,
145
+ cancelable: true
146
+ });
147
+ });
148
+
149
+ cy.get(chart).should('have.css', 'transform', beforeTransform);
150
+ });
151
+ });
152
+
153
+ it('zooms in on touch pinch out', () => {
154
+ cy.window().then((win) => {
155
+ const chartContainerElement = win.document.querySelector(chartContainer);
156
+
157
+ pinchAtPoint(win, chartContainerElement, { x: 160, y: 160 }, 40, 80);
158
+ });
159
+
160
+ cy.get(chart)
161
+ .invoke('css', 'transform')
162
+ .then((transformValue) => {
163
+ expect(parseMatrixScale(transformValue)).to.be.greaterThan(1);
164
+ });
165
+ });
166
+
167
+ it('zooms out on touch pinch in', () => {
168
+ cy.window().then((win) => {
169
+ const chartContainerElement = win.document.querySelector(chartContainer);
170
+
171
+ pinchAtPoint(win, chartContainerElement, { x: 160, y: 160 }, 80, 40);
172
+ });
173
+
174
+ cy.get(chart)
175
+ .invoke('css', 'transform')
176
+ .then((transformValue) => {
177
+ expect(parseMatrixScale(transformValue)).to.be.lessThan(1);
178
+ });
179
+ });
180
+
181
+ it('keeps the pinch midpoint inside the same node during touch pinch', () => {
182
+ cy.contains('.node .title', 'Lao Lao')
183
+ .closest('.node')
184
+ .then(($node) => {
185
+ const beforeCenter = getNodeCenter($node);
186
+
187
+ cy.window().then((win) => {
188
+ const chartContainerElement = win.document.querySelector(chartContainer);
189
+
190
+ pinchAtPoint(win, chartContainerElement, beforeCenter, 40, 80);
191
+ });
192
+
193
+ cy.contains('.node .title', 'Lao Lao')
194
+ .closest('.node')
195
+ .then(($zoomedNode) => {
196
+ const rect = $zoomedNode[0].getBoundingClientRect();
197
+
198
+ expect(beforeCenter.x).to.be.greaterThan(rect.left - pinchAnchorTolerance);
199
+ expect(beforeCenter.x).to.be.lessThan(rect.right + pinchAnchorTolerance);
200
+ expect(beforeCenter.y).to.be.greaterThan(rect.top - pinchAnchorTolerance);
201
+ expect(beforeCenter.y).to.be.lessThan(rect.bottom + pinchAnchorTolerance);
202
+ });
203
+ });
204
+ });
205
+
41
206
  });
@@ -0,0 +1,20 @@
1
+ describe('Report Path', () => {
2
+
3
+ const danDan = '.node:contains("Dan Dan")';
4
+ const selectedNode = '#selected-node';
5
+ const reportButton = '#btn-report-path';
6
+
7
+ beforeEach(() => {
8
+ cy.visit('demo/report-path.html');
9
+ });
10
+
11
+ it('keeps the chosen node selected after drawing its management path', () => {
12
+ cy.get(danDan).click();
13
+ cy.get(selectedNode).should('have.value', 'Dan Dan');
14
+
15
+ cy.get(reportButton).click().should('be.disabled');
16
+ cy.get(danDan).should('have.class', 'focused');
17
+ cy.get(selectedNode).should('have.value', 'Dan Dan');
18
+ });
19
+
20
+ });
@@ -15,14 +15,16 @@ 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();
18
+ cy.get(dandan).find('.toggleBtn').click({ force: true });
19
+ cy.get(dandan).find('.toggleBtn').should('have.class', 'oci-minus-square');
19
20
  cy.get(erdan).get(sandan).should('be.visible');
20
21
  cy.get(sidan).get(wudan).should('not.be.visible');
21
22
 
22
- cy.get(heihei).find('.toggleBtn').click();
23
+ cy.get(heihei).find('.toggleBtn').click({ force: true });
24
+ cy.get(heihei).find('.toggleBtn').should('have.class', 'oci-plus-square');
23
25
  cy.get(pangpang).get(dandan).get(erdan).get(sandan).should('not.be.visible');
24
26
 
25
- cy.get(heihei).find('.toggleBtn').click();
27
+ cy.get(heihei).find('.toggleBtn').click({ force: true });
26
28
  cy.get(pangpang).get(dandan).should('be.visible');
27
29
  });
28
30
 
@@ -100,7 +100,7 @@ test('horizontalEdge of heihei node works well', async t => {
100
100
  await t
101
101
  .hover(heihei)
102
102
  .click(heihei.find('.topEdge'))
103
- /* take into account the transition settings in jquery.orgchart.css
103
+ /* take into account the transition settings in orgchart.css
104
104
  * .orgchart .node {
105
105
  * transition: transform 0.3s, opacity 0.3s;
106
106
  * }