Skip to content
This repository was archived by the owner on Nov 4, 2025. It is now read-only.

Commit bb543b9

Browse files
authored
fix: should realign when align object changes (#133)
* add demo for random align * fix: should realign when align object changes * fix lint * tsc --noEmit * fix tsc errors * add test case
1 parent 807c540 commit bb543b9

File tree

6 files changed

+72
-7
lines changed

6 files changed

+72
-7
lines changed

examples/simple.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import React, { Component } from 'react';
22
import Align from '../src';
33

4+
const allPoints = ['tl', 'tc', 'tr', 'cl', 'cc', 'cr', 'bl', 'bc', 'br'];
5+
46
class Test extends Component {
57
state = {
68
monitor: true,
@@ -62,6 +64,17 @@ class Test extends Component {
6264
}));
6365
};
6466

67+
randomAlign = () => {
68+
const randomPoints = [];
69+
randomPoints.push(allPoints[Math.floor(Math.random() * 100) % allPoints.length]);
70+
randomPoints.push(allPoints[Math.floor(Math.random() * 100) % allPoints.length]);
71+
this.setState({
72+
align: {
73+
points: randomPoints,
74+
},
75+
});
76+
};
77+
6578
forceAlign = () => {
6679
this.$align.forceAlign();
6780
};
@@ -91,6 +104,10 @@ class Test extends Component {
91104
Resize Source
92105
</button>
93106
&nbsp;&nbsp;&nbsp;
107+
<button type="button" onClick={this.randomAlign}>
108+
Random Align
109+
</button>
110+
&nbsp;&nbsp;&nbsp;
94111
<label>
95112
<input type="checkbox" checked={this.state.monitor} onChange={this.toggleMonitor} />
96113
&nbsp; Monitor window resize

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"start": "cross-env NODE_ENV=development father doc dev --storybook",
2929
"build": "father doc build --storybook",
3030
"compile": "father build",
31+
"tsc": "tsc",
3132
"prepublishOnly": "npm run compile && np --yolo --no-publish",
3233
"lint": "eslint src/ examples/ --ext .tsx,.ts,.jsx,.js",
3334
"test": "father test",
@@ -42,6 +43,7 @@
4243
"@babel/runtime": "^7.10.1",
4344
"classnames": "2.x",
4445
"dom-align": "^1.7.0",
46+
"lodash": "^4.17.21",
4547
"rc-util": "^5.3.0",
4648
"resize-observer-polyfill": "^1.5.1"
4749
},

src/Align.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import { composeRef } from 'rc-util/lib/ref';
88
import isVisible from 'rc-util/lib/Dom/isVisible';
99
import { alignElement, alignPoint } from 'dom-align';
1010
import addEventListener from 'rc-util/lib/Dom/addEventListener';
11+
import isEqual from 'lodash/isEqual';
1112

1213
import { isSamePoint, restoreFocus, monitorResize } from './util';
13-
import { AlignType, AlignResult, TargetType, TargetPoint } from './interface';
14+
import type { AlignType, AlignResult, TargetType, TargetPoint } from './interface';
1415
import useBuffer from './hooks/useBuffer';
1516

1617
type OnAlign = (source: HTMLElement, result: AlignResult) => void;
@@ -48,7 +49,9 @@ const Align: React.ForwardRefRenderFunction<RefAlign, AlignProps> = (
4849
{ children, disabled, target, align, onAlign, monitorWindowResize, monitorBufferTime = 0 },
4950
ref,
5051
) => {
51-
const cacheRef = React.useRef<{ element?: HTMLElement; point?: TargetPoint }>({});
52+
const cacheRef = React.useRef<{ element?: HTMLElement; point?: TargetPoint; align?: AlignType }>(
53+
{},
54+
);
5255
const nodeRef = React.useRef();
5356
let childNode = React.Children.only(children);
5457

@@ -57,16 +60,19 @@ const Align: React.ForwardRefRenderFunction<RefAlign, AlignProps> = (
5760
const forceAlignPropsRef = React.useRef<{
5861
disabled?: boolean;
5962
target?: TargetType;
63+
align?: AlignType;
6064
onAlign?: OnAlign;
6165
}>({});
6266
forceAlignPropsRef.current.disabled = disabled;
6367
forceAlignPropsRef.current.target = target;
68+
forceAlignPropsRef.current.align = align;
6469
forceAlignPropsRef.current.onAlign = onAlign;
6570

6671
const [forceAlign, cancelForceAlign] = useBuffer(() => {
6772
const {
6873
disabled: latestDisabled,
6974
target: latestTarget,
75+
align: latestAlign,
7076
onAlign: latestOnAlign,
7177
} = forceAlignPropsRef.current;
7278
if (!latestDisabled && latestTarget) {
@@ -78,6 +84,7 @@ const Align: React.ForwardRefRenderFunction<RefAlign, AlignProps> = (
7884

7985
cacheRef.current.element = element;
8086
cacheRef.current.point = point;
87+
cacheRef.current.align = latestAlign;
8188

8289
// IE lose focus after element realign
8390
// We should record activeElement and restore later
@@ -121,7 +128,11 @@ const Align: React.ForwardRefRenderFunction<RefAlign, AlignProps> = (
121128
sourceResizeMonitor.current.cancel = monitorResize(nodeRef.current, forceAlign);
122129
}
123130

124-
if (cacheRef.current.element !== element || !isSamePoint(cacheRef.current.point, point)) {
131+
if (
132+
cacheRef.current.element !== element ||
133+
!isSamePoint(cacheRef.current.point, point) ||
134+
!isEqual(cacheRef.current.align, align)
135+
) {
125136
forceAlign();
126137

127138
// Add resize observer
@@ -181,7 +192,7 @@ const Align: React.ForwardRefRenderFunction<RefAlign, AlignProps> = (
181192
return childNode;
182193
};
183194

184-
const RefAlign = React.forwardRef(Align);
185-
RefAlign.displayName = 'Align';
195+
const RcAlign = React.forwardRef(Align);
196+
RcAlign.displayName = 'Align';
186197

187-
export default RefAlign;
198+
export default RcAlign;

src/util.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import ResizeObserver from 'resize-observer-polyfill';
22
import contains from 'rc-util/lib/Dom/contains';
3-
import { TargetPoint } from './interface';
3+
import type { TargetPoint } from './interface';
44

55
export function isSamePoint(prev: TargetPoint, next: TargetPoint) {
66
if (prev === next) return true;

tests/element.test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,24 @@ describe('element align', () => {
8888
expect(onAlign).toHaveBeenCalled();
8989
});
9090

91+
// https://github.com/ant-design/ant-design/issues/31717
92+
it('changing align should trigger onAlign', () => {
93+
const onAlign = jest.fn();
94+
const wrapper = mount(<Test align={{ points: ['cc', 'cc'] }} onAlign={onAlign} />);
95+
expect(onAlign).toHaveBeenCalledTimes(1);
96+
expect(onAlign).toHaveBeenLastCalledWith(
97+
expect.any(HTMLElement),
98+
expect.objectContaining({ points: ['cc', 'cc'] }),
99+
);
100+
wrapper.setProps({ align: { points: ['cc', 'tl'] } });
101+
jest.runAllTimers();
102+
expect(onAlign).toHaveBeenCalledTimes(2);
103+
expect(onAlign).toHaveBeenLastCalledWith(
104+
expect.any(HTMLElement),
105+
expect.objectContaining({ points: ['cc', 'tl'] }),
106+
);
107+
});
108+
91109
it('should switch to the correct align callback after starting the timers', () => {
92110
// This test case is tricky. An error occurs if the following things happen
93111
// exactly in this order:

tsconfig.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"compilerOptions": {
3+
"target": "esnext",
4+
"moduleResolution": "node",
5+
"baseUrl": "./",
6+
"jsx": "preserve",
7+
"declaration": true,
8+
"skipLibCheck": true,
9+
"esModuleInterop": true,
10+
"noEmit": true,
11+
"paths": {
12+
"@/*": ["src/*"],
13+
"@@/*": ["src/.umi/*"],
14+
"rc-dialog": ["src/index.ts"]
15+
}
16+
}
17+
}

0 commit comments

Comments
 (0)