Skip to content

feat: Add dialog component - #4904

Open
amanabiy wants to merge 3 commits into
mainfrom
dev-v3-amanabiy-dialog
Open

feat: Add dialog component#4904
amanabiy wants to merge 3 commits into
mainfrom
dev-v3-amanabiy-dialog

Conversation

@amanabiy

@amanabiy amanabiy commented Aug 13, 2026

Copy link
Copy Markdown
Member

Description

Adds an inline, non-modal Dialog component for in-context prompts, authorizations, and feedback. Dialog supports header actions, content, and footer slots, keeps the surrounding page interactive, manages initial and restored focus, and supports dismissal through the close button and Escape.

Related links, issue #, if available: 6f6tUHjL27wB - doc

How has this been tested?

Review checklist

The following items are to be evaluated by the author(s) and the reviewer(s).

Correctness

  • Changes include appropriate documentation updates.
  • Changes are backward-compatible if not indicated, see CONTRIBUTING.md.
  • Changes do not include unsupported browser features, see CONTRIBUTING.md.
  • Changes were manually tested for accessibility, see accessibility guidelines.

Security

Testing

  • Changes are covered with new/existing unit tests?
  • Changes are covered with new/existing integration tests?

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@codecov

codecov Bot commented Aug 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.11321% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 97.67%. Comparing base (1562d7b) to head (81a1279).
⚠️ Report is 18 commits behind head on main.

Files with missing lines Patch % Lines
src/dialog/internal.tsx 96.87% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #4904   +/-   ##
=======================================
  Coverage   97.66%   97.67%           
=======================================
  Files         959      963    +4     
  Lines       31345    31421   +76     
  Branches    11579    11604   +25     
=======================================
+ Hits        30614    30689   +75     
- Misses        724      725    +1     
  Partials        7        7           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@amanabiy
amanabiy force-pushed the dev-v3-amanabiy-dialog branch from 4fb0a5e to f87b9bc Compare August 27, 2026 12:35
@amanabiy
amanabiy force-pushed the dev-v3-amanabiy-dialog branch 2 times, most recently from f87b9bc to 5f24213 Compare August 27, 2026 12:46
@amanabiy
amanabiy force-pushed the dev-v3-amanabiy-dialog branch from 5f24213 to 2133113 Compare September 1, 2026 09:12
@amanabiy
amanabiy force-pushed the dev-v3-amanabiy-dialog branch from 2133113 to e62996c Compare September 1, 2026 11:42
@amanabiy
amanabiy force-pushed the dev-v3-amanabiy-dialog branch from e62996c to 4cd8d21 Compare September 1, 2026 11:54
@amanabiy
amanabiy force-pushed the dev-v3-amanabiy-dialog branch from 4cd8d21 to 97f9d8a Compare September 4, 2026 12:48
Comment thread pages/dialog/simple.page.tsx
Comment thread src/dialog/__tests__/dialog.test.tsx Outdated
Comment thread src/dialog/__tests__/dialog.test.tsx Outdated
Comment thread src/dialog/__tests__/dialog.test.tsx Outdated
Comment thread src/dialog/interfaces.ts Outdated

export namespace DialogProps {
export interface DismissDetail {
reason: string;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we call this field "reason" in some other components? E.g. in the Drawer a similar prop is called "method".

Why does it have a string type and not a union of possible values?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also take a look at how we name the values in other components - e.g. should we use "closeButton" or "close-button" (I'd argue we also need to call it "dismiss-button" to match the terminology used in the component).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thinking some more about dialog dismissal - is there any good reason to prevent dialog from closing on-escape, while keeping the dismiss button? If we don't have any use cases - let's remove the property, too.

If in the future we allow hiding the dismiss button - then we should disable the on-escape dismissal, too - relying on consumers to provide it if needed.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The DismissDetails with reason as a strign is similar to the API in modal

reason: string;
The documentation that says closeButton and keyboard is also similar to the modal. I think adding the reason is beneficial if customers need to control the closing behavior when it is triggered by escape.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The reason is a string (like Modal) instead of a union. The detail is an output type we can't widen later, so a union would lock the members and we couldn't add a future dismiss reason without a breaking change. string keeps it forward compatible. New reasons can be added later, with the current values (closeButton, escape) documented in the JSDoc if needed.

That said, I'm also fine dropping reason for now since we don't have a consumer for it yet. Adding a detail property later is non breaking, so we lose nothing by waiting and adding it when a real use case defines the shape. Happy to go either way.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks, I did not realise we use the same API in the modal already - that is a good call out. We can do the same for consistency in this case, but I'd still wait for the use cases. When dismiss button is not optional - the reason prop should not be needed.

Comment thread src/dialog/__tests__/dialog.test.tsx Outdated
Comment thread src/dialog/__tests__/dialog.test.tsx Outdated
Comment thread src/dialog/__tests__/dialog.test.tsx Outdated
Comment thread src/dialog/__tests__/dialog.test.tsx Outdated
Comment thread src/dialog/__tests__/dialog.test.tsx Outdated
Comment thread src/dialog/__tests__/dialog.test.tsx Outdated
Comment thread src/dialog/__tests__/dialog.test.tsx Outdated
Comment thread src/dialog/__tests__/dialog.test.tsx Outdated
Comment thread src/dialog/__tests__/dialog.test.tsx Outdated
Comment thread src/dialog/index.tsx Outdated
Comment thread src/dialog/interfaces.ts Outdated
Comment thread src/dialog/interfaces.ts Outdated
Comment thread src/dialog/interfaces.ts Outdated
Comment thread src/dialog/internal.tsx Outdated
Comment thread src/dialog/internal.tsx Outdated
Comment thread src/dialog/internal.tsx Outdated
Comment thread src/dialog/internal.tsx Outdated
Comment thread style-dictionary/visual-refresh/metadata/borders.ts Outdated
Comment thread style-dictionary/visual-refresh/colors.ts Outdated
@@ -125,6 +125,7 @@ const tokens: StyleDictionary.ColorsDictionary = {
colorBackgroundAlertSuccess: '{colorBackgroundStatusSuccess}',
colorBackgroundAlertWarning: '{colorBackgroundStatusWarning}',
colorBackgroundDialog: '{colorBackgroundStatusInfo}',

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Kept the existing public colorBackgroundDialog unchanged and added a private colorBackgroundDialogDefault token for the new Dialog component.

Comment thread pages/dialog/focus-restoration.page.tsx Outdated
Comment thread pages/dialog/focus-restoration.page.tsx Outdated

const dismissDialog = () => {
setOpen(false);
window.setTimeout(() => focusTargetRef.current?.focus(), 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we avoid having setTimeout here?

What if the original target element is available, but the consumer intent is to move the focus elsewhere - will then both elements be focused in a close sequence? Can this trigger extra announcement by the screen reader?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, we don’t need setTimeout. I updated the example to move focus when Dialog transitions from open to closed. Our Focus management principles recommend returning focus to the trigger when it is still available. If it no longer exists, the builder should move focus programmatically to a logical fallback, usually the next focusable element in the DOM order. This example covers the latter case and does not override an available trigger. If a customer intentionally overrides restoration while the original trigger still exists, both elements could receive focus in sequence and cause an additional announcement as you mentioned.

Comment thread src/dialog/internal.tsx Outdated
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.

3 participants