jqtree 1.7.4 → 1.8.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 (94) hide show
  1. package/.eslintrc +5 -1
  2. package/bower.json +1 -1
  3. package/config/babel.config.json +1 -1
  4. package/config/production +2 -0
  5. package/devserver/devserver_scroll.js +8 -0
  6. package/devserver/test_scroll.html +28 -0
  7. package/devserver/test_scroll_container.html +39 -0
  8. package/docs/_config.yml +1 -1
  9. package/docs/_entries/general/changelog.md +12 -0
  10. package/docs/package.json +1 -1
  11. package/docs/pnpm-lock.yaml +30 -30
  12. package/package.json +31 -27
  13. package/src/dataLoader.ts +44 -19
  14. package/src/dragAndDropHandler/dragElement.ts +42 -0
  15. package/src/dragAndDropHandler/hitAreasGenerator.ts +175 -0
  16. package/src/dragAndDropHandler/index.ts +470 -0
  17. package/src/dragAndDropHandler/types.ts +12 -0
  18. package/src/dragAndDropHandler/visibleNodeIterator.ts +97 -0
  19. package/src/elementsRenderer.ts +75 -40
  20. package/src/jqtreeMethodTypes.ts +40 -0
  21. package/src/jqtreeOptions.ts +43 -25
  22. package/src/keyHandler.ts +59 -38
  23. package/src/mouse.widget.ts +3 -3
  24. package/src/mouseWidgetTypes.ts +6 -0
  25. package/src/node.ts +32 -48
  26. package/src/nodeElement/borderDropHint.ts +32 -0
  27. package/src/nodeElement/folderElement.ts +133 -0
  28. package/src/nodeElement/ghostDropHint.ts +68 -0
  29. package/src/nodeElement/index.ts +101 -0
  30. package/src/nodeUtils.ts +10 -0
  31. package/src/playwright/coverage.ts +1 -4
  32. package/src/playwright/playwright.test.ts +203 -15
  33. package/src/playwright/testUtils.ts +23 -15
  34. package/src/saveStateHandler.ts +75 -26
  35. package/src/scrollHandler/containerScrollParent.ts +177 -0
  36. package/src/scrollHandler/createScrollParent.ts +50 -0
  37. package/src/scrollHandler/documentScrollParent.ts +182 -0
  38. package/src/scrollHandler/types.ts +7 -0
  39. package/src/scrollHandler.ts +36 -248
  40. package/src/selectNodeHandler.ts +10 -16
  41. package/src/test/jqTree/keyboard.test.ts +18 -23
  42. package/src/test/jqTree/loadOnDemand.test.ts +2 -3
  43. package/src/test/jqTree/methods.test.ts +33 -4
  44. package/src/test/jqTree/options.test.ts +15 -4
  45. package/src/test/node.test.ts +85 -26
  46. package/src/test/nodeUtils.test.ts +21 -0
  47. package/src/tree.jquery.ts +262 -83
  48. package/src/util.ts +3 -0
  49. package/src/version.ts +1 -1
  50. package/tree.jquery.debug.js +1922 -2608
  51. package/tree.jquery.debug.js.map +1 -1
  52. package/tree.jquery.js +2 -2
  53. package/tree.jquery.js.map +1 -1
  54. package/lib/dataLoader.js +0 -124
  55. package/lib/dragAndDropHandler.js +0 -596
  56. package/lib/elementsRenderer.js +0 -268
  57. package/lib/jqtreeOptions.js +0 -1
  58. package/lib/keyHandler.js +0 -115
  59. package/lib/mouse.widget.js +0 -256
  60. package/lib/node.js +0 -717
  61. package/lib/nodeElement.js +0 -277
  62. package/lib/playwright/coverage.js +0 -96
  63. package/lib/playwright/playwright.test.js +0 -228
  64. package/lib/playwright/testUtils.js +0 -184
  65. package/lib/saveStateHandler.js +0 -278
  66. package/lib/scrollHandler.js +0 -250
  67. package/lib/selectNodeHandler.js +0 -129
  68. package/lib/simple.widget.js +0 -159
  69. package/lib/test/global.d.js +0 -3
  70. package/lib/test/jqTree/accessibility.test.js +0 -37
  71. package/lib/test/jqTree/create.test.js +0 -48
  72. package/lib/test/jqTree/events.test.js +0 -210
  73. package/lib/test/jqTree/keyboard.test.js +0 -225
  74. package/lib/test/jqTree/loadOnDemand.test.js +0 -218
  75. package/lib/test/jqTree/methods.test.js +0 -1347
  76. package/lib/test/jqTree/options.test.js +0 -548
  77. package/lib/test/node.test.js +0 -1160
  78. package/lib/test/nodeUtil.test.js +0 -27
  79. package/lib/test/support/exampleData.js +0 -36
  80. package/lib/test/support/jqTreeMatchers.js +0 -70
  81. package/lib/test/support/matchers.d.js +0 -1
  82. package/lib/test/support/setupTests.js +0 -7
  83. package/lib/test/support/testUtil.js +0 -32
  84. package/lib/test/support/treeStructure.js +0 -39
  85. package/lib/test/util.test.js +0 -26
  86. package/lib/tree.jquery.d.js +0 -1
  87. package/lib/tree.jquery.js +0 -1106
  88. package/lib/types.js +0 -1
  89. package/lib/typings.d.js +0 -2
  90. package/lib/util.js +0 -18
  91. package/lib/version.js +0 -9
  92. package/src/dragAndDropHandler.ts +0 -719
  93. package/src/nodeElement.ts +0 -272
  94. package/src/types.ts +0 -19
@@ -1,276 +1,64 @@
1
- import { JqTreeWidget } from "./tree.jquery";
2
- import { HitArea, PositionInfo } from "./types";
1
+ import { PositionInfo } from "./mouseWidgetTypes";
2
+ import { ScrollParent } from "./scrollHandler/types";
3
+ import createScrollParent from "./scrollHandler/createScrollParent";
3
4
 
4
- export default class ScrollHandler {
5
- private treeWidget: JqTreeWidget;
6
- private previousTop: number;
7
- private isInitialized: boolean;
8
- private $scrollParent: JQuery | null;
9
- private scrollParentTop: number;
5
+ interface ScrollHandlerParams {
6
+ refreshHitAreas: () => void;
7
+ $treeElement: JQuery<HTMLElement>;
8
+ }
10
9
 
11
- constructor(treeWidget: JqTreeWidget) {
12
- this.treeWidget = treeWidget;
13
- this.previousTop = -1;
14
- this.isInitialized = false;
10
+ export default class ScrollHandler {
11
+ private refreshHitAreas: () => void;
12
+ private scrollParent?: ScrollParent;
13
+ private $treeElement: JQuery<HTMLElement>;
14
+
15
+ constructor({ refreshHitAreas, $treeElement }: ScrollHandlerParams) {
16
+ this.refreshHitAreas = refreshHitAreas;
17
+ this.scrollParent = undefined;
18
+ this.$treeElement = $treeElement;
15
19
  }
16
20
 
17
- public checkScrolling(): void {
18
- this.ensureInit();
19
- this.checkVerticalScrolling();
20
- this.checkHorizontalScrolling();
21
+ public checkScrolling(positionInfo: PositionInfo): void {
22
+ this.checkVerticalScrolling(positionInfo);
23
+ this.checkHorizontalScrolling(positionInfo);
21
24
  }
22
25
 
23
- public scrollToY(top: number): void {
24
- this.ensureInit();
25
-
26
- if (this.$scrollParent && this.$scrollParent[0]) {
27
- this.$scrollParent[0].scrollTop = top;
28
- } else {
29
- const offset = this.treeWidget.$el.offset();
30
- const treeTop = offset ? offset.top : 0;
31
-
32
- jQuery(document).scrollTop(top + treeTop);
33
- }
26
+ public stopScrolling() {
27
+ this.getScrollParent().stopScrolling();
34
28
  }
35
29
 
36
- public isScrolledIntoView($element: JQuery): boolean {
37
- this.ensureInit();
38
-
39
- let elementBottom: number;
40
- let viewBottom: number;
41
- let elementTop: number;
42
- let viewTop: number;
43
-
44
- const elHeight = $element.height() || 0;
45
-
46
- if (this.$scrollParent) {
47
- viewTop = 0;
48
- viewBottom = this.$scrollParent.height() || 0;
49
-
50
- const offset = $element.offset();
51
- const originalTop = offset ? offset.top : 0;
52
-
53
- elementTop = originalTop - this.scrollParentTop;
54
- elementBottom = elementTop + elHeight;
55
- } else {
56
- viewTop = jQuery(window).scrollTop() || 0;
57
-
58
- const windowHeight = jQuery(window).height() || 0;
59
- viewBottom = viewTop + windowHeight;
60
-
61
- const offset = $element.offset();
62
-
63
- elementTop = offset ? offset.top : 0;
64
- elementBottom = elementTop + elHeight;
65
- }
66
-
67
- return elementBottom <= viewBottom && elementTop >= viewTop;
30
+ public scrollToY(top: number): void {
31
+ this.getScrollParent().scrollToY(top);
68
32
  }
69
33
 
70
34
  public getScrollLeft(): number {
71
- if (!this.$scrollParent) {
72
- return 0;
73
- } else {
74
- return this.$scrollParent.scrollLeft() || 0;
75
- }
76
- }
77
-
78
- private initScrollParent(): void {
79
- const getParentWithOverflow = (): JQuery | null => {
80
- const cssAttributes = ["overflow", "overflow-y"];
81
-
82
- const hasOverFlow = ($el: JQuery): boolean => {
83
- for (const attr of cssAttributes) {
84
- const overflowValue = $el.css(attr);
85
- if (
86
- overflowValue === "auto" ||
87
- overflowValue === "scroll"
88
- ) {
89
- return true;
90
- }
91
- }
92
-
93
- return false;
94
- };
95
-
96
- if (hasOverFlow(this.treeWidget.$el)) {
97
- return this.treeWidget.$el;
98
- }
99
-
100
- for (const el of this.treeWidget.$el.parents().get()) {
101
- const $el = jQuery(el);
102
- if (hasOverFlow($el)) {
103
- return $el;
104
- }
105
- }
106
-
107
- return null;
108
- };
109
-
110
- const setDocumentAsScrollParent = (): void => {
111
- this.scrollParentTop = 0;
112
- this.$scrollParent = null;
113
- };
114
-
115
- if (this.treeWidget.$el.css("position") === "fixed") {
116
- setDocumentAsScrollParent();
117
- }
118
-
119
- const $scrollParent = getParentWithOverflow();
120
-
121
- if (
122
- $scrollParent &&
123
- $scrollParent.length &&
124
- $scrollParent[0]?.tagName !== "HTML"
125
- ) {
126
- this.$scrollParent = $scrollParent;
127
-
128
- const offset = this.$scrollParent.offset();
129
- this.scrollParentTop = offset ? offset.top : 0;
130
- } else {
131
- setDocumentAsScrollParent();
132
- }
133
-
134
- this.isInitialized = true;
135
- }
136
-
137
- private ensureInit(): void {
138
- if (!this.isInitialized) {
139
- this.initScrollParent();
140
- }
35
+ return this.getScrollParent().getScrollLeft();
141
36
  }
142
37
 
143
- private handleVerticalScrollingWithScrollParent(area: HitArea): void {
144
- const scrollParent = this.$scrollParent && this.$scrollParent[0];
145
-
146
- if (!scrollParent) {
38
+ private checkVerticalScrolling(positionInfo: PositionInfo): void {
39
+ if (positionInfo.pageY == null) {
147
40
  return;
148
41
  }
149
42
 
150
- const distanceBottom =
151
- this.scrollParentTop + scrollParent.offsetHeight - area.bottom;
152
-
153
- if (distanceBottom < 20) {
154
- scrollParent.scrollTop += 20;
155
- this.treeWidget.refreshHitAreas();
156
- this.previousTop = -1;
157
- } else if (area.top - this.scrollParentTop < 20) {
158
- scrollParent.scrollTop -= 20;
159
- this.treeWidget.refreshHitAreas();
160
- this.previousTop = -1;
161
- }
162
- }
163
-
164
- private handleVerticalScrollingWithDocument(area: HitArea): void {
165
- const scrollTop = jQuery(document).scrollTop() || 0;
166
- const distanceTop = area.top - scrollTop;
167
-
168
- if (distanceTop < 20) {
169
- jQuery(document).scrollTop(scrollTop - 20);
170
- } else {
171
- const windowHeight = jQuery(window).height() || 0;
172
-
173
- if (windowHeight - (area.bottom - scrollTop) < 20) {
174
- jQuery(document).scrollTop(scrollTop + 20);
175
- }
176
- }
43
+ this.getScrollParent().checkVerticalScrolling(positionInfo.pageY);
177
44
  }
178
45
 
179
- private checkVerticalScrolling(): void {
180
- const hoveredArea = this.treeWidget.dndHandler.hoveredArea;
181
-
182
- if (hoveredArea && hoveredArea.top !== this.previousTop) {
183
- this.previousTop = hoveredArea.top;
184
-
185
- if (this.$scrollParent) {
186
- this.handleVerticalScrollingWithScrollParent(hoveredArea);
187
- } else {
188
- this.handleVerticalScrollingWithDocument(hoveredArea);
189
- }
190
- }
191
- }
192
-
193
- private checkHorizontalScrolling(): void {
194
- const positionInfo = this.treeWidget.dndHandler.positionInfo;
195
-
196
- if (!positionInfo) {
46
+ private checkHorizontalScrolling(positionInfo: PositionInfo): void {
47
+ if (positionInfo.pageX == null) {
197
48
  return;
198
49
  }
199
50
 
200
- if (this.$scrollParent) {
201
- this.handleHorizontalScrollingWithParent(positionInfo);
202
- } else {
203
- this.handleHorizontalScrollingWithDocument(positionInfo);
204
- }
51
+ this.getScrollParent().checkHorizontalScrolling(positionInfo.pageX);
205
52
  }
206
53
 
207
- private handleHorizontalScrollingWithParent(
208
- positionInfo: PositionInfo
209
- ): void {
210
- if (
211
- positionInfo.pageX === undefined ||
212
- positionInfo.pageY === undefined
213
- ) {
214
- return;
215
- }
216
-
217
- const $scrollParent = this.$scrollParent;
218
- const scrollParentOffset = $scrollParent && $scrollParent.offset();
219
-
220
- if (!($scrollParent && scrollParentOffset)) {
221
- return;
222
- }
223
-
224
- const scrollParent = $scrollParent[0];
225
-
226
- if (!scrollParent) {
227
- return;
228
- }
229
-
230
- const canScrollRight =
231
- scrollParent.scrollLeft + scrollParent.clientWidth <
232
- scrollParent.scrollWidth;
233
- const canScrollLeft = scrollParent.scrollLeft > 0;
234
-
235
- const rightEdge = scrollParentOffset.left + scrollParent.clientWidth;
236
- const leftEdge = scrollParentOffset.left;
237
- const isNearRightEdge = positionInfo.pageX > rightEdge - 20;
238
- const isNearLeftEdge = positionInfo.pageX < leftEdge + 20;
239
-
240
- if (isNearRightEdge && canScrollRight) {
241
- scrollParent.scrollLeft = Math.min(
242
- scrollParent.scrollLeft + 20,
243
- scrollParent.scrollWidth
54
+ private getScrollParent(): ScrollParent {
55
+ if (!this.scrollParent) {
56
+ this.scrollParent = createScrollParent(
57
+ this.$treeElement,
58
+ this.refreshHitAreas,
244
59
  );
245
- } else if (isNearLeftEdge && canScrollLeft) {
246
- scrollParent.scrollLeft = Math.max(scrollParent.scrollLeft - 20, 0);
247
60
  }
248
- }
249
-
250
- private handleHorizontalScrollingWithDocument(
251
- positionInfo: PositionInfo
252
- ): void {
253
- if (
254
- positionInfo.pageX === undefined ||
255
- positionInfo.pageY === undefined
256
- ) {
257
- return;
258
- }
259
-
260
- const $document = jQuery(document);
261
61
 
262
- const scrollLeft = $document.scrollLeft() || 0;
263
- const windowWidth = jQuery(window).width() || 0;
264
-
265
- const canScrollLeft = scrollLeft > 0;
266
-
267
- const isNearRightEdge = positionInfo.pageX > windowWidth - 20;
268
- const isNearLeftEdge = positionInfo.pageX - scrollLeft < 20;
269
-
270
- if (isNearRightEdge) {
271
- $document.scrollLeft(scrollLeft + 20);
272
- } else if (isNearLeftEdge && canScrollLeft) {
273
- $document.scrollLeft(Math.max(scrollLeft - 20, 0));
274
- }
62
+ return this.scrollParent;
275
63
  }
276
64
  }
@@ -1,13 +1,17 @@
1
1
  import { Node } from "./node";
2
- import { JqTreeWidget } from "./tree.jquery";
2
+ import { GetNodeById } from "./jqtreeMethodTypes";
3
+
4
+ interface SelectNodeHandlerParameters {
5
+ getNodeById: GetNodeById;
6
+ }
3
7
 
4
8
  export default class SelectNodeHandler {
5
- private treeWidget: JqTreeWidget;
9
+ private getNodeById: GetNodeById;
6
10
  private selectedNodes: Set<NodeId>;
7
11
  private selectedSingleNode: Node | null;
8
12
 
9
- constructor(treeWidget: JqTreeWidget) {
10
- this.treeWidget = treeWidget;
13
+ constructor({ getNodeById }: SelectNodeHandlerParameters) {
14
+ this.getNodeById = getNodeById;
11
15
  this.selectedNodes = new Set<NodeId>();
12
16
  this.clear();
13
17
  }
@@ -29,7 +33,7 @@ export default class SelectNodeHandler {
29
33
  const selectedNodes: Node[] = [];
30
34
 
31
35
  this.selectedNodes.forEach((id) => {
32
- const node = this.treeWidget.getNodeById(id);
36
+ const node = this.getNodeById(id);
33
37
  if (node) {
34
38
  selectedNodes.push(node);
35
39
  }
@@ -53,7 +57,7 @@ export default class SelectNodeHandler {
53
57
  if (
54
58
  Object.prototype.hasOwnProperty.call(this.selectedNodes, id)
55
59
  ) {
56
- const node = this.treeWidget.getNodeById(id);
60
+ const node = this.getNodeById(id);
57
61
  if (node && parent.isParentOf(node)) {
58
62
  selectedNodes.push(node);
59
63
  }
@@ -108,14 +112,4 @@ export default class SelectNodeHandler {
108
112
  this.selectedSingleNode = node;
109
113
  }
110
114
  }
111
-
112
- public isFocusOnTree(): boolean {
113
- const activeElement = document.activeElement;
114
-
115
- return Boolean(
116
- activeElement &&
117
- activeElement.tagName === "SPAN" &&
118
- this.treeWidget._containsElement(activeElement as HTMLElement)
119
- );
120
- }
121
115
  }
@@ -1,4 +1,5 @@
1
1
  import getGiven from "givens";
2
+ import { userEvent } from "@testing-library/user-event";
2
3
  import "../../tree.jquery";
3
4
  import exampleData from "../support/exampleData";
4
5
 
@@ -18,22 +19,16 @@ describe("keyboard support", () => {
18
19
  interface Vars {
19
20
  autoOpen: boolean;
20
21
  initialSelectedNode: INode | null;
21
- pressedKey: number;
22
+ pressedKey: string;
22
23
  $tree: JQuery<HTMLElement>;
23
24
  }
24
25
 
25
- const KEY_DOWN = 40;
26
- const KEY_LEFT = 37;
27
- const KEY_RIGHT = 39;
28
- const KEY_UP = 38;
29
- const KEY_PAGE_UP = 33;
30
-
31
26
  const given = getGiven<Vars>();
32
27
  given("autoOpen", () => false);
33
28
  given("initialSelectedNode", () => null);
34
29
  given("$tree", () => $("#tree1"));
35
30
 
36
- beforeEach(() => {
31
+ beforeEach(async () => {
37
32
  given.$tree.tree({
38
33
  animationSpeed: 0,
39
34
  autoOpen: given.autoOpen,
@@ -44,15 +39,15 @@ describe("keyboard support", () => {
44
39
  given.$tree.tree("selectNode", given.initialSelectedNode);
45
40
  }
46
41
 
47
- given.$tree.trigger($.Event("keydown", { which: given.pressedKey }));
42
+ await userEvent.keyboard(`{${given.pressedKey}}`);
48
43
  });
49
44
 
50
45
  context("with key down", () => {
51
- given("pressedKey", () => KEY_DOWN);
46
+ given("pressedKey", () => "ArrowDown");
52
47
 
53
48
  context("when a node is selected", () => {
54
49
  given("initialSelectedNode", () =>
55
- given.$tree.tree("getNodeByNameMustExist", "node1")
50
+ given.$tree.tree("getNodeByNameMustExist", "node1"),
56
51
  );
57
52
 
58
53
  it("selects the next node", () => {
@@ -71,7 +66,7 @@ describe("keyboard support", () => {
71
66
 
72
67
  context("when the last node is selected", () => {
73
68
  given("initialSelectedNode", () =>
74
- given.$tree.tree("getNodeByNameMustExist", "node2")
69
+ given.$tree.tree("getNodeByNameMustExist", "node2"),
75
70
  );
76
71
 
77
72
  it("keeps the node selected", () => {
@@ -83,11 +78,11 @@ describe("keyboard support", () => {
83
78
  });
84
79
 
85
80
  context("with key up", () => {
86
- given("pressedKey", () => KEY_UP);
81
+ given("pressedKey", () => "ArrowUp");
87
82
 
88
83
  context("when a node is selected", () => {
89
84
  given("initialSelectedNode", () =>
90
- given.$tree.tree("getNodeByNameMustExist", "node2")
85
+ given.$tree.tree("getNodeByNameMustExist", "node2"),
91
86
  );
92
87
 
93
88
  it("selects the next node", () => {
@@ -106,11 +101,11 @@ describe("keyboard support", () => {
106
101
  });
107
102
 
108
103
  context("with key right", () => {
109
- given("pressedKey", () => KEY_RIGHT);
104
+ given("pressedKey", () => "ArrowRight");
110
105
 
111
106
  context("when a closed folder is selected", () => {
112
107
  given("initialSelectedNode", () =>
113
- given.$tree.tree("getNodeByNameMustExist", "node1")
108
+ given.$tree.tree("getNodeByNameMustExist", "node1"),
114
109
  );
115
110
 
116
111
  it("opens the folder", () => {
@@ -132,7 +127,7 @@ describe("keyboard support", () => {
132
127
  context("when an open folder is selected", () => {
133
128
  given("autoOpen", () => true);
134
129
  given("initialSelectedNode", () =>
135
- given.$tree.tree("getNodeByNameMustExist", "node1")
130
+ given.$tree.tree("getNodeByNameMustExist", "node1"),
136
131
  );
137
132
 
138
133
  it("selects the first child", () => {
@@ -168,7 +163,7 @@ describe("keyboard support", () => {
168
163
 
169
164
  context("when a child is selected", () => {
170
165
  given("initialSelectedNode", () =>
171
- given.$tree.tree("getNodeByNameMustExist", "child1")
166
+ given.$tree.tree("getNodeByNameMustExist", "child1"),
172
167
  );
173
168
 
174
169
  it("does nothing", () => {
@@ -179,11 +174,11 @@ describe("keyboard support", () => {
179
174
  });
180
175
  });
181
176
  context("with key left", () => {
182
- given("pressedKey", () => KEY_LEFT);
177
+ given("pressedKey", () => "ArrowLeft");
183
178
 
184
179
  context("when a closed folder is selected", () => {
185
180
  given("initialSelectedNode", () =>
186
- given.$tree.tree("getNodeByNameMustExist", "node3")
181
+ given.$tree.tree("getNodeByNameMustExist", "node3"),
187
182
  );
188
183
 
189
184
  it("selects the previous node", () => {
@@ -210,7 +205,7 @@ describe("keyboard support", () => {
210
205
  context("when an open folder is selected", () => {
211
206
  given("autoOpen", () => true);
212
207
  given("initialSelectedNode", () =>
213
- given.$tree.tree("getNodeByNameMustExist", "node2")
208
+ given.$tree.tree("getNodeByNameMustExist", "node2"),
214
209
  );
215
210
 
216
211
  it("closes the folder", () => {
@@ -237,10 +232,10 @@ describe("keyboard support", () => {
237
232
  });
238
233
 
239
234
  context("with page up key", () => {
240
- given("pressedKey", () => KEY_PAGE_UP);
235
+ given("pressedKey", () => "PageUp");
241
236
 
242
237
  given("initialSelectedNode", () =>
243
- given.$tree.tree("getNodeByNameMustExist", "child1")
238
+ given.$tree.tree("getNodeByNameMustExist", "child1"),
244
239
  );
245
240
 
246
241
  it("does nothing", () => {
@@ -136,9 +136,8 @@ context("when a node has load_on_demand in the data", () => {
136
136
 
137
137
  context("when the node is selected and doesn't have the focus", () => {
138
138
  beforeEach(() => {
139
- given.$tree.tree("selectNode", given.node, {
140
- mustSetFocus: false,
141
- });
139
+ given.$tree.tree("selectNode", given.node);
140
+ (document.activeElement as HTMLElement).blur(); // eslint-disable-line testing-library/no-node-access
142
141
  });
143
142
 
144
143
  it("keeps the node selected and not focused", async () => {
@@ -134,7 +134,7 @@ describe("addToSelection", () => {
134
134
  given("child1", () => given.$tree.tree("getNodeByNameMustExist", "child1"));
135
135
  given("child2", () => given.$tree.tree("getNodeByNameMustExist", "child2"));
136
136
 
137
- beforeEach(() => {
137
+ it("selects the nodes", () => {
138
138
  given.$tree.tree({
139
139
  autoOpen: true,
140
140
  data: exampleData,
@@ -142,15 +142,21 @@ describe("addToSelection", () => {
142
142
 
143
143
  given.$tree.tree("addToSelection", given.child1);
144
144
  given.$tree.tree("addToSelection", given.child2);
145
- });
146
145
 
147
- it("selects the nodes", () => {
148
146
  expect(given.$tree.tree("getSelectedNodes")).toEqual(
149
147
  expect.arrayContaining([given.child1, given.child2]),
150
148
  );
151
149
  });
152
150
 
153
151
  it("renders the nodes correctly", () => {
152
+ given.$tree.tree({
153
+ autoOpen: true,
154
+ data: exampleData,
155
+ });
156
+
157
+ given.$tree.tree("addToSelection", given.child1);
158
+ given.$tree.tree("addToSelection", given.child2);
159
+
154
160
  expect(given.$tree).toHaveTreeStructure([
155
161
  expect.objectContaining({
156
162
  name: "node1",
@@ -172,6 +178,20 @@ describe("addToSelection", () => {
172
178
  }),
173
179
  ]);
174
180
  });
181
+
182
+ it("opens the parent node when it's closed", () => {
183
+ given.$tree.tree({
184
+ autoOpen: false,
185
+ data: exampleData,
186
+ });
187
+
188
+ const node1 = given.$tree.tree("getNodeByNameMustExist", "node1");
189
+ expect(node1.is_open).toBeFalsy();
190
+
191
+ given.$tree.tree("addToSelection", given.child1);
192
+
193
+ expect(node1.is_open).toBe(true);
194
+ });
175
195
  });
176
196
 
177
197
  describe("appendNode", () => {
@@ -984,7 +1004,7 @@ describe("refresh", () => {
984
1004
 
985
1005
  it("rerenders the tree", () => {
986
1006
  const tree = given.$tree.tree("getTree");
987
- (tree.children[0] as INode).name = "node1a";
1007
+ (tree.children[0] as INode).name = "node1a"; // eslint-disable-line testing-library/no-node-access
988
1008
 
989
1009
  expect(given.$tree).toHaveTreeStructure([
990
1010
  expect.objectContaining({ name: "node1" }),
@@ -1207,6 +1227,15 @@ describe("selectNode", () => {
1207
1227
  expect(given.$tree.tree("getSelectedNode")).toBeFalse();
1208
1228
  });
1209
1229
  });
1230
+
1231
+ it("opens the parent node when it's closed", () => {
1232
+ expect(given.node1.is_open).toBeFalsy();
1233
+
1234
+ const child1 = given.$tree.tree("getNodeByNameMustExist", "child1");
1235
+ given.$tree.tree("selectNode", child1);
1236
+
1237
+ expect(given.node1.is_open).toBe(true);
1238
+ });
1210
1239
  });
1211
1240
 
1212
1241
  describe("setOption", () => {
@@ -199,19 +199,30 @@ describe("closedIcon", () => {
199
199
  });
200
200
 
201
201
  it("renders a html element", () => {
202
- const closedIcon = document.createElement("span");
203
- closedIcon.className = "abc";
204
- closedIcon.textContent = "test";
202
+ const icon = document.createElement("span");
203
+ icon.className = "abc";
204
+ icon.textContent = "test";
205
205
 
206
206
  const $tree = $("#tree1");
207
207
  $tree.tree({
208
- closedIcon,
208
+ closedIcon: icon,
209
209
  data: exampleData,
210
210
  });
211
211
 
212
212
  const $span = $tree.find("a.jqtree-toggler:first span.abc");
213
213
  expect($span.text()).toBe("test");
214
214
  });
215
+
216
+ it("renders a default when the option is empty", () => {
217
+ const $tree = $("#tree1");
218
+ $tree.tree({
219
+ closedIcon: undefined,
220
+ data: exampleData,
221
+ });
222
+
223
+ const $span = $tree.find("a.jqtree-toggler:first");
224
+ expect($span.text()).toBe("►");
225
+ });
215
226
  });
216
227
 
217
228
  describe("dataUrl", () => {