Skip to content

Commit

Permalink
EVG-16959: Reintroduce navigation warning modal (#1851)
Browse files Browse the repository at this point in the history
  • Loading branch information
sophstad committed Jun 1, 2023
1 parent fde47fb commit e225d86
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 2 deletions.
5 changes: 4 additions & 1 deletion cypress/integration/projectSettings/project_settings.ts
Expand Up @@ -603,9 +603,12 @@ describe(
cy.dataCy("display-name-input").should("not.have.attr", "placeholder");
});

it.skip("Shows a navigation warning modal when navigating away from project settings", () => {
it("Shows a navigation warning modal that lists the general page when navigating away from project settings", () => {
cy.contains("My Patches").click();
cy.dataCy("navigation-warning-modal").should("be.visible");
cy.dataCy("unsaved-pages").within(() => {
cy.get("li").should("have.length", 1);
});
cy.get("body").type("{esc}");
});

Expand Down
26 changes: 25 additions & 1 deletion src/pages/projectSettings/Context.tsx
Expand Up @@ -203,6 +203,25 @@ const usePopulateForm = <T extends WritableTabRoutes>(
}, [formData]); // eslint-disable-line react-hooks/exhaustive-deps
};

const useHasUnsavedTab = (): {
hasUnsaved: boolean;
unsavedTabs: ProjectSettingsTabRoutes[];
} => {
const { tabs } = useProjectSettingsContext();
const unsavedTabs = useMemo(
() =>
Object.entries(tabs)
.filter(([, tabData]) => tabData.hasChanges)
.map(([tab]) => tab as ProjectSettingsTabRoutes),
[tabs]
);

return {
unsavedTabs,
hasUnsaved: !!unsavedTabs.length,
};
};

const getDefaultTabState = <T extends unknown>(
defaultValue: T
): Record<WritableTabRoutes, T> =>
Expand All @@ -213,4 +232,9 @@ const getDefaultTabState = <T extends unknown>(
}))
);

export { ProjectSettingsProvider, usePopulateForm, useProjectSettingsContext };
export {
ProjectSettingsProvider,
useHasUnsavedTab,
usePopulateForm,
useProjectSettingsContext,
};
59 changes: 59 additions & 0 deletions src/pages/projectSettings/NavigationModal.tsx
@@ -0,0 +1,59 @@
import { Body } from "@leafygreen-ui/typography";
import {
matchPath,
unstable_useBlocker as useBlocker,
useParams,
} from "react-router-dom";
import { ConfirmationModal } from "components/ConfirmationModal";
import { getProjectSettingsRoute, routes } from "constants/routes";
import { useHasUnsavedTab } from "./Context";
import { getTabTitle } from "./getTabTitle";

export const NavigationModal: React.VFC = () => {
const { hasUnsaved, unsavedTabs } = useHasUnsavedTab();
const { projectIdentifier } = useParams();

const shouldConfirmNavigation = ({ nextLocation }): boolean => {
const isProjectSettingsRoute =
nextLocation &&
!!matchPath(`${routes.projectSettings}/*`, nextLocation.pathname);
if (!isProjectSettingsRoute) {
return hasUnsaved;
}

/* Identify if the user is navigating to a new project's settings via project select dropdown */
const currentProjectRoute = getProjectSettingsRoute(projectIdentifier);
const isNewProjectSettingsRoute = !matchPath(
`${currentProjectRoute}/*`,
nextLocation.pathname
);
if (isNewProjectSettingsRoute) {
return hasUnsaved;
}

return false;
};

const blocker = useBlocker(shouldConfirmNavigation);

return (
blocker.state === "blocked" && (
<ConfirmationModal
buttonText="Leave"
data-cy="navigation-warning-modal"
open
onCancel={() => blocker.reset?.()}
onConfirm={() => blocker.proceed?.()}
title="You have unsaved changes that will be discarded. Are you sure you want to leave?"
variant="danger"
>
<Body>Unsaved changes are present on the following pages:</Body>
<ol data-cy="unsaved-pages">
{unsavedTabs.map((tab) => (
<li key={tab}>{getTabTitle(tab).title}</li>
))}
</ol>
</ConfirmationModal>
)
);
};
2 changes: 2 additions & 0 deletions src/pages/projectSettings/Tabs.tsx
Expand Up @@ -6,6 +6,7 @@ import { ProjectSettingsQuery, RepoSettingsQuery } from "gql/generated/types";
import { isProduction } from "utils/environmentVariables";
import { useProjectSettingsContext } from "./Context";
import { Header } from "./Header";
import { NavigationModal } from "./NavigationModal";
import {
AccessTab,
ContainersTab,
Expand Down Expand Up @@ -57,6 +58,7 @@ export const ProjectSettingsTabs: React.VFC<Props> = ({

return (
<Container>
<NavigationModal />
<Header
attachedRepoId={projectData?.projectRef?.repoRefId}
id={projectId || repoId}
Expand Down

0 comments on commit e225d86

Please sign in to comment.