diff --git a/packages/eui/changelogs/upcoming/7759.md b/packages/eui/changelogs/upcoming/7759.md
new file mode 100644
index 00000000000..b87098a3008
--- /dev/null
+++ b/packages/eui/changelogs/upcoming/7759.md
@@ -0,0 +1 @@
+- `EuiFlyoutResizable` now respects `size` prop updates, allowing for controlled `size` usage
diff --git a/packages/eui/src/components/flyout/flyout_resizable.spec.tsx b/packages/eui/src/components/flyout/flyout_resizable.spec.tsx
index fb28eb6fef4..3b01819bd66 100644
--- a/packages/eui/src/components/flyout/flyout_resizable.spec.tsx
+++ b/packages/eui/src/components/flyout/flyout_resizable.spec.tsx
@@ -10,7 +10,7 @@
///
///
-import React from 'react';
+import React, { useState, useCallback } from 'react';
import { EuiFlyoutResizable } from './flyout_resizable';
@@ -196,6 +196,65 @@ describe('EuiFlyoutResizable', () => {
expect(onResize.lastCall.args).to.eql([600]);
});
});
+
+ it('responds to `size` prop updates after user resize', () => {
+ let onResizeCalls = 0;
+ const TestComponent = () => {
+ const [size, setSize] = useState(400);
+ const onResize = useCallback((width: number) => {
+ setSize(width);
+ onResizeCalls++;
+ }, []);
+
+ return (
+
+
+
+
+
+ );
+ };
+ cy.mount();
+ cy.get('.euiFlyout').should('have.css', 'inline-size', '400px');
+
+ // User resizing
+ cy.get('[data-test-subj="euiResizableButton"]').focus();
+ cy.realPress('ArrowLeft').then(() => {
+ onResizeCalls = 0; // Reset resize calls. Cypress is flaky here so we shouldn't directly assert on the number of calls
+ });
+ cy.get('.euiFlyout').should('have.css', 'inline-size', '410px');
+
+ // Consumer resizing
+ cy.wait(100); // Wait a tick for flyout to finish rerendering
+ cy.get('[data-test-subj="resetSize"]').realClick();
+ cy.get('.euiFlyout')
+ .should('have.css', 'inline-size', '400px')
+ .then(() => {
+ expect(onResizeCalls).to.eql(0);
+ });
+
+ cy.wait(100); // Wait a tick for flyout to finish rerendering
+ cy.get('[data-test-subj="setSize"]').realClick();
+ cy.get('.euiFlyout')
+ .should('have.css', 'inline-size', '200px')
+ .then(() => {
+ expect(onResizeCalls).to.eql(0);
+ });
+
+ cy.wait(100); // Wait a tick for flyout to finish rerendering
+ cy.get('[data-test-subj="setSizeKey"]').realClick();
+ cy.get('.euiFlyout')
+ .should('have.css', 'inline-size', '384px')
+ .then(() => {
+ expect(onResizeCalls).to.eql(0);
+ });
+ });
});
describe('push flyouts', () => {
diff --git a/packages/eui/src/components/flyout/flyout_resizable.tsx b/packages/eui/src/components/flyout/flyout_resizable.tsx
index bdbc63fd854..a91d4b20c54 100644
--- a/packages/eui/src/components/flyout/flyout_resizable.tsx
+++ b/packages/eui/src/components/flyout/flyout_resizable.tsx
@@ -65,12 +65,20 @@ export const EuiFlyoutResizable = forwardRef(
// Must use state for the flyout ref in order for the useEffect to be correctly called after render
const [flyoutRef, setFlyoutRef] = useState(null);
const setRefs = useCombinedRefs([setFlyoutRef, ref]);
+
+ useEffect(() => {
+ if (!flyoutWidth && flyoutRef) {
+ setCallOnResize(false); // Don't call `onResize` for non-user width changes
+ setFlyoutWidth(getFlyoutMinMaxWidth(flyoutRef.offsetWidth));
+ }
+ }, [flyoutWidth, flyoutRef, getFlyoutMinMaxWidth]);
+
+ // Update flyout width when consumers pass in a new `size`
useEffect(() => {
- setCallOnResize(false); // Don't call `onResize` for non-user width changes
- setFlyoutWidth(
- flyoutRef ? getFlyoutMinMaxWidth(flyoutRef.offsetWidth) : 0
- );
- }, [flyoutRef, getFlyoutMinMaxWidth, size]);
+ setCallOnResize(false);
+ // For string `size`s, resetting flyoutWidth to 0 will trigger the above useEffect's recalculation
+ setFlyoutWidth(typeof size === 'number' ? getFlyoutMinMaxWidth(size) : 0);
+ }, [size, getFlyoutMinMaxWidth]);
// Initial numbers to calculate from, on resize drag start
const initialWidth = useRef(0);