Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit0899548

Browse files
authored
feat: have user type name of thing to delete for extra safety (#4080)
* Add info and text field to delete dialog* Format* Use DeleteDialog for Users, nix info except for Workspaces* Format* Update storybook* Add and update tests* Fix the worst of the UsersPage test bugs* Fix users page tests* Fix workspace tests* Format
1 parenteb71053 commit0899548

File tree

14 files changed

+243
-140
lines changed

14 files changed

+243
-140
lines changed

‎site/src/components/Dialogs/ConfirmDialog/ConfirmDialog.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ export const ConfirmDialog: React.FC<React.PropsWithChildren<ConfirmDialogProps>
8383
confirmLoading,
8484
confirmText,
8585
description,
86+
disabled=false,
8687
hideCancel,
8788
onClose,
8889
onConfirm,
@@ -122,6 +123,7 @@ export const ConfirmDialog: React.FC<React.PropsWithChildren<ConfirmDialogProps>
122123
confirmDialog
123124
confirmLoading={confirmLoading}
124125
confirmText={confirmText||defaults.confirmText}
126+
disabled={disabled}
125127
onCancel={!hideCancel ?onClose :undefined}
126128
onConfirm={onConfirm||onClose}
127129
type={type}

‎site/src/components/Dialogs/DeleteDialog/DeleteDialog.stories.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ export default {
1515
control:"boolean",
1616
defaultValue:true,
1717
},
18-
title:{
19-
defaultValue:"Delete Something",
18+
entity:{
19+
defaultValue:"foo",
2020
},
21-
description:{
22-
defaultValue:
23-
"This is irreversible. To confirm, type the name of the thing you want to delete.",
21+
name:{
22+
defaultValue:"MyFoo",
23+
},
24+
info:{
25+
defaultValue:"Here's some info about the foo so you know you're deleting the right one.",
2426
},
2527
},
2628
}asComponentMeta<typeofDeleteDialog>
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import{screen}from"@testing-library/react"
2+
importuserEventfrom"@testing-library/user-event"
3+
importi18nextfrom"i18next"
4+
import{render}from"testHelpers/renderHelpers"
5+
import{DeleteDialog}from"./DeleteDialog"
6+
7+
describe("DeleteDialog",()=>{
8+
it("disables confirm button when the text field is empty",()=>{
9+
render(
10+
<DeleteDialog
11+
isOpen
12+
onConfirm={jest.fn()}
13+
onCancel={jest.fn()}
14+
entity="template"
15+
name="MyTemplate"
16+
/>,
17+
)
18+
constconfirmButton=screen.getByRole("button",{name:"Delete"})
19+
expect(confirmButton).toBeDisabled()
20+
})
21+
22+
it("disables confirm button when the text field is filled incorrectly",async()=>{
23+
const{ t}=i18next
24+
render(
25+
<DeleteDialog
26+
isOpen
27+
onConfirm={jest.fn()}
28+
onCancel={jest.fn()}
29+
entity="template"
30+
name="MyTemplate"
31+
/>,
32+
)
33+
constlabelText=t("deleteDialog.confirmLabel",{ns:"common",entity:"template"})
34+
consttextField=screen.getByLabelText(labelText)
35+
awaituserEvent.type(textField,"MyTemplateWrong")
36+
constconfirmButton=screen.getByRole("button",{name:"Delete"})
37+
expect(confirmButton).toBeDisabled()
38+
})
39+
40+
it("enables confirm button when the text field is filled correctly",async()=>{
41+
const{ t}=i18next
42+
render(
43+
<DeleteDialog
44+
isOpen
45+
onConfirm={jest.fn()}
46+
onCancel={jest.fn()}
47+
entity="template"
48+
name="MyTemplate"
49+
/>,
50+
)
51+
constlabelText=t("deleteDialog.confirmLabel",{ns:"common",entity:"template"})
52+
consttextField=screen.getByLabelText(labelText)
53+
awaituserEvent.type(textField,"MyTemplate")
54+
constconfirmButton=screen.getByRole("button",{name:"Delete"})
55+
expect(confirmButton).not.toBeDisabled()
56+
})
57+
})
Lines changed: 66 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,80 @@
1-
importReact,{ReactNode}from"react"
1+
importFormHelperTextfrom"@material-ui/core/FormHelperText"
2+
importmakeStylesfrom"@material-ui/core/styles/makeStyles"
3+
importTextFieldfrom"@material-ui/core/TextField"
4+
importTypographyfrom"@material-ui/core/Typography"
5+
import{Maybe}from"components/Conditionals/Maybe"
6+
import{Stack}from"components/Stack/Stack"
7+
importReact,{ChangeEvent,useState}from"react"
8+
import{useTranslation}from"react-i18next"
29
import{ConfirmDialog}from"../ConfirmDialog/ConfirmDialog"
310

411
exportinterfaceDeleteDialogProps{
512
isOpen:boolean
613
onConfirm:()=>void
714
onCancel:()=>void
8-
title:string
9-
description:string|ReactNode
15+
entity:string
16+
name:string
17+
info?:string
1018
confirmLoading?:boolean
1119
}
1220

1321
exportconstDeleteDialog:React.FC<React.PropsWithChildren<DeleteDialogProps>>=({
1422
isOpen,
1523
onCancel,
1624
onConfirm,
17-
title,
18-
description,
25+
entity,
26+
info,
27+
name,
1928
confirmLoading,
20-
})=>(
21-
<ConfirmDialog
22-
type="delete"
23-
hideCancel={false}
24-
open={isOpen}
25-
title={title}
26-
onConfirm={onConfirm}
27-
onClose={onCancel}
28-
description={description}
29-
confirmLoading={confirmLoading}
30-
/>
31-
)
29+
})=>{
30+
conststyles=useStyles()
31+
const{ t}=useTranslation("common")
32+
const[nameValue,setNameValue]=useState("")
33+
constconfirmed=name===nameValue
34+
consthandleChange=(event:ChangeEvent<HTMLInputElement>)=>{
35+
setNameValue(event.target.value)
36+
}
37+
38+
constcontent=(
39+
<>
40+
<Typography>{t("deleteDialog.intro",{ entity})}</Typography>
41+
<Maybecondition={info!==undefined}>
42+
<TypographyclassName={styles.warning}>{info}</Typography>
43+
</Maybe>
44+
<Typography>{t("deleteDialog.confirm",{ entity})}</Typography>
45+
<Stackspacing={1}>
46+
<TextField
47+
name="confirmation"
48+
id="confirmation"
49+
placeholder={name}
50+
value={nameValue}
51+
onChange={handleChange}
52+
label={t("deleteDialog.confirmLabel",{ entity})}
53+
/>
54+
<Maybecondition={nameValue.length>0&&!confirmed}>
55+
<FormHelperTexterror>{t("deleteDialog.incorrectName",{ entity})}</FormHelperText>
56+
</Maybe>
57+
</Stack>
58+
</>
59+
)
60+
61+
return(
62+
<ConfirmDialog
63+
type="delete"
64+
hideCancel={false}
65+
open={isOpen}
66+
title={t("deleteDialog.title",{ entity})}
67+
onConfirm={onConfirm}
68+
onClose={onCancel}
69+
description={content}
70+
confirmLoading={confirmLoading}
71+
disabled={!confirmed}
72+
/>
73+
)
74+
}
75+
76+
constuseStyles=makeStyles((theme)=>({
77+
warning:{
78+
color:theme.palette.warning.light,
79+
},
80+
}))

‎site/src/components/Dialogs/Dialog.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ export interface DialogActionButtonsProps {
7272
confirmLoading?:boolean
7373
/** Whether or not this is a confirm dialog */
7474
confirmDialog?:boolean
75+
/** Whether or not the submit button is disabled */
76+
disabled?:boolean
7577
/** Called when cancel is clicked */
7678
onCancel?:()=>void
7779
/** Called when confirm is clicked */
@@ -94,6 +96,7 @@ export const DialogActionButtons: React.FC<DialogActionButtonsProps> = ({
9496
confirmText="Confirm",
9597
confirmLoading=false,
9698
confirmDialog,
99+
disabled=false,
97100
onCancel,
98101
onConfirm,
99102
type="info",
@@ -122,6 +125,7 @@ export const DialogActionButtons: React.FC<DialogActionButtonsProps> = ({
122125
onClick={onConfirm}
123126
color={typeToColor(type)}
124127
loading={confirmLoading}
128+
disabled={disabled}
125129
type="submit"
126130
className={combineClasses({
127131
[styles.dialogButton]:true,

‎site/src/components/EnterpriseSnackbar/EnterpriseSnackbar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export const EnterpriseSnackbar: FC<React.PropsWithChildren<EnterpriseSnackbarPr
4545
<divclassName={styles.actionWrapper}>
4646
{action}
4747
<IconButtononClick={onClose}className={styles.iconButton}>
48-
<CloseIconclassName={styles.closeIcon}/>
48+
<CloseIconclassName={styles.closeIcon}aria-label="close"/>
4949
</IconButton>
5050
</div>
5151
}

‎site/src/i18n/en/common.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,12 @@
1111
"canceled":"Canceled action",
1212
"failed":"Failed",
1313
"queued":"Queued"
14+
},
15+
"deleteDialog": {
16+
"title":"Delete {{entity}}",
17+
"intro":"Deleting this {{entity}} is irreversible!",
18+
"confirm":"Are you sure you want to proceed? Type the name of this {{entity}} below to confirm.",
19+
"confirmLabel":"Name of {{entity}} to delete",
20+
"incorrectName":"Incorrect {{entity}} name."
1421
}
1522
}

‎site/src/i18n/en/templatePage.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
11
{
2-
"deleteDialog": {
3-
"title":"Delete template",
4-
"description":"Deleting a template is irreversible. Are you sure you want to proceed?"
5-
},
62
"deleteSuccess":"Template successfully deleted."
73
}

‎site/src/i18n/en/workspacePage.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
{
22
"deleteDialog": {
3-
"title":"Delete workspace",
4-
"description":"Deleting a workspace is irreversible. Are you sure you want to proceed?"
3+
"info":"This workspace was created {{timeAgo}}."
54
},
65
"workspaceScheduleButton": {
76
"schedule":"Schedule",

‎site/src/pages/TemplatePage/TemplatePage.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { useMachine, useSelector } from "@xstate/react"
22
import{DeleteDialog}from"components/Dialogs/DeleteDialog/DeleteDialog"
33
import{FC,useContext}from"react"
44
import{Helmet}from"react-helmet-async"
5-
import{useTranslation}from"react-i18next"
65
import{Navigate,useParams}from"react-router-dom"
76
import{selectPermissions}from"xServices/auth/authSelectors"
87
import{XServiceContext}from"xServices/StateContext"
@@ -24,7 +23,6 @@ const useTemplateName = () => {
2423

2524
exportconstTemplatePage:FC<React.PropsWithChildren<unknown>>=()=>{
2625
constorganizationId=useOrganizationId()
27-
const{ t}=useTranslation("templatePage")
2826
consttemplateName=useTemplateName()
2927
const[templateState,templateSend]=useMachine(templateMachine,{
3028
context:{
@@ -77,8 +75,8 @@ export const TemplatePage: FC<React.PropsWithChildren<unknown>> = () => {
7775
<DeleteDialog
7876
isOpen={templateState.matches("confirmingDelete")}
7977
confirmLoading={templateState.matches("deleting")}
80-
title={t("deleteDialog.title")}
81-
description={t("deleteDialog.description")}
78+
entity="template"
79+
name={template.name}
8280
onConfirm={()=>{
8381
templateSend("CONFIRM_DELETE")
8482
}}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp