diff --git a/packages/fluentui/CHANGELOG.md b/packages/fluentui/CHANGELOG.md
index 36faefd1d1525..b69fec65b93b4 100644
--- a/packages/fluentui/CHANGELOG.md
+++ b/packages/fluentui/CHANGELOG.md
@@ -30,6 +30,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- fix `Tooltip` constrast style for pointing and subtle=false @yuanboxue-amber ([#22696](https://github.com/microsoft/fluentui/pull/22696))
- Vertical menu background color should not change on focus in dark theme @yuanboxue-amber ([#22707](https://github.com/microsoft/fluentui/pull/22707))
- Align `default` scheme colors between v0 and v9 @jurokapsiar ([#22699](https://github.com/microsoft/fluentui/pull/22699))
+- Fix `Dialog` to keep it open when press click on content and release outside @yuanboxue-amber ([#22849](https://github.com/microsoft/fluentui/pull/22849))
- Align Avatar `font-weight` between v0 and v9 @annabratseiko ([#22822](https://github.com/microsoft/fluentui/pull/22822))
- Fix `Pill` to be selectable by keyboard @chpalac ([#22839](https://github.com/microsoft/fluentui/pull/22839))
diff --git a/packages/fluentui/e2e/tests/dialog-example.tsx b/packages/fluentui/e2e/tests/dialog-example.tsx
new file mode 100644
index 0000000000000..9780b7f8a0d53
--- /dev/null
+++ b/packages/fluentui/e2e/tests/dialog-example.tsx
@@ -0,0 +1,18 @@
+import * as React from 'react';
+import { Button, Dialog } from '@fluentui/react-northstar';
+
+export const selectors = {
+ trigger: 'trigger',
+ cancelButton: 'cancelButton',
+};
+
+const DialogBlockBodyScrollExample = () => (
+ }
+ />
+);
+
+export default DialogBlockBodyScrollExample;
diff --git a/packages/fluentui/e2e/tests/dialog.spec.ts b/packages/fluentui/e2e/tests/dialog.spec.ts
new file mode 100644
index 0000000000000..1b301af7b2f9c
--- /dev/null
+++ b/packages/fluentui/e2e/tests/dialog.spec.ts
@@ -0,0 +1,50 @@
+import { selectors } from './dialog-example';
+
+describe('Dialog', () => {
+ const trigger = `#${selectors.trigger}`;
+ const cancelButton = `#${selectors.cancelButton}`;
+
+ beforeEach(() => {
+ cy.gotoTestCase(__filename, trigger);
+ cy.get('body').click('bottomRight');
+ });
+
+ it('should open on click trigger', () => {
+ cy.clickOn(trigger);
+ cy.visible(cancelButton);
+ });
+
+ it('should close on click cancel button', () => {
+ cy.clickOn(trigger);
+ cy.visible(cancelButton);
+
+ cy.clickOn(cancelButton);
+ cy.notExist(cancelButton);
+ });
+
+ it('should close on click overlay', () => {
+ cy.clickOn(trigger);
+ cy.visible(cancelButton);
+
+ cy.get('.ui-dialog__overlay').click('topLeft');
+ cy.notExist(cancelButton);
+ });
+
+ it('should keep open when mouse down on button, and drag mouse outside of Dialog', () => {
+ cy.clickOn(trigger);
+ cy.visible(cancelButton);
+
+ // press click within Dialog content, drag mouse outside of Dialog content
+ cy.get(cancelButton).trigger('mousedown', { eventConstructor: 'MouseEvent', button: 0 }).trigger('mousemove', {
+ eventConstructor: 'MouseEvent',
+ clientX: 1,
+ clientY: 1,
+ pageX: 1,
+ pageY: 1,
+ screenX: 1,
+ screenY: 1,
+ }); // move mouse to top-left corner
+ cy.get('.ui-dialog__overlay').click('topLeft');
+ cy.visible(cancelButton);
+ });
+});
diff --git a/packages/fluentui/react-northstar/src/components/Dialog/Dialog.tsx b/packages/fluentui/react-northstar/src/components/Dialog/Dialog.tsx
index 806ff2f355010..e7dbdf5706a7d 100644
--- a/packages/fluentui/react-northstar/src/components/Dialog/Dialog.tsx
+++ b/packages/fluentui/react-northstar/src/components/Dialog/Dialog.tsx
@@ -258,10 +258,24 @@ export const Dialog = (React.forwardRef((props, ref
},
});
+ // when press left click on Dialog content and hold, and mouse up on Dialog overlay, Dialog should keep open
+ const isMouseDownInsideContent = React.useRef(false);
+ const registerMouseDownOnDialogContent = (e: React.MouseEvent) => {
+ if (e.button === 0) {
+ isMouseDownInsideContent.current = true;
+ }
+ if (unhandledProps.onMouseDown) {
+ _.invoke(unhandledProps, 'onMouseDown', e);
+ }
+ };
+
const handleOverlayClick = (e: MouseEvent) => {
// Dialog has different conditions to close than Popup, so we don't need to iterate across all
// refs
- const isInsideContentClick = doesNodeContainClick(contentRef.current, e, context.target);
+ const isInsideContentClick =
+ isMouseDownInsideContent.current || doesNodeContainClick(contentRef.current, e, context.target);
+ isMouseDownInsideContent.current = false;
+
const isInsideOverlayClick = doesNodeContainClick(overlayRef.current, e, context.target);
const shouldClose = !isInsideContentClick && isInsideOverlayClick;
@@ -318,6 +332,7 @@ export const Dialog = (React.forwardRef((props, ref
className: classes.root,
ref,
...unhandledProps,
+ onMouseDown: registerMouseDownOnDialogContent,
})}
>
{Header.create(header, {