diff --git a/ash-tree/package.json b/ash-tree/package.json index 8925afe..8d76280 100644 --- a/ash-tree/package.json +++ b/ash-tree/package.json @@ -1,6 +1,6 @@ { "name": "ash-tree", - "description": "React Ash Tree.", + "description": "A virtualized, flexible tree component for React", "license": "Apache-2.0", "version": "0.1.0", "private": true, diff --git a/package.json b/package.json index de6c985..9ab3c5b 100644 --- a/package.json +++ b/package.json @@ -8,10 +8,12 @@ "@mui/icons-material": "^5.11.16", "@mui/material": "^5.12.3", "@mui/system": "^5.11.15", + "ash-tree": "link:./ash-tree/build/", + "rc-tree": "^5.8.5", "react": "^18.2.0", + "react-accessible-treeview": "^2.8.3", "react-dom": "^18.2.0", "react-scripts": "5.0.1", - "ash-tree": "link:./ash-tree/build/", "web-vitals": "^3.3.2" }, "devDependencies": { @@ -56,10 +58,13 @@ "test-app-no-update": "react-scripts test --testPathIgnorePatterns=performance.test.tsx --watchAll=false", "test-app": "yarn update-package && yarn test-app-no-update", "lint": "eslint -c .eslintrc.js \"ash-tree/src/**/*.{ts,tsx}\" --fix && eslint -c .eslintrc.js \"src/**/*.{ts,tsx}\" --fix", + "compare-sizes": "yarn minify-and-size && yarn minify-and-size-react-accessible-treeview && yarn minify-and-size-rc-tree", "minify-and-size": "esbuild ash-tree/build/src/index.js --bundle --outfile=minified-bundle.js && gzip-size minified-bundle.js && rm minified-bundle.js && rm minified-bundle.css", + "minify-and-size-react-accessible-treeview": "esbuild node_modules/react-accessible-treeview/dist/react-accessible-treeview.esm.js --bundle --outfile=minified-bundle-react-accessible-treeview.js && gzip-size minified-bundle-react-accessible-treeview.js && rm minified-bundle-react-accessible-treeview.js", + "minify-and-size-rc-tree": "esbuild node_modules/rc-tree/lib/index.js --bundle --outfile=minified-bundle-rc-tree.js && gzip-size minified-bundle-rc-tree.js && rm minified-bundle-rc-tree.js", "test-performance-no-update": "react-scripts test --testMatch='**/performance.test.tsx' --watchAll=false", "test-performance": "yarn update-package && yarn test-performance-no-update", - "ci": "yarn setup && yarn lint && yarn test-no-update && yarn minify-and-size", + "ci": "yarn setup && yarn lint && yarn test-no-update && yarn compare-sizes", "test-compile": "tsc -p ./", "prepare": "husky install", "generate-notice": "run-script-os", diff --git a/src/Components/RcTreeComparison/RcTreeBasic.tsx b/src/Components/RcTreeComparison/RcTreeBasic.tsx new file mode 100644 index 0000000..b01a94e --- /dev/null +++ b/src/Components/RcTreeComparison/RcTreeBasic.tsx @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: TNG Technology Consulting GmbH +// SPDX-FileCopyrightText: Leslie Lazzarino +// SPDX-FileCopyrightText: Benedikt Richter +// +// SPDX-License-Identifier: Apache-2.0 + +import React, { ReactElement } from 'react'; +import '../../styles.css'; +import { testNodes } from '../shared'; +import Tree from 'rc-tree'; +import { NodesForAshTree } from 'ash-tree'; + +// Main drawbacks of this library found so far: lack of examples, typing really difficult to understand, huge package +// size, unusable default structure (no indentation, no collapsing/expanding) +export function RcTreeBasic(): ReactElement { + const treeData = getTreeData(testNodes); + + return ( +
+

Basic Tree

+ +
+ ); +} + +function getTreeData( + nodes: NodesForAshTree, + path: string = '', + // eslint-disable-next-line @typescript-eslint/no-explicit-any +): any { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const treeData: any = []; + for (const name of Object.keys(nodes)) { + const node = nodes[name]; + treeData.push({ + key: `${path}/${name}`, + title: name ? name : '/', + children: node && node !== 1 ? getTreeData(node) : undefined, + }); + } + + return treeData; +} diff --git a/src/Components/RcTreeComparison/__tests__/performance.test.tsx b/src/Components/RcTreeComparison/__tests__/performance.test.tsx new file mode 100644 index 0000000..5097c73 --- /dev/null +++ b/src/Components/RcTreeComparison/__tests__/performance.test.tsx @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: TNG Technology Consulting GmbH +// SPDX-FileCopyrightText: Leslie Lazzarino +// SPDX-FileCopyrightText: Benedikt Richter +// +// SPDX-License-Identifier: Apache-2.0 + +import React from 'react'; +import { fireEvent, render, screen } from '@testing-library/react'; +import { RcTreeBasic } from '../RcTreeBasic'; + +describe('The BasicTree', () => { + it('takes so long to expand and collapse 100 times', () => { + render(); + + logTimeTakenToExpandAndCollapseRoot100TimesRcTree(); + }); +}); + +export function logTimeTakenToExpandAndCollapseRoot100TimesRcTree(): void { + const startTime = new Date().getTime(); + for (let i = 0; i < 100; i++) { + fireEvent.click(screen.getByText('/')); + fireEvent.click(screen.getByText('/')); + } + const endTime = new Date().getTime(); + console.log( + 'Time taken to render the basic rc tree 100 times: ', + endTime - startTime, + 'ms', + ); +} diff --git a/src/Components/ReactAccessibleTreeviewComparison/ReactAccessibleTreeviewBasic.tsx b/src/Components/ReactAccessibleTreeviewComparison/ReactAccessibleTreeviewBasic.tsx new file mode 100644 index 0000000..a2564c3 --- /dev/null +++ b/src/Components/ReactAccessibleTreeviewComparison/ReactAccessibleTreeviewBasic.tsx @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: TNG Technology Consulting GmbH +// SPDX-FileCopyrightText: Leslie Lazzarino +// SPDX-FileCopyrightText: Benedikt Richter +// +// SPDX-License-Identifier: Apache-2.0 + +import React, { ReactElement } from 'react'; +import '../../styles.css'; +import { testNodes } from '../shared'; +import { NodesForAshTree } from 'ash-tree'; +import TreeView, { INode } from 'react-accessible-treeview'; +import { IFlatMetadata } from 'react-accessible-treeview/dist/TreeView/utils'; + +// Main drawbacks of this library found so far: only one root node allowed +export function ReactAccessibleTreeviewBasic(): ReactElement { + const treeData = getTreeData({ home: testNodes }); + + const nodeRenderer = ({ + element, + getNodeProps, + level, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + handleSelect, // eslint-disable-next-line @typescript-eslint/no-explicit-any + }: any): ReactElement => ( +
+ {element.name} +
+ ); + + return ( +
+

Basic Tree

+ +
+ ); +} + +function getTreeData( + nodes: NodesForAshTree, + parentPath: string | null = null, + treeData: INode[] = [], +): INode[] { + for (const name of Object.keys(nodes)) { + const displayName = name || '/'; + const node = nodes[name]; + const nodePath = `${parentPath || ''}/${displayName}`; + treeData.push({ + id: nodePath, + parent: parentPath, + name: displayName, + children: + node !== 1 + ? Object.keys(node).map( + (childName: string) => `${nodePath}/${childName || '/'}`, + ) + : [], + }); + if (node !== 1) { + getTreeData(node, nodePath, treeData); + } + } + + return treeData; +} diff --git a/src/Components/ReactAccessibleTreeviewComparison/__tests__/performance.test.tsx b/src/Components/ReactAccessibleTreeviewComparison/__tests__/performance.test.tsx new file mode 100644 index 0000000..35346f1 --- /dev/null +++ b/src/Components/ReactAccessibleTreeviewComparison/__tests__/performance.test.tsx @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: TNG Technology Consulting GmbH +// SPDX-FileCopyrightText: Leslie Lazzarino +// SPDX-FileCopyrightText: Benedikt Richter +// +// SPDX-License-Identifier: Apache-2.0 + +import React from 'react'; +import { fireEvent, render, screen } from '@testing-library/react'; +import { ReactAccessibleTreeviewBasic } from '../ReactAccessibleTreeviewBasic'; + +describe('The BasicTree', () => { + it('takes so long to expand and collapse 100 times', () => { + render(); + + logTimeTakenToExpandAndCollapseRoot100TimesRcTree(); + }); +}); + +export function logTimeTakenToExpandAndCollapseRoot100TimesRcTree(): void { + const startTime = new Date().getTime(); + for (let i = 0; i < 100; i++) { + fireEvent.click(screen.getByText('/')); + fireEvent.click(screen.getByText('/')); + } + const endTime = new Date().getTime(); + console.log( + 'Time taken to render the basic rc tree 100 times: ', + endTime - startTime, + 'ms', + ); +} diff --git a/yarn.lock b/yarn.lock index e10fdf1..ec72bd0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1041,6 +1041,13 @@ "@babel/helper-validator-option" "^7.18.6" "@babel/plugin-transform-typescript" "^7.18.6" +"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.11.1", "@babel/runtime@^7.20.0": + version "7.23.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7" + integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.20.7", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.20.13" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.13.tgz#7055ab8a7cff2b8f6058bf6ae45ff84ad2aded4b" @@ -3919,6 +3926,11 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== +classnames@2.x, classnames@^2.2.1, classnames@^2.2.6: + version "2.5.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" + integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== + clean-css@^5.2.2: version "5.3.2" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.2.tgz#70ecc7d4d4114921f5d298349ff86a31a9975224" @@ -7837,6 +7849,11 @@ memfs@^3.1.2, memfs@^3.4.3: dependencies: fs-monkey "^1.0.3" +"memoize-one@>=3.1.1 <6": + version "5.2.1" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" + integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== + meow@^10.1.2: version "10.1.5" resolved "https://registry.yarnpkg.com/meow/-/meow-10.1.5.tgz#be52a1d87b5f5698602b0f32875ee5940904aa7f" @@ -9157,6 +9174,59 @@ raw-body@2.5.1: iconv-lite "0.4.24" unpipe "1.0.0" +rc-motion@^2.0.1: + version "2.9.0" + resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.0.tgz#9e18a1b8d61e528a97369cf9a7601e9b29205710" + integrity sha512-XIU2+xLkdIr1/h6ohPZXyPBMvOmuyFZQ/T0xnawz+Rh+gh4FINcnZmMT5UTIj6hgI0VLDjTaPeRd+smJeSPqiQ== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-util "^5.21.0" + +rc-resize-observer@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.4.0.tgz#7bba61e6b3c604834980647cce6451914750d0cc" + integrity sha512-PnMVyRid9JLxFavTjeDXEXo65HCRqbmLBw9xX9gfC4BZiSzbLXKzW3jPz+J0P71pLbD5tBMTT+mkstV5gD0c9Q== + dependencies: + "@babel/runtime" "^7.20.7" + classnames "^2.2.1" + rc-util "^5.38.0" + resize-observer-polyfill "^1.5.1" + +rc-tree@^5.8.5: + version "5.8.5" + resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.8.5.tgz#f714a383be27bd87366cf32f7f85b2af1fbae6b6" + integrity sha512-PRfcZtVDNkR7oh26RuNe1hpw11c1wfgzwmPFL0lnxGnYefe9lDAO6cg5wJKIAwyXFVt5zHgpjYmaz0CPy1ZtKg== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "2.x" + rc-motion "^2.0.1" + rc-util "^5.16.1" + rc-virtual-list "^3.5.1" + +rc-util@^5.16.1, rc-util@^5.21.0, rc-util@^5.36.0, rc-util@^5.38.0: + version "5.38.1" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.38.1.tgz#4915503b89855f5c5cd9afd4c72a7a17568777bb" + integrity sha512-e4ZMs7q9XqwTuhIK7zBIVFltUtMSjphuPPQXHoHlzRzNdOwUxDejo0Zls5HYaJfRKNURcsS/ceKVULlhjBrxng== + dependencies: + "@babel/runtime" "^7.18.3" + react-is "^18.2.0" + +rc-virtual-list@^3.5.1: + version "3.11.4" + resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.11.4.tgz#d0a8937843160b7b00d5586854290bf56d396af7" + integrity sha512-NbBi0fvyIu26gP69nQBiWgUMTPX3mr4FcuBQiVqagU0BnuX8WQkiivnMs105JROeuUIFczLrlgUhLQwTWV1XDA== + dependencies: + "@babel/runtime" "^7.20.0" + classnames "^2.2.6" + rc-resize-observer "^1.0.0" + rc-util "^5.36.0" + +react-accessible-treeview@^2.8.3: + version "2.8.3" + resolved "https://registry.yarnpkg.com/react-accessible-treeview/-/react-accessible-treeview-2.8.3.tgz#4d13b2d4e9e088ab01b1243e0b4363cee1a73f41" + integrity sha512-taDTIYZ6p96/zIhJBUKvyGTXcInudatP/9fwKG0BW+VRf1PmU5hOT2FkDovDKzSwj2VSOj1PRx+E6ojhOA+2xA== + react-app-polyfill@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz#95221e0a9bd259e5ca6b177c7bb1cb6768f68fd7" @@ -9304,6 +9374,14 @@ react-transition-group@^4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" +react-window@^1.8.8: + version "1.8.10" + resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.10.tgz#9e6b08548316814b443f7002b1cf8fd3a1bdde03" + integrity sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg== + dependencies: + "@babel/runtime" "^7.0.0" + memoize-one ">=3.1.1 <6" + react@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" @@ -9497,6 +9575,11 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== +resize-observer-polyfill@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"