mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-27 20:49:53 +08:00
fix: ResizeObserver should ignore shaking (#18289)
* fix: ResizeObserver should ignore shaking * fix lint * fix domHook
This commit is contained in:
parent
2f365f8273
commit
fd284ecc47
@ -1,10 +1,12 @@
|
||||
const __NULL__ = { notExist: true };
|
||||
|
||||
export function spyElementPrototypes(Element, properties) {
|
||||
const propNames = Object.keys(properties);
|
||||
const originDescriptors = {};
|
||||
|
||||
propNames.forEach(propName => {
|
||||
const originDescriptor = Object.getOwnPropertyDescriptor(Element.prototype, propName);
|
||||
originDescriptors[propName] = originDescriptor;
|
||||
originDescriptors[propName] = originDescriptor || __NULL__;
|
||||
|
||||
const spyProp = properties[propName];
|
||||
|
||||
@ -37,7 +39,9 @@ export function spyElementPrototypes(Element, properties) {
|
||||
mockRestore() {
|
||||
propNames.forEach(propName => {
|
||||
const originDescriptor = originDescriptors[propName];
|
||||
if (typeof originDescriptor === 'function') {
|
||||
if (originDescriptor === __NULL__) {
|
||||
delete Element.prototype[propName];
|
||||
} else if (typeof originDescriptor === 'function') {
|
||||
Element.prototype[propName] = originDescriptor;
|
||||
} else {
|
||||
Object.defineProperty(Element.prototype, propName, originDescriptor);
|
||||
|
@ -9,6 +9,8 @@ import triggerEvent from '../triggerEvent';
|
||||
import Wave from '../wave';
|
||||
import TransButton from '../transButton';
|
||||
import openAnimation from '../openAnimation';
|
||||
import ResizeObserver from '../resizeObserver';
|
||||
import { spyElementPrototype } from '../../__tests__/util/domHook';
|
||||
|
||||
describe('Test utils function', () => {
|
||||
beforeAll(() => {
|
||||
@ -143,7 +145,9 @@ describe('Test utils function', () => {
|
||||
it('bindAnimationEvent should return when node is null', () => {
|
||||
const wrapper = mount(
|
||||
<Wave>
|
||||
<button type="button" disabled>button</button>
|
||||
<button type="button" disabled>
|
||||
button
|
||||
</button>
|
||||
</Wave>,
|
||||
).instance();
|
||||
expect(wrapper.bindAnimationEvent()).toBe(undefined);
|
||||
@ -152,7 +156,9 @@ describe('Test utils function', () => {
|
||||
it('bindAnimationEvent.onClick should return when children is hidden', () => {
|
||||
const wrapper = mount(
|
||||
<Wave>
|
||||
<button type="button" style={{ display: 'none' }}>button</button>
|
||||
<button type="button" style={{ display: 'none' }}>
|
||||
button
|
||||
</button>
|
||||
</Wave>,
|
||||
).instance();
|
||||
expect(wrapper.bindAnimationEvent()).toBe(undefined);
|
||||
@ -220,4 +226,47 @@ describe('Test utils function', () => {
|
||||
expect(done).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ResizeObserver', () => {
|
||||
let domMock;
|
||||
|
||||
beforeAll(() => {
|
||||
domMock = spyElementPrototype(HTMLDivElement, 'getBoundingClientRect', () => {
|
||||
return {
|
||||
width: 1128 + Math.random(),
|
||||
height: 903 + Math.random(),
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
domMock.mockRestore();
|
||||
});
|
||||
|
||||
it('should not trigger `onResize` if size shaking', () => {
|
||||
const onResize = jest.fn();
|
||||
let divNode;
|
||||
|
||||
const wrapper = mount(
|
||||
<ResizeObserver onResize={onResize}>
|
||||
<div
|
||||
ref={node => {
|
||||
divNode = node;
|
||||
}}
|
||||
/>
|
||||
</ResizeObserver>,
|
||||
);
|
||||
|
||||
// First trigger
|
||||
wrapper.instance().onResize([{ target: divNode }]);
|
||||
onResize.mockReset();
|
||||
|
||||
// Repeat trigger should not trigger outer `onResize` with shaking
|
||||
for (let i = 0; i < 10; i += 1) {
|
||||
wrapper.instance().onResize([{ target: divNode }]);
|
||||
}
|
||||
|
||||
expect(onResize).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -10,9 +10,19 @@ interface ResizeObserverProps {
|
||||
onResize?: () => void;
|
||||
}
|
||||
|
||||
class ReactResizeObserver extends React.Component<ResizeObserverProps, {}> {
|
||||
interface ResizeObserverState {
|
||||
height: number;
|
||||
width: number;
|
||||
}
|
||||
|
||||
class ReactResizeObserver extends React.Component<ResizeObserverProps, ResizeObserverState> {
|
||||
resizeObserver: ResizeObserver | null = null;
|
||||
|
||||
state = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.onComponentUpdated();
|
||||
}
|
||||
@ -38,10 +48,30 @@ class ReactResizeObserver extends React.Component<ResizeObserverProps, {}> {
|
||||
}
|
||||
}
|
||||
|
||||
onResize = () => {
|
||||
onResize: ResizeObserverCallback = (entries: ResizeObserverEntry[]) => {
|
||||
const { onResize } = this.props;
|
||||
if (onResize) {
|
||||
onResize();
|
||||
|
||||
const { target } = entries[0];
|
||||
|
||||
const { width, height } = target.getBoundingClientRect();
|
||||
|
||||
/**
|
||||
* Resize observer trigger when content size changed.
|
||||
* In most case we just care about element size,
|
||||
* let's use `boundary` instead of `contentRect` here to avoid shaking.
|
||||
*/
|
||||
const fixedWidth = Math.floor(width);
|
||||
const fixedHeight = Math.floor(height);
|
||||
|
||||
if (this.state.width !== fixedWidth || this.state.height !== fixedHeight) {
|
||||
this.setState({
|
||||
width: fixedWidth,
|
||||
height: fixedHeight,
|
||||
});
|
||||
|
||||
if (onResize) {
|
||||
onResize();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -3,6 +3,7 @@ import { mount } from 'enzyme';
|
||||
import Affix from '..';
|
||||
import { getObserverEntities } from '../utils';
|
||||
import Button from '../../button';
|
||||
import { spyElementPrototype } from '../../__tests__/util/domHook';
|
||||
|
||||
const events = {};
|
||||
|
||||
@ -40,6 +41,7 @@ class AffixMounter extends React.Component {
|
||||
|
||||
describe('Affix Render', () => {
|
||||
let wrapper;
|
||||
let domMock;
|
||||
|
||||
const classRect = {
|
||||
container: {
|
||||
@ -48,23 +50,21 @@ describe('Affix Render', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const originGetBoundingClientRect = HTMLElement.prototype.getBoundingClientRect;
|
||||
HTMLElement.prototype.getBoundingClientRect = function getBoundingClientRect() {
|
||||
return (
|
||||
classRect[this.className] || {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
domMock = spyElementPrototype(HTMLElement, 'getBoundingClientRect', function mockBounding() {
|
||||
return (
|
||||
classRect[this.className] || {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
HTMLElement.prototype.getBoundingClientRect = originGetBoundingClientRect;
|
||||
domMock.mockRestore();
|
||||
});
|
||||
const movePlaceholder = top => {
|
||||
classRect.fixed = {
|
||||
@ -185,7 +185,7 @@ describe('Affix Render', () => {
|
||||
.find('ReactResizeObserver')
|
||||
.at(index)
|
||||
.instance()
|
||||
.onResize();
|
||||
.onResize([{ target: { getBoundingClientRect: () => ({ width: 99, height: 99 }) } }]);
|
||||
jest.runAllTimers();
|
||||
|
||||
expect(updateCalled).toHaveBeenCalled();
|
||||
|
Loading…
Reference in New Issue
Block a user