@rovula/ui 0.1.15 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -44,6 +44,8 @@ export * from "./components/Tree";
44
44
  export * from "./components/FocusedScrollView/FocusedScrollView";
45
45
  export * from "./components/RadioGroup/RadioGroup";
46
46
  export * from "./components/Form";
47
+ export * from "./patterns/confirm-dialog/ConfirmDialog";
48
+ export * from "./patterns/form-dialog/FormDialog";
47
49
  export type { ButtonProps } from "./components/Button/Button";
48
50
  export type { InputProps } from "./components/TextInput/TextInput";
49
51
  export type { PasswordInputProps } from "./components/PasswordInput/PasswordInput";
@@ -20,5 +20,6 @@ export type ConfirmDialogProps = {
20
20
  * When true, hides the cancel button — useful for info/error alerts that only need one action.
21
21
  */
22
22
  hideCancelButton?: boolean;
23
+ testId?: string;
23
24
  };
24
25
  export declare const ConfirmDialog: React.FC<ConfirmDialogProps>;
@@ -21,6 +21,7 @@ declare const meta: {
21
21
  trigger?: React.ReactNode;
22
22
  typeToConfirm?: string | undefined;
23
23
  hideCancelButton?: boolean | undefined;
24
+ testId?: string | undefined;
24
25
  }>) => import("react/jsx-runtime").JSX.Element)[];
25
26
  argTypes: {
26
27
  open: {
@@ -35,5 +35,6 @@ export type FormDialogProps = {
35
35
  * Use together with a <Form id={formId} .../> inside children.
36
36
  */
37
37
  formId?: string;
38
+ testId?: string;
38
39
  };
39
40
  export declare const FormDialog: React.FC<FormDialogProps>;
@@ -21,6 +21,7 @@ declare const meta: {
21
21
  scrollable?: boolean | undefined;
22
22
  className?: string | undefined;
23
23
  formId?: string | undefined;
24
+ testId?: string | undefined;
24
25
  }>) => import("react/jsx-runtime").JSX.Element)[];
25
26
  argTypes: {
26
27
  open: {
package/dist/index.d.ts CHANGED
@@ -19,6 +19,7 @@ import { ToastProviderProps } from '@radix-ui/react-toast';
19
19
  import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
20
20
  import { FieldValues, UseFormReturn, DefaultValues, SubmitHandler, SubmitErrorHandler, Resolver, Mode, FieldPath, RegisterOptions, FieldPathValue } from 'react-hook-form';
21
21
  import * as yup from 'yup';
22
+ import Button$1 from '@/components/Button/Button';
22
23
  import { ClassValue } from 'clsx';
23
24
 
24
25
  type ButtonProps = {
@@ -1281,6 +1282,70 @@ declare const useOptionBridge: <TValue extends OptionValue = string, TOption ext
1281
1282
  optionsByValue: Map<TValue, TOption>;
1282
1283
  };
1283
1284
 
1285
+ type ConfirmDialogProps = {
1286
+ open?: boolean;
1287
+ onOpenChange?: (open: boolean) => void;
1288
+ title: string;
1289
+ description?: string;
1290
+ confirmLabel?: string;
1291
+ cancelLabel?: string;
1292
+ onConfirm?: () => void;
1293
+ onCancel?: () => void;
1294
+ /** Fires whenever the dialog closes, regardless of how (cancel button, overlay, Escape). */
1295
+ onClose?: () => void;
1296
+ trigger?: React.ReactNode;
1297
+ /**
1298
+ * When provided, the user must type this exact text before the confirm button is enabled.
1299
+ * e.g. typeToConfirm="confirm" or typeToConfirm="delete"
1300
+ */
1301
+ typeToConfirm?: string;
1302
+ /**
1303
+ * When true, hides the cancel button — useful for info/error alerts that only need one action.
1304
+ */
1305
+ hideCancelButton?: boolean;
1306
+ testId?: string;
1307
+ };
1308
+ declare const ConfirmDialog: React.FC<ConfirmDialogProps>;
1309
+
1310
+ type FormDialogAction = {
1311
+ label: string;
1312
+ onClick?: () => void;
1313
+ variant?: React.ComponentProps<typeof Button$1>["variant"];
1314
+ color?: React.ComponentProps<typeof Button$1>["color"];
1315
+ disabled?: boolean;
1316
+ isLoading?: boolean;
1317
+ type?: "button" | "submit" | "reset";
1318
+ };
1319
+ type FormDialogProps = {
1320
+ open?: boolean;
1321
+ onOpenChange?: (open: boolean) => void;
1322
+ title: string;
1323
+ description?: string;
1324
+ children?: React.ReactNode;
1325
+ trigger?: React.ReactNode;
1326
+ /**
1327
+ * Primary action (right side of footer).
1328
+ */
1329
+ confirmAction?: FormDialogAction;
1330
+ /**
1331
+ * Secondary cancel action (right side of footer, outline style).
1332
+ */
1333
+ cancelAction?: FormDialogAction;
1334
+ /**
1335
+ * Optional extra action placed on the left side of the footer.
1336
+ */
1337
+ extraAction?: FormDialogAction;
1338
+ scrollable?: boolean;
1339
+ className?: string;
1340
+ /**
1341
+ * When provided, the confirm button becomes type="submit" and is linked to this form id.
1342
+ * Use together with a <Form id={formId} .../> inside children.
1343
+ */
1344
+ formId?: string;
1345
+ testId?: string;
1346
+ };
1347
+ declare const FormDialog: React.FC<FormDialogProps>;
1348
+
1284
1349
  declare const resloveTimestamp: (timestamp: number) => number;
1285
1350
  declare const getStartDateOfDay: (date: Date) => Date;
1286
1351
  declare const getEndDateOfDay: (date: Date) => Date;
@@ -1720,4 +1785,4 @@ declare const srgbToHex: (color: string) => string;
1720
1785
  */
1721
1786
  declare function getLucideIconNames(): Promise<string[]>;
1722
1787
 
1723
- export { ActionButton, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, Button, type ButtonProps, Calendar, Checkbox, Collapsible, type ControlledFormFactoryOptions, type CustomSliderProps, DataTable, type DataTableProps, DatePicker, Dialog, DialogBody, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Dropdown, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, type DropdownProps, Field, FieldMessage, type FieldMessageProps, type FieldProps, FocusedScrollView, Footer, type FooterProps, type FooterVariant, Form, type FormController, type FormProps, Icon, Input, InputFilter, type InputFilterProps, type InputProps, Label, Loading, type MaskRule, MaskedTextInput, type MaskedTextInputProps, Menu, MenuItem, type MenuItemType, MenuLabel, type MenuOption, type MenuProps, MenuSeparator, Navbar, type NavbarProps, type NavbarVariant, NumberInput, type NumberInputProps, type OptionLike, type Options$1 as Options, OtpInput, OtpInputGroup, type OtpInputGroupProps, type OtpInputProps, PasswordInput, type PasswordInputProps, Popover, PopoverContent, PopoverTrigger, ProgressBar, RadioGroup, RadioGroupItem, Search, type SearchProps, Slider, type SliderProps, Switch, THEME_COLOR_KEYS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, Text, TextArea, type TextAreaProps, TextInput, type ThemeColorKey, Toast$1 as Toast, ToastAction, type ToastActionElement, ToastClose, ToastDescription, type ToastProps, ToastProvider, ToastTitle, ToastViewport, Toaster, Tooltip, TooltipArrow, TooltipContent, TooltipProvider, TooltipSimple, TooltipTrigger, Tree, type TreeData, TreeItem, type TreeItemProps, type TreeProps, type UseControlledFormOptions, type UseOptionBridgeOptions, ValidationHintList, type ValidationHintListProps, type ValidationHintMode, type ValidationHintRule, type ValidationHintState, cn, createControlledForm, createYupResolver, getEndDateOfDay, getLucideIconNames, getStartDateOfDay, getStartEndTimestampOfDay, getThemeColor, getThemeColors, getTimestampUTC, reducer, resloveTimestamp, srgbToHex, toast, useControlledForm, useOptionBridge, usePrevious, useToast };
1788
+ export { ActionButton, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, Avatar, AvatarGroup, type AvatarGroupProps, type AvatarProps, Button, type ButtonProps, Calendar, Checkbox, Collapsible, ConfirmDialog, type ConfirmDialogProps, type ControlledFormFactoryOptions, type CustomSliderProps, DataTable, type DataTableProps, DatePicker, Dialog, DialogBody, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Dropdown, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, type DropdownProps, Field, FieldMessage, type FieldMessageProps, type FieldProps, FocusedScrollView, Footer, type FooterProps, type FooterVariant, Form, type FormController, FormDialog, type FormDialogAction, type FormDialogProps, type FormProps, Icon, Input, InputFilter, type InputFilterProps, type InputProps, Label, Loading, type MaskRule, MaskedTextInput, type MaskedTextInputProps, Menu, MenuItem, type MenuItemType, MenuLabel, type MenuOption, type MenuProps, MenuSeparator, Navbar, type NavbarProps, type NavbarVariant, NumberInput, type NumberInputProps, type OptionLike, type Options$1 as Options, OtpInput, OtpInputGroup, type OtpInputGroupProps, type OtpInputProps, PasswordInput, type PasswordInputProps, Popover, PopoverContent, PopoverTrigger, ProgressBar, RadioGroup, RadioGroupItem, Search, type SearchProps, Slider, type SliderProps, Switch, THEME_COLOR_KEYS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, Text, TextArea, type TextAreaProps, TextInput, type ThemeColorKey, Toast$1 as Toast, ToastAction, type ToastActionElement, ToastClose, ToastDescription, type ToastProps, ToastProvider, ToastTitle, ToastViewport, Toaster, Tooltip, TooltipArrow, TooltipContent, TooltipProvider, TooltipSimple, TooltipTrigger, Tree, type TreeData, TreeItem, type TreeItemProps, type TreeProps, type UseControlledFormOptions, type UseOptionBridgeOptions, ValidationHintList, type ValidationHintListProps, type ValidationHintMode, type ValidationHintRule, type ValidationHintState, cn, createControlledForm, createYupResolver, getEndDateOfDay, getLucideIconNames, getStartDateOfDay, getStartEndTimestampOfDay, getThemeColor, getThemeColors, getTimestampUTC, reducer, resloveTimestamp, srgbToHex, toast, useControlledForm, useOptionBridge, usePrevious, useToast };
package/dist/index.js CHANGED
@@ -46,6 +46,9 @@ export * from "./components/Tree";
46
46
  export * from "./components/FocusedScrollView/FocusedScrollView";
47
47
  export * from "./components/RadioGroup/RadioGroup";
48
48
  export * from "./components/Form";
49
+ // Patterns
50
+ export * from "./patterns/confirm-dialog/ConfirmDialog";
51
+ export * from "./patterns/form-dialog/FormDialog";
49
52
  // UTILS
50
53
  export { resloveTimestamp, getStartDateOfDay, getEndDateOfDay, getStartEndTimestampOfDay, getTimestampUTC, } from "./utils/datetime";
51
54
  // Hooks
@@ -5,7 +5,7 @@ import * as yup from "yup";
5
5
  import { AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogAction, AlertDialogCancel, AlertDialogTrigger, } from "@/components/AlertDialog/AlertDialog";
6
6
  import { useControlledForm, Field } from "@/components/Form";
7
7
  import { TextInput } from "@/components/TextInput/TextInput";
8
- export const ConfirmDialog = ({ open, onOpenChange, title, description, confirmLabel = "Confirm", cancelLabel = "Cancel", onConfirm, onCancel, onClose, trigger, typeToConfirm, hideCancelButton = false, }) => {
8
+ export const ConfirmDialog = ({ open, onOpenChange, title, description, confirmLabel = "Confirm", cancelLabel = "Cancel", onConfirm, onCancel, onClose, trigger, typeToConfirm, hideCancelButton = false, testId, }) => {
9
9
  const formId = React.useId();
10
10
  const requiresInput = !!typeToConfirm;
11
11
  const validationSchema = React.useMemo(() => yup.object({
@@ -33,12 +33,13 @@ export const ConfirmDialog = ({ open, onOpenChange, title, description, confirmL
33
33
  onCancel === null || onCancel === void 0 ? void 0 : onCancel();
34
34
  onClose === null || onClose === void 0 ? void 0 : onClose();
35
35
  };
36
- return (_jsxs(AlertDialog, { open: open, onOpenChange: handleOpenChange, children: [trigger && _jsx(AlertDialogTrigger, { asChild: true, children: trigger }), _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: title }), description && (_jsx(AlertDialogDescription, { children: description }))] }), requiresInput && (_jsxs(FormRoot, { id: formId, className: "flex flex-col gap-4 w-full", onSubmit: () => onConfirm === null || onConfirm === void 0 ? void 0 : onConfirm(), children: [_jsxs("p", { className: "typography-small1 text-text-contrast-max", children: ["Type \u201C", typeToConfirm, "\u201D to proceed."] }), _jsx(Field, { name: "confirmInput", component: TextInput, componentProps: {
36
+ return (_jsxs(AlertDialog, { open: open, onOpenChange: handleOpenChange, children: [trigger && _jsx(AlertDialogTrigger, { asChild: true, children: trigger }), _jsxs(AlertDialogContent, { "data-testid": testId, children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { "data-testid": testId && `${testId}-title`, children: title }), description && (_jsx(AlertDialogDescription, { "data-testid": testId && `${testId}-description`, children: description }))] }), requiresInput && (_jsxs(FormRoot, { id: formId, className: "flex flex-col gap-4 w-full", onSubmit: () => onConfirm === null || onConfirm === void 0 ? void 0 : onConfirm(), children: [_jsxs("p", { className: "typography-small1 text-text-contrast-max", children: ["Type \u201C", typeToConfirm, "\u201D to proceed."] }), _jsx(Field, { name: "confirmInput", component: TextInput, componentProps: {
37
37
  label: "Type to confirm",
38
38
  required: true,
39
39
  hasClearIcon: true,
40
40
  keepFooterSpace: true,
41
41
  fullwidth: true,
42
- } })] })), _jsxs(AlertDialogFooter, { children: [!hideCancelButton && (_jsx(AlertDialogCancel, { onClick: handleCancel, children: cancelLabel })), _jsx(AlertDialogAction, { type: "submit", form: requiresInput ? formId : undefined, disabled: requiresInput && !isFormValid, onClick: requiresInput ? undefined : () => onConfirm === null || onConfirm === void 0 ? void 0 : onConfirm(), children: confirmLabel })] })] })] }));
42
+ testId: testId && `${testId}-type-to-confirm-input`,
43
+ } })] })), _jsxs(AlertDialogFooter, { children: [!hideCancelButton && (_jsx(AlertDialogCancel, { "data-testid": testId && `${testId}-cancel-button`, onClick: handleCancel, children: cancelLabel })), _jsx(AlertDialogAction, { type: "submit", form: requiresInput ? formId : undefined, disabled: requiresInput && !isFormValid, onClick: requiresInput ? undefined : () => onConfirm === null || onConfirm === void 0 ? void 0 : onConfirm(), "data-testid": testId && `${testId}-confirm-button`, children: confirmLabel })] })] })] }));
43
44
  };
44
45
  ConfirmDialog.displayName = "ConfirmDialog";
@@ -2,9 +2,9 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogBody, DialogFooter, DialogTrigger, } from "@/components/Dialog/Dialog";
4
4
  import Button from "@/components/Button/Button";
5
- export const FormDialog = ({ open, onOpenChange, title, description, children, trigger, confirmAction, cancelAction, extraAction, scrollable = false, className, formId, }) => {
5
+ export const FormDialog = ({ open, onOpenChange, title, description, children, trigger, confirmAction, cancelAction, extraAction, scrollable = false, className, formId, testId, }) => {
6
6
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
7
7
  const hasFooter = confirmAction || cancelAction || extraAction;
8
- return (_jsxs(Dialog, { open: open, onOpenChange: onOpenChange, children: [trigger && _jsx(DialogTrigger, { asChild: true, children: trigger }), _jsxs(DialogContent, { className: className, children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: title }), description && (_jsx(DialogDescription, { children: description }))] }), children && (_jsx(DialogBody, { scrollable: scrollable, children: children })), hasFooter && (_jsxs(DialogFooter, { className: extraAction ? "justify-between" : undefined, children: [extraAction && (_jsx(Button, { type: (_a = extraAction.type) !== null && _a !== void 0 ? _a : "button", variant: (_b = extraAction.variant) !== null && _b !== void 0 ? _b : "outline", color: (_c = extraAction.color) !== null && _c !== void 0 ? _c : "secondary", fullwidth: false, disabled: extraAction.disabled, isLoading: extraAction.isLoading, onClick: extraAction.onClick, children: extraAction.label })), _jsxs("div", { className: "flex items-center gap-4", children: [cancelAction && (_jsx(Button, { type: (_d = cancelAction.type) !== null && _d !== void 0 ? _d : "button", variant: (_e = cancelAction.variant) !== null && _e !== void 0 ? _e : "outline", color: (_f = cancelAction.color) !== null && _f !== void 0 ? _f : "primary", fullwidth: false, disabled: cancelAction.disabled, isLoading: cancelAction.isLoading, onClick: cancelAction.onClick, children: cancelAction.label })), confirmAction && (_jsx(Button, { type: formId ? "submit" : ((_g = confirmAction.type) !== null && _g !== void 0 ? _g : "button"), form: formId, variant: (_h = confirmAction.variant) !== null && _h !== void 0 ? _h : "solid", color: (_j = confirmAction.color) !== null && _j !== void 0 ? _j : "primary", fullwidth: false, disabled: confirmAction.disabled, isLoading: confirmAction.isLoading, onClick: confirmAction.onClick, children: confirmAction.label }))] })] }))] })] }));
8
+ return (_jsxs(Dialog, { open: open, onOpenChange: onOpenChange, children: [trigger && _jsx(DialogTrigger, { asChild: true, children: trigger }), _jsxs(DialogContent, { className: className, "data-testid": testId, children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { "data-testid": testId && `${testId}-title`, children: title }), description && (_jsx(DialogDescription, { "data-testid": testId && `${testId}-description`, children: description }))] }), children && (_jsx(DialogBody, { scrollable: scrollable, children: children })), hasFooter && (_jsxs(DialogFooter, { className: extraAction ? "justify-between" : undefined, children: [extraAction && (_jsx(Button, { type: (_a = extraAction.type) !== null && _a !== void 0 ? _a : "button", variant: (_b = extraAction.variant) !== null && _b !== void 0 ? _b : "outline", color: (_c = extraAction.color) !== null && _c !== void 0 ? _c : "secondary", fullwidth: false, disabled: extraAction.disabled, isLoading: extraAction.isLoading, onClick: extraAction.onClick, "data-testid": testId && `${testId}-extra-button`, children: extraAction.label })), _jsxs("div", { className: "flex items-center gap-4", children: [cancelAction && (_jsx(Button, { type: (_d = cancelAction.type) !== null && _d !== void 0 ? _d : "button", variant: (_e = cancelAction.variant) !== null && _e !== void 0 ? _e : "outline", color: (_f = cancelAction.color) !== null && _f !== void 0 ? _f : "primary", fullwidth: false, disabled: cancelAction.disabled, isLoading: cancelAction.isLoading, onClick: cancelAction.onClick, "data-testid": testId && `${testId}-cancel-button`, children: cancelAction.label })), confirmAction && (_jsx(Button, { type: formId ? "submit" : (_g = confirmAction.type) !== null && _g !== void 0 ? _g : "button", form: formId, variant: (_h = confirmAction.variant) !== null && _h !== void 0 ? _h : "solid", color: (_j = confirmAction.color) !== null && _j !== void 0 ? _j : "primary", fullwidth: false, disabled: confirmAction.disabled, isLoading: confirmAction.isLoading, onClick: confirmAction.onClick, "data-testid": testId && `${testId}-confirm-button`, children: confirmAction.label }))] })] }))] })] }));
9
9
  };
10
10
  FormDialog.displayName = "FormDialog";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rovula/ui",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "main": "dist/cjs/bundle.js",
5
5
  "module": "dist/esm/bundle.js",
6
6
  "types": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -52,6 +52,10 @@ export * from "./components/FocusedScrollView/FocusedScrollView";
52
52
  export * from "./components/RadioGroup/RadioGroup";
53
53
  export * from "./components/Form";
54
54
 
55
+ // Patterns
56
+ export * from "./patterns/confirm-dialog/ConfirmDialog";
57
+ export * from "./patterns/form-dialog/FormDialog";
58
+
55
59
  // Export component types
56
60
  export type { ButtonProps } from "./components/Button/Button";
57
61
  export type { InputProps } from "./components/TextInput/TextInput";
@@ -37,6 +37,7 @@ export type ConfirmDialogProps = {
37
37
  * When true, hides the cancel button — useful for info/error alerts that only need one action.
38
38
  */
39
39
  hideCancelButton?: boolean;
40
+ testId?: string;
40
41
  };
41
42
 
42
43
  type ConfirmFormValues = { confirmInput: string };
@@ -54,6 +55,7 @@ export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
54
55
  trigger,
55
56
  typeToConfirm,
56
57
  hideCancelButton = false,
58
+ testId,
57
59
  }) => {
58
60
  const formId = React.useId();
59
61
  const requiresInput = !!typeToConfirm;
@@ -99,11 +101,15 @@ export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
99
101
  return (
100
102
  <AlertDialog open={open} onOpenChange={handleOpenChange}>
101
103
  {trigger && <AlertDialogTrigger asChild>{trigger}</AlertDialogTrigger>}
102
- <AlertDialogContent>
104
+ <AlertDialogContent data-testid={testId}>
103
105
  <AlertDialogHeader>
104
- <AlertDialogTitle>{title}</AlertDialogTitle>
106
+ <AlertDialogTitle data-testid={testId && `${testId}-title`}>
107
+ {title}
108
+ </AlertDialogTitle>
105
109
  {description && (
106
- <AlertDialogDescription>{description}</AlertDialogDescription>
110
+ <AlertDialogDescription data-testid={testId && `${testId}-description`}>
111
+ {description}
112
+ </AlertDialogDescription>
107
113
  )}
108
114
  </AlertDialogHeader>
109
115
 
@@ -125,6 +131,7 @@ export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
125
131
  hasClearIcon: true,
126
132
  keepFooterSpace: true,
127
133
  fullwidth: true,
134
+ testId: testId && `${testId}-type-to-confirm-input`,
128
135
  }}
129
136
  />
130
137
  </FormRoot>
@@ -132,7 +139,10 @@ export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
132
139
 
133
140
  <AlertDialogFooter>
134
141
  {!hideCancelButton && (
135
- <AlertDialogCancel onClick={handleCancel}>
142
+ <AlertDialogCancel
143
+ data-testid={testId && `${testId}-cancel-button`}
144
+ onClick={handleCancel}
145
+ >
136
146
  {cancelLabel}
137
147
  </AlertDialogCancel>
138
148
  )}
@@ -141,6 +151,7 @@ export const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
141
151
  form={requiresInput ? formId : undefined}
142
152
  disabled={requiresInput && !isFormValid}
143
153
  onClick={requiresInput ? undefined : () => onConfirm?.()}
154
+ data-testid={testId && `${testId}-confirm-button`}
144
155
  >
145
156
  {confirmLabel}
146
157
  </AlertDialogAction>
@@ -50,6 +50,7 @@ export type FormDialogProps = {
50
50
  * Use together with a <Form id={formId} .../> inside children.
51
51
  */
52
52
  formId?: string;
53
+ testId?: string;
53
54
  };
54
55
 
55
56
  export const FormDialog: React.FC<FormDialogProps> = ({
@@ -65,17 +66,20 @@ export const FormDialog: React.FC<FormDialogProps> = ({
65
66
  scrollable = false,
66
67
  className,
67
68
  formId,
69
+ testId,
68
70
  }) => {
69
71
  const hasFooter = confirmAction || cancelAction || extraAction;
70
72
 
71
73
  return (
72
74
  <Dialog open={open} onOpenChange={onOpenChange}>
73
75
  {trigger && <DialogTrigger asChild>{trigger}</DialogTrigger>}
74
- <DialogContent className={className}>
76
+ <DialogContent className={className} data-testid={testId}>
75
77
  <DialogHeader>
76
- <DialogTitle>{title}</DialogTitle>
78
+ <DialogTitle data-testid={testId && `${testId}-title`}>{title}</DialogTitle>
77
79
  {description && (
78
- <DialogDescription>{description}</DialogDescription>
80
+ <DialogDescription data-testid={testId && `${testId}-description`}>
81
+ {description}
82
+ </DialogDescription>
79
83
  )}
80
84
  </DialogHeader>
81
85
 
@@ -94,6 +98,7 @@ export const FormDialog: React.FC<FormDialogProps> = ({
94
98
  disabled={extraAction.disabled}
95
99
  isLoading={extraAction.isLoading}
96
100
  onClick={extraAction.onClick}
101
+ data-testid={testId && `${testId}-extra-button`}
97
102
  >
98
103
  {extraAction.label}
99
104
  </Button>
@@ -108,13 +113,14 @@ export const FormDialog: React.FC<FormDialogProps> = ({
108
113
  disabled={cancelAction.disabled}
109
114
  isLoading={cancelAction.isLoading}
110
115
  onClick={cancelAction.onClick}
116
+ data-testid={testId && `${testId}-cancel-button`}
111
117
  >
112
118
  {cancelAction.label}
113
119
  </Button>
114
120
  )}
115
121
  {confirmAction && (
116
122
  <Button
117
- type={formId ? "submit" : (confirmAction.type ?? "button")}
123
+ type={formId ? "submit" : confirmAction.type ?? "button"}
118
124
  form={formId}
119
125
  variant={confirmAction.variant ?? "solid"}
120
126
  color={confirmAction.color ?? "primary"}
@@ -122,6 +128,7 @@ export const FormDialog: React.FC<FormDialogProps> = ({
122
128
  disabled={confirmAction.disabled}
123
129
  isLoading={confirmAction.isLoading}
124
130
  onClick={confirmAction.onClick}
131
+ data-testid={testId && `${testId}-confirm-button`}
125
132
  >
126
133
  {confirmAction.label}
127
134
  </Button>