From a9d08739a251b35b7429739bb9aac3d767d0a187 Mon Sep 17 00:00:00 2001
From: Cee Chen <549407+cee-chen@users.noreply.github.com>
Date: Tue, 23 Jan 2024 12:11:13 -0800
Subject: [PATCH] [EuiFlyoutResizable] Add optional `onResize` callback (#7464)
---
changelogs/upcoming/7464.md | 1 +
.../flyout/flyout_resizable.spec.tsx | 30 +++++++++++++++++++
src/components/flyout/flyout_resizable.tsx | 19 ++++++++++++
3 files changed, 50 insertions(+)
create mode 100644 changelogs/upcoming/7464.md
diff --git a/changelogs/upcoming/7464.md b/changelogs/upcoming/7464.md
new file mode 100644
index 00000000000..24cd812e1d0
--- /dev/null
+++ b/changelogs/upcoming/7464.md
@@ -0,0 +1 @@
+- Updated `EuiFlyoutResizable` with new optional `onResize` callback
diff --git a/src/components/flyout/flyout_resizable.spec.tsx b/src/components/flyout/flyout_resizable.spec.tsx
index d789c8cdafc..fb28eb6fef4 100644
--- a/src/components/flyout/flyout_resizable.spec.tsx
+++ b/src/components/flyout/flyout_resizable.spec.tsx
@@ -166,6 +166,36 @@ describe('EuiFlyoutResizable', () => {
cy.get('.euiFlyout').should('have.css', 'inline-size', '400px');
};
});
+
+ it('calls the optional onResize callback on mouseup and keyboard events only', () => {
+ const onResize = cy.stub();
+ cy.mount(
+
+ );
+
+ cy.get('[data-test-subj="euiResizableButton"]')
+ .trigger('mousedown', { clientX: 400 })
+ .trigger('mousemove', { clientX: 600 })
+ .then(() => {
+ expect(onResize).not.have.been.called;
+ });
+ cy.get('[data-test-subj="euiResizableButton"]')
+ .trigger('mouseup')
+ .then(() => {
+ expect(onResize.callCount).to.eql(1);
+ expect(onResize).to.have.been.calledWith(600);
+ });
+
+ cy.get('[data-test-subj="euiResizableButton"]').focus();
+ cy.realPress('ArrowRight').then(() => {
+ expect(onResize.callCount).to.eql(2);
+ expect(onResize).to.have.been.calledWith(590);
+ });
+ cy.realPress('ArrowLeft').then(() => {
+ expect(onResize.callCount).to.eql(3);
+ expect(onResize.lastCall.args).to.eql([600]);
+ });
+ });
});
describe('push flyouts', () => {
diff --git a/src/components/flyout/flyout_resizable.tsx b/src/components/flyout/flyout_resizable.tsx
index 7005aafd69c..4ce990d42cf 100644
--- a/src/components/flyout/flyout_resizable.tsx
+++ b/src/components/flyout/flyout_resizable.tsx
@@ -25,6 +25,10 @@ import { euiFlyoutResizableButtonStyles } from './flyout_resizable.styles';
export type EuiFlyoutResizableProps = Omit & {
maxWidth?: number;
minWidth?: number;
+ /**
+ * Optional callback that fires on user resize with the new flyout width
+ */
+ onResize?: (width: number) => void;
};
export const EuiFlyoutResizable = forwardRef(
@@ -33,6 +37,7 @@ export const EuiFlyoutResizable = forwardRef(
size,
maxWidth,
minWidth = 200,
+ onResize,
side = 'right',
type = 'overlay',
children,
@@ -56,11 +61,13 @@ export const EuiFlyoutResizable = forwardRef(
);
const [flyoutWidth, setFlyoutWidth] = useState(0);
+ const [callOnResize, setCallOnResize] = useState(false);
// 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(() => {
+ setCallOnResize(false); // Don't call `onResize` for non-user width changes
setFlyoutWidth(
flyoutRef ? getFlyoutMinMaxWidth(flyoutRef.offsetWidth) : 0
);
@@ -92,6 +99,7 @@ export const EuiFlyoutResizable = forwardRef(
);
const onMouseUp = useCallback(() => {
+ setCallOnResize(true);
initialMouseX.current = 0;
window.removeEventListener('mousemove', onMouseMove);
@@ -102,6 +110,7 @@ export const EuiFlyoutResizable = forwardRef(
const onMouseDown = useCallback(
(e: React.MouseEvent | React.TouchEvent) => {
+ setCallOnResize(false);
initialMouseX.current = getPosition(e, true);
initialWidth.current = flyoutRef?.offsetWidth ?? 0;
@@ -117,6 +126,7 @@ export const EuiFlyoutResizable = forwardRef(
const onKeyDown = useCallback(
(e: React.KeyboardEvent) => {
+ setCallOnResize(true);
const KEYBOARD_OFFSET = 10;
switch (e.key) {
@@ -136,6 +146,15 @@ export const EuiFlyoutResizable = forwardRef(
[getFlyoutMinMaxWidth, direction]
);
+ // To reduce unnecessary calls, only fire onResize callback:
+ // 1. After initial mount / on user width change events only
+ // 2. If not currently mouse dragging
+ useEffect(() => {
+ if (callOnResize) {
+ onResize?.(flyoutWidth);
+ }
+ }, [onResize, callOnResize, flyoutWidth]);
+
return (