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 (