Skip to content

Commit

Permalink
Merge pull request #353 from AllenInstitute/feature/metadata-editing/…
Browse files Browse the repository at this point in the history
…warn-on-dismiss

Intercept modal dismissal with unsaved changes
  • Loading branch information
aswallace authored Dec 4, 2024
2 parents 29ed3ff + 2be6a2f commit 83f441d
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,12 @@ export default function ExistingAnnotationPathway(props: ExistingAnnotationProps
/>
)}
<div className={classNames(styles.footer, styles.footerAlignRight)}>
<SecondaryButton title="Cancel" text="CANCEL" onClick={props.onDismiss} />
<SecondaryButton title="" text="CANCEL" onClick={props.onDismiss} />
{valueCount && (
<PrimaryButton
className={styles.primaryButton}
disabled={!newValues}
title="Replace"
title=""
text="REPLACE"
onClick={onSubmit}
/>
Expand Down
17 changes: 12 additions & 5 deletions packages/core/components/EditMetadata/NewAnnotationPathway.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ enum EditStep {

interface NewAnnotationProps {
onDismiss: () => void;
onUnsavedChanges: () => void;
selectedFileCount: number;
}

Expand Down Expand Up @@ -57,6 +58,12 @@ export default function NewAnnotationPathway(props: NewAnnotationProps) {
props.onDismiss();
}

// TO DO: determine when to submit the new annotation post req
function onCreateNewAnnotation() {
props?.onUnsavedChanges();
setStep(EditStep.EDIT_FILES);
}

return (
<>
{/* TO DO: Prevent user from entering a name that collides with existing annotation */}
Expand Down Expand Up @@ -137,14 +144,14 @@ export default function NewAnnotationPathway(props: NewAnnotationProps) {
<StackItem>
{step === EditStep.EDIT_FILES && (
<SecondaryButton
title="Go back"
title=""
text="BACK"
onClick={() => setStep(EditStep.CREATE_FIELD)}
/>
)}
</StackItem>
<StackItem grow align="end" className={styles.footerAlignRight}>
<SecondaryButton title="Cancel" text="CANCEL" onClick={props.onDismiss} />
<SecondaryButton title="" text="CANCEL" onClick={props.onDismiss} />
{step === EditStep.CREATE_FIELD && (
<PrimaryButton
className={styles.primaryButton}
Expand All @@ -154,8 +161,8 @@ export default function NewAnnotationPathway(props: NewAnnotationProps) {
!dropdownOptions.length)
}
text="NEXT"
title="Next"
onClick={() => setStep(EditStep.EDIT_FILES)}
title=""
onClick={onCreateNewAnnotation}
/>
)}
{step === EditStep.EDIT_FILES && (
Expand All @@ -168,7 +175,7 @@ export default function NewAnnotationPathway(props: NewAnnotationProps) {
!newValues
}
text="SUBMIT"
title="Submit"
title=""
onClick={onSubmit}
/>
)}
Expand Down
28 changes: 17 additions & 11 deletions packages/core/components/EditMetadata/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import * as React from "react";
import { useDispatch, useSelector } from "react-redux";
import { useSelector } from "react-redux";

import ExistingAnnotationPathway from "./ExistingAnnotationPathway";
import NewAnnotationPathway from "./NewAnnotationPathway";
import ChoiceGroup from "../ChoiceGroup";
import { TOP_LEVEL_FILE_ANNOTATION_NAMES } from "../../constants";
import { interaction, metadata, selection } from "../../state";
import { metadata, selection } from "../../state";

import styles from "./EditMetadata.module.css";

Expand All @@ -14,15 +14,17 @@ enum EditMetadataPathway {
NEW = "new",
}

interface EditMetadataProps {
className?: string;
onDismiss: () => void;
onUnsavedChanges: (hasUnsavedChanges: boolean) => void;
}

/**
* Form that acts as a wrapper for both metadata editing pathways (new vs existing annotations).
* Performs all necessary state selections on render and passes data as props to child components
*/
export default function EditMetadataForm() {
const dispatch = useDispatch();
const onDismiss = () => {
dispatch(interaction.actions.hideVisibleModal());
};
export default function EditMetadataForm(props: EditMetadataProps) {
const fileSelection = useSelector(selection.selectors.getFileSelection);
const fileCount = fileSelection.count();
// Don't allow users to edit top level annotations (e.g., File Name)
Expand Down Expand Up @@ -65,7 +67,7 @@ export default function EditMetadataForm() {
}, [fileSelection]);

return (
<>
<div className={props.className}>
<ChoiceGroup
className={styles.choiceGroup}
defaultSelectedKey={editPathway}
Expand All @@ -90,15 +92,19 @@ export default function EditMetadataForm() {
<div className={styles.contentWrapper}>
{editPathway === EditMetadataPathway.EXISTING ? (
<ExistingAnnotationPathway
onDismiss={onDismiss}
onDismiss={props.onDismiss}
annotationValueMap={annotationValueMap}
annotationOptions={annotationOptions}
selectedFileCount={fileCount}
/>
) : (
<NewAnnotationPathway onDismiss={onDismiss} selectedFileCount={fileCount} />
<NewAnnotationPathway
onDismiss={props.onDismiss}
onUnsavedChanges={() => props.onUnsavedChanges(true)}
selectedFileCount={fileCount}
/>
)}
</div>
</>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.footer {
margin-top: 20px;
width: 100%;
}

.footer-align-right {
width: 100%;
display: flex;
}

.footer-align-right > * {
margin-left: 10px;
width: min-content;
}

.footer-align-right > *:first-child{
margin-left: auto;
}

.hidden {
display: none;
}

.warning {
font-size: var(--l-paragraph-size);
}
44 changes: 41 additions & 3 deletions packages/core/components/Modal/EditMetadata/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import classNames from "classnames";
import * as React from "react";
import { useSelector } from "react-redux";

import { ModalProps } from "..";
import BaseModal from "../BaseModal";
import { PrimaryButton, SecondaryButton } from "../../Buttons";
import EditMetadataForm from "../../EditMetadata";
import { selection } from "../../../state";
import FileSelection from "../../../entity/FileSelection";

import styles from "./EditMetadata.module.css";

/**
* Dialog to display workflow for editing metadata for selected files
*/
export default function EditMetadata({ onDismiss }: ModalProps) {
const [hasUnsavedChanges, setHasUnsavedChanges] = React.useState<boolean>(false);
const [showWarning, setShowWarning] = React.useState<boolean>(false);
const fileSelection = useSelector(
selection.selectors.getFileSelection,
FileSelection.selectionsAreEqual
Expand All @@ -20,11 +26,43 @@ export default function EditMetadata({ onDismiss }: ModalProps) {
totalFilesSelected === 1 ? "" : "s"
})`;

function onDismissWithWarning() {
if (hasUnsavedChanges) setShowWarning(true);
else onDismiss();
}

return (
<BaseModal
body={<EditMetadataForm />}
onDismiss={onDismiss}
title={`Edit Metadata ${filesSelectedCountString}`}
body={
// Use styling instead of conditionals to persist rendered data
<>
<EditMetadataForm
className={classNames({ [styles.hidden]: showWarning })}
onDismiss={onDismissWithWarning}
onUnsavedChanges={setHasUnsavedChanges}
/>
<div className={classNames({ [styles.hidden]: !showWarning })}>
<p className={styles.warning}>
Some edits will not be completed and could cause inaccuracies. Are you
sure you want to quit now?
</p>
<div className={classNames(styles.footer, styles.footerAlignRight)}>
<SecondaryButton
title=""
text="Back"
onClick={() => setShowWarning(false)}
/>
<PrimaryButton title="" text="Yes, Quit" onClick={onDismiss} />
</div>
</div>
</>
}
onDismiss={onDismissWithWarning}
title={
showWarning
? "Warning! Edits in progress."
: `Edit Metadata ${filesSelectedCountString}`
}
/>
);
}

0 comments on commit 83f441d

Please sign in to comment.