Skip to content

Commit d67e437

Browse files
authored
Reduce duplicate code for NavBar components (#1017)
* Reduce duplicate code for NavBar components This patch attempts to reduce duplicate code by moving components that would usually be passed to the NavBar component INTO the NavBar component. This does come with the trade-off that the NavBar component is now less general. However, as it is the NavBar component was pretty pointless anyway so this should not be an issue. * Change comment to reflect code (five seconds)
1 parent 3e95d9d commit d67e437

17 files changed

Lines changed: 453 additions & 875 deletions

File tree

src/components/About.tsx

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,19 @@ import React, { useEffect, useState } from "react";
22
import Header from "./Header";
33
import NavBar from "./NavBar";
44
import Footer from "./Footer";
5-
import MainNav from "./shared/MainNav";
65
import { useTranslation } from "react-i18next";
7-
import { Link, useLocation } from "react-router";
8-
import cn from "classnames";
6+
import { useLocation } from "react-router";
97
import axios from 'axios';
108
import i18n from "../i18n/i18n";
119
import DOMPurify from "dompurify";
1210

13-
const About: React.FC = () => {
11+
const About = () => {
1412
const { t } = useTranslation();
1513
const location = useLocation();
1614

1715
const [displayNavigation, setNavigation] = useState(false);
1816
const [aboutContent, setAboutContent] = useState<string>("");
1917

20-
const toggleNavigation = () => {
21-
setNavigation(!displayNavigation);
22-
};
23-
2418
useEffect(() => {
2519
const getURL = (language: string) => {
2620
return `/ui/config/admin-ui/${location.pathname.split("/").pop()}.${language}.html`;
@@ -46,12 +40,24 @@ const About: React.FC = () => {
4640
return (
4741
<span>
4842
<Header />
49-
<NavBar>
50-
<MainNav isOpen={displayNavigation} toggleMenu={toggleNavigation} />
51-
<nav>
52-
<Link to="/about/imprint" className={cn({ active: location.pathname === "/about/imprint" })} onClick={() => { }}>{t("ABOUT.IMPRINT")}</Link>
53-
<Link to="/about/privacy" className={cn({ active: location.pathname === "/about/privacy" })} onClick={() => { }}>{t("ABOUT.PRIVACY")}</Link>
54-
</nav>
43+
<NavBar
44+
displayNavigation={displayNavigation}
45+
setNavigation={setNavigation}
46+
links={[
47+
{
48+
path: "/about/imprint",
49+
accessRole: "ROLE_UI_USERS_VIEW",
50+
loadFn: () => { },
51+
text: "ABOUT.IMPRINT"
52+
},
53+
{
54+
path: "/about/privacy",
55+
accessRole: "ROLE_UI_GROUPS_VIEW",
56+
loadFn: () => { },
57+
text: "ABOUT.PRIVACY"
58+
},
59+
]}
60+
>
5561
</NavBar>
5662
<div className="about">
5763
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(aboutContent) }} ></div>

src/components/NavBar.tsx

Lines changed: 121 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,131 @@
1-
import React from "react";
1+
import React, { useRef } from "react";
2+
import { Link, useLocation } from "react-router";
3+
import { hasAccess } from "../utils/utils";
4+
import { AppDispatch, useAppDispatch, useAppSelector } from "../store";
5+
import { getUserInformation } from "../selectors/userInfoSelectors";
6+
import cn from "classnames";
7+
import { useTranslation } from "react-i18next";
8+
import MainNav from "./shared/MainNav";
9+
import { setOffset } from "../slices/tableSlice";
10+
import NewResourceModal, { NewResource } from "./shared/NewResourceModal";
11+
import { useHotkeys } from "react-hotkeys-hook";
12+
import { ModalHandle } from "./shared/modals/Modal";
213

314
/**
415
* Component that renders the nav bar
516
*/
6-
const NavBar: React.FC<{ children: React.ReactNode }> = ({ children }) => {
17+
type LinkType = {
18+
path: string
19+
accessRole: string
20+
loadFn: (dispatch: AppDispatch) => void
21+
text: string
22+
}
23+
24+
type CreateType = {
25+
accessRole: string
26+
onShowModal?: () => Promise<void>
27+
onHideModal?: () => void
28+
text: string
29+
isDisplay?: boolean
30+
resource: NewResource
31+
hotkeySequence?: string[]
32+
hotkeyDescription?: string
33+
}
34+
35+
const NavBar = ({
36+
children,
37+
navAriaLabel,
38+
displayNavigation,
39+
setNavigation,
40+
links,
41+
create,
42+
} : {
43+
children?: React.ReactNode
44+
navAriaLabel?: string
45+
displayNavigation: boolean
46+
setNavigation: React.Dispatch<React.SetStateAction<boolean>>
47+
links: LinkType[]
48+
create?: CreateType
49+
}) => {
50+
51+
const { t } = useTranslation();
52+
const dispatch = useAppDispatch();
53+
const location = useLocation();
54+
55+
const user = useAppSelector(state => getUserInformation(state));
56+
57+
const newResourceModalRef = useRef<ModalHandle>(null);
58+
59+
const showNewResourceModal = async () => {
60+
create && create.onShowModal && await create.onShowModal()
61+
newResourceModalRef.current?.open()
62+
};
63+
64+
const hideNewResourceModal = () => {
65+
create && create.onHideModal && create.onHideModal()
66+
newResourceModalRef.current?.close?.()
67+
};
68+
69+
const toggleNavigation = () => {
70+
setNavigation(!displayNavigation);
71+
};
72+
73+
useHotkeys(
74+
(create && create.hotkeySequence) ?? [],
75+
() => showNewResourceModal(),
76+
{ description: t((create && create.hotkeyDescription) ?? "") ?? undefined },
77+
[showNewResourceModal]
78+
);
79+
780
return (
881
<section className="action-nav-bar" role="navigation">
82+
{/* Display modal for new resource if add button is clicked */}
83+
{ create && (create.isDisplay === undefined || create.isDisplay) &&
84+
<NewResourceModal
85+
handleClose={hideNewResourceModal}
86+
resource={create.resource}
87+
modalRef={newResourceModalRef}
88+
/>
89+
}
90+
91+
{/* Include Burger-button menu */}
92+
<MainNav isOpen={displayNavigation} toggleMenu={toggleNavigation} />
93+
94+
<nav aria-label={navAriaLabel && t(navAriaLabel)}>
95+
{links.map((link) =>
96+
{return (hasAccess(link.accessRole, user) && (
97+
<Link
98+
to={link.path}
99+
className={cn({ active: location.pathname === link.path })}
100+
onClick={() => {
101+
if (location.pathname !== link.path) {
102+
// Reset the current page to first page
103+
dispatch(setOffset(0));
104+
}
105+
link.loadFn(dispatch)
106+
}}
107+
>
108+
{t(link.text)}
109+
</Link>
110+
))}
111+
)}
112+
</nav>
113+
9114
{children}
115+
116+
{create &&
117+
<div className="btn-group">
118+
{hasAccess(create.accessRole, user) && (
119+
<button
120+
className="add"
121+
onClick={showNewResourceModal}
122+
>
123+
<i className="fa fa-plus" />
124+
<span>{t(create.text)}</span>
125+
</button>
126+
)}
127+
</div>
128+
}
10129
</section>
11130
);
12131
};

src/components/configuration/Themes.tsx

Lines changed: 19 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,19 @@
1-
import React, { useEffect, useRef, useState } from "react";
1+
import React, { useEffect, useState } from "react";
22
import { useTranslation } from "react-i18next";
3-
import MainNav from "../shared/MainNav";
4-
import { Link } from "react-router";
5-
import cn from "classnames";
63
import TableFilters from "../shared/TableFilters";
74
import Table from "../shared/Table";
85
import { fetchFilters, editTextFilter } from "../../slices/tableFilterSlice";
96
import { themesTemplateMap } from "../../configs/tableConfigs/themesTableMap";
107
import { getTotalThemes } from "../../selectors/themeSelectors";
118
import { loadThemesIntoTable } from "../../thunks/tableThunks";
129
import Notifications from "../shared/Notifications";
13-
import NewResourceModal from "../shared/NewResourceModal";
1410
import Header from "../Header";
1511
import NavBar from "../NavBar";
1612
import MainView from "../MainView";
1713
import Footer from "../Footer";
18-
import { getUserInformation } from "../../selectors/userInfoSelectors";
19-
import { hasAccess } from "../../utils/utils";
2014
import { getCurrentFilterResource } from "../../selectors/tableFilterSelectors";
2115
import { useAppDispatch, useAppSelector } from "../../store";
2216
import { fetchThemes } from "../../slices/themeSlice";
23-
import { ModalHandle } from "../shared/modals/Modal";
2417

2518
/**
2619
* This component renders the table view of events
@@ -32,9 +25,7 @@ const Themes = () => {
3225
const currentFilterType = useAppSelector(state => getCurrentFilterResource(state));
3326

3427
const [displayNavigation, setNavigation] = useState(false);
35-
const newThemesModalRef = useRef<ModalHandle>(null);
3628

37-
const user = useAppSelector(state => getUserInformation(state));
3829
const themes = useAppSelector(state => getTotalThemes(state));
3930

4031
const loadThemes = async () => {
@@ -54,7 +45,7 @@ const Themes = () => {
5445
dispatch(editTextFilter(""));
5546

5647
// Load themes on mount
57-
loadThemes().then((r) => console.info(r));
48+
loadThemes();
5849

5950
// Fetch themes every minute
6051
let fetchThemesInterval = setInterval(loadThemes, 5000);
@@ -63,54 +54,26 @@ const Themes = () => {
6354
// eslint-disable-next-line react-hooks/exhaustive-deps
6455
}, []);
6556

66-
const toggleNavigation = () => {
67-
setNavigation(!displayNavigation);
68-
};
69-
70-
const showNewThemesModal = () => {
71-
newThemesModalRef.current?.open();
72-
};
73-
74-
const hideNewThemesModal = () => {
75-
newThemesModalRef.current?.close?.();
76-
};
77-
7857
return (
7958
<>
8059
<Header />
81-
<NavBar>
82-
{/* Display modal for new series if add series button is clicked */}
83-
<NewResourceModal
84-
handleClose={hideNewThemesModal}
85-
resource={"themes"}
86-
modalRef={newThemesModalRef}
87-
/>
88-
89-
{/* Include Burger-button menu*/}
90-
<MainNav isOpen={displayNavigation} toggleMenu={toggleNavigation} />
91-
92-
<nav>
93-
{hasAccess("ROLE_UI_THEMES_VIEW", user) && (
94-
<Link
95-
to="/configuration/themes"
96-
className={cn({ active: true })}
97-
onClick={() => loadThemes()}
98-
>
99-
{t("CONFIGURATION.NAVIGATION.THEMES")}
100-
</Link>
101-
)}
102-
</nav>
103-
104-
{/* Add theme button */}
105-
<div className="btn-group">
106-
{hasAccess("ROLE_UI_THEMES_CREATE", user) && (
107-
<button className="add" onClick={() => showNewThemesModal()}>
108-
<i className="fa fa-plus" />
109-
<span>{t("CONFIGURATION.ACTIONS.ADD_THEME")}</span>
110-
</button>
111-
)}
112-
</div>
113-
</NavBar>
60+
<NavBar
61+
displayNavigation={displayNavigation}
62+
setNavigation={setNavigation}
63+
links={[
64+
{
65+
path: "/configuration/themes",
66+
accessRole: "ROLE_UI_THEMES_VIEW",
67+
loadFn: loadThemes,
68+
text: "CONFIGURATION.NAVIGATION.THEMES"
69+
},
70+
]}
71+
create={{
72+
accessRole: "ROLE_UI_THEMES_CREATE",
73+
text: "CONFIGURATION.ACTIONS.ADD_THEME",
74+
resource: "themes",
75+
}}
76+
/>
11477

11578
<MainView open={displayNavigation}>
11679
{/* Include notifications component */}

0 commit comments

Comments
 (0)