react-native-tree-multi-select 1.2.0 → 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 (152) 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/README.md +12 -3
  19. package/babel.config.js +3 -0
  20. package/example/.bundle/config +2 -0
  21. package/example/.watchmanconfig +1 -0
  22. package/example/Gemfile +6 -0
  23. package/example/README.md +79 -0
  24. package/example/android/app/build.gradle +130 -0
  25. package/example/android/app/debug.keystore +0 -0
  26. package/example/android/app/proguard-rules.pro +10 -0
  27. package/example/android/app/src/debug/AndroidManifest.xml +13 -0
  28. package/example/android/app/src/debug/java/com/treemultiselectexample/ReactNativeFlipper.java +75 -0
  29. package/example/android/app/src/main/AndroidManifest.xml +25 -0
  30. package/example/android/app/src/main/java/com/treemultiselectexample/MainActivity.java +32 -0
  31. package/example/android/app/src/main/java/com/treemultiselectexample/MainApplication.java +62 -0
  32. package/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +36 -0
  33. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  34. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  35. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  36. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  37. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  38. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  39. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  40. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  41. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  42. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  43. package/example/android/app/src/main/res/values/strings.xml +3 -0
  44. package/example/android/app/src/main/res/values/styles.xml +9 -0
  45. package/example/android/app/src/release/java/com/treemultiselectexample/ReactNativeFlipper.java +20 -0
  46. package/example/android/build.gradle +23 -0
  47. package/example/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  48. package/example/android/gradle/wrapper/gradle-wrapper.properties +6 -0
  49. package/example/android/gradle.properties +44 -0
  50. package/example/android/gradlew +244 -0
  51. package/example/android/gradlew.bat +92 -0
  52. package/example/android/settings.gradle +4 -0
  53. package/example/app.json +4 -0
  54. package/example/babel.config.js +17 -0
  55. package/example/index.js +5 -0
  56. package/example/ios/.xcode.env +11 -0
  57. package/example/ios/File.swift +6 -0
  58. package/example/ios/Podfile +62 -0
  59. package/example/ios/Podfile.lock +639 -0
  60. package/example/ios/TreeMultiSelectExample/AppDelegate.h +6 -0
  61. package/example/ios/TreeMultiSelectExample/AppDelegate.mm +26 -0
  62. package/example/ios/TreeMultiSelectExample/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
  63. package/example/ios/TreeMultiSelectExample/Images.xcassets/Contents.json +6 -0
  64. package/example/ios/TreeMultiSelectExample/Info.plist +74 -0
  65. package/example/ios/TreeMultiSelectExample/LaunchScreen.storyboard +47 -0
  66. package/example/ios/TreeMultiSelectExample/main.m +10 -0
  67. package/example/ios/TreeMultiSelectExample-Bridging-Header.h +3 -0
  68. package/example/ios/TreeMultiSelectExample.xcodeproj/project.pbxproj +706 -0
  69. package/example/ios/TreeMultiSelectExample.xcodeproj/xcshareddata/xcschemes/TreeMultiSelectExample.xcscheme +88 -0
  70. package/example/ios/TreeMultiSelectExample.xcworkspace/contents.xcworkspacedata +10 -0
  71. package/example/ios/TreeMultiSelectExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  72. package/example/ios/TreeMultiSelectExampleTests/Info.plist +24 -0
  73. package/example/ios/TreeMultiSelectExampleTests/TreeMultiSelectExampleTests.m +66 -0
  74. package/example/metro.config.js +45 -0
  75. package/example/package.json +41 -0
  76. package/example/react-native.config.js +10 -0
  77. package/example/src/App.tsx +70 -0
  78. package/example/src/components/CustomArrow.tsx +71 -0
  79. package/example/src/components/CustomCheckboxView.tsx +119 -0
  80. package/example/src/components/CustomNodeRowView.tsx +124 -0
  81. package/example/src/components/SearchInput.tsx +68 -0
  82. package/example/src/screens/CustomArrowScreen.tsx +99 -0
  83. package/example/src/screens/CustomCheckboxScreen.tsx +99 -0
  84. package/example/src/screens/CustomNodeRowViewScreen.tsx +99 -0
  85. package/example/src/screens/LargeDataScreen.tsx +95 -0
  86. package/example/src/screens/MediumDataScreen.tsx +96 -0
  87. package/example/src/screens/SmallDataScreen.tsx +96 -0
  88. package/example/src/screens/screens.styles.ts +25 -0
  89. package/example/src/utils/sampleDataGenerator.ts +32 -0
  90. package/example/yarn.lock +6368 -0
  91. package/lefthook.yml +17 -0
  92. package/lib/commonjs/TreeView.js +13 -3
  93. package/lib/commonjs/TreeView.js.map +1 -1
  94. package/lib/commonjs/helpers/expandCollapse.helper.js +88 -21
  95. package/lib/commonjs/helpers/expandCollapse.helper.js.map +1 -1
  96. package/lib/commonjs/helpers/toggleCheckbox.helper.js +0 -1
  97. package/lib/commonjs/helpers/toggleCheckbox.helper.js.map +1 -1
  98. package/lib/commonjs/utils/usePreviousState.js +20 -0
  99. package/lib/commonjs/utils/usePreviousState.js.map +1 -0
  100. package/lib/module/TreeView.js +14 -4
  101. package/lib/module/TreeView.js.map +1 -1
  102. package/lib/module/helpers/expandCollapse.helper.js +86 -21
  103. package/lib/module/helpers/expandCollapse.helper.js.map +1 -1
  104. package/lib/module/helpers/toggleCheckbox.helper.js +0 -1
  105. package/lib/module/helpers/toggleCheckbox.helper.js.map +1 -1
  106. package/lib/module/utils/usePreviousState.js +15 -0
  107. package/lib/module/utils/usePreviousState.js.map +1 -0
  108. package/lib/typescript/TreeView.d.ts.map +1 -1
  109. package/lib/typescript/__mocks__/generateTree.mock.d.ts +17 -0
  110. package/lib/typescript/__mocks__/generateTree.mock.d.ts.map +1 -0
  111. package/lib/typescript/__mocks__/zustand.d.ts +3 -0
  112. package/lib/typescript/__mocks__/zustand.d.ts.map +1 -0
  113. package/lib/typescript/__tests__/expandCollapse.helper.test.d.ts +2 -0
  114. package/lib/typescript/__tests__/expandCollapse.helper.test.d.ts.map +1 -0
  115. package/lib/typescript/__tests__/flattenTree.helper.test.d.ts +2 -0
  116. package/lib/typescript/__tests__/flattenTree.helper.test.d.ts.map +1 -0
  117. package/lib/typescript/__tests__/initNodeMap.helper.test.d.ts +2 -0
  118. package/lib/typescript/__tests__/initNodeMap.helper.test.d.ts.map +1 -0
  119. package/lib/typescript/__tests__/search.helper.test.d.ts +2 -0
  120. package/lib/typescript/__tests__/search.helper.test.d.ts.map +1 -0
  121. package/lib/typescript/__tests__/selectAll.helper.test.d.ts +2 -0
  122. package/lib/typescript/__tests__/selectAll.helper.test.d.ts.map +1 -0
  123. package/lib/typescript/__tests__/store.test.d.ts +2 -0
  124. package/lib/typescript/__tests__/store.test.d.ts.map +1 -0
  125. package/lib/typescript/__tests__/toggleCheckbox.helper.test.d.ts +2 -0
  126. package/lib/typescript/__tests__/toggleCheckbox.helper.test.d.ts.map +1 -0
  127. package/lib/typescript/helpers/expandCollapse.helper.d.ts +12 -0
  128. package/lib/typescript/helpers/expandCollapse.helper.d.ts.map +1 -1
  129. package/lib/typescript/types/treeView.types.d.ts +3 -0
  130. package/lib/typescript/types/treeView.types.d.ts.map +1 -1
  131. package/lib/typescript/utils/usePreviousState.d.ts +7 -0
  132. package/lib/typescript/utils/usePreviousState.d.ts.map +1 -0
  133. package/package.json +5 -3
  134. package/scripts/bootstrap.js +29 -0
  135. package/src/TreeView.tsx +25 -4
  136. package/src/__mocks__/generateTree.mock.ts +125 -0
  137. package/src/__mocks__/zustand.ts +24 -0
  138. package/src/__tests__/expandCollapse.helper.test.ts +189 -0
  139. package/src/__tests__/flattenTree.helper.test.ts +78 -0
  140. package/src/__tests__/initNodeMap.helper.test.ts +46 -0
  141. package/src/__tests__/search.helper.test.ts +47 -0
  142. package/src/__tests__/selectAll.helper.test.ts +233 -0
  143. package/src/__tests__/store.test.ts +208 -0
  144. package/src/__tests__/toggleCheckbox.helper.test.ts +124 -0
  145. package/src/helpers/expandCollapse.helper.ts +82 -21
  146. package/src/helpers/toggleCheckbox.helper.ts +1 -1
  147. package/src/types/treeView.types.ts +5 -0
  148. package/src/utils/usePreviousState.ts +16 -0
  149. package/tsconfig.build.json +5 -0
  150. package/tsconfig.json +31 -0
  151. package/turbo.json +34 -0
  152. package/yarn.lock +9953 -0
@@ -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":""}
@@ -15,4 +15,16 @@ export declare function expandAll(): void;
15
15
  * Collapse all nodes in the tree.
16
16
  */
17
17
  export declare function collapseAll(): void;
18
+ /**
19
+ * Expand tree nodes of given ids. If the id is of a child, it also expands
20
+ * the parent which it belongs to.
21
+ * @param ids Ids of nodes to expand.
22
+ */
23
+ export declare function expandNodes(ids: string[]): void;
24
+ /**
25
+ * Collapse tree nodes of given ids. If the id is of a parent, it also collapses
26
+ * the children inside it.
27
+ * @param ids Ids of nodes to collapse.
28
+ */
29
+ export declare function collapseNodes(ids: string[]): void;
18
30
  //# sourceMappingURL=expandCollapse.helper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"expandCollapse.helper.d.ts","sourceRoot":"","sources":["../../../src/helpers/expandCollapse.helper.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,QAmE5C;AAED;;GAEG;AACH,wBAAgB,SAAS,SAKxB;AAED;;GAEG;AACH,wBAAgB,WAAW,SAK1B"}
1
+ {"version":3,"file":"expandCollapse.helper.d.ts","sourceRoot":"","sources":["../../../src/helpers/expandCollapse.helper.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,QA+C5C;AAED;;GAEG;AACH,wBAAgB,SAAS,SAKxB;AAED;;GAEG;AACH,wBAAgB,WAAW,SAK1B;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,QAwBxC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,QAuB1C"}
@@ -44,6 +44,7 @@ export interface TreeViewProps extends NodeListProps {
44
44
  onCheck?: (checkedIds: string[]) => void;
45
45
  onExpand?: (expandedIds: string[]) => void;
46
46
  preselectedIds?: string[];
47
+ preExpandedIds?: string[];
47
48
  }
48
49
  type CheckboxProps = Omit<RNPaperCheckboxAndroidProps, "onPress" | "status">;
49
50
  export interface CheckBoxViewProps {
@@ -66,6 +67,8 @@ export interface TreeViewRef {
66
67
  unselectAllFiltered: () => void;
67
68
  expandAll: () => void;
68
69
  collapseAll: () => void;
70
+ expandNodes: (ids: string[]) => void;
71
+ collapseNodes: (ids: string[]) => void;
69
72
  setSearchText: (searchText: string, searchKeys?: string[]) => void;
70
73
  }
71
74
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"treeView.types.d.ts","sourceRoot":"","sources":["../../../src/types/treeView.types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"treeView.types.d.ts","sourceRoot":"","sources":["../../../src/types/treeView.types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Get previous value of a state
3
+ * @param value state
4
+ * @returns previous value of @param value after it's updated
5
+ */
6
+ export default function usePreviousState<T>(value: T): T | undefined;
7
+ //# sourceMappingURL=usePreviousState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePreviousState.d.ts","sourceRoot":"","sources":["../../../src/utils/usePreviousState.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,iBAQnD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-tree-multi-select",
3
- "version": "1.2.0",
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",
@@ -119,7 +121,7 @@
119
121
  "engines": {
120
122
  "node": ">= 16.0.0"
121
123
  },
122
- "packageManager": "^yarn@1.22.15",
124
+ "packageManager": "yarn@1.22.22",
123
125
  "jest": {
124
126
  "preset": "react-native",
125
127
  "setupFilesAfterEnv": [
@@ -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;
package/src/TreeView.tsx CHANGED
@@ -14,9 +14,12 @@ import {
14
14
  initializeNodeMaps,
15
15
  expandAll,
16
16
  collapseAll,
17
- toggleCheckboxes
17
+ toggleCheckboxes,
18
+ expandNodes,
19
+ collapseNodes
18
20
  } from './helpers';
19
21
  import { useTreeViewStore } from './store/treeView.store';
22
+ import usePreviousState from './utils/usePreviousState';
20
23
 
21
24
  const _TreeView = React.forwardRef<TreeViewRef, TreeViewProps>(
22
25
  (props, ref) => {
@@ -28,6 +31,8 @@ const _TreeView = React.forwardRef<TreeViewRef, TreeViewProps>(
28
31
 
29
32
  preselectedIds = [],
30
33
 
34
+ preExpandedIds = [],
35
+
31
36
  treeFlashListProps,
32
37
  checkBoxViewStyleProps,
33
38
  indentationMultiplier,
@@ -66,9 +71,14 @@ const _TreeView = React.forwardRef<TreeViewRef, TreeViewProps>(
66
71
  expandAll,
67
72
  collapseAll,
68
73
 
74
+ expandNodes,
75
+ collapseNodes,
76
+
69
77
  setSearchText
70
78
  }));
71
79
 
80
+ const prevSearchText = usePreviousState(searchText);
81
+
72
82
  function setSearchText(text: string, keys: string[] = ["name"]) {
73
83
  updateSearchText(text);
74
84
  updateSearchKeys(keys);
@@ -79,8 +89,11 @@ const _TreeView = React.forwardRef<TreeViewRef, TreeViewProps>(
79
89
 
80
90
  initializeNodeMaps(data);
81
91
 
82
- // Check any preselected nodes
92
+ // Check any pre-selected nodes
83
93
  toggleCheckboxes(preselectedIds, true);
94
+
95
+ // Expand pre-expanded nodes
96
+ expandNodes(preExpandedIds);
84
97
  // eslint-disable-next-line react-hooks/exhaustive-deps
85
98
  }, []);
86
99
 
@@ -108,12 +121,20 @@ const _TreeView = React.forwardRef<TreeViewRef, TreeViewProps>(
108
121
  )));
109
122
  });
110
123
  }
111
- else {
124
+ else if (prevSearchText && prevSearchText !== "") {
125
+ /* Collapse all nodes only if previous search query was non-empty: this is
126
+ done to prevent node collapse on first render if preExpandedIds is provided */
112
127
  InteractionManager.runAfterInteractions(() => {
113
128
  updateExpanded(new Set());
114
129
  });
115
130
  }
116
- }, [getIds, initialTreeViewData, searchText, updateExpanded]);
131
+ }, [
132
+ getIds,
133
+ initialTreeViewData,
134
+ prevSearchText,
135
+ searchText,
136
+ updateExpanded
137
+ ]);
117
138
 
118
139
  React.useEffect(() => {
119
140
  return () => {
@@ -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
+ });