From 2d1212ada5e72a819bc02d98b1b0821127d35b73 Mon Sep 17 00:00:00 2001 From: hatim dinia Date: Tue, 13 Feb 2024 15:05:59 +0100 Subject: [PATCH] react data grid test --- webapp/package-lock.json | 123 +++++++++++++++++- webapp/package.json | 1 + webapp/src/components/App/index.tsx | 3 +- webapp/src/components/common/Glide/utils.ts | 2 +- .../src/components/common/ReactGrid/index.tsx | 99 ++++++++++++++ .../src/components/common/ReactGrid/utils.ts | 68 ++++++++++ 6 files changed, 290 insertions(+), 6 deletions(-) create mode 100644 webapp/src/components/common/ReactGrid/index.tsx create mode 100644 webapp/src/components/common/ReactGrid/utils.ts diff --git a/webapp/package-lock.json b/webapp/package-lock.json index acddd3e02e..acb626b34d 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -17,6 +17,7 @@ "@mui/material": "5.14.11", "@mui/x-date-pickers": "6.18.3", "@reduxjs/toolkit": "1.9.6", + "@silevis/reactgrid": "^4.1.3", "axios": "1.5.1", "clsx": "2.0.0", "d3": "5.16.0", @@ -2293,6 +2294,19 @@ "win32" ] }, + "node_modules/@silevis/reactgrid": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@silevis/reactgrid/-/reactgrid-4.1.3.tgz", + "integrity": "sha512-SjiRE3WLr3kJYTiL1wwWqdl6c9+8jsoUm7f/122tlR3P0XWXdex2Q5yYkOH6B7mkoOiK2iGMW1IDP2BALNuVeA==", + "dependencies": { + "sass": "^1.62.1", + "tslib": "^2.5.2" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0 || ^18.2.0", + "react-dom": "^16.13.1 || ^17.0.0 || ^18.2.0" + } + }, "node_modules/@sphinxxxx/color-conversion": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@sphinxxxx/color-conversion/-/color-conversion-2.2.2.tgz", @@ -4449,6 +4463,18 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -4733,6 +4759,14 @@ "node": "*" } }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, "node_modules/binary-search-bounds": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-2.0.5.tgz", @@ -4941,6 +4975,40 @@ "regexp-to-ast": "0.4.0" } }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", @@ -6837,7 +6905,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -7758,6 +7825,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-boolean-object": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", @@ -7866,7 +7944,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -7921,7 +7998,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -8914,6 +8990,14 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/normalize-svg-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-0.1.0.tgz", @@ -10281,6 +10365,17 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/redux": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", @@ -10691,6 +10786,27 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/sass": { + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz", + "integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass/node_modules/immutable": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", + "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==" + }, "node_modules/sax": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", @@ -10914,7 +11030,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true, "engines": { "node": ">=0.10.0" } diff --git a/webapp/package.json b/webapp/package.json index 40e77ca700..93b8794d92 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -20,6 +20,7 @@ "@mui/material": "5.14.11", "@mui/x-date-pickers": "6.18.3", "@reduxjs/toolkit": "1.9.6", + "@silevis/reactgrid": "^4.1.3", "axios": "1.5.1", "clsx": "2.0.0", "d3": "5.16.0", diff --git a/webapp/src/components/App/index.tsx b/webapp/src/components/App/index.tsx index 0c9abfff51..e59e9e01dc 100644 --- a/webapp/src/components/App/index.tsx +++ b/webapp/src/components/App/index.tsx @@ -55,6 +55,7 @@ import ThermalForm from "./Singlestudy/explore/Modelization/Areas/Thermal/Form"; import RenewablesForm from "./Singlestudy/explore/Modelization/Areas/Renewables/Form"; import SplitHydroMatrix from "./Singlestudy/explore/Modelization/Areas/Hydro/SplitHydroMatrix"; import GlideDataGrid from "../common/Glide"; +import ReactDataGrid from "../common/ReactGrid"; function App() { return ( @@ -186,7 +187,7 @@ function App() { } /> } /> - } /> + } /> }> } /> diff --git a/webapp/src/components/common/Glide/utils.ts b/webapp/src/components/common/Glide/utils.ts index abdf702b35..2aea61cf75 100644 --- a/webapp/src/components/common/Glide/utils.ts +++ b/webapp/src/components/common/Glide/utils.ts @@ -1,4 +1,4 @@ -import { useState, useCallback } from "react"; +import { useCallback } from "react"; import { Person } from "."; export const generateRandomData = ( diff --git a/webapp/src/components/common/ReactGrid/index.tsx b/webapp/src/components/common/ReactGrid/index.tsx new file mode 100644 index 0000000000..0e6a4086da --- /dev/null +++ b/webapp/src/components/common/ReactGrid/index.tsx @@ -0,0 +1,99 @@ +import "@silevis/reactgrid/styles.css"; +import { ReactGrid, Column, Row } from "@silevis/reactgrid"; +import { useCallback, useEffect, useState } from "react"; +import { generateRandomData, usePerformanceMeasure } from "./utils"; + +export interface Person { + name: string; + company: string; + email: string; + phone: string; +} +const getPeople = (): Person[] => [ + { + name: "John Doe", + company: "TechCorp", + email: "test@test.com", + phone: "1234567890", + }, + { + name: "Jane Smith", + company: "Innovatech", + email: "jane@test.com", + phone: "1234567890", + }, + { + name: "Alice Johnson", + company: "BuildIt", + email: "alice@test.com", + phone: "1234567890", + }, +]; + +const getColumns = (): Column[] => [ + { + columnId: "name", + width: 200, + }, + { + columnId: "company", + width: 200, + }, + { + columnId: "email", + width: 200, + }, + { + columnId: "phone", + width: 200, + }, +]; + +const headerRow: Row = { + rowId: "header", + cells: [ + { type: "header", text: "Name" }, + { type: "header", text: "Company" }, + { type: "header", text: "Email" }, + { type: "header", text: "Phone" }, + ], +}; + +const getRows = (people: Person[]): Row[] => [ + headerRow, + ...people.map((person, idx) => ({ + rowId: idx, + cells: [ + { type: "text", text: person.name }, + { type: "text", text: person.company }, + { type: "text", text: person.email }, + { type: "text", text: person.phone }, + ], + })), +]; + +function ReactDataGrid() { + const [data, setData] = useState(getPeople()); + const { startMeasurement, endMeasurement } = + usePerformanceMeasure("DataFetchAndRender"); + + const fetchData = useCallback(async () => { + startMeasurement(); + + const fetchedData = await generateRandomData(1000000); + setData(fetchedData); + + endMeasurement(); + }, [startMeasurement, endMeasurement]); + + useEffect(() => { + fetchData(); + }, [fetchData]); + + const rows = getRows(data); + const columns = getColumns(); + + return ; +} + +export default ReactDataGrid; diff --git a/webapp/src/components/common/ReactGrid/utils.ts b/webapp/src/components/common/ReactGrid/utils.ts new file mode 100644 index 0000000000..2aea61cf75 --- /dev/null +++ b/webapp/src/components/common/ReactGrid/utils.ts @@ -0,0 +1,68 @@ +import { useCallback } from "react"; +import { Person } from "."; + +export const generateRandomData = ( + numberOfEntries: number, +): Promise => { + return new Promise((resolve) => { + const names = [ + "John Doe", + "Jane Smith", + "Alice Johnson", + "Chris Lee", + "Mike Brown", + ]; + const companies = [ + "TechCorp", + "Innovatech", + "BuildIt", + "Designify", + "NetSolutions", + ]; + const domains = [ + "example.com", + "sample.org", + "demo.net", + "test.io", + "placeholder.co", + ]; + + const randomData: Person[] = []; + + for (let i = 0; i < numberOfEntries; i++) { + const name = names[Math.floor(Math.random() * names.length)]; + const company = companies[Math.floor(Math.random() * companies.length)]; + const email = `${name.split(" ")[0].toLowerCase()}.${name + .split(" ")[1] + .toLowerCase()}@${domains[Math.floor(Math.random() * domains.length)]}`; + const phone = `+1 (${Math.floor(Math.random() * 900) + 100}) ${ + Math.floor(Math.random() * 900) + 100 + }-${Math.floor(Math.random() * 9000) + 1000}`; + + randomData.push({ name, company, email, phone }); + } + + // to simulate a network request delay increase the timeout + setTimeout(() => resolve(randomData), 0); + }); +}; + +export function usePerformanceMeasure(measureName: string) { + const startMeasurement = useCallback(() => { + performance.mark(`${measureName}-start`); + }, [measureName]); + + const endMeasurement = useCallback(() => { + const endMark = `${measureName}-end`; + performance.mark(endMark); + performance.measure(measureName, `${measureName}-start`, endMark); + + const measure = performance.getEntriesByName(measureName).pop(); + console.log(`${measureName}: ${measure?.duration}ms`); + performance.clearMarks(`${measureName}-start`); + performance.clearMarks(endMark); + performance.clearMeasures(measureName); + }, [measureName]); + + return { startMeasurement, endMeasurement }; +}