diff --git a/docs-ui/src/content/components/dialog.mdx b/docs-ui/src/content/components/dialog.mdx
index 5f465ddb0f..4aa43c69de 100644
--- a/docs-ui/src/content/components/dialog.mdx
+++ b/docs-ui/src/content/components/dialog.mdx
@@ -11,9 +11,10 @@ import {
dialogClosePropDefs,
dialogUsageSnippet,
dialogDefaultSnippet,
- dialogScrollableSnippet,
+ dialogFixedWidthAndHeightSnippet,
dialogWithFormSnippet,
dialogWithNoTriggerSnippet,
+ dialogCloseSnippet,
} from './dialog.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
@@ -37,18 +38,18 @@ import { ChangelogComponent } from '@/components/ChangelogComponent';
## API reference
-### Dialog
-
-The main dialog container that renders as a modal overlay.
-
-
-
### DialogTrigger
Wraps a trigger element and the dialog content to handle open/close state.
+### Dialog
+
+The main dialog container that renders as a modal overlay.
+
+
+
### DialogHeader
Displays the dialog title with a built-in close button.
@@ -67,23 +68,21 @@ Contains action buttons or other footer content.
-### DialogClose
+If you want to close the dialog while pressing a button, you can use the `slot="close"` prop on the button.
-A button that closes the dialog when pressed.
-
-
+
## Examples
-### Scrollable Content
+### Fixed Width and Height
Dialog with a fixed height body that scrolls when content overflows.
}
- code={dialogScrollableSnippet}
+ preview={}
+ code={dialogFixedWidthAndHeightSnippet}
/>
### Dialog with Form
diff --git a/docs-ui/src/content/components/dialog.props.ts b/docs-ui/src/content/components/dialog.props.ts
index 8f5d2f6f87..162ec307dc 100644
--- a/docs-ui/src/content/components/dialog.props.ts
+++ b/docs-ui/src/content/components/dialog.props.ts
@@ -4,14 +4,52 @@ import {
type PropDef,
} from '@/utils/propDefs';
-export const dialogPropDefs: Record = {
- children: { type: 'enum', values: ['ReactNode'], responsive: false },
- ...classNamePropDefs,
- ...stylePropDefs,
-};
-
export const dialogTriggerPropDefs: Record = {
children: { type: 'enum', values: ['ReactNode'], responsive: false },
+ isOpen: {
+ type: 'boolean',
+ description: 'Whether the overlay is open by default (controlled).',
+ },
+ defaultOpen: {
+ type: 'boolean',
+ description: 'Whether the overlay is open by default (uncontrolled).',
+ },
+ onOpenChange: {
+ type: 'enum',
+ values: ['(isOpen: boolean) => void'],
+ description:
+ "Handler that is called when the overlay's open state changes.",
+ },
+};
+
+export const dialogPropDefs: Record = {
+ children: { type: 'enum', values: ['ReactNode'], responsive: false },
+ isOpen: {
+ type: 'boolean',
+ description: 'Whether the overlay is open by default (controlled).',
+ },
+ defaultOpen: {
+ type: 'boolean',
+ description: 'Whether the overlay is open by default (uncontrolled).',
+ },
+ onOpenChange: {
+ type: 'enum',
+ values: ['(isOpen: boolean) => void'],
+ description:
+ "Handler that is called when the overlay's open state changes.",
+ },
+ width: {
+ type: 'enum',
+ values: ['number', 'string'],
+ responsive: false,
+ },
+ height: {
+ type: 'enum',
+ values: ['number', 'string'],
+ responsive: false,
+ },
+ ...classNamePropDefs,
+ ...stylePropDefs,
};
export const dialogHeaderPropDefs: Record = {
@@ -55,7 +93,6 @@ export const dialogUsageSnippet = `import {
DialogHeader,
DialogBody,
DialogFooter,
- DialogClose
} from '@backstage/ui';
@@ -64,7 +101,7 @@ export const dialogUsageSnippet = `import {
Title
Content
- Close
+
`;
@@ -77,39 +114,22 @@ export const dialogDefaultSnippet = `
This is a basic dialog example.
- Close
- Save
+
+
`;
-export const dialogScrollableSnippet = `
+export const dialogFixedWidthAndHeightSnippet = `
`;
@@ -130,10 +150,8 @@ export const dialogWithFormSnippet = `
-
- Cancel
- Create User
-
+
+
`;
@@ -146,9 +164,9 @@ export const dialogWithNoTriggerSnippet = `const [isOpen, setIsOpen] = useState(
Your content
-
- Cancel
- Create User
-
+
+
`;
+
+export const dialogCloseSnippet = ``;
diff --git a/packages/ui/report.api.md b/packages/ui/report.api.md
index e231cfb8e2..1674fef093 100644
--- a/packages/ui/report.api.md
+++ b/packages/ui/report.api.md
@@ -443,8 +443,8 @@ export const componentDefinitions: {
};
readonly Dialog: {
readonly classNames: {
- readonly root: 'bui-Dialog';
- readonly content: 'bui-DialogContent';
+ readonly overlay: 'bui-DialogOverlay';
+ readonly dialog: 'bui-Dialog';
readonly header: 'bui-DialogHeader';
readonly headerTitle: 'bui-DialogHeaderTitle';
readonly body: 'bui-DialogBody';
@@ -729,19 +729,6 @@ export interface DialogBodyProps {
children?: React.ReactNode;
// (undocumented)
className?: string;
- // (undocumented)
- height?: number | string;
-}
-
-// @public (undocumented)
-export const DialogClose: ForwardRefExoticComponent<
- DialogCloseProps & RefAttributes
->;
-
-// @public
-export interface DialogCloseProps extends Omit {
- // (undocumented)
- variant?: 'primary' | 'secondary' | 'tertiary';
}
// @public (undocumented)
@@ -772,6 +759,10 @@ export interface DialogProps extends ModalOverlayProps {
children?: React.ReactNode;
// (undocumented)
className?: string;
+ // (undocumented)
+ height?: number | string;
+ // (undocumented)
+ width?: number | string;
}
// @public (undocumented)
diff --git a/packages/ui/src/components/Dialog/Dialog.stories.tsx b/packages/ui/src/components/Dialog/Dialog.stories.tsx
index f1b89bc5ad..98d2492913 100644
--- a/packages/ui/src/components/Dialog/Dialog.stories.tsx
+++ b/packages/ui/src/components/Dialog/Dialog.stories.tsx
@@ -20,7 +20,6 @@ import {
DialogHeader,
DialogBody,
DialogFooter,
- DialogClose,
} from './Dialog';
import { Button, Flex, Text, TextField, Select } from '@backstage/ui';
import { useArgs } from 'storybook/preview-api';
@@ -44,16 +43,20 @@ type Story = StoryObj;
export const Default: Story = {
render: args => {
return (
-
+
-
@@ -87,52 +90,89 @@ export const NoTrigger: Story = {
This is a basic dialog example.
- Close
- Save
+
+
);
},
};
-export const Scrollable: Story = {
+export const FixedWidth: Story = {
args: {
- isOpen: true,
+ defaultOpen: true,
+ width: 600,
},
render: args => (
-
-
-
+
+
+
Long Content Dialog
-
-
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
- eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
- ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
- aliquip ex ea commodo consequat.
-
-
- Duis aute irure dolor in reprehenderit in voluptate velit esse
- cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
- cupidatat non proident, sunt in culpa qui officia deserunt mollit
- anim id est laborum.
-
-
- Sed ut perspiciatis unde omnis iste natus error sit voluptatem
- accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
- quae ab illo inventore veritatis et quasi architecto beatae vitae
- dicta sunt explicabo.
-
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
+ eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
+ enim ad minim veniam, quis nostrud exercitation ullamco laboris
+ nisi ut aliquip ex ea commodo consequat.
+
+
+ Duis aute irure dolor in reprehenderit in voluptate velit esse
+ cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
+ cupidatat non proident, sunt in culpa qui officia deserunt mollit
+ anim id est laborum.
+
+
+ Sed ut perspiciatis unde omnis iste natus error sit voluptatem
+ accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
+ quae ab illo inventore veritatis et quasi architecto beatae vitae
+ dicta sunt explicabo.
+
+
- Cancel
- Accept
+
+
),
};
+export const FixedHeight: Story = {
+ args: {
+ defaultOpen: true,
+ height: 500,
+ },
+ render: FixedWidth.render,
+};
+
+export const FixedWidthAndHeight: Story = {
+ args: {
+ defaultOpen: true,
+ width: 600,
+ height: 400,
+ },
+ render: FixedWidth.render,
+};
+
+export const FullWidthAndHeight: Story = {
+ args: {
+ defaultOpen: true,
+ width: '100%',
+ height: '100%',
+ },
+ render: FixedWidth.render,
+};
+
export const Confirmation: Story = {
args: {
isOpen: true,
@@ -149,8 +189,12 @@ export const Confirmation: Story = {
- Cancel
- Delete
+
+
@@ -181,26 +225,30 @@ export const WithForm: Story = {
- Cancel
- Create User
+
+
),
};
-export const PreviewScrollable: Story = {
+export const PreviewFixedWidthAndHeight: Story = {
args: {
- ...Scrollable.args,
- isOpen: false,
+ defaultOpen: undefined,
+ width: 600,
+ height: 400,
},
- render: Scrollable.render,
+ render: FixedWidth.render,
};
export const PreviewWithForm: Story = {
args: {
- ...WithForm.args,
- isOpen: false,
+ defaultOpen: undefined,
},
render: WithForm.render,
};
diff --git a/packages/ui/src/components/Dialog/Dialog.styles.css b/packages/ui/src/components/Dialog/Dialog.styles.css
index 5e7cd7afc9..9a2ffc52ca 100644
--- a/packages/ui/src/components/Dialog/Dialog.styles.css
+++ b/packages/ui/src/components/Dialog/Dialog.styles.css
@@ -1,5 +1,5 @@
/* Backdrop */
-.bui-Dialog {
+.bui-DialogOverlay {
position: fixed;
top: 0;
left: 0;
@@ -16,34 +16,36 @@
background: rgba(0, 0, 0, 0.5);
}
-.bui-Dialog[data-entering] {
+.bui-DialogOverlay[data-entering] {
animation: fade-in 200ms ease-out forwards;
}
-.bui-Dialog[data-exiting] {
+.bui-DialogOverlay[data-exiting] {
animation: fade-out 150ms ease-out forwards;
}
-.bui-DialogContent {
+.bui-Dialog {
background: var(--bui-bg-surface-1);
border-radius: 0.5rem;
border: 1px solid var(--bui-border);
color: var(--bui-fg-primary);
position: relative;
- width: 24rem;
+ width: min(var(--bui-dialog-min-width, 400px), calc(100vw - 3rem));
max-width: calc(100vw - 3rem);
+ height: min(var(--bui-dialog-min-height, auto), calc(100vh - 3rem));
+ max-height: calc(100vh - 3rem);
display: flex;
flex-direction: column;
outline: none;
}
/* Dialog entering animation */
-.bui-Dialog[data-entering] .bui-DialogContent {
+.bui-DialogOverlay[data-entering] .bui-Dialog {
animation: dialog-enter 150ms ease-out forwards;
}
/* Dialog exiting animation */
-.bui-Dialog[data-exiting] .bui-DialogContent {
+.bui-DialogOverlay[data-exiting] .bui-Dialog {
animation: dialog-exit 150ms ease-out forwards;
}
@@ -74,6 +76,8 @@
.bui-DialogBody {
padding: var(--bui-space-3);
+ flex: 1;
+ overflow-y: auto;
}
/* Keyframe animations */
diff --git a/packages/ui/src/components/Dialog/Dialog.tsx b/packages/ui/src/components/Dialog/Dialog.tsx
index 40c6a5c277..49eeac1508 100644
--- a/packages/ui/src/components/Dialog/Dialog.tsx
+++ b/packages/ui/src/components/Dialog/Dialog.tsx
@@ -27,11 +27,9 @@ import type {
DialogHeaderProps,
DialogProps,
DialogBodyProps,
- DialogCloseProps,
} from './types';
import './Dialog.styles.css';
import { RiCloseLine } from '@remixicon/react';
-import { ScrollArea } from '../ScrollArea';
import { Button } from '../Button';
import { useStyles } from '../../hooks/useStyles';
import { Flex } from '../Flex';
@@ -41,6 +39,41 @@ export const DialogTrigger = (props: DialogTriggerProps) => {
return ;
};
+/** @public */
+export const Dialog = forwardRef, DialogProps>(
+ ({ className, children, width, height, style, ...props }, ref) => {
+ const { classNames } = useStyles('Dialog');
+
+ return (
+
+
+ {children}
+
+
+ );
+ },
+);
+
+Dialog.displayName = 'Dialog';
+
/** @public */
export const DialogHeader = forwardRef<
React.ElementRef<'div'>,
@@ -63,27 +96,13 @@ DialogHeader.displayName = 'DialogHeader';
/** @public */
export const DialogBody = forwardRef, DialogBodyProps>(
- ({ className, children, height, ...props }, ref) => {
+ ({ className, children, ...props }, ref) => {
const { classNames } = useStyles('Dialog');
return (
-
- {children}
-
-
-
-
+
+ {children}
+
);
},
);
@@ -104,43 +123,3 @@ export const DialogFooter = forwardRef<
);
});
DialogFooter.displayName = 'DialogFooter';
-
-/** @public */
-export const DialogClose = forwardRef<
- React.ElementRef,
- DialogCloseProps
->(({ className, variant = 'secondary', children, ...props }, ref) => {
- return (
-
- );
-});
-DialogClose.displayName = 'DialogClose';
-
-/** @public */
-export const Dialog = forwardRef, DialogProps>(
- ({ className, children, ...props }, ref) => {
- const { classNames } = useStyles('Dialog');
-
- return (
-
- {children}
-
- );
- },
-);
-
-Dialog.displayName = 'Dialog';
diff --git a/packages/ui/src/components/Dialog/types.ts b/packages/ui/src/components/Dialog/types.ts
index b13f496d58..04f9dd4dfc 100644
--- a/packages/ui/src/components/Dialog/types.ts
+++ b/packages/ui/src/components/Dialog/types.ts
@@ -19,7 +19,6 @@ import type {
ModalOverlayProps as RAModalProps,
HeadingProps as RAHeadingProps,
} from 'react-aria-components';
-import type { ButtonProps } from '../Button';
/**
* Props for the DialogTrigger component.
@@ -27,6 +26,17 @@ import type { ButtonProps } from '../Button';
*/
export interface DialogTriggerProps extends RADialogTriggerProps {}
+/**
+ * Props for the Dialog component.
+ * @public
+ */
+export interface DialogProps extends RAModalProps {
+ className?: string;
+ children?: React.ReactNode;
+ width?: number | string;
+ height?: number | string;
+}
+
/**
* Props for the DialogHeader component.
* @public
@@ -43,22 +53,4 @@ export interface DialogHeaderProps extends RAHeadingProps {
export interface DialogBodyProps {
children?: React.ReactNode;
className?: string;
- height?: number | string;
-}
-
-/**
- * Props for the DialogClose component.
- * @public
- */
-export interface DialogCloseProps extends Omit {
- variant?: 'primary' | 'secondary' | 'tertiary';
-}
-
-/**
- * Props for the Dialog component.
- * @public
- */
-export interface DialogProps extends RAModalProps {
- className?: string;
- children?: React.ReactNode;
}
diff --git a/packages/ui/src/utils/componentDefinitions.ts b/packages/ui/src/utils/componentDefinitions.ts
index 92991db990..5f71a2a21f 100644
--- a/packages/ui/src/utils/componentDefinitions.ts
+++ b/packages/ui/src/utils/componentDefinitions.ts
@@ -87,8 +87,8 @@ export const componentDefinitions = {
},
Dialog: {
classNames: {
- root: 'bui-Dialog',
- content: 'bui-DialogContent',
+ overlay: 'bui-DialogOverlay',
+ dialog: 'bui-Dialog',
header: 'bui-DialogHeader',
headerTitle: 'bui-DialogHeaderTitle',
body: 'bui-DialogBody',