react-native-tree-multi-select 1.2.1 → 1.2.3

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 (123) hide show
  1. package/.editorconfig +15 -0
  2. package/.gitattributes +3 -0
  3. package/.github/FUNDING.yml +13 -0
  4. package/.github/ISSUE_TEMPLATE/bug-report.md +42 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.md +30 -0
  6. package/.github/actions/setup/action.yml +28 -0
  7. package/.github/assets/Jairaj_Jangle_Google_Pay_UPI_QR_Code.jpg +0 -0
  8. package/.github/assets/paypal_donate.png +0 -0
  9. package/.github/assets/upi.png +0 -0
  10. package/.github/workflows/ci.yml +225 -0
  11. package/.gitignore +73 -0
  12. package/.nvmrc +1 -0
  13. package/.watchmanconfig +1 -0
  14. package/.yarnrc +3 -0
  15. package/CHANGELOG.md +145 -0
  16. package/CODE_OF_CONDUCT.md +133 -0
  17. package/CONTRIBUTING.md +114 -0
  18. package/babel.config.js +3 -0
  19. package/example/.bundle/config +2 -0
  20. package/example/.watchmanconfig +1 -0
  21. package/example/Gemfile +6 -0
  22. package/example/README.md +79 -0
  23. package/example/android/app/build.gradle +130 -0
  24. package/example/android/app/debug.keystore +0 -0
  25. package/example/android/app/proguard-rules.pro +10 -0
  26. package/example/android/app/src/debug/AndroidManifest.xml +13 -0
  27. package/example/android/app/src/debug/java/com/treemultiselectexample/ReactNativeFlipper.java +75 -0
  28. package/example/android/app/src/main/AndroidManifest.xml +25 -0
  29. package/example/android/app/src/main/java/com/treemultiselectexample/MainActivity.java +32 -0
  30. package/example/android/app/src/main/java/com/treemultiselectexample/MainApplication.java +62 -0
  31. package/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +36 -0
  32. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  33. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  34. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  35. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  36. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  37. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  38. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  39. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  40. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  41. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  42. package/example/android/app/src/main/res/values/strings.xml +3 -0
  43. package/example/android/app/src/main/res/values/styles.xml +9 -0
  44. package/example/android/app/src/release/java/com/treemultiselectexample/ReactNativeFlipper.java +20 -0
  45. package/example/android/build.gradle +23 -0
  46. package/example/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  47. package/example/android/gradle/wrapper/gradle-wrapper.properties +6 -0
  48. package/example/android/gradle.properties +44 -0
  49. package/example/android/gradlew +244 -0
  50. package/example/android/gradlew.bat +92 -0
  51. package/example/android/settings.gradle +4 -0
  52. package/example/app.json +4 -0
  53. package/example/babel.config.js +17 -0
  54. package/example/index.js +5 -0
  55. package/example/ios/.xcode.env +11 -0
  56. package/example/ios/File.swift +6 -0
  57. package/example/ios/Podfile +62 -0
  58. package/example/ios/Podfile.lock +639 -0
  59. package/example/ios/TreeMultiSelectExample/AppDelegate.h +6 -0
  60. package/example/ios/TreeMultiSelectExample/AppDelegate.mm +26 -0
  61. package/example/ios/TreeMultiSelectExample/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
  62. package/example/ios/TreeMultiSelectExample/Images.xcassets/Contents.json +6 -0
  63. package/example/ios/TreeMultiSelectExample/Info.plist +74 -0
  64. package/example/ios/TreeMultiSelectExample/LaunchScreen.storyboard +47 -0
  65. package/example/ios/TreeMultiSelectExample/main.m +10 -0
  66. package/example/ios/TreeMultiSelectExample-Bridging-Header.h +3 -0
  67. package/example/ios/TreeMultiSelectExample.xcodeproj/project.pbxproj +706 -0
  68. package/example/ios/TreeMultiSelectExample.xcodeproj/xcshareddata/xcschemes/TreeMultiSelectExample.xcscheme +88 -0
  69. package/example/ios/TreeMultiSelectExample.xcworkspace/contents.xcworkspacedata +10 -0
  70. package/example/ios/TreeMultiSelectExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  71. package/example/ios/TreeMultiSelectExampleTests/Info.plist +24 -0
  72. package/example/ios/TreeMultiSelectExampleTests/TreeMultiSelectExampleTests.m +66 -0
  73. package/example/metro.config.js +45 -0
  74. package/example/package.json +41 -0
  75. package/example/react-native.config.js +10 -0
  76. package/example/src/App.tsx +70 -0
  77. package/example/src/components/CustomArrow.tsx +71 -0
  78. package/example/src/components/CustomCheckboxView.tsx +119 -0
  79. package/example/src/components/CustomNodeRowView.tsx +124 -0
  80. package/example/src/components/SearchInput.tsx +68 -0
  81. package/example/src/screens/CustomArrowScreen.tsx +99 -0
  82. package/example/src/screens/CustomCheckboxScreen.tsx +99 -0
  83. package/example/src/screens/CustomNodeRowViewScreen.tsx +99 -0
  84. package/example/src/screens/LargeDataScreen.tsx +95 -0
  85. package/example/src/screens/MediumDataScreen.tsx +96 -0
  86. package/example/src/screens/SmallDataScreen.tsx +96 -0
  87. package/example/src/screens/screens.styles.ts +25 -0
  88. package/example/src/utils/sampleDataGenerator.ts +32 -0
  89. package/example/yarn.lock +6368 -0
  90. package/lefthook.yml +17 -0
  91. package/lib/typescript/__mocks__/generateTree.mock.d.ts +17 -0
  92. package/lib/typescript/__mocks__/generateTree.mock.d.ts.map +1 -0
  93. package/lib/typescript/__mocks__/zustand.d.ts +3 -0
  94. package/lib/typescript/__mocks__/zustand.d.ts.map +1 -0
  95. package/lib/typescript/__tests__/expandCollapse.helper.test.d.ts +2 -0
  96. package/lib/typescript/__tests__/expandCollapse.helper.test.d.ts.map +1 -0
  97. package/lib/typescript/__tests__/flattenTree.helper.test.d.ts +2 -0
  98. package/lib/typescript/__tests__/flattenTree.helper.test.d.ts.map +1 -0
  99. package/lib/typescript/__tests__/initNodeMap.helper.test.d.ts +2 -0
  100. package/lib/typescript/__tests__/initNodeMap.helper.test.d.ts.map +1 -0
  101. package/lib/typescript/__tests__/search.helper.test.d.ts +2 -0
  102. package/lib/typescript/__tests__/search.helper.test.d.ts.map +1 -0
  103. package/lib/typescript/__tests__/selectAll.helper.test.d.ts +2 -0
  104. package/lib/typescript/__tests__/selectAll.helper.test.d.ts.map +1 -0
  105. package/lib/typescript/__tests__/store.test.d.ts +2 -0
  106. package/lib/typescript/__tests__/store.test.d.ts.map +1 -0
  107. package/lib/typescript/__tests__/toggleCheckbox.helper.test.d.ts +2 -0
  108. package/lib/typescript/__tests__/toggleCheckbox.helper.test.d.ts.map +1 -0
  109. package/package.json +4 -2
  110. package/scripts/bootstrap.js +29 -0
  111. package/src/__mocks__/generateTree.mock.ts +125 -0
  112. package/src/__mocks__/zustand.ts +24 -0
  113. package/src/__tests__/expandCollapse.helper.test.ts +189 -0
  114. package/src/__tests__/flattenTree.helper.test.ts +78 -0
  115. package/src/__tests__/initNodeMap.helper.test.ts +46 -0
  116. package/src/__tests__/search.helper.test.ts +47 -0
  117. package/src/__tests__/selectAll.helper.test.ts +233 -0
  118. package/src/__tests__/store.test.ts +208 -0
  119. package/src/__tests__/toggleCheckbox.helper.test.ts +124 -0
  120. package/tsconfig.build.json +5 -0
  121. package/tsconfig.json +31 -0
  122. package/turbo.json +34 -0
  123. package/yarn.lock +9953 -0
package/lefthook.yml ADDED
@@ -0,0 +1,17 @@
1
+ pre-commit:
2
+ parallel: true
3
+ commands:
4
+ test:
5
+ run: yarn test
6
+ lint:
7
+ glob: "*.{js,ts,jsx,tsx}"
8
+ run: yarn lint
9
+ types:
10
+ files: git diff --name-only @{push}
11
+ glob: "*.{js,ts,jsx,tsx}"
12
+ run: npx tsc --noEmit
13
+ commit-msg:
14
+ parallel: true
15
+ commands:
16
+ commitlint:
17
+ run: npx commitlint --edit
@@ -0,0 +1,17 @@
1
+ import { TreeNode, __FlattenedTreeNode__ } from "../types/treeView.types";
2
+ export declare const tree3d2b: {
3
+ id: string;
4
+ name: string;
5
+ children: {
6
+ id: string;
7
+ name: string;
8
+ children: {
9
+ id: string;
10
+ name: string;
11
+ }[];
12
+ }[];
13
+ }[];
14
+ export declare function generateTree(depth: number, breadth: number, prefix?: string): TreeNode[];
15
+ export declare function generateExpectedFlatTree(nodes: TreeNode[], expandedIds: Set<string>, level?: number): __FlattenedTreeNode__[];
16
+ export declare function createRandomNumberSet(): Set<string>;
17
+ //# sourceMappingURL=generateTree.mock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateTree.mock.d.ts","sourceRoot":"","sources":["../../../src/__mocks__/generateTree.mock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAE1E,eAAO,MAAM,QAAQ;;;;;;;;;;;GAqEpB,CAAC;AAEF,wBAAgB,YAAY,CACxB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAAW,GACpB,QAAQ,EAAE,CAiBZ;AAED,wBAAgB,wBAAwB,CACpC,KAAK,EAAE,QAAQ,EAAE,EACjB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EACxB,KAAK,GAAE,MAAU,GAClB,qBAAqB,EAAE,CAYzB;AAED,wBAAgB,qBAAqB,gBAUpC"}
@@ -0,0 +1,3 @@
1
+ import { StateCreator } from 'zustand';
2
+ export declare const create: <S>(createState: StateCreator<S>) => any;
3
+ //# sourceMappingURL=zustand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zustand.d.ts","sourceRoot":"","sources":["../../../src/__mocks__/zustand.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAQvC,eAAO,MAAM,MAAM,mBAAoB,aAAa,CAAC,CAAC,QAKrD,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=expandCollapse.helper.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expandCollapse.helper.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/expandCollapse.helper.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=flattenTree.helper.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flattenTree.helper.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/flattenTree.helper.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=initNodeMap.helper.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initNodeMap.helper.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/initNodeMap.helper.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=search.helper.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.helper.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/search.helper.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=selectAll.helper.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selectAll.helper.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/selectAll.helper.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=store.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/store.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=toggleCheckbox.helper.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toggleCheckbox.helper.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/toggleCheckbox.helper.test.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-tree-multi-select",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "Super-fast Tree view with multi-selection capabilities, using checkboxes and search filtering.",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -37,7 +37,8 @@
37
37
  "build:android": "cd example/android && ./gradlew assembleDebug --no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a",
38
38
  "build:ios": "cd example/ios && pod install && xcodebuild -workspace TreeMultiSelectExample.xcworkspace -scheme TreeMultiSelectExample -configuration Debug -sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO",
39
39
  "bootstrap": "yarn example && yarn install && yarn example pods",
40
- "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build"
40
+ "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build",
41
+ "generate-changelog": "auto-changelog"
41
42
  },
42
43
  "keywords": [
43
44
  "react-native",
@@ -88,6 +89,7 @@
88
89
  "@types/react-native": "0.70.0",
89
90
  "@types/react-native-vector-icons": "^6.4.13",
90
91
  "@types/react-test-renderer": "^18.0.0",
92
+ "auto-changelog": "^2.4.0",
91
93
  "commitlint": "^17.0.2",
92
94
  "del-cli": "^5.0.0",
93
95
  "eslint": "^8.4.1",
@@ -0,0 +1,29 @@
1
+ const os = require('os');
2
+ const path = require('path');
3
+ const child_process = require('child_process');
4
+
5
+ const root = path.resolve(__dirname, '..');
6
+ const args = process.argv.slice(2);
7
+ const options = {
8
+ cwd: process.cwd(),
9
+ env: process.env,
10
+ stdio: 'inherit',
11
+ encoding: 'utf-8',
12
+ };
13
+
14
+ if (os.type() === 'Windows_NT') {
15
+ options.shell = true;
16
+ }
17
+
18
+ let result;
19
+
20
+ if (process.cwd() !== root || args.length) {
21
+ // We're not in the root of the project, or additional arguments were passed
22
+ // In this case, forward the command to `yarn`
23
+ result = child_process.spawnSync('yarn', args, options);
24
+ } else {
25
+ // If `yarn` is run without arguments, perform bootstrap
26
+ result = child_process.spawnSync('yarn', ['bootstrap'], options);
27
+ }
28
+
29
+ process.exitCode = result.status;
@@ -0,0 +1,125 @@
1
+ import { TreeNode, __FlattenedTreeNode__ } from "../types/treeView.types";
2
+
3
+ export const tree3d2b = [
4
+ {
5
+ "id": "1",
6
+ "name": "node1",
7
+ "children": [
8
+ {
9
+ "id": "1.1",
10
+ "name": "node1.1",
11
+ "children": [
12
+ {
13
+ "id": "1.1.1",
14
+ "name": "node1.1.1"
15
+ },
16
+ {
17
+ "id": "1.1.2",
18
+ "name": "node1.1.2"
19
+ }
20
+ ]
21
+ },
22
+ {
23
+ "id": "1.2",
24
+ "name": "node1.2",
25
+ "children": [
26
+ {
27
+ "id": "1.2.1",
28
+ "name": "node1.2.1"
29
+ },
30
+ {
31
+ "id": "1.2.2",
32
+ "name": "node1.2.2"
33
+ }
34
+ ]
35
+ }
36
+ ]
37
+ },
38
+ {
39
+ "id": "2",
40
+ "name": "node2",
41
+ "children": [
42
+ {
43
+ "id": "2.1",
44
+ "name": "node2.1",
45
+ "children": [
46
+ {
47
+ "id": "2.1.1",
48
+ "name": "node2.1.1"
49
+ },
50
+ {
51
+ "id": "2.1.2",
52
+ "name": "node2.1.2"
53
+ }
54
+ ]
55
+ },
56
+ {
57
+ "id": "2.2",
58
+ "name": "node2.2",
59
+ "children": [
60
+ {
61
+ "id": "2.2.1",
62
+ "name": "node2.2.1"
63
+ },
64
+ {
65
+ "id": "2.2.2",
66
+ "name": "node2.2.2"
67
+ }
68
+ ]
69
+ }
70
+ ]
71
+ }
72
+ ];
73
+
74
+ export function generateTree(
75
+ depth: number,
76
+ breadth: number,
77
+ prefix: string = ''
78
+ ): TreeNode[] {
79
+ let nodes: TreeNode[] = [];
80
+
81
+ for (let i = 1; i <= breadth; i++) {
82
+ let node: TreeNode = {
83
+ id: `${prefix}${i}`,
84
+ name: `node${prefix}${i}`
85
+ };
86
+
87
+ if (depth > 1) {
88
+ node.children = generateTree(depth - 1, breadth, `${prefix}${i}.`);
89
+ }
90
+
91
+ nodes.push(node);
92
+ }
93
+
94
+ return nodes;
95
+ }
96
+
97
+ export function generateExpectedFlatTree(
98
+ nodes: TreeNode[],
99
+ expandedIds: Set<string>,
100
+ level: number = 0
101
+ ): __FlattenedTreeNode__[] {
102
+ let flattened: __FlattenedTreeNode__[] = [];
103
+ for (let node of nodes) {
104
+ flattened.push({ ...node, level: level });
105
+ if (node.children && expandedIds.has(node.id)) {
106
+ flattened = [
107
+ ...flattened,
108
+ ...generateExpectedFlatTree(node.children, expandedIds, level + 1)
109
+ ];
110
+ }
111
+ }
112
+ return flattened;
113
+ }
114
+
115
+ export function createRandomNumberSet() {
116
+ let randomNumbers = new Set<string>();
117
+
118
+ while (randomNumbers.size < 10) {
119
+ // Random number between 0 and 99
120
+ let randomNumber = Math.floor(Math.random() * 100);
121
+ randomNumbers.add(randomNumber.toString());
122
+ }
123
+
124
+ return randomNumbers;
125
+ }
@@ -0,0 +1,24 @@
1
+ import { act } from '@testing-library/react-native';
2
+ import { StateCreator } from 'zustand';
3
+
4
+ const { create: actualCreate } = jest.requireActual('zustand');
5
+
6
+ // a variable to hold reset functions for all stores declared in the app
7
+ const storeResetFns = new Set<() => void>();
8
+
9
+ // when creating a store, we get its initial state, create a reset function and add it in the set
10
+ export const create = <S>(createState: StateCreator<S>) => {
11
+ const store = actualCreate(createState);
12
+ const initialState = store.getState();
13
+ storeResetFns.add(() => store.setState(initialState, true));
14
+ return store;
15
+ };
16
+
17
+ // Reset all stores after each test run
18
+ beforeEach(async () => {
19
+ await act(() =>
20
+ storeResetFns.forEach(resetFn => {
21
+ resetFn();
22
+ })
23
+ );
24
+ });
@@ -0,0 +1,189 @@
1
+ jest.mock('zustand');
2
+
3
+ import { useTreeViewStore } from '../store/treeView.store';
4
+ import { tree3d2b } from "../__mocks__/generateTree.mock";
5
+ import {
6
+ collapseAll,
7
+ collapseNodes,
8
+ expandAll,
9
+ expandNodes,
10
+ handleToggleExpand,
11
+ initializeNodeMaps
12
+ } from '../helpers';
13
+ import { act } from 'react-test-renderer';
14
+
15
+ describe('handleToggleExpand', () => {
16
+ beforeEach(() => {
17
+ useTreeViewStore.setState(useTreeViewStore.getState(), true);
18
+
19
+ useTreeViewStore.getState().updateInitialTreeViewData(tree3d2b);
20
+ initializeNodeMaps(tree3d2b);
21
+ });
22
+
23
+ test('handleToggleExpand correctly toggles the expanded state of a tree node', () => {
24
+ act(() => {
25
+ handleToggleExpand('1');
26
+ });
27
+
28
+ // Node '1' should now be expanded
29
+ let { expanded } = useTreeViewStore.getState();
30
+ expect(expanded.has('1')).toBeTruthy();
31
+
32
+ act(() => {
33
+ handleToggleExpand('1.2');
34
+ });
35
+
36
+ // Node '1.2' should now be expanded, Node '1'(parent) should remain expanded
37
+ expanded = useTreeViewStore.getState().expanded;
38
+ expect(expanded.has('1.2')).toBeTruthy();
39
+ expect(expanded.has('1')).toBeTruthy();
40
+
41
+ act(() => {
42
+ handleToggleExpand('1');
43
+ handleToggleExpand('2');
44
+ });
45
+
46
+ // Node '1' and its descendants should now be collapsed but Node '2' should remain expanded
47
+ expanded = useTreeViewStore.getState().expanded;
48
+
49
+ expect(expanded.has('1')).toBeFalsy();
50
+ expect(expanded.has('1.1')).toBeFalsy();
51
+ expect(expanded.has('1.2')).toBeFalsy();
52
+ expect(expanded.has('1.2.1')).toBeFalsy();
53
+
54
+ expect(expanded.has('2')).toBeTruthy();
55
+ });
56
+ });
57
+
58
+ describe('expandAll', () => {
59
+ beforeEach(() => {
60
+ useTreeViewStore.setState(useTreeViewStore.getState(), true);
61
+
62
+ // Setup mock tree
63
+ useTreeViewStore.getState().updateInitialTreeViewData(tree3d2b);
64
+ initializeNodeMaps(tree3d2b);
65
+ });
66
+
67
+ it('calls expandAll on initial tree(all collapsed)', () => {
68
+ act(() => {
69
+ expandAll();
70
+ });
71
+
72
+ const { expanded, nodeMap } = useTreeViewStore.getState();
73
+
74
+ // Convert nodeMap.keys() iterator to a Set for comparison
75
+ const nodeKeys = new Set(nodeMap.keys());
76
+ expect(expanded).toEqual(nodeKeys);
77
+ });
78
+
79
+ it('expands all node in tree with some nodes which are already expanded', () => {
80
+ act(() => {
81
+ handleToggleExpand('1');
82
+ handleToggleExpand('2');
83
+ handleToggleExpand('1.1');
84
+ handleToggleExpand('1.2');
85
+ handleToggleExpand('1.1');
86
+
87
+ expandAll();
88
+ });
89
+
90
+ const { expanded, nodeMap } = useTreeViewStore.getState();
91
+
92
+ // Convert nodeMap.keys() iterator to a Set for comparison
93
+ const nodeKeys = new Set(nodeMap.keys());
94
+ expect(expanded).toEqual(nodeKeys);
95
+ });
96
+ });
97
+
98
+ describe('collapseAll', () => {
99
+ beforeEach(() => {
100
+ useTreeViewStore.setState(useTreeViewStore.getState(), true);
101
+
102
+ // Setup mock tree
103
+ useTreeViewStore.getState().updateInitialTreeViewData(tree3d2b);
104
+ initializeNodeMaps(tree3d2b);
105
+ });
106
+
107
+ it('calls collapseAll on initial tree(all collapsed)', () => {
108
+ act(() => {
109
+ collapseAll();
110
+ });
111
+
112
+ const { expanded } = useTreeViewStore.getState();
113
+ expect(expanded).toEqual(new Set<string>());
114
+ });
115
+
116
+ it('collapses all node in tree with some nodes which are already expanded', () => {
117
+ act(() => {
118
+ handleToggleExpand('1');
119
+ handleToggleExpand('2');
120
+ handleToggleExpand('1.1');
121
+ handleToggleExpand('1.2');
122
+ handleToggleExpand('1.1');
123
+
124
+ collapseAll();
125
+ });
126
+
127
+ const { expanded } = useTreeViewStore.getState();
128
+
129
+ expect(expanded).toEqual(new Set<string>());
130
+ });
131
+ });
132
+
133
+ describe('expandNodes & collapseNodes', () => {
134
+ beforeEach(() => {
135
+ useTreeViewStore.setState(useTreeViewStore.getState(), true);
136
+
137
+ useTreeViewStore.getState().updateInitialTreeViewData(tree3d2b);
138
+ initializeNodeMaps(tree3d2b);
139
+ });
140
+
141
+ it('expands and then collapses multiple nodes as needed', () => {
142
+ act(() => {
143
+ expandNodes(["1", "2.1", "2.2.2"]);
144
+ });
145
+
146
+ const { expanded: expandedAfterExpandNodes } = useTreeViewStore.getState();
147
+ // Both nodes and their parents should be expanded
148
+ expect(expandedAfterExpandNodes.has("1")).toBeTruthy();
149
+ expect(expandedAfterExpandNodes.has("1.1")).toBeFalsy();
150
+ expect(expandedAfterExpandNodes.has("1.1.1")).toBeFalsy();
151
+ expect(expandedAfterExpandNodes.has("1.1.2")).toBeFalsy();
152
+ expect(expandedAfterExpandNodes.has("1.2")).toBeFalsy();
153
+ expect(expandedAfterExpandNodes.has("1.2.1")).toBeFalsy();
154
+ expect(expandedAfterExpandNodes.has("1.2.2")).toBeFalsy();
155
+
156
+ expect(expandedAfterExpandNodes.has("2")).toBeTruthy();
157
+ expect(expandedAfterExpandNodes.has("2.1")).toBeTruthy();
158
+ expect(expandedAfterExpandNodes.has("2.1.1")).toBeFalsy();
159
+ expect(expandedAfterExpandNodes.has("2.1.2")).toBeFalsy();
160
+
161
+ expect(expandedAfterExpandNodes.has("2.2")).toBeTruthy();
162
+ expect(expandedAfterExpandNodes.has("2.2.1")).toBeFalsy();
163
+ expect(expandedAfterExpandNodes.has("2.2.2")).toBeTruthy();
164
+
165
+ // Then collapse the same nodes
166
+ act(() => {
167
+ collapseNodes(["1", "2.2"]);
168
+ });
169
+
170
+ const { expanded: expandedAfterCollapseNodes } = useTreeViewStore.getState();
171
+ // Both nodes and their children should be collapsed
172
+ expect(expandedAfterCollapseNodes.has("1")).toBeFalsy();
173
+ expect(expandedAfterCollapseNodes.has("1.1")).toBeFalsy();
174
+ expect(expandedAfterCollapseNodes.has("1.1.1")).toBeFalsy();
175
+ expect(expandedAfterCollapseNodes.has("1.1.2")).toBeFalsy();
176
+ expect(expandedAfterCollapseNodes.has("1.2")).toBeFalsy();
177
+ expect(expandedAfterCollapseNodes.has("1.2.1")).toBeFalsy();
178
+ expect(expandedAfterCollapseNodes.has("1.2.2")).toBeFalsy();
179
+
180
+ expect(expandedAfterCollapseNodes.has("2")).toBeTruthy();
181
+ expect(expandedAfterCollapseNodes.has("2.1")).toBeTruthy();
182
+ expect(expandedAfterCollapseNodes.has("2.1.1")).toBeFalsy();
183
+ expect(expandedAfterCollapseNodes.has("2.1.2")).toBeFalsy();
184
+
185
+ expect(expandedAfterCollapseNodes.has("2.2")).toBeFalsy();
186
+ expect(expandedAfterCollapseNodes.has("2.2.1")).toBeFalsy();
187
+ expect(expandedAfterCollapseNodes.has("2.2.2")).toBeFalsy();
188
+ });
189
+ });
@@ -0,0 +1,78 @@
1
+ import { getFlattenedTreeData } from "../helpers";
2
+ import { generateExpectedFlatTree, tree3d2b } from "../__mocks__/generateTree.mock";
3
+ import { TreeNode } from "../types/treeView.types";
4
+
5
+ describe('getFlattenedTreeData', () => {
6
+ let nodes: TreeNode[];
7
+ let expandedIds: Set<string>;
8
+
9
+ beforeEach(() => {
10
+ nodes = tree3d2b;
11
+ expandedIds = new Set<string>();
12
+ });
13
+
14
+ // Hardcoded test
15
+ test('should correctly flatten a simple tree', () => {
16
+ const simpleNodes: TreeNode[] = [{
17
+ id: '1',
18
+ name: 'node1',
19
+ children: [
20
+ {
21
+ id: '1.1', name: 'node1.1', children: [
22
+ { id: '1.1.1', name: 'node1.1.1' },
23
+ { id: '1.1.2', name: 'node1.1.2' },
24
+ { id: '1.1.3', name: 'node1.1.3' },
25
+ ]
26
+ },
27
+ {
28
+ id: '1.2', name: 'node1.2', children: [
29
+ { id: '1.2.1', name: 'node1.2.1' },
30
+ { id: '1.2.2', name: 'node1.2.2' },
31
+ { id: '1.2.3', name: 'node1.2.3' },
32
+ ]
33
+ },
34
+ {
35
+ id: '1.3', name: 'node1.3', children: [
36
+ { id: '1.3.1', name: 'node1.3.1' },
37
+ { id: '1.3.2', name: 'node1.3.2' },
38
+ { id: '1.3.3', name: 'node1.3.3' },
39
+ ]
40
+ },
41
+ ],
42
+ }];
43
+ const simpleExpandedIds = new Set(['1', '1.1', '1.2', '1.3']);
44
+ const result = getFlattenedTreeData(simpleNodes, simpleExpandedIds);
45
+ expect(result).toEqual([
46
+ { id: '1', name: 'node1', level: 0, children: simpleNodes?.[0]?.children },
47
+ { id: '1.1', name: 'node1.1', level: 1, children: simpleNodes?.[0]?.children?.[0]?.children },
48
+ { id: '1.1.1', name: 'node1.1.1', level: 2 },
49
+ { id: '1.1.2', name: 'node1.1.2', level: 2 },
50
+ { id: '1.1.3', name: 'node1.1.3', level: 2 },
51
+ { id: '1.2', name: 'node1.2', level: 1, children: simpleNodes?.[0]?.children?.[1]?.children },
52
+ { id: '1.2.1', name: 'node1.2.1', level: 2 },
53
+ { id: '1.2.2', name: 'node1.2.2', level: 2 },
54
+ { id: '1.2.3', name: 'node1.2.3', level: 2 },
55
+ { id: '1.3', name: 'node1.3', level: 1, children: simpleNodes?.[0]?.children?.[2]?.children },
56
+ { id: '1.3.1', name: 'node1.3.1', level: 2 },
57
+ { id: '1.3.2', name: 'node1.3.2', level: 2 },
58
+ { id: '1.3.3', name: 'node1.3.3', level: 2 },
59
+ ]);
60
+ });
61
+
62
+ // Dynamic test
63
+ test('should return a flat array of nodes with correct level property', () => {
64
+ expandedIds.add('1');
65
+ expandedIds.add('1.1');
66
+ expandedIds.add('1.2');
67
+ expandedIds.add('1.3');
68
+ const result = getFlattenedTreeData(nodes, expandedIds);
69
+ const expected = generateExpectedFlatTree(nodes, expandedIds);
70
+ expect(result).toEqual(expected);
71
+ });
72
+
73
+ // Edge case: tree with no expanded nodes
74
+ test('should return a flat array of root nodes when no nodes are expanded', () => {
75
+ const result = getFlattenedTreeData(nodes, new Set());
76
+ expect(result).toEqual(nodes.map(node => ({ ...node, level: 0 })));
77
+ });
78
+ });
@@ -0,0 +1,46 @@
1
+ jest.mock('zustand');
2
+
3
+ import { act } from 'react-test-renderer';
4
+ import { useTreeViewStore } from '../store/treeView.store';
5
+ import { TreeNode } from 'src/types/treeView.types';
6
+ import { initializeNodeMaps } from '../helpers';
7
+
8
+ describe('initNodeMap helper', () => {
9
+ beforeEach(() => {
10
+ useTreeViewStore.setState(useTreeViewStore.getState(), true);
11
+ });
12
+
13
+ test('initializeNodeMaps correctly initializes the node maps', () => {
14
+ const initialData: TreeNode[] = [
15
+ {
16
+ id: '1',
17
+ name: 'Node 1',
18
+ children: [
19
+ { id: '1.1', name: 'Node 1.1' },
20
+ { id: '1.2', name: 'Node 1.2', children: [{ id: '1.2.1', name: 'Node 1.2.1' }] },
21
+ ],
22
+ },
23
+ { id: '2', name: 'Node 2' },
24
+ ];
25
+
26
+ act(() => {
27
+ initializeNodeMaps(initialData);
28
+ });
29
+
30
+ const { nodeMap, childToParentMap } = useTreeViewStore.getState();
31
+
32
+ // The nodeMap should contain all nodes, regardless of depth
33
+ expect(nodeMap.has('1')).toBeTruthy();
34
+ expect(nodeMap.has('1.1')).toBeTruthy();
35
+ expect(nodeMap.has('1.2')).toBeTruthy();
36
+ expect(nodeMap.has('1.2.1')).toBeTruthy();
37
+ expect(nodeMap.has('2')).toBeTruthy();
38
+
39
+ // The childToParentMap should contain all non-root nodes
40
+ expect(childToParentMap.has('1')).toBeFalsy(); // Root node
41
+ expect(childToParentMap.get('1.1')).toEqual('1');
42
+ expect(childToParentMap.get('1.2')).toEqual('1');
43
+ expect(childToParentMap.get('1.2.1')).toEqual('1.2');
44
+ expect(childToParentMap.has('2')).toBeFalsy(); // Root node
45
+ });
46
+ });