Skip to content

refactor(dialog): MD3 updates, updated Dialog API - #5072

Open
kachmashk wants to merge 5 commits into
callstack:mainfrom
kachmashk:refactor/dialog-improvements
Open

refactor(dialog): MD3 updates, updated Dialog API#5072
kachmashk wants to merge 5 commits into
callstack:mainfrom
kachmashk:refactor/dialog-improvements

Conversation

@kachmashk

@kachmashk kachmashk commented Aug 25, 2026

Copy link
Copy Markdown

Motivation

The goal of this PR is to address Material Design 3 guidelines for the Dialog component based on the official documentation as well as this issue. This PR also provides ability to avoid using previous compound components implementation in favor of props directly passed to the Dialog component.

Changes

Material Design 3:

  • Dialog.ScrollArea divider border color uses outline,
  • Dialog has now restricted minimum and maximum width set to 280dp and 560dp,
  • Fixed an issue when DialogIcon is the first component in the Dialog and it had a total top margin of 48dp instead of 24dp,
  • Dialog now detects if Dialog.Icon and Dialog.Title are present and if so enforcing Dialog.Title to be centered.

Dialog:

  • Dialog component now supports icon, title, content, actions, scrollable props instead of using compound components. Dialog still uses compound components underneath to properly comply with Material Design specs as these components enforce proper styling,
  • Added new test cases for Dialog to ensure the new approach works as intended.

Example:

  • Updated Dialog screen within Example app with the same dialogs as before but using new API to make sure they look and work as expected.

Related issue

Test plan

  • lint
  • typecheck
  • test

Reviewers can through new Dialogs added to the Example app. They are duplicated as previous dialogs but they use new approach.


const DIALOG_ELEVATION: number = 24;

const renderChildren = (children: React.ReactNode) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React.Children + cloneElement is the composition problem v6 is meant to remove (#4954), and this adds child.type === sniffing on top. On the prop path you build the array yourself - pass the margin and centring down as props instead.

children?: never;
};

type LegacyProps = {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This leaves v6 with two Dialog APIs - children deprecated but kept, plus seven duplicated NewDialog* example screens. Which one ships? A major shouldn't carry a deprecation shim, but dropping children is a break worth deciding deliberately.

const { colors } = theme;
const borderStyles = {
borderColor: colors.surfaceVariant,
borderColor: colors.outline,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-    borderColor: colors.outline,
+    borderColor: colors.outlineVariant,

md.comp.dialog.with-divider.divider.color is deprecated in favour of md.comp.divider.color - outline-variant, which is what Divider already uses.


if (topMarginStyle || titleAlignmentStyle) {
return React.cloneElement(child, {
style: [topMarginStyle, child.props.style, titleAlignmentStyle],

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-          style: [topMarginStyle, child.props.style, titleAlignmentStyle],
+          style: [topMarginStyle, titleAlignmentStyle, child.props.style],

As written, <Dialog.Title style={{ textAlign: 'left' }}> stops working once there's an icon.

* Content of the dialog. Non-empty strings are rendered as Material 3
* supporting text.
*/
content: React.ReactNode;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

content and actions are required, so a title-only dialog won't typecheck and NewDialogWithLoadingIndicator.tsx:35 has to pass actions={[]}. Make both optional.

testID?: string;
};

type DialogActionsProps = Omit<ButtonProps, 'children'> & { label: string };

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why { label }[] rather than actions?: React.ReactNode? An array of Button props can't express a custom action component, and it's a second shape to keep in sync with Button.

) : null;

return [dialogIcon, dialogTitle, dialogContent, dialogActions];
}, [children, props, theme.colors.onSurfaceVariant]);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

props is a fresh rest object every render, so this useMemo never hits. Drop it, or list the individual props.

@@ -5206,10 +5206,10 @@
"Dialog/Dialog": {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Run yarn docs generate - componentDocs6x.json and the .mdx pages are generated, and the JSDoc you added to DialogActions, DialogContent, DialogScrollArea and DialogTitle hasn't reached either. The hand-typed ### children (depracated…) heading will be overwritten.

{actions.map(
({ label, onPress: onActionPress, ...buttonProps }, index) => (
<Button
key={index}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

key={index} on a consumer-supplied array - the JSDoc note "Keep their order stable between renders" is working around it. Key on label.

const dialogIcon = icon ? (
<DialogIcon icon={icon} key="dialogIcon" />
) : null;
const dialogTitle = title ? <DialogTitle>{title}</DialogTitle> : null;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new API knows the title, but nothing gives the dialog an accessible name - Modal sets aria-modal with no label. Worth wiring title through while the API is being designed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants