From 9bd4b5ffa809e0b540f36ef8b59cdbf5d785256f Mon Sep 17 00:00:00 2001 From: zombiej Date: Mon, 6 Jan 2020 17:28:02 +0800 Subject: [PATCH 1/2] feat: Add keepAlign support --- examples/position.js | 79 ++++++++++++++++++++++++++++++++++++++++++++ src/Align.tsx | 41 ++++++++++++++++++++--- 2 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 examples/position.js diff --git a/examples/position.js b/examples/position.js new file mode 100644 index 0000000..fb170a3 --- /dev/null +++ b/examples/position.js @@ -0,0 +1,79 @@ +import React, { Component } from 'react'; +import Align from '../src'; + +const align = { + points: ['cc', 'cc'], +}; + +class Demo extends Component { + state = { + left: 0, + top: 0, + }; + + eleRef = React.createRef(); + + render() { + const { left, top } = this.state; + + return ( +
+
+
+
+ + this.eleRef.current} + align={align} + keepingAlign + > +
+ Align +
+
+ + +
+ ); + } +} + +export default Demo; diff --git a/src/Align.tsx b/src/Align.tsx index ce6b1ac..521feff 100644 --- a/src/Align.tsx +++ b/src/Align.tsx @@ -22,6 +22,8 @@ export interface AlignProps { monitorWindowResize?: boolean; disabled?: boolean; children: React.ReactElement; + /** Always trigger align with each render */ + keepAlign?: boolean; } interface MonitorRef { @@ -44,10 +46,21 @@ function getPoint(point: TargetType) { } const Align: React.RefForwardingComponent = ( - { children, disabled, target, align, onAlign, monitorWindowResize, monitorBufferTime = 0 }, + { + children, + disabled, + target, + align, + onAlign, + monitorWindowResize, + monitorBufferTime = 0, + keepAlign, + }, ref, ) => { - const cacheRef = React.useRef<{ element?: HTMLElement; point?: TargetPoint }>({}); + const cacheRef = React.useRef<{ element?: HTMLElement; point?: TargetPoint }>( + {}, + ); const nodeRef = React.useRef(); let childNode = React.Children.only(children); @@ -63,7 +76,10 @@ const Align: React.RefForwardingComponent = ( forceAlignPropsRef.current.onAlign = onAlign; const [forceAlign, cancelForceAlign] = useBuffer(() => { - const { disabled: latestDisabled, target: latestTarget } = forceAlignPropsRef.current; + const { + disabled: latestDisabled, + target: latestTarget, + } = forceAlignPropsRef.current; if (!latestDisabled && latestTarget) { const source = nodeRef.current; @@ -112,10 +128,16 @@ const Align: React.RefForwardingComponent = ( if (nodeRef.current !== sourceResizeMonitor.current.element) { sourceResizeMonitor.current.cancel(); sourceResizeMonitor.current.element = nodeRef.current; - sourceResizeMonitor.current.cancel = monitorResize(nodeRef.current, forceAlign); + sourceResizeMonitor.current.cancel = monitorResize( + nodeRef.current, + forceAlign, + ); } - if (cacheRef.current.element !== element || !isSamePoint(cacheRef.current.point, point)) { + if ( + cacheRef.current.element !== element || + !isSamePoint(cacheRef.current.point, point) + ) { forceAlign(); // Add resize observer @@ -136,6 +158,15 @@ const Align: React.RefForwardingComponent = ( } }, [disabled]); + /** + * [Legacy] Should keep re-algin since we don't know if target position changed. + */ + React.useEffect(() => { + if (keepAlign && !disabled) { + forceAlign(true); + } + }); + // Listen for window resize const winResizeRef = React.useRef<{ remove: Function }>(null); React.useEffect(() => { From df43684af2fd858127c5c2557e7ebc20edb63ff5 Mon Sep 17 00:00:00 2001 From: zombiej Date: Mon, 6 Jan 2020 17:50:25 +0800 Subject: [PATCH 2/2] add test case --- src/Align.tsx | 12 ++++++++++++ tests/element.test.js | 28 ++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/Align.tsx b/src/Align.tsx index 521feff..7433ffc 100644 --- a/src/Align.tsx +++ b/src/Align.tsx @@ -45,6 +45,10 @@ function getPoint(point: TargetType) { return point; } +interface InternalTestProps { + INTERNAL_TRIGGER_ALIGN?: Function; +} + const Align: React.RefForwardingComponent = ( { children, @@ -55,6 +59,7 @@ const Align: React.RefForwardingComponent = ( monitorWindowResize, monitorBufferTime = 0, keepAlign, + ...restProps }, ref, ) => { @@ -76,6 +81,13 @@ const Align: React.RefForwardingComponent = ( forceAlignPropsRef.current.onAlign = onAlign; const [forceAlign, cancelForceAlign] = useBuffer(() => { + if ( + process.env.NODE_ENV !== 'production' && + (restProps as InternalTestProps).INTERNAL_TRIGGER_ALIGN + ) { + (restProps as InternalTestProps).INTERNAL_TRIGGER_ALIGN(); + } + const { disabled: latestDisabled, target: latestTarget, diff --git a/tests/element.test.js b/tests/element.test.js index 42d2344..b747632 100644 --- a/tests/element.test.js +++ b/tests/element.test.js @@ -26,7 +26,10 @@ describe('element align', () => { render() { return (
-
+
target
@@ -73,12 +76,33 @@ describe('element align', () => { it('disabled should trigger align', () => { const onAlign = jest.fn(); - const wrapper = mount(); + const wrapper = mount( + , + ); expect(onAlign).not.toHaveBeenCalled(); wrapper.setProps({ disabled: false }); jest.runAllTimers(); expect(onAlign).toHaveBeenCalled(); }); + + it('keepAlign', () => { + const triggerAlign = jest.fn(); + + class TestAlign extends React.Component { + state = {}; + + render = () => ; + } + + const wrapper = mount(); + const times = triggerAlign.mock.calls.length; + + for (let i = 0; i < 10; i += 1) { + wrapper.instance().forceUpdate(); + } + + expect(triggerAlign.mock.calls.length > times).toBeTruthy(); + }); }); /* eslint-enable */