-
Notifications
You must be signed in to change notification settings - Fork 585
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
Code eval tool: use toast upon deletion instead of alert #10165
base: master
Are you sure you want to change the base?
Changes from all commits
1cb2d56
1d1e11c
66ea6a2
641a31f
98a0d0f
275732b
7f5298d
10f135f
7cf2545
02d42a8
0935b20
6a0d7b5
5031335
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,9 @@ import { Button } from "react-common/components/controls/Button"; | |
import { setEvalResult } from "../transforms/setEvalResult"; | ||
import { showToast } from "../transforms/showToast"; | ||
import { makeToast } from "../utils"; | ||
import { reAddCriteriaToChecklist } from "../transforms/reAddCriteriaToChecklist"; | ||
import { dismissToast } from "../state/actions"; | ||
import { softDeleteCriteriaFromChecklist } from "../transforms/softDeleteCriteriaFromChecklist"; | ||
|
||
interface CriteriaResultNotesProps { | ||
criteriaId: string; | ||
|
@@ -76,6 +79,25 @@ const CriteriaResultError: React.FC<CriteriaResultErrorProps> = ({ criteriaInsta | |
); | ||
}; | ||
|
||
const UndoDeleteCriteriaButton: React.FC<{ criteriaId: string, toastId: string }> = ({ criteriaId, toastId }) => { | ||
const { dispatch } = useContext(AppStateContext); | ||
const handleUndoClicked = () => { | ||
reAddCriteriaToChecklist(criteriaId); | ||
if (toastId) { | ||
dispatch(dismissToast(toastId)); | ||
} | ||
} | ||
|
||
return ( | ||
<Button | ||
className="undo-button" | ||
title={Strings.Undo} | ||
label={Strings.Undo} | ||
onClick={handleUndoClicked} | ||
/> | ||
) | ||
} | ||
|
||
const CriteriaResultToolbarTray: React.FC<{ criteriaId: string }> = ({ criteriaId }) => { | ||
const { state: teacherTool } = useContext(AppStateContext); | ||
|
||
|
@@ -91,9 +113,14 @@ const CriteriaResultToolbarTray: React.FC<{ criteriaId: string }> = ({ criteriaI | |
} | ||
|
||
async function handleDeleteClickedAsync() { | ||
if (confirm(Strings.ConfirmDeleteCriteriaInstance)) { | ||
const toastTimeout = 5000; | ||
softDeleteCriteriaFromChecklist(criteriaId); | ||
const toast = makeToast("info", Strings.CriteriaDeleted, toastTimeout); | ||
toast.jsx = <UndoDeleteCriteriaButton criteriaId={criteriaId} toastId={toast.id} />; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. I originally just had the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. I would probably lean towards removing it in that case, at least for now. Either way is fine though. |
||
showToast(toast); | ||
setTimeout(() => { | ||
removeCriteriaFromChecklist(criteriaId); | ||
} | ||
}, toastTimeout + 500); | ||
} | ||
|
||
return ( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -75,3 +75,14 @@ | |
.pass > .common-button.common-dropdown-button { | ||
border: 3px solid var(--pxt-success-accent); | ||
} | ||
|
||
.undo-button { | ||
width: 3rem; | ||
height: 2rem; | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
justify-content: center; | ||
background-color: var(--pxt-info-accent-darkened); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should probably change these colors based on the type of toast containing them (error, info, etc...). I believe there are different css classes for each type of toast we can reference. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I think that's a good idea. Can this be done in a follow-up? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure |
||
color: var(--pxt-button-secondary-foreground); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { stateAndDispatch } from "../state"; | ||
import { logDebug } from "../services/loggingService"; | ||
import { setChecklist } from "./setChecklist"; | ||
import { Ticks } from "../constants"; | ||
import { getCriteriaInstanceWithId } from "../state/helpers"; | ||
|
||
export function reAddCriteriaToChecklist(criteriaInstanceId: string) { | ||
const { state: teacherTool } = stateAndDispatch(); | ||
|
||
logDebug(`Re-adding criteria with id: ${criteriaInstanceId}`); | ||
|
||
const instance = getCriteriaInstanceWithId(teacherTool, criteriaInstanceId); | ||
const catalogCriteriaId = instance?.catalogCriteriaId; | ||
const allCriteria = [...teacherTool.checklist.criteria]; | ||
const criteriaIndex = allCriteria.findIndex(c => c.instanceId === criteriaInstanceId); | ||
allCriteria[criteriaIndex].deleted = false; | ||
|
||
const newChecklist = { | ||
...teacherTool.checklist, | ||
criteria: allCriteria, | ||
}; | ||
|
||
setChecklist(newChecklist); | ||
|
||
if (catalogCriteriaId) { | ||
pxt.tickEvent(Ticks.RemoveCriteria, { catalogCriteriaId }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My original comment on this is out of date now, but just a note that I think we need to update this tick from Remove to Re-Add. |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,10 @@ export function removeCriteriaFromChecklist(criteriaInstanceId: string) { | |
|
||
const newChecklist = { | ||
...teacherTool.checklist, | ||
criteria: teacherTool.checklist.criteria.filter(c => c.instanceId !== criteriaInstanceId), | ||
criteria: teacherTool.checklist.criteria.filter(c => | ||
c.instanceId !== criteriaInstanceId || | ||
c.instanceId === criteriaInstanceId && !c.deleted | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I'm wondering if there's a scenario where we would want this action to work even for non-deleted criteria (in which case we could have a separate action There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the |
||
), | ||
}; | ||
|
||
setChecklist(newChecklist); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { stateAndDispatch } from "../state"; | ||
import { logDebug } from "../services/loggingService"; | ||
import { setChecklist } from "./setChecklist"; | ||
import { Ticks } from "../constants"; | ||
import { getCriteriaInstanceWithId } from "../state/helpers"; | ||
|
||
export function softDeleteCriteriaFromChecklist(criteriaInstanceId: string) { | ||
const { state: teacherTool } = stateAndDispatch(); | ||
|
||
logDebug(`Soft deleting criteria with id: ${criteriaInstanceId}`); | ||
|
||
const instance = getCriteriaInstanceWithId(teacherTool, criteriaInstanceId); | ||
const catalogCriteriaId = instance?.catalogCriteriaId; | ||
const allCriteria = [...teacherTool.checklist.criteria]; | ||
const criteriaIndex = allCriteria.findIndex(c => c.instanceId === criteriaInstanceId); | ||
allCriteria[criteriaIndex].deleted = true; | ||
|
||
|
||
const newChecklist = { | ||
...teacherTool.checklist, | ||
criteria: allCriteria, | ||
}; | ||
|
||
setChecklist(newChecklist); | ||
|
||
if (catalogCriteriaId) { | ||
pxt.tickEvent(Ticks.RemoveCriteria, { catalogCriteriaId }); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ export interface CriteriaInstance { | |
instanceId: string; | ||
params: CriteriaParameterValue[] | undefined; | ||
userFeedback?: UserFeedback; | ||
deleted?: boolean; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should still ensure deleted criteria get filtered out of export/serialization. It's not as big of an issue since it gets removed once the toast goes away, but the user could still export while the toast is around (or more likely, close the browser/tab, so it gets serialized and stored in browser storage), at which point the deleted criteria would be there forever since the delete code won't run on it anymore. |
||
} | ||
|
||
// Represents a parameter definition in a catalog criteria. | ||
|
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.
As much as I like the sleekness of this design, something to keep in mind is from an accessibility perspective, how can a user navigate to this button without a mouse? If this means we need to treat toasts like modals and have them steal focus, that could be somewhat confusing/irritating. Maybe we could do that only if the toast is "interactive" in some way? Or there may be other workarounds.
Not necessarily a blocker, but good to keep it in mind.
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.
Good point. I wonder what Outlook does here.