diff --git a/packages/fluentui/CHANGELOG.md b/packages/fluentui/CHANGELOG.md
index bbd8732a1b385a..80264d7d45a7bc 100644
--- a/packages/fluentui/CHANGELOG.md
+++ b/packages/fluentui/CHANGELOG.md
@@ -23,6 +23,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Use shadow8 for better contrast of `Tooltip` @jurokapsiar ([#21316](https://github.com/microsoft/fluentui/pull/21316))
- `FocusTrapZone`: add handleRef method instead of function to prevent calling it on each re-render @annabratseiko ([#21337](https://github.com/microsoft/fluentui/pull/21337))
+### Features
+- Add new Popup prop `closeOnScroll` to close popup when scroll happens outside of the popover element @yuanboxue-amber ([#21453](https://github.com/microsoft/fluentui/pull/21453))
+
## [v0.60.1](https://github.com/microsoft/fluentui/tree/@fluentui/react-northstar_v0.60.1) (2022-01-17)
[Compare changes](https://github.com/microsoft/fluentui/compare/@fluentui/react-northstar_v0.60.0..@fluentui/react-northstar_v0.60.1)
diff --git a/packages/fluentui/e2e/tests/popupDismissScroll-example.tsx b/packages/fluentui/e2e/tests/popupDismissScroll-example.tsx
new file mode 100644
index 00000000000000..c1fa9ef811bc27
--- /dev/null
+++ b/packages/fluentui/e2e/tests/popupDismissScroll-example.tsx
@@ -0,0 +1,52 @@
+import React from 'react';
+import { Button, Popup, Flex } from '@fluentui/react-northstar';
+
+export const selectors = {
+ simplePopup: {
+ triggerId: 'trigger',
+ contentId: 'content',
+ },
+ contextPopup: {
+ triggerId: 'trigger-context',
+ contentId: 'content-context',
+ },
+ dismissScrollPopup: {
+ triggerId: 'trigger-dismiss',
+ contentId: 'content-dismiss',
+ },
+};
+
+const PopupClickHandlingExample = () => {
+ return (
+
+ simple popup
+ }
+ content={{
+ content: 'Open a popup',
+ id: selectors.simplePopup.contentId,
+ }}
+ />
+ popup open on context
+ }
+ content={{
+ content: 'Open a popup',
+ id: selectors.contextPopup.contentId,
+ }}
+ on="context"
+ />
+ popup with closeOnScroll
+ }
+ content={{
+ content: 'Open a popup',
+ id: selectors.dismissScrollPopup.contentId,
+ }}
+ closeOnScroll
+ />
+
+ );
+};
+
+export default PopupClickHandlingExample;
diff --git a/packages/fluentui/e2e/tests/popupDismissScroll.spec.ts b/packages/fluentui/e2e/tests/popupDismissScroll.spec.ts
new file mode 100644
index 00000000000000..a0ec61a47b634b
--- /dev/null
+++ b/packages/fluentui/e2e/tests/popupDismissScroll.spec.ts
@@ -0,0 +1,28 @@
+import { selectors } from './popupDismissScroll-example';
+
+describe('Popup - dismiss on scroll container', () => {
+ beforeEach(() => {
+ cy.gotoTestCase(__filename, `#${selectors.simplePopup.triggerId}`);
+ });
+
+ it('simple popup does not dismiss content on scroll', () => {
+ cy.clickOn(`#${selectors.simplePopup.triggerId}`); // opens popup
+ cy.visible(`#${selectors.simplePopup.contentId}`); // popup visible
+ cy.get('body').trigger('wheel'); // page scroll
+ cy.visible(`#${selectors.simplePopup.contentId}`); // popup still visible
+ });
+
+ it('popup on context dismiss content on scroll', () => {
+ cy.get(`#${selectors.contextPopup.triggerId}`).rightclick(); // opens popup
+ cy.visible(`#${selectors.contextPopup.contentId}`); // popup visible
+ cy.get('body').trigger('wheel'); // page scroll
+ cy.notExist(`#${selectors.contextPopup.contentId}`); // popup dismissed
+ });
+
+ it('popup with closeOnScroll prop dismiss content on scroll', () => {
+ cy.clickOn(`#${selectors.dismissScrollPopup.triggerId}`); // opens popup
+ cy.visible(`#${selectors.dismissScrollPopup.contentId}`); // popup visible
+ cy.get('body').trigger('wheel'); // page scroll
+ cy.notExist(`#${selectors.dismissScrollPopup.contentId}`); // popup dismissed
+ });
+});
diff --git a/packages/fluentui/react-northstar/src/components/Popup/Popup.tsx b/packages/fluentui/react-northstar/src/components/Popup/Popup.tsx
index e81f7db8a2051e..a389745e80b510 100644
--- a/packages/fluentui/react-northstar/src/components/Popup/Popup.tsx
+++ b/packages/fluentui/react-northstar/src/components/Popup/Popup.tsx
@@ -131,6 +131,9 @@ export interface PopupProps
/** Controls whether or not auto focus should be applied, using boolean or AutoFocusZoneProps type value. */
autoFocus?: boolean | AutoFocusZoneProps;
+
+ /** Close the popup when scroll happens outside of Popup */
+ closeOnScroll?: boolean;
}
export const popupClassName = 'ui-popup';
@@ -170,6 +173,7 @@ export const Popup: React.FC &
unstable_disableTether,
unstable_pinned,
autoSize,
+ closeOnScroll,
} = props;
const [open, setOpen] = useAutoControlled({
@@ -477,7 +481,7 @@ export const Popup: React.FC &
capture
/>
- {isOpenedByRightClick && (
+ {(isOpenedByRightClick || closeOnScroll) && (
<>
@@ -685,6 +689,7 @@ Popup.propTypes = {
contentRef: customPropTypes.ref,
trapFocus: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]),
autoFocus: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]),
+ closeOnScroll: PropTypes.bool,
};
Popup.defaultProps = {
accessibility: popupBehavior,