Skip to content
This repository was archived by the owner on May 13, 2025. It is now read-only.

Commit e6d532f

Browse files
authored
feat: improve UX and DX in delete and reset modals (#382)
1 parent 0f41f2d commit e6d532f

File tree

13 files changed

+305
-353
lines changed

13 files changed

+305
-353
lines changed

src/components/Button/IconButton.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const IconButton: FC<IconButtonProps> = (props) => {
1818
return (
1919
<Tooltip label={tooltipLabel}>
2020
<ActionIcon
21-
data-testId={props.data_id}
21+
data-testid={props.data_id}
2222
size={props.size ? props.size : 'xl'}
2323
className={`${classes.iconBtn} ${props.active && classes.iconBtnActive}`}
2424
onClick={props.onClick && props.onClick}>
@@ -29,7 +29,7 @@ const IconButton: FC<IconButtonProps> = (props) => {
2929
} else {
3030
return (
3131
<ActionIcon
32-
data-testId={props.data_id}
32+
data-testid={props.data_id}
3333
size={props.size ? props.size : 'xl'}
3434
className={`${classes.iconBtn} ${props.active && classes.iconBtnActive}`}
3535
onClick={props.onClick && props.onClick}>
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { Box, Button, Modal, Stack, Text, TextInput } from '@mantine/core';
2+
import classes from './styles/DeleteOrResetModal.module.css';
3+
import { ChangeEvent, useCallback, useState } from 'react';
4+
5+
type BaseProps = {
6+
isModalOpen: boolean;
7+
onClose: () => void;
8+
modalHeader: string;
9+
specialContent?: React.ReactNode;
10+
modalContent: string;
11+
actionProcessingContent?: React.ReactNode;
12+
isActionInProgress?: boolean;
13+
onConfirm: () => void;
14+
};
15+
16+
// Note: The `confirmationText` and `placeholder` props are required for 'delete' and 'reset' types, but not for 'simple' type.
17+
type DeleteOrResetModalProps =
18+
| (BaseProps & {
19+
type: 'simple';
20+
confirmationText?: never; // Will throw an error if `confirmationText` is passed
21+
placeholder?: never;
22+
})
23+
| (BaseProps & {
24+
type: 'delete' | 'reset';
25+
confirmationText: string;
26+
placeholder: string;
27+
});
28+
29+
/**
30+
* Confirmation modal for deleting or resetting an item.
31+
* @param type - Specifies the type of modal ('simple', 'delete', or 'reset').
32+
* @param isModalOpen - Controls whether the modal is visible.
33+
* @param onClose - Callback to close the modal and reset the state.
34+
* @param modalHeader - Header text displayed in the modal title.
35+
* @param specialContent - Optional content for additional context or customization.
36+
* @param modalContent - Main descriptive content of the modal.
37+
* @param placeholder - Input placeholder for confirmation text (applicable to 'delete' and 'reset').
38+
* @param confirmationText - Text required to confirm the action (applicable to 'delete' and 'reset').
39+
* @param actionProcessingContent - Optional content below text input for showing progress status or related information.
40+
* @param isActionInProgress - Disables the confirm button when action is in progress.
41+
* @param onConfirm - Callback function to be executed when the confirm button is clicked.
42+
*/
43+
const DeleteOrResetModal = ({
44+
type,
45+
isModalOpen,
46+
onClose,
47+
modalHeader,
48+
specialContent,
49+
modalContent,
50+
placeholder,
51+
confirmationText,
52+
actionProcessingContent,
53+
isActionInProgress,
54+
onConfirm,
55+
}: DeleteOrResetModalProps) => {
56+
const [confirmText, setConfirmText] = useState<string>('');
57+
58+
// Handler for the confirmation input field
59+
const onChangeHandler = useCallback((e: ChangeEvent<HTMLInputElement>) => {
60+
setConfirmText(e.target.value);
61+
}, []);
62+
63+
// Function to validate and trigger confirmation logic
64+
const tryConfirm = useCallback(() => {
65+
if (type === 'simple' || confirmationText === confirmText) {
66+
setConfirmText('');
67+
onConfirm();
68+
}
69+
}, [type, confirmationText, confirmText, onConfirm]);
70+
71+
// Function to close the modal and reset the confirmation text state.
72+
const closeModal = useCallback(() => {
73+
setConfirmText('');
74+
onClose();
75+
}, [onClose]);
76+
77+
return (
78+
<Modal
79+
withinPortal
80+
opened={isModalOpen}
81+
onClose={closeModal}
82+
size="auto"
83+
centered
84+
classNames={{
85+
body: classes.modalBody,
86+
header: classes.modalHeader,
87+
}}
88+
title={<Text className={classes.headerText}>{modalHeader}</Text>}>
89+
<Stack>
90+
<Stack gap={8}>
91+
{specialContent}
92+
<Text className={classes.warningText}>{modalContent}</Text>
93+
94+
{/* Render confirmation field for 'delete' or 'reset' types */}
95+
{type !== 'simple' && (
96+
<>
97+
<Text className={classes.confirmationText}>
98+
Please type <span className={classes.confirmationTextHighlight}>{`"${confirmationText}"`}</span> to
99+
confirm {type === 'delete' ? 'deletion' : 'reset'}.
100+
</Text>
101+
<TextInput value={confirmText} onChange={onChangeHandler} placeholder={placeholder} required />
102+
</>
103+
)}
104+
105+
{/* Renders the action processing content if provided */}
106+
{actionProcessingContent}
107+
</Stack>
108+
109+
{/* Action buttons */}
110+
<Stack className={classes.actionButtonsContainer}>
111+
<Box>
112+
<Button variant="outline" onClick={closeModal}>
113+
Cancel
114+
</Button>
115+
</Box>
116+
<Box>
117+
{/* Disable the button if the confirmation text is not correct or the action is processing. */}
118+
<Button
119+
disabled={(type !== 'simple' && confirmationText !== confirmText) || isActionInProgress}
120+
onClick={tryConfirm}>
121+
{type === 'reset' ? 'Reset' : 'Delete'}
122+
</Button>
123+
</Box>
124+
</Stack>
125+
</Stack>
126+
</Modal>
127+
);
128+
};
129+
130+
export default DeleteOrResetModal;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
.modalBody {
2+
padding: 0 1rem 1rem 1rem;
3+
width: 400px;
4+
}
5+
6+
.modalHeader {
7+
padding: 1rem;
8+
padding-bottom: 0.4rem;
9+
}
10+
11+
.headerText {
12+
font-size: 0.9rem;
13+
font-weight: 600;
14+
}
15+
16+
.warningText {
17+
margin-top: 0.4rem;
18+
font-size: 0.7rem;
19+
font-weight: 500;
20+
color: var(--mantine-color-gray-8);
21+
}
22+
23+
.confirmationText {
24+
font-size: 0.7rem;
25+
color: var(--mantine-color-gray-7);
26+
}
27+
28+
.confirmationTextHighlight {
29+
font-weight: 500;
30+
}
31+
32+
.actionButtonsContainer {
33+
display: flex;
34+
flex-direction: row;
35+
align-items: center;
36+
justify-content: flex-end;
37+
}

0 commit comments

Comments
 (0)