diff --git a/components/frontend/package-lock.json b/components/frontend/package-lock.json index 7443d4b9b5..80ee869384 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 1d77202036..4dbb156174 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} - /> - - - - - - - - + + + + + } + settings={settings} + setUIMode={setMode} + uiMode={mode} + /> + + + + + + + + + ) } diff --git a/components/frontend/src/AppUI.test.js b/components/frontend/src/AppUI.test.js index 2084e29dd4..861dd0dba8 100644 --- a/components/frontend/src/AppUI.test.js +++ b/components/frontend/src/AppUI.test.js @@ -59,6 +59,6 @@ async function renderAppUI() { it("resets all settings", async () => { history.push("?date_interval=2") await act(async () => await renderAppUI()) - fireEvent.click(screen.getByLabelText("Reset reports overview settings")) + fireEvent.click(screen.getByText("Reset settings")) expect(history.location.search).toBe("") }) diff --git a/components/frontend/src/header_footer/CollapseButton.js b/components/frontend/src/header_footer/CollapseButton.js deleted file mode 100644 index d4b6c48cfe..0000000000 --- a/components/frontend/src/header_footer/CollapseButton.js +++ /dev/null @@ -1,32 +0,0 @@ -import { Button, Icon } from "semantic-ui-react" - -import { Popup } from "../semantic_ui_react_wrappers" -import { stringsURLSearchQueryPropType } from "../sharedPropTypes" - -export function CollapseButton({ expandedItems }) { - const label = "Collapse all headers and metrics" - return ( - - expandedItems.reset()} - inverted - > - Collapse all - - - } - content={label} - /> - ) -} -CollapseButton.propTypes = { - expandedItems: stringsURLSearchQueryPropType, -} diff --git a/components/frontend/src/header_footer/Menubar.css b/components/frontend/src/header_footer/Menubar.css index 18f8aec0ff..51967bdde6 100644 --- a/components/frontend/src/header_footer/Menubar.css +++ b/components/frontend/src/header_footer/Menubar.css @@ -1,26 +1,5 @@ @media print { - .menubar { + .MuiAppBar-root { display: none !important; } } - -.menubar { - opacity: 0.98; -} - -.menu .center { - display: grid; - place-content: center; -} - -.panel { - background-color: black; - border: 0px; - left: 0px; - margin: 0px; - opacity: 0.98; - position: fixed; - top: 64px; - width: 100%; - z-index: 4; -} diff --git a/components/frontend/src/header_footer/Menubar.js b/components/frontend/src/header_footer/Menubar.js index 854a6eb4f8..6012bb7de7 100644 --- a/components/frontend/src/header_footer/Menubar.js +++ b/components/frontend/src/header_footer/Menubar.js @@ -1,18 +1,20 @@ import "./Menubar.css" +import { AppBar, Button, Drawer, Stack, Toolbar } from "@mui/material" import { element, func, string } from "prop-types" import { useEffect, useState } from "react" -import FocusLock from "react-focus-lock" -import { Button, Dropdown, Icon, Image, Menu, Portal } from "semantic-ui-react" +import { Dropdown, Icon } from "semantic-ui-react" import { login, logout } from "../api/auth" -import { Form, Message, Modal, Popup } from "../semantic_ui_react_wrappers" +import { Form, Message, Modal } from "../semantic_ui_react_wrappers" import { optionalDatePropType, settingsPropType, uiModePropType } from "../sharedPropTypes" import { Avatar } from "../widgets/Avatar" -import { DatePicker } from "../widgets/DatePicker" -import { CollapseButton } from "./CollapseButton" -import { DownloadAsPDFButton } from "./DownloadAsPDFButton" -import { ResetSettingsButton } from "./ResetSettingsButton" +import { CollapseButton } from "./buttons/CollapseButton" +import { DatePickerButton } from "./buttons/DatePickerButton" +import { DownloadAsPDFButton } from "./buttons/DownloadAsPDFButton" +import { HomeButton } from "./buttons/HomeButton" +import { ResetSettingsButton } from "./buttons/ResetSettingsButton" +import { SettingsButton } from "./buttons/SettingsButton" import { UIModeMenu } from "./UIModeMenu" function Login({ set_user }) { @@ -49,7 +51,7 @@ function Login({ set_user }) { return ( + Login @@ -144,113 +146,42 @@ export function Menubar({ const atReportsOverview = report_uuid === "" return ( <> - - - { - event.preventDefault() - setSettingsPanelVisible(false) - openReportsOverview() - }} - tabIndex={atReportsOverview ? -1 : 0} - > - { - setSettingsPanelVisible(false) - openReportsOverview() - } - } - > - - Quality-time - - - } - /> - - { - event.preventDefault() - setSettingsPanelVisible(!settingsPanelVisible) - }} - > - { - event.stopPropagation() - setSettingsPanelVisible(!settingsPanelVisible) - }} - tabIndex={0} - > - - Settings - - - - + theme.zIndex.drawer + 1 }}> + + + + - - - - - - - - - - - - - } - /> - + + + onDate(date ? date.$d : null)} reportDate={report_date} /> - - {user !== null ? ( ) : ( )} - - - - { - event.stopPropagation() - setSettingsPanelVisible(false) - }} - unmountOnHide - > - - {panel} - - + + + + setSettingsPanelVisible(false)}> + + {panel} + > ) } diff --git a/components/frontend/src/header_footer/Menubar.test.js b/components/frontend/src/header_footer/Menubar.test.js index afdd1328c5..7a0090744b 100644 --- a/components/frontend/src/header_footer/Menubar.test.js +++ b/components/frontend/src/header_footer/Menubar.test.js @@ -1,4 +1,4 @@ -import { act, fireEvent, render, screen } from "@testing-library/react" +import { act, fireEvent, render, screen, waitFor } from "@testing-library/react" import userEvent from "@testing-library/user-event" import history from "history/browser" @@ -99,7 +99,7 @@ it("does not go to home page if on reports overview", async () => { const openReportsOverview = jest.fn() renderMenubar({ report_uuid: "", openReportsOverview: openReportsOverview }) act(() => { - fireEvent.click(screen.getByAltText(/Go home/)) + fireEvent.click(screen.getByAltText(/Go to reports overview/)) }) expect(openReportsOverview).not.toHaveBeenCalled() }) @@ -108,7 +108,7 @@ it("goes to home page if on report", async () => { const openReportsOverview = jest.fn() renderMenubar({ openReportsOverview: openReportsOverview }) await act(async () => { - fireEvent.click(screen.getByAltText(/Go home/)) + fireEvent.click(screen.getByAltText(/Go to reports overview/)) }) expect(openReportsOverview).toHaveBeenCalled() }) @@ -116,7 +116,7 @@ it("goes to home page if on report", async () => { it("goes to home page on keypress", async () => { const openReportsOverview = jest.fn() renderMenubar({ openReportsOverview: openReportsOverview }) - await userEvent.type(screen.getByAltText(/Go home/), "{Enter}") + await userEvent.type(screen.getByAltText(/Go to reports overview/), "{Enter}") expect(openReportsOverview).toHaveBeenCalled() }) @@ -132,12 +132,12 @@ it("shows the view panel on space", async () => { expect(screen.getAllByText(/Hello/).length).toBe(1) }) -it("hides the view panel on click", () => { +it("hides the view panel on click", async () => { renderMenubar({ panel: Hello }) fireEvent.click(screen.getByText(/Settings/)) expect(screen.getAllByText(/Hello/).length).toBe(1) fireEvent.click(screen.getByText(/Settings/)) - expect(screen.queryAllByText(/Hello/).length).toBe(0) + await waitFor(() => expect(screen.queryAllByText(/Hello/).length).toBe(0)) }) it("hides the view panel on escape", async () => { @@ -145,5 +145,5 @@ it("hides the view panel on escape", async () => { fireEvent.click(screen.getByText(/Settings/)) expect(screen.getAllByText(/Hello/).length).toBe(1) await userEvent.keyboard("{Escape}") - expect(screen.queryAllByText(/Hello/).length).toBe(0) + await waitFor(() => expect(screen.queryAllByText(/Hello/).length).toBe(0)) }) diff --git a/components/frontend/src/header_footer/ResetSettingsButton.js b/components/frontend/src/header_footer/ResetSettingsButton.js deleted file mode 100644 index e4982f9f6e..0000000000 --- a/components/frontend/src/header_footer/ResetSettingsButton.js +++ /dev/null @@ -1,43 +0,0 @@ -import { bool, func } from "prop-types" -import { Button, Icon } from "semantic-ui-react" - -import { Popup } from "../semantic_ui_react_wrappers" -import { optionalDatePropType, settingsPropType } from "../sharedPropTypes" - -export function ResetSettingsButton({ atReportsOverview, handleDateChange, reportDate, settings }) { - const label = `Reset ${atReportsOverview ? "reports overview" : "this report's"} settings` - return ( - - { - handleDateChange(null) - settings.reset() - }} - inverted - > - - - - - Reset settings - - - } - content={label} - /> - ) -} -ResetSettingsButton.propTypes = { - atReportsOverview: bool, - handleDateChange: func, - reportDate: optionalDatePropType, - settings: settingsPropType, -} diff --git a/components/frontend/src/header_footer/SettingsPanel.test.js b/components/frontend/src/header_footer/SettingsPanel.test.js index 2a02fe6ba0..2cb0b4e16c 100644 --- a/components/frontend/src/header_footer/SettingsPanel.test.js +++ b/components/frontend/src/header_footer/SettingsPanel.test.js @@ -127,14 +127,6 @@ it("sorts a column by keypress", async () => { expect(handleSort).toHaveBeenCalledWith("comment") }) -it("ignores a keypress if the menu item is disabled", async () => { - history.push("?hidden_columns=comment") - const handleSort = jest.fn() - renderSettingsPanel({ handleSort: handleSort }) - await userEvent.type(screen.getAllByText(/Comment/)[1], " ") - expect(handleSort).not.toHaveBeenCalledWith("comment") -}) - it("sets the number of dates", async () => { history.push("?nr_dates=2") renderSettingsPanel() diff --git a/components/frontend/src/header_footer/UIModeMenu.js b/components/frontend/src/header_footer/UIModeMenu.js index 960889f80b..2996814565 100644 --- a/components/frontend/src/header_footer/UIModeMenu.js +++ b/components/frontend/src/header_footer/UIModeMenu.js @@ -1,25 +1,59 @@ +import Brightness4Icon from "@mui/icons-material/Brightness4" +import { IconButton, Menu, MenuItem, Tooltip } from "@mui/material" import { func } from "prop-types" -import { Dropdown } from "semantic-ui-react" +import { useState } from "react" import { uiModePropType } from "../sharedPropTypes" -import { IconCombi } from "../widgets/IconCombi" export function UIModeMenu({ setUIMode, uiMode }) { + const [anchorEl, setAnchorEl] = useState() + const handleMenu = (event) => setAnchorEl(event.currentTarget) + const handleClose = () => setAnchorEl(null) return ( - }> - - Dark/light mode - setUIMode("system")}> - Follow OS setting - - setUIMode("dark")}> - Dark mode - - setUIMode("light")}> - Light mode - - - + + + + + + + { + handleClose() + setUIMode("system") + }} + selected={uiMode === "system"} + > + Follow OS setting + + { + handleClose() + setUIMode("dark") + }} + selected={uiMode === "dark"} + > + Dark mode + + { + handleClose() + setUIMode("light") + }} + selected={uiMode === "light"} + > + Light mode + + + + ) } UIModeMenu.propTypes = { diff --git a/components/frontend/src/header_footer/UIModeMenu.test.js b/components/frontend/src/header_footer/UIModeMenu.test.js index b53829ae82..594ba2a9f2 100644 --- a/components/frontend/src/header_footer/UIModeMenu.test.js +++ b/components/frontend/src/header_footer/UIModeMenu.test.js @@ -6,6 +6,7 @@ import { UIModeMenu } from "./UIModeMenu" it("sets dark mode", () => { const setUIMode = jest.fn() render() + fireEvent.click(screen.getByLabelText(/Dark\/light mode/)) fireEvent.click(screen.getByText(/Dark mode/)) expect(setUIMode).toHaveBeenCalledWith("dark") }) @@ -13,6 +14,7 @@ it("sets dark mode", () => { it("sets light mode", () => { const setUIMode = jest.fn() render() + fireEvent.click(screen.getByLabelText(/Dark\/light mode/)) fireEvent.click(screen.getByText(/Light mode/)) expect(setUIMode).toHaveBeenCalledWith("light") }) @@ -20,6 +22,7 @@ it("sets light mode", () => { it("sets follows os mode", () => { const setUIMode = jest.fn() render() + fireEvent.click(screen.getByLabelText(/Dark\/light mode/)) fireEvent.click(screen.getByText(/Follow OS/)) expect(setUIMode).toHaveBeenCalledWith("system") }) @@ -27,6 +30,7 @@ it("sets follows os mode", () => { it("sets dark mode on keypress", async () => { const setUIMode = jest.fn() render() + fireEvent.click(screen.getByLabelText(/Dark\/light mode/)) await userEvent.type(screen.getByText(/Dark mode/), " ") expect(setUIMode).toHaveBeenCalledWith("dark") }) diff --git a/components/frontend/src/header_footer/buttons/CollapseButton.js b/components/frontend/src/header_footer/buttons/CollapseButton.js new file mode 100644 index 0000000000..e3b21eedbf --- /dev/null +++ b/components/frontend/src/header_footer/buttons/CollapseButton.js @@ -0,0 +1,25 @@ +import UnfoldLessIcon from "@mui/icons-material/UnfoldLess" +import { Button, Tooltip } from "@mui/material" + +import { stringsURLSearchQueryPropType } from "../../sharedPropTypes" + +export function CollapseButton({ expandedItems }) { + return ( + + + expandedItems.reset()} + startIcon={} + sx={{ height: "100%" }} + > + Collapse all + + + + ) +} +CollapseButton.propTypes = { + expandedItems: stringsURLSearchQueryPropType, +} diff --git a/components/frontend/src/header_footer/CollapseButton.test.js b/components/frontend/src/header_footer/buttons/CollapseButton.test.js similarity index 87% rename from components/frontend/src/header_footer/CollapseButton.test.js rename to components/frontend/src/header_footer/buttons/CollapseButton.test.js index 0d9ee2bc54..2bd044e4b2 100644 --- a/components/frontend/src/header_footer/CollapseButton.test.js +++ b/components/frontend/src/header_footer/buttons/CollapseButton.test.js @@ -1,8 +1,8 @@ import { fireEvent, render, renderHook, screen } from "@testing-library/react" import history from "history/browser" -import { createTestableSettings } from "../__fixtures__/fixtures" -import { useExpandedItemsSearchQuery } from "../app_ui_settings" +import { createTestableSettings } from "../../__fixtures__/fixtures" +import { useExpandedItemsSearchQuery } from "../../app_ui_settings" import { CollapseButton } from "./CollapseButton" beforeEach(() => { @@ -19,7 +19,7 @@ it("resets the expanded items", () => { const expandedItems = renderHook(() => useExpandedItemsSearchQuery()) expect(expandedItems.result.current.value).toStrictEqual(["tab"]) renderCollapseButton({ expandedItems: expandedItems.result.current }) - fireEvent.click(screen.getByRole("button", { name: "Collapse all headers and metrics" })) + fireEvent.click(screen.getByRole("button", { name: "Collapse all" })) expandedItems.rerender() expect(expandedItems.result.current.value).toStrictEqual([]) }) @@ -28,7 +28,7 @@ it("doesn't change the expanded items if there are none", () => { const expandedItems = renderHook(() => useExpandedItemsSearchQuery()) expect(expandedItems.result.current.value).toStrictEqual([]) renderCollapseButton({ expandedItems: expandedItems.result.current }) - fireEvent.click(screen.getByRole("button", { name: "Collapse all headers and metrics" })) + fireEvent.click(screen.getByRole("button", { name: "Collapse all" })) expandedItems.rerender() expect(expandedItems.result.current.value).toStrictEqual([]) }) diff --git a/components/frontend/src/header_footer/buttons/DatePickerButton.js b/components/frontend/src/header_footer/buttons/DatePickerButton.js new file mode 100644 index 0000000000..f523ff988e --- /dev/null +++ b/components/frontend/src/header_footer/buttons/DatePickerButton.js @@ -0,0 +1,48 @@ +import EventIcon from "@mui/icons-material/Event" +import { Button, Menu, Tooltip } from "@mui/material" +import { StaticDatePicker } from "@mui/x-date-pickers/StaticDatePicker" +import { func } from "prop-types" +import { useState } from "react" + +import { datePropType } from "../../sharedPropTypes" + +export function DatePickerButton({ onChange, reportDate }) { + const [anchorEl, setAnchorEl] = useState() + const handleMenu = (event) => setAnchorEl(event.currentTarget) + const handleClose = () => setAnchorEl(null) + return ( + + + } + sx={{ height: "100%" }} + > + {reportDate ? reportDate.toDateString() : "today"} + + + { + handleClose() + onChange(value) + }} + slots={{ toolbar: null }} + slotProps={{ + actionBar: { + actions: ["today"], + }, + }} + /> + + + + ) +} +DatePickerButton.propTypes = { + onChange: func, + reportDate: datePropType, +} diff --git a/components/frontend/src/header_footer/DownloadAsPDFButton.js b/components/frontend/src/header_footer/buttons/DownloadAsPDFButton.js similarity index 61% rename from components/frontend/src/header_footer/DownloadAsPDFButton.js rename to components/frontend/src/header_footer/buttons/DownloadAsPDFButton.js index 565e9f7181..a096386531 100644 --- a/components/frontend/src/header_footer/DownloadAsPDFButton.js +++ b/components/frontend/src/header_footer/buttons/DownloadAsPDFButton.js @@ -1,11 +1,12 @@ +import PictureAsPdf from "@mui/icons-material/PictureAsPdf" +import { LoadingButton } from "@mui/lab" +import { Tooltip } from "@mui/material" import { string } from "prop-types" import { useState } from "react" -import { Icon } from "semantic-ui-react" -import { get_report_pdf } from "../api/report" -import { registeredURLSearchParams } from "../hooks/url_search_query" -import { Button, Popup } from "../semantic_ui_react_wrappers" -import { showMessage } from "../widgets/toast" +import { get_report_pdf } from "../../api/report" +import { registeredURLSearchParams } from "../../hooks/url_search_query" +import { showMessage } from "../../widgets/toast" function download_pdf(report_uuid, query_string, callback) { const reportId = report_uuid ? `report-${report_uuid}` : "reports-overview" @@ -41,29 +42,24 @@ export function DownloadAsPDFButton({ report_uuid }) { const itemType = report_uuid ? "report" : "reports overview" const label = `Download ${itemType} as PDF` return ( - { - if (!loading) { - setLoading(true) - download_pdf(report_uuid, `?${query.toString()}`, () => { - setLoading(false) - }) - } - }} - inverted - > - Download as PDF - - } - content={`Generate a PDF version of the ${itemType} as currently displayed. This may take some time.`} - /> + + { + if (!loading) { + setLoading(true) + download_pdf(report_uuid, `?${query.toString()}`, () => { + setLoading(false) + }) + } + }} + startIcon={} + > + Download as PDF + + ) } DownloadAsPDFButton.propTypes = { diff --git a/components/frontend/src/header_footer/DownloadAsPDFButton.test.js b/components/frontend/src/header_footer/buttons/DownloadAsPDFButton.test.js similarity index 70% rename from components/frontend/src/header_footer/DownloadAsPDFButton.test.js rename to components/frontend/src/header_footer/buttons/DownloadAsPDFButton.test.js index d8b8abb8e0..8e84a4f9d6 100644 --- a/components/frontend/src/header_footer/DownloadAsPDFButton.test.js +++ b/components/frontend/src/header_footer/buttons/DownloadAsPDFButton.test.js @@ -1,7 +1,7 @@ import { act, fireEvent, render, screen } from "@testing-library/react" import history from "history/browser" -import * as fetch_server_api from "../api/fetch_server_api" +import * as fetch_server_api from "../../api/fetch_server_api" import { DownloadAsPDFButton } from "./DownloadAsPDFButton" beforeEach(() => { @@ -22,12 +22,16 @@ test("DownloadAsPDFButton has the correct label for a report", () => { const test_report = { report_uuid: "report_uuid" } -test("DownloadAsPDFButton indicates loading on click", async () => { - render() +async function clickDownload() { await act(async () => { - fireEvent.click(screen.getByLabelText(/Download/)) + fireEvent.click(screen.getByText(/Download as PDF/)) }) - expect(screen.getByLabelText(/Download/).className).toContain("loading") +} + +test("DownloadAsPDFButton indicates loading on click", async () => { + render() + await clickDownload() + expect(screen.getByLabelText(/Download as PDF/).className).toContain("MuiCircularProgress-indeterminate") expect(fetch_server_api.fetch_server_api).toHaveBeenCalledWith( "get", "report/report_uuid/pdf?report_url=http%3A%2F%2Flocalhost%2F", @@ -39,9 +43,7 @@ test("DownloadAsPDFButton indicates loading on click", async () => { test("DownloadAsPDFButton ignores unregistered query parameters", async () => { history.push("?unregister_key=value&nr_dates=4") render() - await act(async () => { - fireEvent.click(screen.getByLabelText(/Download/)) - }) + await clickDownload() expect(fetch_server_api.fetch_server_api).toHaveBeenCalledWith( "get", "report/report_uuid/pdf?nr_dates=4&report_url=http%3A%2F%2Flocalhost%2F%3Fnr_dates%3D4", @@ -52,13 +54,10 @@ test("DownloadAsPDFButton ignores unregistered query parameters", async () => { test("DownloadAsPDFButton ignores a second click", async () => { render() - await act(async () => { - fireEvent.click(screen.getByLabelText(/Download/)) - }) - await act(async () => { - fireEvent.click(screen.getByLabelText(/Download/)) - }) - expect(screen.getByLabelText(/Download/).className).toContain("loading") + await clickDownload() + expect(screen.getByLabelText(/Download as PDF/).className).toContain("MuiCircularProgress-indeterminate") + await clickDownload() + expect(screen.getByLabelText(/Download as PDF/).className).toContain("MuiCircularProgress-indeterminate") }) test("DownloadAsPDFButton stops loading after returning pdf", async () => { @@ -66,17 +65,20 @@ test("DownloadAsPDFButton stops loading after returning pdf", async () => { HTMLAnchorElement.prototype.click = jest.fn() // Prevent "Not implemented: navigation (except hash changes)" window.URL.createObjectURL = jest.fn() render() - await act(async () => { - fireEvent.click(screen.getByLabelText(/Download/)) - }) - expect(screen.getByLabelText(/Download/).className).not.toContain("loading") + await clickDownload() + expect(screen.getByLabelText(/Download/).className).not.toContain("MuiCircularProgress-indeterminate") }) test("DownloadAsPDFButton stops loading after receiving error", async () => { fetch_server_api.fetch_server_api = jest.fn().mockResolvedValue({ ok: false }) render() - await act(async () => { - fireEvent.click(screen.getByLabelText(/Download/)) - }) - expect(screen.getByLabelText(/Download/).className).not.toContain("loading") + await clickDownload() + expect(screen.getByLabelText(/Download/).className).not.toContain("MuiCircularProgress-indeterminate") +}) + +test("DownloadAsPDFButton catches errors", async () => { + fetch_server_api.fetch_server_api.mockResolvedValue(new Error("Oops")) + render() + await clickDownload() + expect(screen.getByLabelText(/Download/).className).not.toContain("MuiCircularProgress-indeterminate") }) diff --git a/components/frontend/src/header_footer/buttons/HomeButton.js b/components/frontend/src/header_footer/buttons/HomeButton.js new file mode 100644 index 0000000000..d7de1fa8a3 --- /dev/null +++ b/components/frontend/src/header_footer/buttons/HomeButton.js @@ -0,0 +1,29 @@ +import { Button, Tooltip, Typography } from "@mui/material" +import { bool, func } from "prop-types" + +export function HomeButton({ atReportsOverview, openReportsOverview, setSettingsPanelVisible }) { + const label = "Go to reports overview" + return ( + + + { + setSettingsPanelVisible(false) + openReportsOverview() + }} + startIcon={} + sx={{ textTransform: "none" }} + > + Quality-time + + + + ) +} +HomeButton.propTypes = { + atReportsOverview: bool, + openReportsOverview: func, + setSettingsPanelVisible: func, +} diff --git a/components/frontend/src/header_footer/buttons/ResetSettingsButton.js b/components/frontend/src/header_footer/buttons/ResetSettingsButton.js new file mode 100644 index 0000000000..fad6c8fea9 --- /dev/null +++ b/components/frontend/src/header_footer/buttons/ResetSettingsButton.js @@ -0,0 +1,33 @@ +import SettingsBackupRestoreIcon from "@mui/icons-material/SettingsBackupRestore" +import { Button, Tooltip } from "@mui/material" +import { bool, func } from "prop-types" + +import { optionalDatePropType, settingsPropType } from "../../sharedPropTypes" + +export function ResetSettingsButton({ atReportsOverview, handleDateChange, reportDate, settings }) { + const label = `Reset ${atReportsOverview ? "reports overview" : "this report's"} settings` + return ( + + + } + onClick={() => { + handleDateChange(null) + settings.reset() + }} + sx={{ height: "100%" }} + > + Reset settings + + + + ) +} +ResetSettingsButton.propTypes = { + atReportsOverview: bool, + handleDateChange: func, + reportDate: optionalDatePropType, + settings: settingsPropType, +} diff --git a/components/frontend/src/header_footer/ResetSettingsButton.test.js b/components/frontend/src/header_footer/buttons/ResetSettingsButton.test.js similarity index 88% rename from components/frontend/src/header_footer/ResetSettingsButton.test.js rename to components/frontend/src/header_footer/buttons/ResetSettingsButton.test.js index 25630f0bd9..f1aa984f18 100644 --- a/components/frontend/src/header_footer/ResetSettingsButton.test.js +++ b/components/frontend/src/header_footer/buttons/ResetSettingsButton.test.js @@ -1,7 +1,7 @@ import { fireEvent, render, screen } from "@testing-library/react" import history from "history/browser" -import { createTestableSettings } from "../__fixtures__/fixtures" +import { createTestableSettings } from "../../__fixtures__/fixtures" import { ResetSettingsButton } from "./ResetSettingsButton" beforeEach(() => { @@ -38,7 +38,7 @@ it("resets the settings", async () => { reportDate: new Date("2023-01-01"), settings: settings, }) - fireEvent.click(screen.getAllByLabelText(/Reset reports overview settings/)[0]) + fireEvent.click(screen.getAllByText(/Reset settings/)[0]) expect(history.location.search).toEqual("") expect(handleDateChange).toHaveBeenCalledWith(null) }) @@ -51,6 +51,6 @@ it("does not reset the settings when all have the default value", async () => { handleDateChange: handleDateChange, settings: settings, }) - fireEvent.click(screen.getAllByLabelText(/Reset this report's settings/)[0]) + fireEvent.click(screen.getAllByText(/Reset settings/)[0]) expect(handleDateChange).not.toHaveBeenCalled() }) diff --git a/components/frontend/src/header_footer/buttons/SettingsButton.js b/components/frontend/src/header_footer/buttons/SettingsButton.js new file mode 100644 index 0000000000..0ad445148a --- /dev/null +++ b/components/frontend/src/header_footer/buttons/SettingsButton.js @@ -0,0 +1,20 @@ +import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown" +import ArrowRightIcon from "@mui/icons-material/ArrowRight" +import { Button } from "@mui/material" +import { bool, func } from "prop-types" + +export function SettingsButton({ settingsPanelVisible, setSettingsPanelVisible }) { + return ( + : } + onClick={() => setSettingsPanelVisible(!settingsPanelVisible)} + > + Settings + + ) +} +SettingsButton.propTypes = { + settingsPanelVisible: bool, + setSettingsPanelVisible: func, +} diff --git a/components/frontend/src/header_footer/settings_menu/SettingsMenu.css b/components/frontend/src/header_footer/settings_menu/SettingsMenu.css deleted file mode 100644 index 5a1ef65c1b..0000000000 --- a/components/frontend/src/header_footer/settings_menu/SettingsMenu.css +++ /dev/null @@ -1,4 +0,0 @@ -.ui.horizontal.segments.equal.width > .ui.segment { - flex-grow: 1; - width: 0; -} diff --git a/components/frontend/src/header_footer/settings_menu/SettingsMenu.js b/components/frontend/src/header_footer/settings_menu/SettingsMenu.js index 87e6fa4d0c..f690e46f67 100644 --- a/components/frontend/src/header_footer/settings_menu/SettingsMenu.js +++ b/components/frontend/src/header_footer/settings_menu/SettingsMenu.js @@ -1,18 +1,13 @@ -import "./SettingsMenu.css" - +import { MenuItem, MenuList, Stack, Tooltip, Typography } from "@mui/material" import { bool, func, number, oneOfType, string } from "prop-types" -import { Header, Menu, Segment } from "semantic-ui-react" -import { Popup } from "../../semantic_ui_react_wrappers" import { childrenPropType, popupContentPropType } from "../../sharedPropTypes" -const activeColor = "grey" - export function SettingsMenuGroup({ children }) { return ( - + {children} - + ) } SettingsMenuGroup.propTypes = { @@ -20,12 +15,11 @@ SettingsMenuGroup.propTypes = { } export function SettingsMenu({ children, title }) { - const menuProps = { compact: true, vertical: true, inverted: true, secondary: true } return ( - - {title} - {children} - + + {title} + {children} + ) } SettingsMenu.propTypes = { @@ -36,8 +30,6 @@ SettingsMenu.propTypes = { export function SettingsMenuItem({ active, children, disabled, disabledHelp, help, onClick, onClickData }) { // A menu item that can can show help when disabled so users can see why the menu item is disabled const props = { - active: active, - color: activeColor, disabled: disabled, onBeforeInput: (event) => { event.preventDefault() @@ -49,25 +41,21 @@ export function SettingsMenuItem({ active, children, disabled, disabledHelp, hel event.preventDefault() onClick(onClickData) }, + selected: active, tabIndex: 0, } if (help || (disabledHelp && disabled)) { - props["style"] = { marginLeft: 0, marginRight: 0, marginBottom: 5 } // Compensate for the span return ( - - {children} - - } - /> + + + {children} + + ) } - return {children} + return {children} } SettingsMenuItem.propTypes = { active: bool, diff --git a/components/frontend/src/header_footer/settings_menu/SortColumnMenu.js b/components/frontend/src/header_footer/settings_menu/SortColumnMenu.js index 4d8077d84f..df378d6cc9 100644 --- a/components/frontend/src/header_footer/settings_menu/SortColumnMenu.js +++ b/components/frontend/src/header_footer/settings_menu/SortColumnMenu.js @@ -1,6 +1,7 @@ +import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown" +import ArrowDropUpIcon from "@mui/icons-material/ArrowDropUp" import { bool, func, string } from "prop-types" -import { Icon } from "../../semantic_ui_react_wrappers" import { popupContentPropType, settingsPropType, @@ -85,11 +86,7 @@ SortColumnMenu.propTypes = { function SortColumnMenuItem({ column, disabled, sortColumn, sortDirection, handleSort, help }) { let sortIndicator = null if (sortColumn.equals(column) && sortDirection.value) { - // We use a triangle because the sort down and up icons are not at the same height - const iconDirection = sortDirection.equals("ascending") ? "up" : "down" - sortIndicator = ( - - ) + sortIndicator = sortDirection.equals("ascending") ? : } return ( - {capitalize(column === "name" ? "metric" : column).replaceAll("_", " ")} {sortIndicator} + {capitalize(column === "name" ? "metric" : column).replaceAll("_", " ")} {sortIndicator} ) } diff --git a/components/frontend/src/widgets/IconCombi.js b/components/frontend/src/widgets/IconCombi.js deleted file mode 100644 index 3ac1972780..0000000000 --- a/components/frontend/src/widgets/IconCombi.js +++ /dev/null @@ -1,17 +0,0 @@ -import { string } from "prop-types" -import { Icon } from "semantic-ui-react" - -export function IconCombi({ iconBottomRight, iconTopLeft, label }) { - const style = { textShadow: "0px 0px" } - return ( - - - - - ) -} -IconCombi.propTypes = { - iconBottomRight: string, - iconTopLeft: string, - label: string, -}