From 08189609f0aded4a5ba4f5e19e1e59c0e638c59b Mon Sep 17 00:00:00 2001
From: Frank Niessink <frank@niessink.com>
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         | 350 +++++++++++++-----
 components/frontend/package.json              |   4 +-
 components/frontend/src/App.js                |   4 +-
 components/frontend/src/App.test.js           |  34 +-
 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    |  21 --
 .../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 |  22 +-
 .../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, 657 insertions(+), 474 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 (78%)
 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 198fe0f493..eeb368e37c 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,10 +9197,6 @@
             "dev": true,
             "license": "MIT"
         },
-        "node_modules/detect-node-es": {
-            "version": "1.1.0",
-            "license": "MIT"
-        },
         "node_modules/detect-port-alt": {
             "version": "1.1.6",
             "dev": true,
@@ -10701,16 +10958,6 @@
             "dev": true,
             "license": "ISC"
         },
-        "node_modules/focus-lock": {
-            "version": "1.3.5",
-            "license": "MIT",
-            "dependencies": {
-                "tslib": "^2.0.3"
-            },
-            "engines": {
-                "node": ">=10"
-            }
-        },
         "node_modules/follow-redirects": {
             "version": "1.15.6",
             "dev": true,
@@ -19528,16 +19775,6 @@
             "dev": true,
             "license": "MIT"
         },
-        "node_modules/react-clientside-effect": {
-            "version": "1.2.6",
-            "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",
@@ -19710,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",
@@ -23611,6 +23825,7 @@
         },
         "node_modules/tslib": {
             "version": "2.6.2",
+            "dev": true,
             "license": "0BSD"
         },
         "node_modules/tsutils": {
@@ -23917,45 +24132,6 @@
                 "requires-port": "^1.0.0"
             }
         },
-        "node_modules/use-callback-ref": {
-            "version": "1.3.2",
-            "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",
-            "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 db08bdc282..a2539922e3 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..0579e832b7 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"
@@ -71,30 +70,49 @@ it("resets the user when the user clicks logout", async () => {
 
 it("handles a date change", async () => {
     render(<App />)
-    await userEvent.type(screen.getByPlaceholderText("YYYY-MM-DD"), "2020-03-13")
-    expect(screen.getAllByDisplayValue("2020-03-13").length).toBe(1)
+    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])
+    })
+    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(<App />)
-    await userEvent.type(screen.getByPlaceholderText("YYYY-MM-DD"), "{Backspace}4")
-    expect(screen.getAllByDisplayValue("2022-03-14").length).toBe(1)
+    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])
+    })
+    expect(screen.getByLabelText("Report date").textContent).toMatch(/15/)
 })
 
 it("reads the report date query parameter", () => {
     history.push("/?report_date=2020-03-13")
     render(<App />)
-    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(<App />)
     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,
             }}
         >
-            <DarkMode.Provider value={darkMode}>
-                <HashLinkObserver />
-                <Menubar
-                    email={email}
-                    handleDateChange={handleDateChange}
-                    openReportsOverview={openReportsOverview}
-                    onDate={handleDateChange}
-                    report_date={report_date}
-                    report_uuid={report_uuid}
-                    set_user={set_user}
-                    user={user}
-                    panel={
-                        <SettingsPanel
-                            atReportsOverview={atReportsOverview}
-                            handleSort={handleSort}
-                            settings={settings}
-                            tags={getReportsTags(reports)}
-                        />
-                    }
-                    settings={settings}
-                    setUIMode={setMode}
-                    uiMode={mode}
-                />
-                <ToastContainer theme="colored" />
-                <Permissions.Provider value={user_permissions}>
-                    <DataModel.Provider value={dataModel}>
-                        <PageContent
-                            changed_fields={changed_fields}
-                            current_report={current_report}
-                            handleSort={handleSort}
-                            lastUpdate={lastUpdate}
-                            loading={loading}
-                            nrMeasurements={nrMeasurements}
-                            openReportsOverview={openReportsOverview}
-                            openReport={openReport}
-                            reload={reload}
-                            report_date={report_date}
-                            report_uuid={report_uuid}
-                            reports={reports}
-                            reports_overview={reports_overview}
-                            settings={settings}
-                        />
-                    </DataModel.Provider>
-                </Permissions.Provider>
-                <Footer lastUpdate={lastUpdate} report={current_report} />
-            </DarkMode.Provider>
+            <LocalizationProvider dateAdapter={AdapterDayjs} adapterLocale={locale_en_gb}>
+                <DarkMode.Provider value={darkMode}>
+                    <HashLinkObserver />
+                    <Menubar
+                        email={email}
+                        handleDateChange={handleDateChange}
+                        openReportsOverview={openReportsOverview}
+                        onDate={handleDateChange}
+                        report_date={report_date}
+                        report_uuid={report_uuid}
+                        set_user={set_user}
+                        user={user}
+                        panel={
+                            <SettingsPanel
+                                atReportsOverview={atReportsOverview}
+                                handleSort={handleSort}
+                                settings={settings}
+                                tags={getReportsTags(reports)}
+                            />
+                        }
+                        settings={settings}
+                        setUIMode={setMode}
+                        uiMode={mode}
+                    />
+                    <ToastContainer theme="colored" />
+                    <Permissions.Provider value={user_permissions}>
+                        <DataModel.Provider value={dataModel}>
+                            <PageContent
+                                changed_fields={changed_fields}
+                                current_report={current_report}
+                                handleSort={handleSort}
+                                lastUpdate={lastUpdate}
+                                loading={loading}
+                                nrMeasurements={nrMeasurements}
+                                openReportsOverview={openReportsOverview}
+                                openReport={openReport}
+                                reload={reload}
+                                report_date={report_date}
+                                report_uuid={report_uuid}
+                                reports={reports}
+                                reports_overview={reports_overview}
+                                settings={settings}
+                            />
+                        </DataModel.Provider>
+                    </Permissions.Provider>
+                    <Footer lastUpdate={lastUpdate} report={current_report} />
+                </DarkMode.Provider>
+            </LocalizationProvider>
         </div>
     )
 }
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 (
-        <Popup
-            on={["hover", "focus"]}
-            trigger={
-                <span // We need a span here to prevent the popup from becoming disabled whenever the button is disabled
-                >
-                    <Button
-                        aria-label={label}
-                        basic
-                        disabled={expandedItems.equals([])}
-                        icon
-                        onClick={() => expandedItems.reset()}
-                        inverted
-                    >
-                        <Icon name="angle double up" /> Collapse all
-                    </Button>
-                </span>
-            }
-            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..299ea3fa50 100644
--- a/components/frontend/src/header_footer/Menubar.css
+++ b/components/frontend/src/header_footer/Menubar.css
@@ -3,24 +3,3 @@
         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 (
         <Modal
             trigger={
-                <Button secondary>
+                <Button color="inherit">
                     <Icon name="user" />
                     Login
                 </Button>
@@ -144,113 +146,42 @@ export function Menubar({
     const atReportsOverview = report_uuid === ""
     return (
         <>
-            <Menu fluid className="menubar" inverted fixed="top">
-                <Menu.Menu position="left">
-                    <Popup
-                        content="Go to reports overview"
-                        disabled={atReportsOverview}
-                        trigger={
-                            <div
-                                onBeforeInput={(event) => {
-                                    event.preventDefault()
-                                    setSettingsPanelVisible(false)
-                                    openReportsOverview()
-                                }}
-                                tabIndex={atReportsOverview ? -1 : 0}
-                            >
-                                <Menu.Item
-                                    header
-                                    onClick={
-                                        atReportsOverview
-                                            ? null
-                                            : () => {
-                                                  setSettingsPanelVisible(false)
-                                                  openReportsOverview()
-                                              }
-                                    }
-                                >
-                                    <Image size="mini" src="/favicon.ico" alt="Go home" />
-                                    <span style={{ paddingLeft: "6mm", fontSize: "2em" }}>Quality-time</span>
-                                </Menu.Item>
-                            </div>
-                        }
-                    />
-                    <FocusLock group="settingsPanel" disabled={!settingsPanelVisible} className="center">
-                        <div
-                            onBeforeInput={(event) => {
-                                event.preventDefault()
-                                setSettingsPanelVisible(!settingsPanelVisible)
-                            }}
-                        >
-                            <Menu.Item
-                                onClick={(event) => {
-                                    event.stopPropagation()
-                                    setSettingsPanelVisible(!settingsPanelVisible)
-                                }}
-                                tabIndex={0}
-                            >
-                                <Icon size="large" name={`caret ${settingsPanelVisible ? "down" : "right"}`} />
-                                Settings
-                            </Menu.Item>
-                        </div>
-                    </FocusLock>
-                    <Menu.Item>
+            <AppBar position="fixed" sx={{ zIndex: (theme) => theme.zIndex.drawer + 1 }}>
+                <Toolbar>
+                    <Stack direction="row" spacing={2} sx={{ flexGrow: 1 }}>
+                        <HomeButton
+                            atReportsOverview={atReportsOverview}
+                            openReportsOverview={openReportsOverview}
+                            setSettingsPanelVisible={setSettingsPanelVisible}
+                        />
+                        <SettingsButton
+                            setSettingsPanelVisible={setSettingsPanelVisible}
+                            settingsPanelVisible={settingsPanelVisible}
+                        />
                         <ResetSettingsButton
                             atReportsOverview={atReportsOverview}
                             handleDateChange={handleDateChange}
                             reportDate={report_date}
                             settings={settings}
                         />
-                    </Menu.Item>
-                    <Menu.Item>
                         <CollapseButton expandedItems={settings.expandedItems} />
-                    </Menu.Item>
-                    <Menu.Item>
                         <DownloadAsPDFButton report_uuid={report_uuid} />
-                    </Menu.Item>
-                </Menu.Menu>
-                <Menu.Menu position="right">
-                    <Popup
-                        content="Show the report as it was on the selected date"
-                        position="left center"
-                        trigger={
-                            <Menu.Item>
-                                <Form>
-                                    <DatePicker
-                                        isClearable={true}
-                                        maxDate={new Date()}
-                                        onChange={onDate}
-                                        selected={report_date}
-                                    />
-                                </Form>
-                            </Menu.Item>
-                        }
-                    />
-                    <Menu.Item>
+                    </Stack>
+                    <Stack direction="row" spacing={2}>
+                        <DatePickerButton onChange={(date) => onDate(date ? date.$d : null)} reportDate={report_date} />
                         <UIModeMenu setUIMode={setUIMode} uiMode={uiMode} />
-                    </Menu.Item>
-                    <Menu.Item>
                         {user !== null ? (
                             <Logout email={email} user={user} set_user={set_user} />
                         ) : (
                             <Login set_user={set_user} />
                         )}
-                    </Menu.Item>
-                </Menu.Menu>
-            </Menu>
-            <Portal
-                closeOnTriggerClick={true}
-                open={settingsPanelVisible}
-                onClose={(event) => {
-                    event.stopPropagation()
-                    setSettingsPanelVisible(false)
-                }}
-                unmountOnHide
-            >
-                <div className="panel">
-                    <FocusLock group="settingsPanel">{panel}</FocusLock>
-                </div>
-            </Portal>
+                    </Stack>
+                </Toolbar>
+            </AppBar>
+            <Drawer anchor="top" open={settingsPanelVisible} onClose={() => setSettingsPanelVisible(false)}>
+                <Toolbar /* Add an empty toolbar to the drawer so the panel is not partly hidden by the appbar. */ />
+                {panel}
+            </Drawer>
         </>
     )
 }
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: <div>Hello</div> })
     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 (
-        <Popup
-            on={["hover", "focus"]}
-            trigger={
-                <span // We need a span here to prevent the popup from becoming disabled whenever the button is disabled
-                >
-                    <Button
-                        aria-label={label}
-                        basic
-                        disabled={settings.allDefault() && reportDate === null}
-                        icon
-                        onClick={() => {
-                            handleDateChange(null)
-                            settings.reset()
-                        }}
-                        inverted
-                    >
-                        <Icon.Group>
-                            <Icon name="undo alternate" />
-                            <Icon name="setting" size="tiny" />
-                        </Icon.Group>
-                        Reset settings
-                    </Button>
-                </span>
-            }
-            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 (
-        <Dropdown icon={<IconCombi iconBottomRight="moon" iconTopLeft="sun" label="Dark/light mode" />}>
-            <Dropdown.Menu>
-                <Dropdown.Header>Dark/light mode</Dropdown.Header>
-                <Dropdown.Item active={uiMode === "system"} onClick={() => setUIMode("system")}>
-                    Follow OS setting
-                </Dropdown.Item>
-                <Dropdown.Item active={uiMode === "dark"} onClick={() => setUIMode("dark")}>
-                    Dark mode
-                </Dropdown.Item>
-                <Dropdown.Item active={uiMode === "light"} onClick={() => setUIMode("light")}>
-                    Light mode
-                </Dropdown.Item>
-            </Dropdown.Menu>
-        </Dropdown>
+        <Tooltip placement="left" title="Change dark/light mode">
+            <span>
+                <IconButton
+                    aria-label="Dark/light mode"
+                    aria-controls="dark-light-menu"
+                    aria-haspopup="true"
+                    color="inherit"
+                    onClick={handleMenu}
+                    size="large"
+                    sx={{ height: "100%" }}
+                >
+                    <Brightness4Icon />
+                </IconButton>
+                <Menu id="dark-light-menu" anchorEl={anchorEl} onClose={handleClose} open={Boolean(anchorEl)}>
+                    <MenuItem
+                        onClick={() => {
+                            handleClose()
+                            setUIMode("system")
+                        }}
+                        selected={uiMode === "system"}
+                    >
+                        Follow OS setting
+                    </MenuItem>
+                    <MenuItem
+                        onClick={() => {
+                            handleClose()
+                            setUIMode("dark")
+                        }}
+                        selected={uiMode === "dark"}
+                    >
+                        Dark mode
+                    </MenuItem>
+                    <MenuItem
+                        onClick={() => {
+                            handleClose()
+                            setUIMode("light")
+                        }}
+                        selected={uiMode === "light"}
+                    >
+                        Light mode
+                    </MenuItem>
+                </Menu>
+            </span>
+        </Tooltip>
     )
 }
 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(<UIModeMenu setUIMode={setUIMode} />)
+    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(<UIModeMenu setUIMode={setUIMode} uiMode="dark" />)
+    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(<UIModeMenu setUIMode={setUIMode} uiMode="dark" />)
+    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(<UIModeMenu setUIMode={setUIMode} />)
+    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 (
+        <Tooltip title={"Collapse all headers and metrics"}>
+            <span /* https://mui.com/material-ui/react-tooltip/#disabled-elements */>
+                <Button
+                    color="inherit"
+                    disabled={expandedItems.equals([])}
+                    onClick={() => expandedItems.reset()}
+                    startIcon={<UnfoldLessIcon />}
+                    sx={{ height: "100%" }}
+                >
+                    Collapse all
+                </Button>
+            </span>
+        </Tooltip>
+    )
+}
+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 (
+        <Tooltip placement="left" title="Show the report as it was on the selected date">
+            <span>
+                <Button
+                    aria-label="Report date"
+                    aria-controls="date-picker-button-menu"
+                    color="inherit"
+                    onClick={handleMenu}
+                    startIcon={<EventIcon />}
+                    sx={{ height: "100%" }}
+                >
+                    {reportDate ? reportDate.toDateString() : "today"}
+                </Button>
+                <Menu id="date-picker-button-menu" anchorEl={anchorEl} onClose={handleClose} open={Boolean(anchorEl)}>
+                    <StaticDatePicker
+                        disableFuture
+                        onChange={(value) => {
+                            handleClose()
+                            onChange(value)
+                        }}
+                        slots={{ toolbar: null }}
+                        slotProps={{
+                            actionBar: {
+                                actions: ["today"],
+                            },
+                        }}
+                    />
+                </Menu>
+            </span>
+        </Tooltip>
+    )
+}
+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 (
-        <Popup
-            on={["hover", "focus"]}
-            trigger={
-                <Button
-                    aria-label={label}
-                    basic
-                    icon
-                    loading={loading}
-                    onClick={() => {
-                        if (!loading) {
-                            setLoading(true)
-                            download_pdf(report_uuid, `?${query.toString()}`, () => {
-                                setLoading(false)
-                            })
-                        }
-                    }}
-                    inverted
-                >
-                    <Icon name="file pdf" /> Download as PDF
-                </Button>
-            }
-            content={`Generate a PDF version of the ${itemType} as currently displayed. This may take some time.`}
-        />
+        <Tooltip title={`Generate a PDF version of the ${itemType} as currently displayed. This may take some time.`}>
+            <LoadingButton
+                aria-label={label}
+                color="inherit"
+                loading={loading}
+                onClick={() => {
+                    if (!loading) {
+                        setLoading(true)
+                        download_pdf(report_uuid, `?${query.toString()}`, () => {
+                            setLoading(false)
+                        })
+                    }
+                }}
+                startIcon={<PictureAsPdf />}
+            >
+                Download as PDF
+            </LoadingButton>
+        </Tooltip>
     )
 }
 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 78%
rename from components/frontend/src/header_footer/DownloadAsPDFButton.test.js
rename to components/frontend/src/header_footer/buttons/DownloadAsPDFButton.test.js
index d8b8abb8e0..712b4e577f 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(() => {
@@ -25,9 +25,9 @@ const test_report = { report_uuid: "report_uuid" }
 test("DownloadAsPDFButton indicates loading on click", async () => {
     render(<DownloadAsPDFButton report={test_report} report_uuid="report_uuid" />)
     await act(async () => {
-        fireEvent.click(screen.getByLabelText(/Download/))
+        fireEvent.click(screen.getByText(/Download as PDF/))
     })
-    expect(screen.getByLabelText(/Download/).className).toContain("loading")
+    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",
@@ -40,7 +40,7 @@ test("DownloadAsPDFButton ignores unregistered query parameters", async () => {
     history.push("?unregister_key=value&nr_dates=4")
     render(<DownloadAsPDFButton report={test_report} report_uuid="report_uuid" />)
     await act(async () => {
-        fireEvent.click(screen.getByLabelText(/Download/))
+        fireEvent.click(screen.getByText(/Download/))
     })
     expect(fetch_server_api.fetch_server_api).toHaveBeenCalledWith(
         "get",
@@ -53,12 +53,12 @@ test("DownloadAsPDFButton ignores unregistered query parameters", async () => {
 test("DownloadAsPDFButton ignores a second click", async () => {
     render(<DownloadAsPDFButton report={test_report} />)
     await act(async () => {
-        fireEvent.click(screen.getByLabelText(/Download/))
+        fireEvent.click(screen.getByText(/Download as PDF/))
     })
     await act(async () => {
-        fireEvent.click(screen.getByLabelText(/Download/))
+        fireEvent.click(screen.getByText(/Download as PDF/))
     })
-    expect(screen.getByLabelText(/Download/).className).toContain("loading")
+    expect(screen.getByLabelText(/Download as PDF/).className).toContain("MuiCircularProgress-indeterminate")
 })
 
 test("DownloadAsPDFButton stops loading after returning pdf", async () => {
@@ -67,16 +67,16 @@ test("DownloadAsPDFButton stops loading after returning pdf", async () => {
     window.URL.createObjectURL = jest.fn()
     render(<DownloadAsPDFButton report={test_report} />)
     await act(async () => {
-        fireEvent.click(screen.getByLabelText(/Download/))
+        fireEvent.click(screen.getByText(/Download as PDF/))
     })
-    expect(screen.getByLabelText(/Download/).className).not.toContain("loading")
+    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(<DownloadAsPDFButton report={test_report} />)
     await act(async () => {
-        fireEvent.click(screen.getByLabelText(/Download/))
+        fireEvent.click(screen.getByText(/Download as PDF/))
     })
-    expect(screen.getByLabelText(/Download/).className).not.toContain("loading")
+    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 (
+        <Tooltip title={label}>
+            <span /* https://mui.com/material-ui/react-tooltip/#disabled-elements */>
+                <Button
+                    color="inherit"
+                    disabled={atReportsOverview}
+                    onClick={() => {
+                        setSettingsPanelVisible(false)
+                        openReportsOverview()
+                    }}
+                    startIcon={<img height="28px" width="28px" src="/favicon.ico" alt={label} />}
+                    sx={{ textTransform: "none" }}
+                >
+                    <Typography variant="h4">Quality-time</Typography>
+                </Button>
+            </span>
+        </Tooltip>
+    )
+}
+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 (
+        <Tooltip title={label}>
+            <span /* https://mui.com/material-ui/react-tooltip/#disabled-elements */>
+                <Button
+                    color="inherit"
+                    disabled={settings.allDefault() && reportDate === null}
+                    startIcon={<SettingsBackupRestoreIcon />}
+                    onClick={() => {
+                        handleDateChange(null)
+                        settings.reset()
+                    }}
+                    sx={{ height: "100%" }}
+                >
+                    Reset settings
+                </Button>
+            </span>
+        </Tooltip>
+    )
+}
+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 (
+        <Button
+            color="inherit"
+            startIcon={settingsPanelVisible ? <ArrowDropDownIcon /> : <ArrowRightIcon />}
+            onClick={() => setSettingsPanelVisible(!settingsPanelVisible)}
+        >
+            Settings
+        </Button>
+    )
+}
+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 (
-        <Segment.Group horizontal className="equal width" style={{ margin: "0px", border: "0px" }}>
+        <Stack direction="row" sx={{ justifyContent: "space-between", padding: "20px" }}>
             {children}
-        </Segment.Group>
+        </Stack>
     )
 }
 SettingsMenuGroup.propTypes = {
@@ -20,12 +15,11 @@ SettingsMenuGroup.propTypes = {
 }
 
 export function SettingsMenu({ children, title }) {
-    const menuProps = { compact: true, vertical: true, inverted: true, secondary: true }
     return (
-        <Segment inverted color="black">
-            <Header size="small">{title}</Header>
-            <Menu {...menuProps}>{children}</Menu>
-        </Segment>
+        <Stack>
+            <Typography variant="h6">{title}</Typography>
+            <MenuList>{children}</MenuList>
+        </Stack>
     )
 }
 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 (
-            <Popup
-                content={disabledHelp || help}
-                inverted
-                position="left center"
-                // We need a span here to prevent the popup from becoming disabled when the menu item is disabled:
-                trigger={
-                    <span>
-                        <Menu.Item {...props}>{children}</Menu.Item>
-                    </span>
-                }
-            />
+            <Tooltip placement="left" title={disabledHelp || help}>
+                <span
+                // We need a span here to prevent the popup from becoming disabled when the menu item is disabled
+                >
+                    <MenuItem {...props}>{children}</MenuItem>
+                </span>
+            </Tooltip>
         )
     }
-    return <Menu.Item {...props}>{children}</Menu.Item>
+    return <MenuItem {...props}>{children}</MenuItem>
 }
 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 = (
-            <Icon disabled={disabled} name={`triangle ${iconDirection}`} aria-label={`sorted ${sortDirection.value}`} />
-        )
+        sortIndicator = sortDirection.equals("ascending") ? <ArrowDropDownIcon /> : <ArrowDropUpIcon />
     }
     return (
         <SettingsMenuItem
@@ -99,7 +96,7 @@ function SortColumnMenuItem({ column, disabled, sortColumn, sortDirection, handl
             onClick={handleSort}
             onClickData={column}
         >
-            {capitalize(column === "name" ? "metric" : column).replaceAll("_", " ")} <span>{sortIndicator}</span>
+            {capitalize(column === "name" ? "metric" : column).replaceAll("_", " ")} {sortIndicator}
         </SettingsMenuItem>
     )
 }
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 (
-        <Icon.Group aria-label={label} size="big">
-            <Icon corner="top left" name={iconTopLeft} style={style} />
-            <Icon corner="bottom right" name={iconBottomRight} style={style} />
-        </Icon.Group>
-    )
-}
-IconCombi.propTypes = {
-    iconBottomRight: string,
-    iconTopLeft: string,
-    label: string,
-}