+): CreateRepositionOnScrollReturnType => {
+ let lastResolvedRepositionOnScroll: boolean | undefined;
+
+ const subscribe = () => {
+ const repositionOnScroll = getRepositionOnScroll(getArgs());
+
+ lastResolvedRepositionOnScroll = repositionOnScroll;
+ if (repositionOnScroll) {
+ window.addEventListener('scroll', getArgs().repositionFn, true);
+ }
+ };
+
+ const update = () => {
+ const repositionOnScroll = getRepositionOnScroll(getArgs());
+
+ if (lastResolvedRepositionOnScroll !== repositionOnScroll) {
+ if (repositionOnScroll) {
+ window.addEventListener('scroll', getArgs().repositionFn, true);
+ } else {
+ window.removeEventListener('scroll', getArgs().repositionFn, true);
+ }
+ lastResolvedRepositionOnScroll = repositionOnScroll;
+ }
+ };
+
+ const cleanup = () => {
+ window.removeEventListener('scroll', getArgs().repositionFn, true);
+ };
+
+ return { subscribe, update, cleanup };
+};
diff --git a/packages/eui/src/test/cypress/test_reposition_on_scroll.tsx b/packages/eui/src/test/cypress/test_reposition_on_scroll.tsx
new file mode 100644
index 00000000000..25a783635ca
--- /dev/null
+++ b/packages/eui/src/test/cypress/test_reposition_on_scroll.tsx
@@ -0,0 +1,90 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+///
+///
+///
+
+import React from 'react';
+
+type ComponentName = 'EuiToolTip' | 'EuiPopover';
+
+interface RepositionTestConfig {
+ shouldReposition: boolean;
+ propValue?: boolean;
+ componentDefaultValue?: boolean;
+ componentName: ComponentName;
+ triggerSelector: string;
+ panelSelector: string;
+ renderComponent: (props: {
+ repositionOnScroll?: boolean;
+ }) => React.ReactElement;
+}
+
+export const testRepositionOnScroll = ({
+ shouldReposition,
+ propValue,
+ componentDefaultValue,
+ componentName,
+ triggerSelector,
+ panelSelector,
+ renderComponent,
+}: RepositionTestConfig) => {
+ const repositionProps = {
+ repositionOnScroll: typeof propValue === 'boolean' ? propValue : undefined,
+ };
+
+ const providerProps =
+ typeof componentDefaultValue === 'boolean'
+ ? {
+ providerProps: {
+ componentDefaults: {
+ [componentName]: { repositionOnScroll: componentDefaultValue },
+ },
+ },
+ }
+ : undefined;
+
+ cy.mount(
+ <>
+
+
+ {renderComponent(repositionProps)}
+
+ >,
+ providerProps
+ );
+
+ cy.get(triggerSelector).click();
+
+ // Wait for positioning to finish
+ cy.get(panelSelector).as('panel').should('exist').waitForPositionToSettle();
+
+ cy.get('@panel').then(($panel) => {
+ const initialRect = $panel[0].getBoundingClientRect();
+ cy.scrollTo(0, 500);
+
+ // Wait for re-positioning to finish
+ cy.get('@panel')
+ .waitForPositionToSettle()
+ .then(($repositionedPanel) => {
+ const finalRect = $repositionedPanel[0].getBoundingClientRect();
+ if (shouldReposition) {
+ expect(finalRect.top).to.equal(initialRect.top);
+ } else {
+ expect(finalRect.top).to.not.equal(initialRect.top);
+ }
+ });
+ });
+};
diff --git a/packages/website/docs/getting-started/setup.mdx b/packages/website/docs/getting-started/setup.mdx
index 6100230524a..9a0e2ca590c 100644
--- a/packages/website/docs/getting-started/setup.mdx
+++ b/packages/website/docs/getting-started/setup.mdx
@@ -321,6 +321,9 @@ While all props can be individually customized via props, some components can ha
EuiTablePagination: { itemsPerPage: 20, },
EuiFocusTrap: { crossFrame: true },
EuiPortal: { insert },
+ EuiFlyout: { includeSelectorInFocusTrap: ['.custom-selector', '[data-custom-selector]'], includeFixedHeadersInFocusTrap: false },
+ EuiPopover: { repositionOnScroll: true },
+ EuiToolTip: { repositionOnScroll: true },
}}
>
diff --git a/packages/website/docs/utilities/provider.mdx b/packages/website/docs/utilities/provider.mdx
index a3a6a8b0cb5..3ed2d2f8946 100644
--- a/packages/website/docs/utilities/provider.mdx
+++ b/packages/website/docs/utilities/provider.mdx
@@ -106,7 +106,9 @@ All EUI components ship with a set of baseline defaults that can usually be conf
EuiTablePagination: { itemsPerPage: 20, },
EuiFocusTrap: { crossFrame: true },
EuiPortal: { insert },
- EuiFlyout: { includeSelectorInFocusTrap: ['.custom-selector', '[data-custom-selector]'], includeFixedHeadersInFocusTrap: false }
+ EuiFlyout: { includeSelectorInFocusTrap: ['.custom-selector', '[data-custom-selector]'], includeFixedHeadersInFocusTrap: false },
+ EuiPopover: { repositionOnScroll: true },
+ EuiToolTip: { repositionOnScroll: true },
}}
>