jqtree 1.6.1 → 1.6.2

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 (84) hide show
  1. package/.eslintrc +1 -0
  2. package/_config.yml +1 -1
  3. package/_entries/10_changelog.md +5 -0
  4. package/_layouts/base.html +0 -22
  5. package/babel.config.json +11 -0
  6. package/babel.coverage.config.json +4 -0
  7. package/bower.json +1 -1
  8. package/jest-browser.config.js +0 -3
  9. package/jest-jsdom.config.js +1 -2
  10. package/jqtree.css +4 -1
  11. package/jqtree.postcss +3 -0
  12. package/lib/dataLoader.js +146 -98
  13. package/lib/dragAndDropHandler.js +668 -472
  14. package/lib/elementsRenderer.js +282 -197
  15. package/lib/jqtreeOptions.js +1 -2
  16. package/lib/keyHandler.js +134 -87
  17. package/lib/mouse.widget.js +285 -186
  18. package/lib/node.js +691 -505
  19. package/lib/nodeElement.js +329 -207
  20. package/lib/playwright/playwright.test.js +216 -189
  21. package/lib/playwright/testUtil.js +429 -193
  22. package/lib/playwright/visualRegression.js +182 -117
  23. package/lib/saveStateHandler.js +311 -204
  24. package/lib/scrollHandler.js +293 -199
  25. package/lib/selectNodeHandler.js +140 -100
  26. package/lib/simple.widget.js +184 -109
  27. package/lib/test/global.d.js +3 -0
  28. package/lib/test/jqTree/create.test.js +44 -40
  29. package/lib/test/jqTree/events.test.js +185 -138
  30. package/lib/test/jqTree/keyboard.test.js +216 -199
  31. package/lib/test/jqTree/loadOnDemand.test.js +233 -157
  32. package/lib/test/jqTree/methods.test.js +1269 -1019
  33. package/lib/test/jqTree/options.test.js +467 -398
  34. package/lib/test/node.test.js +1036 -873
  35. package/lib/test/nodeUtil.test.js +21 -20
  36. package/lib/test/support/exampleData.js +35 -23
  37. package/lib/test/support/jqTreeMatchers.js +72 -54
  38. package/lib/test/support/matchers.d.js +1 -0
  39. package/lib/test/support/setupTests.js +9 -3
  40. package/lib/test/support/testUtil.js +35 -15
  41. package/lib/test/support/treeStructure.js +41 -32
  42. package/lib/test/util.test.js +21 -20
  43. package/lib/tree.jquery.d.js +1 -0
  44. package/lib/tree.jquery.js +1264 -892
  45. package/lib/types.js +1 -2
  46. package/lib/typings.d.js +2 -0
  47. package/lib/util.js +19 -8
  48. package/lib/version.js +8 -3
  49. package/package.json +42 -34
  50. package/production +4 -4
  51. package/rollup.config.js +16 -11
  52. package/src/dataLoader.ts +6 -6
  53. package/src/dragAndDropHandler.ts +0 -4
  54. package/src/elementsRenderer.ts +4 -0
  55. package/src/jqtreeOptions.ts +1 -1
  56. package/src/mouse.widget.ts +19 -15
  57. package/src/node.ts +27 -41
  58. package/src/nodeElement.ts +17 -9
  59. package/src/playwright/.eslintrc +5 -0
  60. package/src/playwright/playwright.test.ts +29 -29
  61. package/src/saveStateHandler.ts +11 -6
  62. package/src/selectNodeHandler.ts +1 -1
  63. package/src/simple.widget.ts +1 -1
  64. package/src/test/.eslintrc +4 -0
  65. package/src/test/jqTree/create.test.ts +0 -1
  66. package/src/test/jqTree/events.test.ts +0 -1
  67. package/src/test/jqTree/keyboard.test.ts +0 -1
  68. package/src/test/jqTree/loadOnDemand.test.ts +46 -1
  69. package/src/test/jqTree/methods.test.ts +35 -10
  70. package/src/test/jqTree/options.test.ts +4 -5
  71. package/src/test/node.test.ts +2 -2
  72. package/src/test/support/jqTreeMatchers.ts +8 -9
  73. package/src/test/support/matchers.d.ts +2 -4
  74. package/src/test/support/setupTests.ts +2 -1
  75. package/src/tree.jquery.d.ts +18 -12
  76. package/src/tree.jquery.ts +25 -21
  77. package/src/version.ts +1 -1
  78. package/static/example.postcss +13 -0
  79. package/static/example_data.js +33 -36
  80. package/tree.jquery.debug.js +4806 -3325
  81. package/tree.jquery.debug.js.map +1 -1
  82. package/tree.jquery.js +2 -2
  83. package/tree.jquery.js.map +1 -1
  84. package/tsconfig.json +1 -0
package/lib/types.js CHANGED
@@ -1,2 +1 @@
1
- "use strict";
2
- exports.__esModule = true;
1
+ "use strict";
@@ -0,0 +1,2 @@
1
+ /// <reference path="./tree.jquery.d.ts" />
2
+ "use strict";
package/lib/util.js CHANGED
@@ -1,13 +1,24 @@
1
1
  "use strict";
2
- exports.__esModule = true;
3
- exports.getBoolString = exports.isFunction = exports.isInt = void 0;
4
- var isInt = function (n) {
5
- return typeof n === "number" && n % 1 === 0;
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isInt = exports.isFunction = exports.getBoolString = void 0;
7
+
8
+ var isInt = function isInt(n) {
9
+ return typeof n === "number" && n % 1 === 0;
6
10
  };
11
+
7
12
  exports.isInt = isInt;
8
- var isFunction = function (v) { return typeof v === "function"; };
13
+
14
+ var isFunction = function isFunction(v) {
15
+ return typeof v === "function";
16
+ };
17
+
9
18
  exports.isFunction = isFunction;
10
- var getBoolString = function (value) {
11
- return value ? "true" : "false";
19
+
20
+ var getBoolString = function getBoolString(value) {
21
+ return value ? "true" : "false";
12
22
  };
13
- exports.getBoolString = getBoolString;
23
+
24
+ exports.getBoolString = getBoolString;
package/lib/version.js CHANGED
@@ -1,4 +1,9 @@
1
1
  "use strict";
2
- exports.__esModule = true;
3
- var version = "1.6.1";
4
- exports["default"] = version;
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports["default"] = void 0;
7
+ var version = "1.6.2";
8
+ var _default = version;
9
+ exports["default"] = _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jqtree",
3
- "version": "1.6.1",
3
+ "version": "1.6.2",
4
4
  "description": "Tree widget for jQuery",
5
5
  "keywords": [
6
6
  "jquery-plugin",
@@ -14,6 +14,7 @@
14
14
  "url": "https://github.com/mbraak/jqtree"
15
15
  },
16
16
  "scripts": {
17
+ "ci": "pnpm run lint && yarn run tsc && pnpm run test",
17
18
  "test": "start-server-and-test 'yarn devserver-with-coverage' http://localhost:8080 'jest --runInBand --coverage --no-cache --verbose'",
18
19
  "test-with-server": "jest --runInBand --coverage",
19
20
  "test-watch": "jest --watch",
@@ -31,44 +32,51 @@
31
32
  "print-coverage": "nyc report"
32
33
  },
33
34
  "dependencies": {
34
- "jquery": ">=1.9"
35
+ "jquery": "^3.6.0"
35
36
  },
36
37
  "devDependencies": {
37
- "@rollup/plugin-typescript": "^8.0.0",
38
- "@testing-library/dom": "^7.21.7",
39
- "@types/jest": "^26.0.8",
40
- "@types/jquery": "^3.5.0",
41
- "@types/pngjs": "^6.0.0",
42
- "@typescript-eslint/eslint-plugin": "^4.0.1",
43
- "@typescript-eslint/parser": "^4.0.1",
44
- "autoprefixer": "^10.0.0",
45
- "coveralls": "^3.1.0",
46
- "eslint": "^7.3.1",
47
- "eslint-plugin-import": "^2.22.0",
48
- "eslint-plugin-jest": "^24.0.0",
49
- "expect-playwright": "^0.5.0",
50
- "givens": "^1.3.4",
51
- "jest": "^27.0.1",
52
- "jest-extended": "^0.11.5",
53
- "jest-playwright-preset": "^1.6.0",
54
- "jsonfile": "^6.0.1",
38
+ "@babel/cli": "^7.16.0",
39
+ "@babel/core": "^7.16.5",
40
+ "@babel/preset-env": "^7.16.5",
41
+ "@babel/preset-typescript": "^7.16.5",
42
+ "@rollup/plugin-babel": "^5.3.0",
43
+ "@rollup/plugin-node-resolve": "^13.1.1",
44
+ "@testing-library/dom": "^8.11.1",
45
+ "@types/jest": "^27.0.3",
46
+ "@types/jquery": "^3.5.10",
47
+ "@types/pngjs": "^6.0.1",
48
+ "@typescript-eslint/eslint-plugin": "^5.7.0",
49
+ "@typescript-eslint/parser": "^5.7.0",
50
+ "autoprefixer": "^10.4.0",
51
+ "babel-jest": "^27.4.5",
52
+ "babel-plugin-istanbul": "^6.1.1",
53
+ "coveralls": "^3.1.1",
54
+ "eslint": "^8.4.1",
55
+ "eslint-plugin-import": "^2.25.3",
56
+ "eslint-plugin-jest": "^25.3.0",
57
+ "eslint-plugin-playwright": "^0.6.0",
58
+ "eslint-plugin-testing-library": "^5.0.1",
59
+ "expect-playwright": "^0.8.0",
60
+ "givens": "^1.3.9",
61
+ "jest": "^27.4.5",
62
+ "jest-extended": "^1.2.0",
63
+ "jest-playwright-preset": "^1.7.0",
64
+ "jsonfile": "^6.1.0",
55
65
  "lodash.template": "^4.5.0",
56
- "msw": "^0.29.0",
66
+ "msw": "^0.36.3",
57
67
  "pixelmatch": "^5.2.1",
58
- "playwright": "^1.4.1",
68
+ "playwright": "^1.17.1",
59
69
  "pngjs": "^6.0.0",
60
- "postcss": "^8.0.7",
61
- "postcss-cli": "^8.0.0",
62
- "postcss-load-config": "^3.0.0",
63
- "postcss-nested": "^5.0.0",
64
- "prettier": "^2.0.5",
65
- "rollup": "^2.7.6",
66
- "rollup-plugin-istanbul2": "^2.0.2",
67
- "rollup-plugin-serve": "^1.0.4",
70
+ "postcss": "^8.4.5",
71
+ "postcss-cli": "^9.1.0",
72
+ "postcss-load-config": "^3.1.0",
73
+ "postcss-nested": "^5.0.6",
74
+ "prettier": "^2.5.1",
75
+ "rollup": "^2.61.1",
76
+ "rollup-plugin-serve": "^1.1.0",
68
77
  "rollup-plugin-terser": "^7.0.2",
69
- "start-server-and-test": "^1.11.3",
70
- "ts-jest": "^27.0.0",
71
- "tslib": "^2.0.1",
72
- "typescript": "^4.1.2"
78
+ "start-server-and-test": "^1.14.0",
79
+ "tslib": "^2.3.1",
80
+ "typescript": "^4.5.4"
73
81
  }
74
82
  }
package/production CHANGED
@@ -1,5 +1,5 @@
1
- rollup -c rollup.config.js
2
- DEBUG_BUILD=true rollup -c rollup.config.js
3
- tsc --outDir lib --noEmit false --project tsconfig.json &&
4
- postcss -o jqtree.css jqtree.postcss
1
+ rollup -c rollup.config.js &&
2
+ DEBUG_BUILD=true rollup -c rollup.config.js &&
3
+ babel src --out-dir lib --extensions .ts &&
4
+ postcss -o jqtree.css jqtree.postcss &&
5
5
  postcss -o static/example.css static/example.postcss
package/rollup.config.js CHANGED
@@ -2,10 +2,10 @@ import fs from "fs";
2
2
  import path from "path";
3
3
  import jsonfile from "jsonfile";
4
4
  import template from "lodash.template";
5
- import typescript from "@rollup/plugin-typescript";
6
- import { terser } from "rollup-plugin-terser";
5
+ import { babel } from "@rollup/plugin-babel";
6
+ import resolve from "@rollup/plugin-node-resolve";
7
7
  import serve from "rollup-plugin-serve";
8
- import coverage from "rollup-plugin-istanbul2";
8
+ import { terser } from "rollup-plugin-terser";
9
9
 
10
10
  const getBanner = () => {
11
11
  const headerTemplate = fs.readFileSync("./src/header.txt", "utf8");
@@ -24,7 +24,19 @@ const debugBuild = Boolean(process.env.DEBUG_BUILD);
24
24
  const devServer = Boolean(process.env.SERVE);
25
25
  const includeCoverage = Boolean(process.env.COVERAGE);
26
26
 
27
- const plugins = [typescript()];
27
+ const resolvePlugin = resolve({ extensions: [".ts"] });
28
+
29
+ const babelConfigFile = includeCoverage
30
+ ? "babel.coverage.config.json"
31
+ : "babel.config.json";
32
+
33
+ const babelPlugin = babel({
34
+ babelHelpers: "bundled",
35
+ configFile: path.resolve(__dirname, babelConfigFile),
36
+ extensions: [".ts"],
37
+ });
38
+
39
+ const plugins = [resolvePlugin, babelPlugin];
28
40
 
29
41
  if (!debugBuild) {
30
42
  const terserPlugin = terser({
@@ -35,13 +47,6 @@ if (!debugBuild) {
35
47
  plugins.push(terserPlugin);
36
48
  }
37
49
 
38
- if (includeCoverage) {
39
- const coveragePlugin = coverage({
40
- esModules: true,
41
- });
42
- plugins.push(coveragePlugin);
43
- }
44
-
45
50
  if (devServer) {
46
51
  const servePlugin = serve({
47
52
  contentBase: [
package/src/dataLoader.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { DefaultRecord, Node, NodeData } from "./node";
1
+ import { Node } from "./node";
2
2
  import { JqTreeWidget } from "./tree.jquery";
3
3
 
4
4
  export type HandleFinishedLoading = () => void;
@@ -28,7 +28,7 @@ export default class DataLoader {
28
28
  this.notifyLoading(false, parentNode, $el);
29
29
  };
30
30
 
31
- const handleSuccess = (data: any): void => {
31
+ const handleSuccess = (data: string | NodeData[]): void => {
32
32
  stopLoading();
33
33
  this.treeWidget.loadData(this.parseData(data), parentNode);
34
34
 
@@ -108,12 +108,12 @@ export default class DataLoader {
108
108
  void jQuery.ajax(ajaxSettings);
109
109
  }
110
110
 
111
- private parseData(data: NodeData): NodeData[] {
111
+ private parseData(data: string | NodeData[]): NodeData[] {
112
112
  const { dataFilter } = this.treeWidget.options;
113
113
 
114
- const getParsedData = (): unknown => {
114
+ const getParsedData = () => {
115
115
  if (typeof data === "string") {
116
- return JSON.parse(data) as unknown;
116
+ return JSON.parse(data) as NodeData[];
117
117
  } else {
118
118
  return data;
119
119
  }
@@ -124,7 +124,7 @@ export default class DataLoader {
124
124
  if (dataFilter) {
125
125
  return dataFilter(parsedData);
126
126
  } else {
127
- return parsedData as DefaultRecord[];
127
+ return parsedData;
128
128
  }
129
129
  }
130
130
  }
@@ -1,12 +1,8 @@
1
- import * as jQueryProxy from "jquery";
2
1
  import { getPositionName, Node, Position } from "./node";
3
2
  import { DropHint, HitArea, PositionInfo } from "./types";
4
3
  import { NodeElement } from "./nodeElement";
5
4
  import { JqTreeWidget } from "./tree.jquery";
6
5
 
7
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
8
- const jQuery: JQueryStatic = (<any>jQueryProxy).default || jQueryProxy;
9
-
10
6
  interface Dimensions {
11
7
  left: number;
12
8
  top: number;
@@ -248,6 +248,10 @@ export default class ElementsRenderer {
248
248
  classes += " jqtree-title-folder";
249
249
  }
250
250
 
251
+ classes += ` jqtree-title-button-${
252
+ this.treeWidget.options.buttonLeft ? "left" : "right"
253
+ }`;
254
+
251
255
  titleSpan.className = classes;
252
256
 
253
257
  titleSpan.setAttribute("role", "treeitem");
@@ -1,4 +1,4 @@
1
- import { Node, NodeData } from "./node";
1
+ import { Node } from "./node";
2
2
 
3
3
  type CanMoveNodeTo = (
4
4
  node: Node,
@@ -22,7 +22,6 @@ const getPositionInfoFromTouch = (
22
22
  });
23
23
 
24
24
  abstract class MouseWidget<WidgetOptions> extends SimpleWidget<WidgetOptions> {
25
- public $el: JQuery<HTMLElement>;
26
25
  protected isMouseStarted: boolean;
27
26
  protected mouseDownInfo: PositionInfo | null;
28
27
  private mouseDelayTimer: number | null;
@@ -30,12 +29,15 @@ abstract class MouseWidget<WidgetOptions> extends SimpleWidget<WidgetOptions> {
30
29
 
31
30
  public init(): void {
32
31
  const element = this.$el.get(0);
33
- element.addEventListener("mousedown", this.mouseDown, {
34
- passive: false,
35
- });
36
- element.addEventListener("touchstart", this.touchStart, {
37
- passive: false,
38
- });
32
+
33
+ if (element) {
34
+ element.addEventListener("mousedown", this.mouseDown, {
35
+ passive: false,
36
+ });
37
+ element.addEventListener("touchstart", this.touchStart, {
38
+ passive: false,
39
+ });
40
+ }
39
41
 
40
42
  this.isMouseStarted = false;
41
43
  this.mouseDelayTimer = null;
@@ -46,15 +48,17 @@ abstract class MouseWidget<WidgetOptions> extends SimpleWidget<WidgetOptions> {
46
48
  public deinit(): void {
47
49
  const el = this.$el.get(0);
48
50
 
49
- // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
50
- (el as any).removeEventListener("mousedown", this.mouseDown, {
51
- passive: false,
52
- });
51
+ if (el) {
52
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
53
+ (el as any).removeEventListener("mousedown", this.mouseDown, {
54
+ passive: false,
55
+ });
53
56
 
54
- // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
55
- (el as any).removeEventListener("touchstart", this.touchStart, {
56
- passive: false,
57
- });
57
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
58
+ (el as any).removeEventListener("touchstart", this.touchStart, {
59
+ passive: false,
60
+ });
61
+ }
58
62
 
59
63
  this.removeMouseMoveEventListeners();
60
64
  }
package/src/node.ts CHANGED
@@ -1,7 +1,6 @@
1
- export type NodeId = number | string;
2
-
3
- export type DefaultRecord = Record<string, unknown>;
4
- export type NodeData = string | DefaultRecord;
1
+ interface NodeRecordWithChildren extends NodeRecord {
2
+ children: NodeData[];
3
+ }
5
4
 
6
5
  export enum Position {
7
6
  Before = 1,
@@ -34,6 +33,13 @@ export const getPositionName = (position: Position): string => {
34
33
  export const getPosition = (name: string): Position | undefined =>
35
34
  positionNames[name];
36
35
 
36
+ const isNodeRecordWithChildren = (
37
+ data: NodeData
38
+ ): data is NodeRecordWithChildren =>
39
+ typeof data === "object" &&
40
+ "children" in data &&
41
+ data["children"] instanceof Array;
42
+
37
43
  export class Node implements INode {
38
44
  public id?: NodeId;
39
45
  public name: string;
@@ -131,15 +137,11 @@ export class Node implements INode {
131
137
  const node = this.createNode(o);
132
138
  this.addChild(node);
133
139
 
134
- if (
135
- typeof o === "object" &&
136
- o["children"] &&
137
- o["children"] instanceof Array
138
- ) {
139
- if (o["children"].length === 0) {
140
+ if (isNodeRecordWithChildren(o)) {
141
+ if (o.children.length === 0) {
140
142
  node.isEmptyFolder = true;
141
143
  } else {
142
- node.loadFromData(o["children"]);
144
+ node.loadFromData(o.children);
143
145
  }
144
146
  }
145
147
  }
@@ -299,7 +301,7 @@ export class Node implements INode {
299
301
  /*
300
302
  Get the tree as data.
301
303
  */
302
- public getData(includeParent = false): DefaultRecord[] {
304
+ public getData(includeParent = false): NodeRecord[] {
303
305
  const getDataFromNodes = (nodes: Node[]): Record<string, unknown>[] => {
304
306
  return nodes.map((node) => {
305
307
  const tmpNode: Record<string, unknown> = {};
@@ -379,12 +381,10 @@ export class Node implements INode {
379
381
  this.parent.addChildAtPosition(node, childIndex + 1);
380
382
 
381
383
  if (
382
- typeof nodeInfo === "object" &&
383
- nodeInfo["children"] &&
384
- nodeInfo["children"] instanceof Array &&
385
- nodeInfo["children"].length
384
+ isNodeRecordWithChildren(nodeInfo) &&
385
+ nodeInfo.children.length
386
386
  ) {
387
- node.loadFromData(nodeInfo["children"]);
387
+ node.loadFromData(nodeInfo.children);
388
388
  }
389
389
 
390
390
  return node;
@@ -401,12 +401,10 @@ export class Node implements INode {
401
401
  this.parent.addChildAtPosition(node, childIndex);
402
402
 
403
403
  if (
404
- typeof nodeInfo === "object" &&
405
- nodeInfo["children"] &&
406
- nodeInfo["children"] instanceof Array &&
407
- nodeInfo["children"].length
404
+ isNodeRecordWithChildren(nodeInfo) &&
405
+ nodeInfo.children.length
408
406
  ) {
409
- node.loadFromData(nodeInfo["children"]);
407
+ node.loadFromData(nodeInfo.children);
410
408
  }
411
409
 
412
410
  return node;
@@ -445,13 +443,8 @@ export class Node implements INode {
445
443
  const node = this.createNode(nodeInfo);
446
444
  this.addChild(node);
447
445
 
448
- if (
449
- typeof nodeInfo === "object" &&
450
- nodeInfo["children"] &&
451
- nodeInfo["children"] instanceof Array &&
452
- nodeInfo["children"].length
453
- ) {
454
- node.loadFromData(nodeInfo["children"]);
446
+ if (isNodeRecordWithChildren(nodeInfo) && nodeInfo.children.length) {
447
+ node.loadFromData(nodeInfo.children);
455
448
  }
456
449
 
457
450
  return node;
@@ -461,13 +454,8 @@ export class Node implements INode {
461
454
  const node = this.createNode(nodeInfo);
462
455
  this.addChildAtPosition(node, 0);
463
456
 
464
- if (
465
- typeof nodeInfo === "object" &&
466
- nodeInfo["children"] &&
467
- nodeInfo["children"] instanceof Array &&
468
- nodeInfo["children"].length
469
- ) {
470
- node.loadFromData(nodeInfo["children"]);
457
+ if (isNodeRecordWithChildren(nodeInfo) && nodeInfo.children.length) {
458
+ node.loadFromData(nodeInfo.children);
471
459
  }
472
460
 
473
461
  return node;
@@ -641,12 +629,10 @@ export class Node implements INode {
641
629
  this.setData(nodeData);
642
630
 
643
631
  if (
644
- typeof nodeData === "object" &&
645
- nodeData["children"] &&
646
- nodeData["children"] instanceof Array &&
647
- nodeData["children"].length
632
+ isNodeRecordWithChildren(nodeData) &&
633
+ nodeData.children.length
648
634
  ) {
649
- addChildren(nodeData["children"]);
635
+ addChildren(nodeData.children);
650
636
  }
651
637
  };
652
638
 
@@ -18,10 +18,16 @@ export class NodeElement {
18
18
  this.treeWidget = treeWidget;
19
19
 
20
20
  if (!node.element) {
21
- node.element = this.treeWidget.element.get(0);
21
+ const element = this.treeWidget.element.get(0);
22
+
23
+ if (element) {
24
+ node.element = element;
25
+ }
22
26
  }
23
27
 
24
- this.$element = jQuery(node.element);
28
+ if (node.element) {
29
+ this.$element = jQuery(node.element);
30
+ }
25
31
  }
26
32
 
27
33
  public addDropHint(position: number): DropHint {
@@ -99,9 +105,8 @@ export class FolderElement extends NodeElement {
99
105
  const buttonEl = $button.get(0);
100
106
 
101
107
  if (buttonEl) {
102
- const icon = this.treeWidget.renderer.openedIconElement.cloneNode(
103
- true
104
- );
108
+ const icon =
109
+ this.treeWidget.renderer.openedIconElement.cloneNode(true);
105
110
 
106
111
  buttonEl.appendChild(icon);
107
112
  }
@@ -147,9 +152,8 @@ export class FolderElement extends NodeElement {
147
152
  const buttonEl = $button.get(0);
148
153
 
149
154
  if (buttonEl) {
150
- const icon = this.treeWidget.renderer.closedIconElement.cloneNode(
151
- true
152
- );
155
+ const icon =
156
+ this.treeWidget.renderer.closedIconElement.cloneNode(true);
153
157
 
154
158
  buttonEl.appendChild(icon);
155
159
  }
@@ -248,7 +252,11 @@ class GhostDropHint implements DropHint {
248
252
  }
249
253
 
250
254
  public moveInsideOpenFolder(): void {
251
- jQuery(this.node.children[0].element).before(this.$ghost);
255
+ const childElement = this.node.children[0].element;
256
+
257
+ if (childElement) {
258
+ jQuery(childElement).before(this.$ghost);
259
+ }
252
260
  }
253
261
 
254
262
  public moveInside(): void {
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": [
3
+ "plugin:playwright/jest-playwright"
4
+ ]
5
+ }
@@ -51,15 +51,15 @@ afterEach(async () => {
51
51
  });
52
52
 
53
53
  it("displays a tree", async () => {
54
- await expect(page).toHaveText("Saurischia");
55
- await expect(page).toHaveText("Ornithischians");
56
- await expect(page).toHaveText("Coelophysoids");
54
+ await expect(page).toMatchText(/.*Saurischia.*/);
55
+ await expect(page).toMatchText(/.*Ornithischians.*/);
56
+ await expect(page).toMatchText(/.*Coelophysoids.*/);
57
57
 
58
58
  await matchScreenshot("displays_a_tree");
59
59
  });
60
60
 
61
61
  it("selects a node", async () => {
62
- await expect(page).toHaveText("Saurischia");
62
+ await expect(page).toMatchText(/.*Saurischia.*/);
63
63
  const saurischia = await findNodeElement("Saurischia");
64
64
  await selectNode(saurischia);
65
65
  await expectToBeSelected(saurischia);
@@ -68,7 +68,7 @@ it("selects a node", async () => {
68
68
  });
69
69
 
70
70
  it("opens a node", async () => {
71
- await expect(page).toHaveText("Saurischia");
71
+ await expect(page).toMatchText(/.*Saurischia.*/);
72
72
 
73
73
  const theropods = await findNodeElement("Theropods");
74
74
  await expectToBeClosed(theropods);
@@ -84,30 +84,30 @@ describe("dragAndDrop", () => {
84
84
  it("moves a node", async () => {
85
85
  await dragAndDrop("Herrerasaurians", "Ornithischians");
86
86
 
87
- await getTreeStructure().then((structure) => {
88
- expect(structure).toEqual([
89
- expect.objectContaining({
90
- name: "Saurischia",
91
- children: [
92
- expect.objectContaining({ name: "Theropods" }),
93
- expect.objectContaining({ name: "Sauropodomorphs" }),
94
- ],
95
- }),
96
- expect.objectContaining({
97
- name: "Ornithischians",
98
- children: [
99
- expect.objectContaining({ name: "Herrerasaurians" }),
100
- expect.objectContaining({ name: "Heterodontosaurids" }),
101
- expect.objectContaining({ name: "Thyreophorans" }),
102
- expect.objectContaining({ name: "Ornithopods" }),
103
- expect.objectContaining({
104
- name: "Pachycephalosaurians",
105
- }),
106
- expect.objectContaining({ name: "Ceratopsians" }),
107
- ],
108
- }),
109
- ]);
110
- });
87
+ const structure = await getTreeStructure();
88
+
89
+ expect(structure).toEqual([
90
+ expect.objectContaining({
91
+ name: "Saurischia",
92
+ children: [
93
+ expect.objectContaining({ name: "Theropods" }),
94
+ expect.objectContaining({ name: "Sauropodomorphs" }),
95
+ ],
96
+ }),
97
+ expect.objectContaining({
98
+ name: "Ornithischians",
99
+ children: [
100
+ expect.objectContaining({ name: "Herrerasaurians" }),
101
+ expect.objectContaining({ name: "Heterodontosaurids" }),
102
+ expect.objectContaining({ name: "Thyreophorans" }),
103
+ expect.objectContaining({ name: "Ornithopods" }),
104
+ expect.objectContaining({
105
+ name: "Pachycephalosaurians",
106
+ }),
107
+ expect.objectContaining({ name: "Ceratopsians" }),
108
+ ],
109
+ }),
110
+ ]);
111
111
 
112
112
  await matchScreenshot("moves_a_node");
113
113
  });