Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add modal for unsaved changes #1168

Merged
merged 5 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/bygger/src/Forms/FormPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ export const FormPage = ({ loadForm, loadTranslations, onSave, onPublish, onUnpu

const saveForm = async (form) => {
const savedForm = await onSave(form);
sessionStorage.removeItem(formPath);

if (!savedForm.error) {
dispatch({ type: 'form-saved', form: savedForm });
return savedForm;
Expand Down
5 changes: 4 additions & 1 deletion packages/bygger/src/components/NavFormBuilder.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ class NavFormBuilder extends Component {
return appConfig?.logger;
}

handleChange = () => {
handleChange = (data) => {
enstulen marked this conversation as resolved.
Show resolved Hide resolved
enstulen marked this conversation as resolved.
Show resolved Hide resolved
if (data.path) {
sessionStorage.setItem(data.path, JSON.stringify({ changed: true }));
}
this.props.onChange(cloneDeep(this.builder.instance.form));
};

Expand Down
11 changes: 10 additions & 1 deletion packages/bygger/src/components/Navbar/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { InternalHeader } from '@navikt/ds-react';
import { useAppConfig } from '@navikt/skjemadigitalisering-shared-components';
import { Link } from 'react-router-dom';
import { useAuth } from '../../context/auth-context';
import useUnsavedChangesModal from '../../hooks/useUnsavedChangesModal';
import AdminMenu from './components/AdminMenu';
import { FormMenu } from './components/FormMenu';
import { ListMenu } from './components/ListMenu';
Expand All @@ -23,10 +24,17 @@ export const NavBar = ({ formPath, formMenu, formListMenu, translationMenu }: Na
const { config } = useAppConfig();
const styles = useNavBarStyles();
const showAdmin = userData?.isAdmin;
const { showUnsavedChangesModal, unsavedChangesModalContent } = useUnsavedChangesModal();

return (
<section>
<InternalHeader className={config?.isDevelopment ? styles.navBarLocal : styles.navBar}>
<Link className={styles.formsLink} to="/forms" aria-label="Gå til skjemaliste">
<Link
className={styles.formsLink}
to={'/forms'}
aria-label="Gå til skjemaliste"
onClick={(e) => showUnsavedChangesModal(e, { redirectTo: '/forms' })}
>
<HomeFilled fontSize="1.5rem" role="presentation" />
</Link>
<div className={styles.navBarLinks}>
Expand All @@ -41,6 +49,7 @@ export const NavBar = ({ formPath, formMenu, formListMenu, translationMenu }: Na
</div>
</InternalHeader>
{config?.isDevelopment && <div className={styles.indicateLocalBorder} />}
{unsavedChangesModalContent}
</section>
);
};
85 changes: 49 additions & 36 deletions packages/bygger/src/components/Navbar/components/AdminMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,60 @@
import { System } from '@navikt/ds-icons';
import { Dropdown, InternalHeader } from '@navikt/ds-react';
import { Link } from 'react-router-dom';
import { useDropdownStyles } from '../styles';
import useUnsavedChangesModal from '../../../hooks/useUnsavedChangesModal';
import { AdminMenuLink } from './AdminMenuLink';

const AdminMenu = ({ showImport = false }) => {
const dropdownStyles = useDropdownStyles();
const { showUnsavedChangesModal, unsavedChangesModalContent } = useUnsavedChangesModal();

return (
<Dropdown>
<InternalHeader.Button as={Dropdown.Toggle} className="ml-auto" aria-label="Åpne meny">
<System fontSize={'1.5rem'} role="presentation" />
</InternalHeader.Button>
<Dropdown.Menu className={dropdownStyles.dropdownMenu}>
<Dropdown.Menu.GroupedList>
<Dropdown.Menu.GroupedList.Item>
{' '}
<Link to="/translations/global/nn-NO/skjematekster">Globale Oversettelser</Link>
</Dropdown.Menu.GroupedList.Item>
<Dropdown.Menu.GroupedList.Item>
{' '}
<Link to="/migrering">Migrering</Link>
</Dropdown.Menu.GroupedList.Item>
<Dropdown.Menu.GroupedList.Item>
{' '}
<Link to="/bulk-publisering">Bulkpublisering</Link>
</Dropdown.Menu.GroupedList.Item>
<Dropdown.Menu.GroupedList.Item>
{' '}
<Link to="/mottaksadresser">Rediger mottaksadresser</Link>
</Dropdown.Menu.GroupedList.Item>
<>
<Dropdown>
<InternalHeader.Button as={Dropdown.Toggle} className="ml-auto" aria-label="Åpne meny">
<System fontSize={'1.5rem'} role="presentation" />
</InternalHeader.Button>
<Dropdown.Menu>
<Dropdown.Menu.GroupedList>
<Dropdown.Menu.GroupedList.Item>
<AdminMenuLink
showUnsavedChangesModal={showUnsavedChangesModal}
to="/translations/global/nn-NO/skjematekster"
>
Globale Oversettelser
</AdminMenuLink>
</Dropdown.Menu.GroupedList.Item>
<Dropdown.Menu.GroupedList.Item>
<AdminMenuLink showUnsavedChangesModal={showUnsavedChangesModal} to="/migrering">
Migrering
</AdminMenuLink>
</Dropdown.Menu.GroupedList.Item>
<Dropdown.Menu.GroupedList.Item>
<AdminMenuLink showUnsavedChangesModal={showUnsavedChangesModal} to="/bulk-publisering">
Bulkpublisering
</AdminMenuLink>
</Dropdown.Menu.GroupedList.Item>
<Dropdown.Menu.GroupedList.Item>
<AdminMenuLink showUnsavedChangesModal={showUnsavedChangesModal} to="/mottaksadresser">
Rediger mottaksadresser
</AdminMenuLink>
</Dropdown.Menu.GroupedList.Item>

<Dropdown.Menu.GroupedList.Item>
{' '}
<Link to="/rapporter">Rapporter</Link>
</Dropdown.Menu.GroupedList.Item>
{showImport && (
<Dropdown.Menu.GroupedList.Item>
{' '}
<Link to="/import/skjema">Importer</Link>
<AdminMenuLink showUnsavedChangesModal={showUnsavedChangesModal} to="/rapporter">
Rapporter
</AdminMenuLink>
</Dropdown.Menu.GroupedList.Item>
)}
</Dropdown.Menu.GroupedList>
</Dropdown.Menu>
</Dropdown>
{showImport && (
<Dropdown.Menu.GroupedList.Item>
<AdminMenuLink showUnsavedChangesModal={showUnsavedChangesModal} to="/import/skjema">
Importer
</AdminMenuLink>
</Dropdown.Menu.GroupedList.Item>
)}
</Dropdown.Menu.GroupedList>
</Dropdown.Menu>
</Dropdown>
{unsavedChangesModalContent}
</>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import { Link } from 'react-router-dom';

interface Props {
children: React.ReactNode;
to: string;
showUnsavedChangesModal: (event: React.MouseEvent, { redirectTo }: { redirectTo: string }) => void;
}

export const AdminMenuLink = ({ children, to, showUnsavedChangesModal }: Props) => {
return (
<>
<Link to={to} onClick={(e) => showUnsavedChangesModal(e, { redirectTo: to })}>
{children}
</Link>
</>
);
};
15 changes: 14 additions & 1 deletion packages/bygger/src/components/Navbar/components/FormMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { EditFilled, EyeFilled, GlobeFilled, SettingsFilled } from '@navikt/ds-icons';
import { makeStyles, useLanguageCodeFromURL } from '@navikt/skjemadigitalisering-shared-components';
import useUnsavedChangesModal from '../../../hooks/useUnsavedChangesModal';
import { MenuLink } from './MenuLink';

const useStyles = makeStyles({
Expand All @@ -13,6 +14,8 @@ const useStyles = makeStyles({
export const FormMenu = ({ formPath }) => {
const styles = useStyles();
const currentLanguage = useLanguageCodeFromURL();
const { unsavedChangesModalContent, showUnsavedChangesModal } = useUnsavedChangesModal();

return (
<>
<MenuLink to={`/forms/${formPath}/settings`} noIconStyling={false}>
Expand All @@ -30,10 +33,20 @@ export const FormMenu = ({ formPath }) => {
<span className={styles.linkText}>Forhåndsvis</span>
</MenuLink>

<MenuLink to={`/translations/${formPath}${currentLanguage ? `/${currentLanguage}` : ''}`} noIconStyling={false}>
<MenuLink
to={`/translations/${formPath}${currentLanguage ? `/${currentLanguage}` : ''}`}
noIconStyling={false}
onClick={(e) =>
showUnsavedChangesModal(e, {
redirectTo: `/translations/${formPath}${currentLanguage ? `/${currentLanguage}` : ''}`,
})
}
>
<GlobeFilled fontSize={'1.5rem'} role="presentation" />
<span className={styles.linkText}>Språk</span>
</MenuLink>

{unsavedChangesModalContent}
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ interface Props {
to: string;
dataKey?: string;
noIconStyling: boolean;
onClick?: (e: React.MouseEvent) => void;
}

const useStyles = makeStyles({
Expand All @@ -33,7 +34,7 @@ const useStyles = makeStyles({
},
});

export const MenuLink = ({ children, to, dataKey, noIconStyling }: Props) => {
export const MenuLink = ({ children, to, dataKey, noIconStyling, onClick }: Props) => {
const styles = useStyles();

return (
Expand All @@ -42,6 +43,7 @@ export const MenuLink = ({ children, to, dataKey, noIconStyling }: Props) => {
className={classNames(styles.navBarLink, { [styles.navBarLinkNoIcon]: noIconStyling })}
to={to}
data-key={dataKey}
onClick={onClick}
>
{children}
</NavLink>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { InformationFilled } from '@navikt/ds-icons';
import { Alert, BodyShort, Detail, Dropdown, Heading, InternalHeader } from '@navikt/ds-react';
import { makeStyles, navCssVariables } from '@navikt/skjemadigitalisering-shared-components';
import { usePusherNotifications } from '../../../context/notifications/NotificationsContext';
import { useDropdownStyles } from '../styles';

const useStyles = makeStyles({
notificationsMenu: {
Expand All @@ -16,7 +15,6 @@ const useStyles = makeStyles({

const NotificationDropdown = () => {
const styles = useStyles();
const dropdownStyles = useDropdownStyles();
const { messages, clearAll } = usePusherNotifications();

if (messages.length === 0) return <></>;
Expand All @@ -26,10 +24,7 @@ const NotificationDropdown = () => {
<InternalHeader.Button as={Dropdown.Toggle} aria-label="Notifikasjoner">
<InformationFilled color={navCssVariables.navWarning} fontSize="1.5rem" role="presentation" />
</InternalHeader.Button>
<Dropdown.Menu
className={`${styles.notificationsMenu} ${dropdownStyles.dropdownMenu}`}
onClose={() => clearAll()}
>
<Dropdown.Menu className={`${styles.notificationsMenu}`} onClose={() => clearAll()}>
<Dropdown.Menu.List>
{messages.map(({ title, message, type, id, created }) => (
<Alert key={id} variant={type} className={styles.messagePanel}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Dropdown, InternalHeader } from '@navikt/ds-react';
import { makeStyles } from '@navikt/skjemadigitalisering-shared-components';
import { Link } from 'react-router-dom';
import { useAuth } from '../../../context/auth-context';
import { useDropdownStyles } from '../styles';

const useStyles = makeStyles({
logOutBtn: {
Expand All @@ -15,17 +14,15 @@ const useStyles = makeStyles({
const UserMenu = () => {
const { logout, userData } = useAuth();
const styles = useStyles();
const dropdownStyles = useDropdownStyles();

if (!userData) return <></>;

return (
<Dropdown>
<InternalHeader.UserButton as={Dropdown.Toggle} name={userData.name ? userData.name : ''} />
<Dropdown.Menu className={dropdownStyles.dropdownMenu}>
<Dropdown.Menu>
<Dropdown.Menu.List>
<Dropdown.Menu.List.Item className={styles.logOutBtn}>
{' '}
<Link className="navds-button navds-button--secondary navds-button--small" to="/" onClick={logout}>
Logg ut
</Link>
Expand Down
6 changes: 0 additions & 6 deletions packages/bygger/src/components/Navbar/styles.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
import { makeStyles } from '@navikt/skjemadigitalisering-shared-components';

export const useDropdownStyles = makeStyles({
dropdownMenu: {
top: '56px !important',
},
});

export const useNavBarStyles = makeStyles({
navBar: {
height: '56px',
Expand Down
60 changes: 60 additions & 0 deletions packages/bygger/src/hooks/useUnsavedChangesModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { ConfirmationModal } from '@navikt/skjemadigitalisering-shared-components';
import { useMemo, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';

interface FormChanged {
changed: boolean;
}

const useUnsavedChangesModal = () => {
const [openModal, setOpenModal] = useState<boolean>(false);
const [redirectTo, setRedirectTo] = useState<string | undefined>(undefined);

const navigate = useNavigate();
const { formPath } = useParams();

const showUnsavedChangesModal = (event: React.MouseEvent, { redirectTo }: { redirectTo: string }) => {
setRedirectTo(redirectTo);

if (formPath) {
const formChangedData = JSON.parse(sessionStorage.getItem(formPath) as string) as FormChanged;
if (formChangedData) {
const formChanged = formChangedData.changed === true;
if (formChanged) {
event.preventDefault();
setOpenModal(true);
}
}
}
};

const modalContent = useMemo(() => {
return (
<ConfirmationModal
open={openModal}
onClose={() => setOpenModal(false)}
onConfirm={() => {
if (formPath) {
sessionStorage.removeItem(formPath);
}
setOpenModal(false);
if (redirectTo) navigate(redirectTo);
}}
confirmType="danger"
texts={{
title: 'Du har ulagrede endringer i skjemaet',
body: 'Vil du forlate siden uten å lagre?',
confirm: 'Forkast endringer',
cancel: 'Avbryt',
}}
/>
);
}, [formPath, redirectTo, navigate, openModal]);

return {
unsavedChangesModalContent: modalContent,
showUnsavedChangesModal,
};
};

export default useUnsavedChangesModal;