From 344b8cf2f76d64fb8bca9a4756f0b38828e026ad Mon Sep 17 00:00:00 2001 From: Frank Niessink Date: Mon, 30 Sep 2024 18:02:00 +0200 Subject: [PATCH] Migrate frontend to MUI. - Settings - Menubar Partially addresses #9796. --- components/frontend/package-lock.json | 360 +++++++++++++----- components/frontend/package.json | 4 +- components/frontend/src/App.js | 4 +- components/frontend/src/App.test.js | 30 +- components/frontend/src/AppUI.js | 97 ++--- components/frontend/src/AppUI.test.js | 2 +- .../src/header_footer/CollapseButton.js | 32 -- .../frontend/src/header_footer/Menubar.css | 23 +- .../frontend/src/header_footer/Menubar.js | 133 ++----- .../src/header_footer/Menubar.test.js | 14 +- .../src/header_footer/ResetSettingsButton.js | 43 --- .../src/header_footer/SettingsPanel.test.js | 8 - .../frontend/src/header_footer/UIModeMenu.js | 66 +++- .../src/header_footer/UIModeMenu.test.js | 4 + .../header_footer/buttons/CollapseButton.js | 25 ++ .../{ => buttons}/CollapseButton.test.js | 8 +- .../header_footer/buttons/DatePickerButton.js | 48 +++ .../{ => buttons}/DownloadAsPDFButton.js | 52 ++- .../{ => buttons}/DownloadAsPDFButton.test.js | 48 +-- .../src/header_footer/buttons/HomeButton.js | 29 ++ .../buttons/ResetSettingsButton.js | 33 ++ .../{ => buttons}/ResetSettingsButton.test.js | 6 +- .../header_footer/buttons/SettingsButton.js | 20 + .../settings_menu/SettingsMenu.css | 4 - .../settings_menu/SettingsMenu.js | 44 +-- .../settings_menu/SortColumnMenu.js | 11 +- components/frontend/src/widgets/IconCombi.js | 17 - 27 files changed, 669 insertions(+), 496 deletions(-) delete mode 100644 components/frontend/src/header_footer/CollapseButton.js delete mode 100644 components/frontend/src/header_footer/ResetSettingsButton.js create mode 100644 components/frontend/src/header_footer/buttons/CollapseButton.js rename components/frontend/src/header_footer/{ => buttons}/CollapseButton.test.js (87%) create mode 100644 components/frontend/src/header_footer/buttons/DatePickerButton.js rename components/frontend/src/header_footer/{ => buttons}/DownloadAsPDFButton.js (61%) rename components/frontend/src/header_footer/{ => buttons}/DownloadAsPDFButton.test.js (69%) create mode 100644 components/frontend/src/header_footer/buttons/HomeButton.js create mode 100644 components/frontend/src/header_footer/buttons/ResetSettingsButton.js rename components/frontend/src/header_footer/{ => buttons}/ResetSettingsButton.test.js (88%) create mode 100644 components/frontend/src/header_footer/buttons/SettingsButton.js delete mode 100644 components/frontend/src/header_footer/settings_menu/SettingsMenu.css delete mode 100644 components/frontend/src/widgets/IconCombi.js diff --git a/components/frontend/package-lock.json b/components/frontend/package-lock.json index 668f607ddc..7945da9701 100644 --- a/components/frontend/package-lock.json +++ b/components/frontend/package-lock.json @@ -11,15 +11,17 @@ "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", "@mui/icons-material": "^6.1.1", + "@mui/lab": "^6.0.0-beta.10", "@mui/material": "^6.1.1", + "@mui/x-date-pickers": "^7.18.0", "crypto-js": "^4.2.0", + "dayjs": "^1.11.13", "fomantic-ui-css": "^2.9.3", "history": "^5.3.0", "prop-types": "^15.8.1", "react": "^18.3.1", "react-datepicker": "^7.4.0", "react-dom": "^18.3.1", - "react-focus-lock": "^2.13.0", "react-grid-layout": "^1.4.4", "react-hash-link": "1.0.2", "react-is": "^18.3.1", @@ -4821,6 +4823,68 @@ "dev": true, "license": "MIT" }, + "node_modules/@mui/base": { + "version": "5.0.0-beta.58", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.58.tgz", + "integrity": "sha512-P0E7ZrxOuyYqBvVv9w8k7wm+Xzx/KRu+BGgFcR2htTsGCpJNQJCSUXNUZ50MUmSU9hzqhwbQWNXhV1MBTl6F7A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.0", + "@floating-ui/react-dom": "^2.1.1", + "@mui/types": "^7.2.15", + "@mui/utils": "6.0.0-rc.0", + "@popperjs/core": "^2.11.8", + "clsx": "^2.1.1", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/base/node_modules/@mui/utils": { + "version": "6.0.0-rc.0", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.0.0-rc.0.tgz", + "integrity": "sha512-tBp0ILEXDL0bbDDT8PnZOjCqSm5Dfk2N0Z45uzRw+wVl6fVvloC9zw8avl+OdX1Bg3ubs/ttKn8nRNv17bpM5A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.0", + "@mui/types": "^7.2.15", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^18.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@mui/core-downloads-tracker": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.1.tgz", @@ -4857,6 +4921,51 @@ } } }, + "node_modules/@mui/lab": { + "version": "6.0.0-beta.10", + "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-6.0.0-beta.10.tgz", + "integrity": "sha512-eqCBz5SZS8Un9To3UcjH01AxkOOgvme/g0ZstFC8Nz1Kg5/EJMA0ByhKS5AvUMzUKrv0FXMdbuPqbBvF3bVrXg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.6", + "@mui/base": "5.0.0-beta.58", + "@mui/system": "^6.1.1", + "@mui/types": "^7.2.17", + "@mui/utils": "^6.1.1", + "clsx": "^2.1.1", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material": "^6.1.1", + "@mui/material-pigment-css": "^6.1.1", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, "node_modules/@mui/material": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.1.tgz", @@ -5050,6 +5159,152 @@ } } }, + "node_modules/@mui/x-date-pickers": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.18.0.tgz", + "integrity": "sha512-12tXIoMj9vpS8fS/bS3kWPCoVrH38vNGCxgplI0vOnUrN9rJuYJz3agLPJe1S0xciTw+9W8ZSe3soaW+owoz1Q==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.6", + "@mui/utils": "^5.16.6", + "@mui/x-internals": "7.18.0", + "@types/react-transition-group": "^4.4.11", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.15.14 || ^6.0.0", + "@mui/system": "^5.15.14 || ^6.0.0", + "date-fns": "^2.25.0 || ^3.2.0 || ^4.0.0", + "date-fns-jalali": "^2.13.0-0 || ^3.2.0-0", + "dayjs": "^1.10.7", + "luxon": "^3.0.2", + "moment": "^2.29.4", + "moment-hijri": "^2.1.2", + "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "date-fns": { + "optional": true + }, + "date-fns-jalali": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + }, + "moment-hijri": { + "optional": true + }, + "moment-jalaali": { + "optional": true + } + } + }, + "node_modules/@mui/x-date-pickers/node_modules/@mui/utils": { + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.6.tgz", + "integrity": "sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/types": "^7.2.15", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^18.3.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/x-internals": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.18.0.tgz", + "integrity": "sha512-lzCHOWIR0cAIY1bGrWSprYerahbnH5C31ql/2OWCEjcngL2NAV1M6oKI2Vp4HheqzJ822c60UyWyapvyjSzY/A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.6", + "@mui/utils": "^5.16.6" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0" + } + }, + "node_modules/@mui/x-internals/node_modules/@mui/utils": { + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.6.tgz", + "integrity": "sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/types": "^7.2.15", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^18.3.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -8771,6 +9026,12 @@ "url": "https://github.com/sponsors/kossnocorp" } }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.3.4", "dev": true, @@ -8936,12 +9197,6 @@ "dev": true, "license": "MIT" }, - "node_modules/detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "license": "MIT" - }, "node_modules/detect-port-alt": { "version": "1.1.6", "dev": true, @@ -10703,18 +10958,6 @@ "dev": true, "license": "ISC" }, - "node_modules/focus-lock": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-1.3.5.tgz", - "integrity": "sha512-QFaHbhv9WPUeLYBDe/PAuLKJ4Dd9OPvKs9xZBr3yLXnUrDNaVXKu2baDBXe3naPY30hgHYSsf2JW4jzas2mDEQ==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/follow-redirects": { "version": "1.15.6", "dev": true, @@ -19532,18 +19775,6 @@ "dev": true, "license": "MIT" }, - "node_modules/react-clientside-effect": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz", - "integrity": "sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.13" - }, - "peerDependencies": { - "react": "^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/react-datepicker": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-7.4.0.tgz", @@ -19716,29 +19947,6 @@ "version": "3.2.2", "license": "MIT" }, - "node_modules/react-focus-lock": { - "version": "2.13.2", - "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.13.2.tgz", - "integrity": "sha512-T/7bsofxYqnod2xadvuwjGKHOoL5GH7/EIPI5UyEvaU/c2CcphvGI371opFtuY/SYdbMsNiuF4HsHQ50nA/TKQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.0.0", - "focus-lock": "^1.3.5", - "prop-types": "^15.6.2", - "react-clientside-effect": "^1.2.6", - "use-callback-ref": "^1.3.2", - "use-sidecar": "^1.1.2" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/react-grid-layout": { "version": "1.4.4", "license": "MIT", @@ -23617,6 +23825,7 @@ }, "node_modules/tslib": { "version": "2.6.2", + "dev": true, "license": "0BSD" }, "node_modules/tsutils": { @@ -23923,49 +24132,6 @@ "requires-port": "^1.0.0" } }, - "node_modules/use-callback-ref": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", - "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-sidecar": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", - "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", - "license": "MIT", - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "dev": true, diff --git a/components/frontend/package.json b/components/frontend/package.json index 5bf20d0d7f..989e21eaab 100644 --- a/components/frontend/package.json +++ b/components/frontend/package.json @@ -7,15 +7,17 @@ "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", "@mui/icons-material": "^6.1.1", + "@mui/lab": "^6.0.0-beta.10", "@mui/material": "^6.1.1", + "@mui/x-date-pickers": "^7.18.0", "crypto-js": "^4.2.0", + "dayjs": "^1.11.13", "fomantic-ui-css": "^2.9.3", "history": "^5.3.0", "prop-types": "^15.8.1", "react": "^18.3.1", "react-datepicker": "^7.4.0", "react-dom": "^18.3.1", - "react-focus-lock": "^2.13.0", "react-grid-layout": "^1.4.4", "react-hash-link": "1.0.2", "react-is": "^18.3.1", diff --git a/components/frontend/src/App.js b/components/frontend/src/App.js index 5241036e43..41d86df088 100644 --- a/components/frontend/src/App.js +++ b/components/frontend/src/App.js @@ -19,7 +19,9 @@ const theme = createTheme({ colorSchemes: { dark: true, // Add a dark theme (light theme is available by default) }, - components: { MuiTooltip: { defaultProps: { arrow: true }, styleOverrides: { tooltip: { fontSize: "1em" } } } }, + components: { + MuiTooltip: { defaultProps: { arrow: true }, styleOverrides: { tooltip: { fontSize: "1em" } } }, + }, }) class App extends Component { diff --git a/components/frontend/src/App.test.js b/components/frontend/src/App.test.js index 592a87d34c..2db484e12f 100644 --- a/components/frontend/src/App.test.js +++ b/components/frontend/src/App.test.js @@ -1,5 +1,4 @@ import { act, fireEvent, render, screen } from "@testing-library/react" -import userEvent from "@testing-library/user-event" import history from "history/browser" import * as auth from "./api/auth" @@ -69,32 +68,47 @@ it("resets the user when the user clicks logout", async () => { expect(screen.queryAllByText(/admin/).length).toBe(0) }) +async function selectDate() { + await act(async () => { + fireEvent.click(screen.getByLabelText("Report date")) + }) + await act(async () => { + fireEvent.click(screen.getByRole("button", { name: "Previous month" })) + }) + await act(async () => { + fireEvent.click(screen.getAllByRole("gridcell", { name: "15" })[0]) + }) +} + it("handles a date change", async () => { render() - await userEvent.type(screen.getByPlaceholderText("YYYY-MM-DD"), "2020-03-13") - expect(screen.getAllByDisplayValue("2020-03-13").length).toBe(1) + await selectDate() + expect(screen.getByLabelText("Report date").textContent).toMatch(/15/) }) it("handles a date change between two dates in the past", async () => { history.push("/?report_date=2022-03-13") render() - await userEvent.type(screen.getByPlaceholderText("YYYY-MM-DD"), "{Backspace}4") - expect(screen.getAllByDisplayValue("2022-03-14").length).toBe(1) + await selectDate() + expect(screen.getByLabelText("Report date").textContent).toMatch(/15/) }) it("reads the report date query parameter", () => { history.push("/?report_date=2020-03-13") render() - expect(screen.getAllByDisplayValue("2020-03-13").length).toBe(1) + expect(screen.getByLabelText("Report date").textContent).toMatch(/2020/) }) it("handles a date reset", async () => { history.push("/?report_date=2020-03-13") render() await act(async () => { - fireEvent.click(screen.getByRole("button", { name: "Close" })) + fireEvent.click(screen.getByLabelText("Report date")) + }) + await act(async () => { + fireEvent.click(screen.getByRole("button", { name: "Today" })) }) - expect(screen.queryAllByDisplayValue("2020-03-13").length).toBe(0) + expect(screen.getByLabelText("Report date").textContent).toMatch(/today/) }) it("handles the nr of measurements event source", async () => { diff --git a/components/frontend/src/AppUI.js b/components/frontend/src/AppUI.js index 93ec29bead..4b35645ba8 100644 --- a/components/frontend/src/AppUI.js +++ b/components/frontend/src/AppUI.js @@ -2,6 +2,9 @@ import "react-toastify/dist/ReactToastify.css" import "./App.css" import { useColorScheme } from "@mui/material/styles" +import { LocalizationProvider } from "@mui/x-date-pickers" +import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs" +import { locale_en_gb } from "dayjs/locale/en-gb" import { bool, func, number, object, string } from "prop-types" import HashLinkObserver from "react-hash-link" import { ToastContainer } from "react-toastify" @@ -73,52 +76,54 @@ export function AppUI({ backgroundColor: backgroundColor, }} > - - - - } - settings={settings} - setUIMode={setMode} - uiMode={mode} - /> - - - - - - -