-
Notifications
You must be signed in to change notification settings - Fork 952
feat: have user type name of thing to delete for extra safety #4080
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
Changes from all commits
614fc86
0dc2717
cf53085
d52354a
a8a78f0
b8a6c53
ce02bed
06e6e65
2a85c53
0ac54d7
c787fbb
b629274
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 |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { screen } from "@testing-library/react" | ||
import userEvent from "@testing-library/user-event" | ||
import i18next from "i18next" | ||
import { render } from "testHelpers/renderHelpers" | ||
import { DeleteDialog } from "./DeleteDialog" | ||
|
||
describe("DeleteDialog", () => { | ||
it("disables confirm button when the text field is empty", () => { | ||
render( | ||
<DeleteDialog | ||
isOpen | ||
onConfirm={jest.fn()} | ||
onCancel={jest.fn()} | ||
entity="template" | ||
name="MyTemplate" | ||
/>, | ||
) | ||
const confirmButton = screen.getByRole("button", { name: "Delete" }) | ||
expect(confirmButton).toBeDisabled() | ||
}) | ||
|
||
it("disables confirm button when the text field is filled incorrectly", async () => { | ||
const { t } = i18next | ||
render( | ||
<DeleteDialog | ||
isOpen | ||
onConfirm={jest.fn()} | ||
onCancel={jest.fn()} | ||
entity="template" | ||
name="MyTemplate" | ||
/>, | ||
) | ||
const labelText = t("deleteDialog.confirmLabel", { ns: "common", entity: "template" }) | ||
const textField = screen.getByLabelText(labelText) | ||
await userEvent.type(textField, "MyTemplateWrong") | ||
const confirmButton = screen.getByRole("button", { name: "Delete" }) | ||
expect(confirmButton).toBeDisabled() | ||
}) | ||
|
||
it("enables confirm button when the text field is filled correctly", async () => { | ||
const { t } = i18next | ||
render( | ||
<DeleteDialog | ||
isOpen | ||
onConfirm={jest.fn()} | ||
onCancel={jest.fn()} | ||
entity="template" | ||
name="MyTemplate" | ||
/>, | ||
) | ||
const labelText = t("deleteDialog.confirmLabel", { ns: "common", entity: "template" }) | ||
const textField = screen.getByLabelText(labelText) | ||
await userEvent.type(textField, "MyTemplate") | ||
const confirmButton = screen.getByRole("button", { name: "Delete" }) | ||
expect(confirmButton).not.toBeDisabled() | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,80 @@ | ||
import React, { ReactNode } from "react" | ||
import FormHelperText from "@material-ui/core/FormHelperText" | ||
import makeStyles from "@material-ui/core/styles/makeStyles" | ||
import TextField from "@material-ui/core/TextField" | ||
import Typography from "@material-ui/core/Typography" | ||
import { Maybe } from "components/Conditionals/Maybe" | ||
import { Stack } from "components/Stack/Stack" | ||
import React, { ChangeEvent, useState } from "react" | ||
import { useTranslation } from "react-i18next" | ||
import { ConfirmDialog } from "../ConfirmDialog/ConfirmDialog" | ||
|
||
export interface DeleteDialogProps { | ||
isOpen: boolean | ||
onConfirm: () => void | ||
onCancel: () => void | ||
title: string | ||
description: string | ReactNode | ||
entity: string | ||
name: string | ||
info?: string | ||
confirmLoading?: boolean | ||
} | ||
|
||
export const DeleteDialog: React.FC<React.PropsWithChildren<DeleteDialogProps>> = ({ | ||
isOpen, | ||
onCancel, | ||
onConfirm, | ||
title, | ||
description, | ||
entity, | ||
info, | ||
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. What kind of info is this? A description? 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, it's intended to help the user identify the entity to delete, so they don't delete the wrong one by accident. @bpmct suggested this on the basis of an article about making the wrong repo private and thus losing all their github stars. 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. |
||
name, | ||
confirmLoading, | ||
}) => ( | ||
<ConfirmDialog | ||
type="delete" | ||
hideCancel={false} | ||
open={isOpen} | ||
title={title} | ||
onConfirm={onConfirm} | ||
onClose={onCancel} | ||
description={description} | ||
confirmLoading={confirmLoading} | ||
/> | ||
) | ||
}) => { | ||
const styles = useStyles() | ||
const { t } = useTranslation("common") | ||
const [nameValue, setNameValue] = useState("") | ||
const confirmed = name === nameValue | ||
const handleChange = (event: ChangeEvent<HTMLInputElement>) => { | ||
setNameValue(event.target.value) | ||
} | ||
|
||
const content = ( | ||
<> | ||
<Typography>{t("deleteDialog.intro", { entity })}</Typography> | ||
<Maybe condition={info !== undefined}> | ||
<Typography className={styles.warning}>{info}</Typography> | ||
</Maybe> | ||
<Typography>{t("deleteDialog.confirm", { entity })}</Typography> | ||
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 love how we're not passing whole strings in so that the language will stay consistent. |
||
<Stack spacing={1}> | ||
<TextField | ||
name="confirmation" | ||
id="confirmation" | ||
placeholder={name} | ||
value={nameValue} | ||
onChange={handleChange} | ||
label={t("deleteDialog.confirmLabel", { entity })} | ||
/> | ||
<Maybe condition={nameValue.length > 0 && !confirmed}> | ||
<FormHelperText error>{t("deleteDialog.incorrectName", { entity })}</FormHelperText> | ||
</Maybe> | ||
</Stack> | ||
</> | ||
) | ||
|
||
return ( | ||
<ConfirmDialog | ||
type="delete" | ||
hideCancel={false} | ||
open={isOpen} | ||
title={t("deleteDialog.title", { entity })} | ||
onConfirm={onConfirm} | ||
onClose={onCancel} | ||
description={content} | ||
confirmLoading={confirmLoading} | ||
disabled={!confirmed} | ||
/> | ||
) | ||
} | ||
|
||
const useStyles = makeStyles((theme) => ({ | ||
warning: { | ||
color: theme.palette.warning.light, | ||
}, | ||
})) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,3 @@ | ||
{ | ||
"deleteDialog": { | ||
"title": "Delete template", | ||
"description": "Deleting a template is irreversible. Are you sure you want to proceed?" | ||
}, | ||
"deleteSuccess": "Template successfully deleted." | ||
} |
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.
❤️