-
Notifications
You must be signed in to change notification settings - Fork 2
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
Created a "You need to save!" Modal on the Candidate Decider #799
Conversation
[diff-counting] Significant lines: 85. |
} | ||
}; | ||
|
||
useEffect(() => { | ||
setIsSaved(currentComment === defaultCurrentComment && currentRating === defaultCurrentRating); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of storing this value in a state, what if we just had a variable that stored it? currentComment, currentRating, defaultCurrentComment, defaultCurrentRating
are all state variables so they'd trigger a re-render anyways when they're changed.
In general it isn't the best pattern to store state that's derived from other states, since it requires you to keep them in sync, and can get lead to buggy behavior if they ever became out of sync
Can we also handle the two other methods of navigation? (Candidate ID dropdown and search bar) |
<p>You have unsaved changes. Do you want to save them before navigating?</p> | ||
</Modal.Content> | ||
<Modal.Actions> | ||
<Button onClick={() => setIsOpen(false)}>Clancel</Button> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<Button onClick={() => setIsOpen(false)}>Clancel</Button> | |
<Button onClick={() => setIsOpen(false)}>Cancel</Button> |
<div className={styles.applicationContainer}> | ||
<div className={styles.searchBar}> | ||
<SearchBar | ||
instance={instance} | ||
setCurrentCandidate={(candidate) => { | ||
setCurrentCandidate(candidate); | ||
populateReviewForCandidate(candidate); | ||
if (!isSaved) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you abstract this function and the below into a helper function? for cleanliness ideally we can put all the handling for this into a single function, as opposed to spreading the logic around.
confirmNavigation(navigationDirection!); | ||
}} | ||
> | ||
Save and {navigationDirection === 'previous' ? 'Go Back' : 'Proceed'} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm worried that this language is confusing "Go Back" might be interpreted as going back to the previous state (someone might think this is the same as "Discard")
maybe we don't need to show the navigation direction? Just "Save", "Discard", and "Cancel" seem good with me
const confirmNavigation = (direction: 'next' | 'previous' | 'other') => { | ||
setIsOpen(false); | ||
setIsBypassingModal(true); | ||
setTimeout(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm, wondering what the point of setTimeout
with 0 milliseconds is?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh oops I forgot to get rid of that - I was trying to fix a problem I was running into where it was making the user click save twice before it worked but I ended up fixing it a different way
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's really coming together! Just some comments to help reduce code duplication
@@ -17,6 +17,14 @@ type CandidateDeciderProps = { | |||
}; | |||
|
|||
const CandidateDecider: React.FC<CandidateDeciderProps> = ({ uuid }) => { | |||
const [isOpen, setIsOpen] = useState(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename to isModalOpen
@@ -17,6 +17,14 @@ type CandidateDeciderProps = { | |||
}; | |||
|
|||
const CandidateDecider: React.FC<CandidateDeciderProps> = ({ uuid }) => { | |||
const [isOpen, setIsOpen] = useState(false); | |||
const [isBypassingModal, setIsBypassingModal] = useState(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure we can get rid of this isBypassingModal
entirely. It feels like a combination of force
and isSaved
is enough.
@@ -109,17 +145,51 @@ const CandidateDecider: React.FC<CandidateDeciderProps> = ({ uuid }) => { | |||
} | |||
}; | |||
|
|||
const handleCandiateChange = (candidate: number) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const handleCandiateChange = (candidate: number) => { | |
const handleCandidateChange = (candidate: number) => { |
@@ -68,7 +79,13 @@ const CandidateDecider: React.FC<CandidateDeciderProps> = ({ uuid }) => { | |||
// eslint-disable-next-line react-hooks/exhaustive-deps | |||
}, [currentCandidate, instance.candidates, reviews]); | |||
|
|||
const next = () => { | |||
const next = (force = false) => { | |||
if (!force && !isSaved && !isBypassingModal) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that we're not using navigationDirection
anymore for the text, it feels like prev
, next
, and handleCandidateChange
can all be condensed into just one function. Can you try deleting prev
and next
and just using handleCandiateChange
? (you might have to check if candidate
number is within 0...instance.candidates.length - 1
but otherwise the looks the same to me)
and then we can get rid of navigationDirection
!
const [navigationDirection, setNavigationDirection] = useState< | ||
'next' | 'previous' | 'other' | null | ||
>(null); | ||
const [navigationDirectionCandidate, setNavigationDirectionCandidate] = useState<number | null>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename? "navigationDirection" doesn't really apply anymore if we're not tracking navigation direction
currentRating ?? 0, | ||
currentComment ?? '' | ||
); | ||
if (nextCandidate !== null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about a function navigateToNextCandidate
for this and the code that's being duplicated below?
<div className={styles.applicationContainer}> | ||
<div className={styles.searchBar}> | ||
<SearchBar | ||
instance={instance} | ||
setCurrentCandidate={(candidate) => { | ||
setCurrentCandidate(candidate); | ||
populateReviewForCandidate(candidate); | ||
handleCandidateChange(candidate); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually you can just pass in handleCandidateChange
here
i.e.
(candidate) => { handleCandidateChange(candidate); }
is the same as
handleCandidateChange
disabled={currentCandidate === 0} | ||
onClick={() => { | ||
if (currentCandidate === 0) return; | ||
setNextCandidate(currentCandidate - 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about let's move setNextCandidate
into handleCandidateChange
? And move the bound checks there as well (==0 and == instance.candidates.length - 1)?
primary | ||
onClick={() => { | ||
if (nextCandidate !== null) { | ||
setCurrentCandidate(nextCandidate); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above, move this into a separate function navigateToNextCandidate
to reduce code duplication
Summary
Created a "You Need to Save!" modal if user tries to navigate away from a candidate without saving in the candidate decider. It allows them to either cancel, discard and proceed, or save and proceed.
Notion/Figma Link
https://www.notion.so/Candidate-Decider-Show-a-You-Need-to-Save-Modal-if-User-Tries-to-Navigate-Away-from-Candidate-W-1440ad723ce1806caae2c09118cacceb
Test Plan
Screen.Recording.2024-12-14.at.10.05.12.AM.mov