diff --git a/src/components/AppBar/NavigationButtons.tsx b/src/components/AppBar/NavigationButtons.tsx
index 5017200c9a..6f9a25d9ed 100644
--- a/src/components/AppBar/NavigationButtons.tsx
+++ b/src/components/AppBar/NavigationButtons.tsx
@@ -1,15 +1,19 @@
import { PlaylistAdd, Rule } from "@mui/icons-material";
import { Button, Hidden, Tooltip, Typography } from "@mui/material";
-import { ReactElement } from "react";
+import { ReactElement, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
+import { Permission } from "api/models";
+import { getCurrentPermissions } from "backend";
import {
TabProps,
appBarHeight,
buttonMinHeight,
tabColor,
} from "components/AppBar/AppBarTypes";
+import { StoreState } from "types";
+import { useAppSelector } from "types/hooks";
import { Path } from "types/path";
import { useWindowSize } from "utilities/useWindowSize";
@@ -20,6 +24,20 @@ const navButtonMaxWidthProportion = 0.2;
/** Buttons for navigating to Data Entry and Data Cleanup */
export default function NavigationButtons(props: TabProps): ReactElement {
+ const projectId = useAppSelector(
+ (state: StoreState) => state.currentProjectState.project.id
+ );
+ const [hasGoalPermission, setHasGoalPermission] = useState(false);
+
+ useEffect(() => {
+ getCurrentPermissions().then((perms) => {
+ setHasGoalPermission(
+ perms.includes(Permission.CharacterInventory) ||
+ perms.includes(Permission.MergeAndReviewEntries)
+ );
+ });
+ }, [projectId]);
+
return (
<>
- }
- targetPath={Path.Goals}
- textId="appBar.dataCleanup"
- />
+ {hasGoalPermission && (
+ }
+ targetPath={Path.Goals}
+ textId="appBar.dataCleanup"
+ />
+ )}
>
);
}
diff --git a/src/components/AppBar/tests/NavigationButtons.test.tsx b/src/components/AppBar/tests/NavigationButtons.test.tsx
index 285205752e..2136106f80 100644
--- a/src/components/AppBar/tests/NavigationButtons.test.tsx
+++ b/src/components/AppBar/tests/NavigationButtons.test.tsx
@@ -1,7 +1,10 @@
+import { Provider } from "react-redux";
import renderer, { ReactTestInstance } from "react-test-renderer";
+import configureMockStore from "redux-mock-store";
import "tests/reactI18nextMock";
+import { Permission } from "api";
import NavigationButtons, {
dataCleanupButtonId,
dataEntryButtonId,
@@ -13,38 +16,98 @@ jest.mock("react-router-dom", () => ({
useNavigate: jest.fn(),
}));
+jest.mock("backend", () => ({
+ getCurrentPermissions: () => mockGetCurrentPermissions(),
+}));
+
+const mockGetCurrentPermissions = jest.fn();
+const mockStore = configureMockStore()({
+ currentProjectState: { project: { id: "" } },
+});
+
let testRenderer: renderer.ReactTestRenderer;
-let entryButton: ReactTestInstance | undefined;
-let cleanButton: ReactTestInstance | undefined;
+let entryButton: ReactTestInstance;
+let cleanButton: ReactTestInstance;
-const renderNavButtons = (path: Path): void => {
- renderer.act(() => {
- testRenderer = renderer.create();
+const renderNavButtons = async (
+ path: Path,
+ permission = Permission.MergeAndReviewEntries
+): Promise => {
+ mockGetCurrentPermissions.mockResolvedValue([permission]);
+ await renderer.act(async () => {
+ testRenderer = renderer.create(
+
+
+
+ );
});
+};
+
+const renderNavButtonsWithPath = async (path: Path): Promise => {
+ await renderNavButtons(path, Permission.MergeAndReviewEntries);
entryButton = testRenderer.root.findByProps({ id: dataEntryButtonId });
cleanButton = testRenderer.root.findByProps({ id: dataCleanupButtonId });
};
+const renderNavButtonsWithPermission = async (
+ perm: Permission
+): Promise => {
+ await renderNavButtons(Path.DataEntry, perm);
+ entryButton = testRenderer.root.findByProps({ id: dataEntryButtonId });
+ const cleanupButtons = testRenderer.root.findAllByProps({
+ id: dataCleanupButtonId,
+ });
+ if (cleanupButtons.length) {
+ cleanButton = cleanupButtons[0];
+ }
+};
+
+beforeEach(() => {
+ jest.resetAllMocks();
+});
+
describe("NavigationButtons", () => {
- it("highlights the correct tab", () => {
- renderNavButtons(Path.Statistics);
- expect(entryButton?.props.style.background).toEqual(themeColors.lightShade);
- expect(cleanButton?.props.style.background).toEqual(themeColors.lightShade);
+ it("only shows the data cleanup tab for the correct permissions", async () => {
+ for (const perm of Object.values(Permission)) {
+ await renderNavButtonsWithPermission(perm);
+ if (
+ perm === Permission.CharacterInventory ||
+ perm === Permission.MergeAndReviewEntries
+ ) {
+ expect(cleanButton).toBeTruthy;
+ } else {
+ expect(cleanButton).toBeUndefined;
+ }
+ }
+ });
- renderNavButtons(Path.DataEntry);
+ it("highlights the correct tab", async () => {
+ await renderNavButtonsWithPath(Path.DataEntry);
expect(entryButton?.props.style.background).toEqual(themeColors.darkShade);
expect(cleanButton?.props.style.background).toEqual(themeColors.lightShade);
- renderNavButtons(Path.Goals);
+ await renderNavButtonsWithPath(Path.Goals);
expect(entryButton?.props.style.background).toEqual(themeColors.lightShade);
expect(cleanButton?.props.style.background).toEqual(themeColors.darkShade);
- renderNavButtons(Path.GoalCurrent);
+ await renderNavButtonsWithPath(Path.GoalCurrent);
expect(entryButton?.props.style.background).toEqual(themeColors.lightShade);
expect(cleanButton?.props.style.background).toEqual(themeColors.darkShade);
- renderNavButtons(Path.GoalNext);
+ await renderNavButtonsWithPath(Path.GoalNext);
expect(entryButton?.props.style.background).toEqual(themeColors.lightShade);
expect(cleanButton?.props.style.background).toEqual(themeColors.darkShade);
+
+ await renderNavButtonsWithPath(Path.ProjSettings);
+ expect(entryButton?.props.style.background).toEqual(themeColors.lightShade);
+ expect(cleanButton?.props.style.background).toEqual(themeColors.lightShade);
+
+ await renderNavButtonsWithPath(Path.Statistics);
+ expect(entryButton?.props.style.background).toEqual(themeColors.lightShade);
+ expect(cleanButton?.props.style.background).toEqual(themeColors.lightShade);
+
+ await renderNavButtonsWithPath(Path.UserSettings);
+ expect(entryButton?.props.style.background).toEqual(themeColors.lightShade);
+ expect(cleanButton?.props.style.background).toEqual(themeColors.lightShade);
});
});
diff --git a/src/components/ProjectScreen/ChooseProject.tsx b/src/components/ProjectScreen/ChooseProject.tsx
index 8d7204017e..8abb790a58 100644
--- a/src/components/ProjectScreen/ChooseProject.tsx
+++ b/src/components/ProjectScreen/ChooseProject.tsx
@@ -36,7 +36,7 @@ export default function ChooseProject(): ReactElement {
const selectProject = (project: Project): void => {
dispatch(setNewCurrentProject(project));
- navigate(Path.Goals);
+ navigate(Path.DataEntry);
};
return (