@ceed/cds 1.37.0 → 1.38.0
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.
|
@@ -4,6 +4,12 @@ export interface DialogFrameProps {
|
|
|
4
4
|
children: React.ReactNode;
|
|
5
5
|
actions: React.ReactNode;
|
|
6
6
|
fullscreen?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* When provided, the dialog root renders as a `<form>` and this handler runs on submit.
|
|
9
|
+
* This enables native submit-on-Enter and lets a `type="submit"` button in `actions`
|
|
10
|
+
* submit the fields in the content area. When omitted, the root stays a `<div>`.
|
|
11
|
+
*/
|
|
12
|
+
onSubmit?: React.FormEventHandler<HTMLFormElement>;
|
|
7
13
|
}
|
|
8
14
|
declare const DialogFrame: React.ForwardRefExoticComponent<Omit<Omit<Pick<{
|
|
9
15
|
children?: React.ReactNode;
|
|
@@ -18,5 +24,5 @@ declare const DialogFrame: React.ForwardRefExoticComponent<Omit<Omit<Pick<{
|
|
|
18
24
|
variant?: import("@mui/types").OverridableStringUnion<import("@mui/joy").VariantProp, import("@mui/joy").ModalDialogPropsVariantOverrides> | undefined;
|
|
19
25
|
} & import("@mui/joy").ModalDialogSlotsAndSlotProps & Omit<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
|
|
20
26
|
ref?: ((instance: HTMLDivElement | null) => void) | React.RefObject<HTMLDivElement> | null | undefined;
|
|
21
|
-
}, "children" | "layout" | "color" | "maxWidth" | "minWidth" | "variant" | "sx" | "size" | "invertedColors" | "orientation" | keyof import("@mui/joy").ModalDialogSlotsAndSlotProps> & import("@mui/system").MUIStyledCommonProps<import("@mui/joy").Theme>, "style" | "title" | "children" | "onAnimationStart" | "onDragStart" | "onDragEnd" | "onDrag" | "layout" | "color" | "content" | "maxWidth" | "minWidth" | "translate" | "ref" | "slot" | "key" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "autoFocus" | "className" | "contentEditable" | "contextMenu" | "dir" | "draggable" | "hidden" | "id" | "lang" | "nonce" | "spellCheck" | "tabIndex" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "rel" | "resource" | "rev" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-braillelabel" | "aria-brailleroledescription" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colindextext" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-description" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowindextext" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onResize" | "onResizeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDragCapture" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerLeave" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "variant" | "size" | "invertedColors" | "orientation" | keyof import("@mui/system").MUIStyledCommonProps<import("@mui/joy").Theme> | keyof import("@mui/joy").ModalDialogSlotsAndSlotProps> & import("@mui/system").MUIStyledCommonProps<import("@mui/joy").Theme>, "title" | "children"> & DialogFrameProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
27
|
+
}, "children" | "layout" | "color" | "maxWidth" | "minWidth" | "variant" | "sx" | "size" | "invertedColors" | "orientation" | keyof import("@mui/joy").ModalDialogSlotsAndSlotProps> & import("@mui/system").MUIStyledCommonProps<import("@mui/joy").Theme>, "style" | "title" | "children" | "onAnimationStart" | "onDragStart" | "onDragEnd" | "onDrag" | "layout" | "color" | "content" | "maxWidth" | "minWidth" | "translate" | "ref" | "slot" | "key" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "autoFocus" | "className" | "contentEditable" | "contextMenu" | "dir" | "draggable" | "hidden" | "id" | "lang" | "nonce" | "spellCheck" | "tabIndex" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "rel" | "resource" | "rev" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-braillelabel" | "aria-brailleroledescription" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colindextext" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-description" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowindextext" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onResize" | "onResizeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDragCapture" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerLeave" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "variant" | "size" | "invertedColors" | "orientation" | keyof import("@mui/system").MUIStyledCommonProps<import("@mui/joy").Theme> | keyof import("@mui/joy").ModalDialogSlotsAndSlotProps> & import("@mui/system").MUIStyledCommonProps<import("@mui/joy").Theme>, "title" | "children" | "onSubmit"> & DialogFrameProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
22
28
|
export { DialogFrame };
|
|
@@ -208,41 +208,45 @@ function DeleteConfirmation({ itemName, open, onClose, onDelete }) {
|
|
|
208
208
|
|
|
209
209
|
### Form Dialog
|
|
210
210
|
|
|
211
|
+
> ✅ **Best Practice** ✅
|
|
212
|
+
>
|
|
213
|
+
> For any dialog that collects input and submits, **always pass `onSubmit`** to DialogFrame — never wire the confirm button with `onClick` alone. When `onSubmit` is provided, DialogFrame renders its root as a `<form>`, so the fields in the content area and the `type="submit"` button in `actions` belong to the same form. This is what enables **submit-on-Enter** (pressing Enter inside an input submits) plus native constraint validation (`required`, `type="email"`, etc.).
|
|
214
|
+
>
|
|
215
|
+
> An `onClick` handler on the action button cannot do this: the button lives in `actions`, a sibling of the content area, so without a wrapping `<form>` there is no implicit submit and Enter does nothing.
|
|
216
|
+
|
|
211
217
|
```tsx
|
|
212
218
|
function QuickAddDialog({ open, onClose, onSubmit }) {
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
onSubmit({ name, email });
|
|
218
|
-
setName('');
|
|
219
|
-
setEmail('');
|
|
219
|
+
const handleSubmit = (event) => {
|
|
220
|
+
event.preventDefault();
|
|
221
|
+
const formData = new FormData(event.currentTarget);
|
|
222
|
+
onSubmit({ name: formData.get('name'), email: formData.get('email') });
|
|
220
223
|
onClose();
|
|
221
224
|
};
|
|
222
225
|
|
|
223
226
|
return (
|
|
224
227
|
<Modal open={open} onClose={onClose}>
|
|
228
|
+
{/* onSubmit makes the root a <form>; Enter in any field submits */}
|
|
225
229
|
<DialogFrame
|
|
226
230
|
title="Add New User"
|
|
231
|
+
onSubmit={handleSubmit}
|
|
227
232
|
actions={
|
|
228
233
|
<>
|
|
229
234
|
<Button variant="plain" color="neutral" onClick={onClose}>
|
|
230
235
|
Cancel
|
|
231
236
|
</Button>
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
</Button>
|
|
237
|
+
{/* type="submit" ties this button to the DialogFrame form */}
|
|
238
|
+
<Button type="submit">Add User</Button>
|
|
235
239
|
</>
|
|
236
240
|
}
|
|
237
241
|
>
|
|
238
242
|
<Stack gap={2}>
|
|
239
243
|
<FormControl>
|
|
240
244
|
<FormLabel>Name</FormLabel>
|
|
241
|
-
<Input
|
|
245
|
+
<Input name="name" placeholder="Enter name" autoFocus required />
|
|
242
246
|
</FormControl>
|
|
243
247
|
<FormControl>
|
|
244
248
|
<FormLabel>Email</FormLabel>
|
|
245
|
-
<Input type="email"
|
|
249
|
+
<Input type="email" name="email" placeholder="Enter email" required />
|
|
246
250
|
</FormControl>
|
|
247
251
|
</Stack>
|
|
248
252
|
</DialogFrame>
|
|
@@ -402,13 +406,14 @@ function SelectOptionDialog({ open, onClose, options, onSelect }) {
|
|
|
402
406
|
|
|
403
407
|
### Key Props
|
|
404
408
|
|
|
405
|
-
| Prop | Type
|
|
406
|
-
| ------------ |
|
|
407
|
-
| `title` | `string`
|
|
408
|
-
| `children` | `ReactNode`
|
|
409
|
-
| `actions` | `ReactNode`
|
|
410
|
-
| `fullscreen` | `boolean`
|
|
411
|
-
| `
|
|
409
|
+
| Prop | Type | Default | Description |
|
|
410
|
+
| ------------ | --------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
411
|
+
| `title` | `string` | - | Dialog title displayed in the header |
|
|
412
|
+
| `children` | `ReactNode` | - | Dialog body content |
|
|
413
|
+
| `actions` | `ReactNode` | - | Action buttons displayed in the footer |
|
|
414
|
+
| `fullscreen` | `boolean` | `false` | Enable fullscreen mode |
|
|
415
|
+
| `onSubmit` | `(event: FormEvent<HTMLFormElement>) => void` | - | When provided, the dialog root renders as a `<form>`, enabling submit-on-Enter and a `type="submit"` action button. Omit it and the root stays a `<div>` |
|
|
416
|
+
| `onKeyDown` | `(event: KeyboardEvent) => void` | - | Keyboard event handler |
|
|
412
417
|
|
|
413
418
|
### Structure
|
|
414
419
|
|
|
@@ -576,6 +581,25 @@ DialogFrame inherits accessibility features from Modal:
|
|
|
576
581
|
/>
|
|
577
582
|
```
|
|
578
583
|
|
|
584
|
+
5. **Use `onSubmit` for input dialogs**: For any dialog that collects values and submits, pass `onSubmit` and mark the confirm button `type="submit"`. This renders the root as a `<form>`, giving you submit-on-Enter and native validation for free.
|
|
585
|
+
|
|
586
|
+
```tsx
|
|
587
|
+
// ✅ Good: onSubmit + type="submit" — Enter submits, `required` is enforced
|
|
588
|
+
<DialogFrame
|
|
589
|
+
onSubmit={handleSubmit}
|
|
590
|
+
actions={
|
|
591
|
+
<>
|
|
592
|
+
<Button variant="plain" color="neutral" onClick={onClose}>
|
|
593
|
+
Cancel
|
|
594
|
+
</Button>
|
|
595
|
+
<Button type="submit">Save</Button>
|
|
596
|
+
</>
|
|
597
|
+
}
|
|
598
|
+
>
|
|
599
|
+
<Input name="email" required />
|
|
600
|
+
</DialogFrame>
|
|
601
|
+
```
|
|
602
|
+
|
|
579
603
|
### ❌ Don't
|
|
580
604
|
|
|
581
605
|
1. **Don't use for notifications**: Use Toast instead
|
|
@@ -611,6 +635,20 @@ showToast({ message: 'Item saved!' });
|
|
|
611
635
|
|
|
612
636
|
4. **Don't block critical workflows**: Important features should be accessible
|
|
613
637
|
|
|
638
|
+
5. **Don't submit input dialogs with `onClick` only**: Pressing Enter in a field won't submit, because the button in `actions` sits outside any form.
|
|
639
|
+
|
|
640
|
+
```tsx
|
|
641
|
+
// ❌ Bad: onClick-only — Enter in the input does nothing
|
|
642
|
+
<DialogFrame actions={<Button onClick={handleSubmit}>Save</Button>}>
|
|
643
|
+
<Input value={value} onChange={...} />
|
|
644
|
+
</DialogFrame>
|
|
645
|
+
|
|
646
|
+
// ✅ Good: onSubmit + type="submit" — Enter submits
|
|
647
|
+
<DialogFrame onSubmit={handleSubmit} actions={<Button type="submit">Save</Button>}>
|
|
648
|
+
<Input name="value" />
|
|
649
|
+
</DialogFrame>
|
|
650
|
+
```
|
|
651
|
+
|
|
614
652
|
## Performance Considerations
|
|
615
653
|
|
|
616
654
|
### Lazy Load Dialog Content
|