Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: ⬆️ BackTop not working in iframe #22788

Merged
merged 8 commits into from
Apr 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions components/_util/__tests__/getScroll.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* @jest-environment node
*/
import getScroll from '../getScroll';

describe('getScroll', () => {
it('getScroll return 0 in node envioronment', async () => {
expect(getScroll(null, true)).toBe(0);
expect(getScroll(null, false)).toBe(0);
});
});
32 changes: 21 additions & 11 deletions components/_util/getScroll.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
export default function getScroll(target: HTMLElement | Window | null, top: boolean): number {
export function isWindow(obj: any) {
return obj !== null && obj !== undefined && obj === obj.window;
}

export default function getScroll(
target: HTMLElement | Window | Document | null,
top: boolean,
): number {
if (typeof window === 'undefined') {
return 0;
}

const prop = top ? 'pageYOffset' : 'pageXOffset';
const method = top ? 'scrollTop' : 'scrollLeft';
const isWindow = target === window;

let ret = isWindow ? (target as Window)[prop] : (target as HTMLElement)[method];
// ie6,7,8 standard mode
if (isWindow && typeof ret !== 'number') {
ret = (document.documentElement as HTMLElement)[method];
let result = 0;
if (isWindow(target)) {
result = (target as Window)[top ? 'pageYOffset' : 'pageXOffset'];
} else if (target instanceof Document) {
result = target.documentElement[method];
} else if (target) {
result = (target as HTMLElement)[method];
}

return ret;
if (target && !isWindow(target) && typeof result !== 'number') {
result = ((target as HTMLElement).ownerDocument || (target as Document)).documentElement[
method
];
}
return result;
}
10 changes: 6 additions & 4 deletions components/_util/scrollTo.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import raf from 'raf';
import getScroll from './getScroll';
import getScroll, { isWindow } from './getScroll';
import { easeInOutCubic } from './easings';

interface ScrollToOptions {
/** Scroll container, default as window */
getContainer?: () => HTMLElement | Window;
getContainer?: () => HTMLElement | Window | Document;
/** Scroll end callback */
callback?: () => any;
/** Animation duration, default as 450 */
Expand All @@ -22,8 +22,10 @@ export default function scrollTo(y: number, options: ScrollToOptions = {}) {
const timestamp = Date.now();
const time = timestamp - startTime;
const nextScrollTop = easeInOutCubic(time > duration ? duration : time, scrollTop, y, duration);
if (container === window) {
window.scrollTo(window.pageXOffset, nextScrollTop);
if (isWindow(container)) {
(container as Window).scrollTo(window.pageXOffset, nextScrollTop);
} else if (container instanceof Document) {
container.documentElement.scrollTop = nextScrollTop;
} else {
(container as HTMLElement).scrollTop = nextScrollTop;
}
Expand Down
48 changes: 35 additions & 13 deletions components/back-top/__tests__/__snapshots__/demo.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,25 +1,47 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders ./components/back-top/demo/basic.md correctly 1`] = `
<div>
Scroll down to see the bottom-right
Array [
<div
class="ant-back-top"
/>,
"Scroll down to see the bottom-right",
<strong
class="site-back-top-basic"
>
gray
</strong>
button.
</div>
</strong>,
"button.",
]
`;

exports[`renders ./components/back-top/demo/custom.md correctly 1`] = `
<div>
Scroll down to see the bottom-right
<strong
style="color:#1088e9"
>
blue
</strong>
button.
<div
style="height:600vh;padding:8px"
>
<div>
Scroll to bottom
</div>
<div>
Scroll to bottom
</div>
<div>
Scroll to bottom
</div>
<div>
Scroll to bottom
</div>
<div>
Scroll to bottom
</div>
<div>
Scroll to bottom
</div>
<div>
Scroll to bottom
</div>
<div
class="ant-back-top"
/>
</div>
`;
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`BackTop rtl render component should be rendered correctly in RTL direction 1`] = `null`;
exports[`BackTop rtl render component should be rendered correctly in RTL direction 1`] = `
<div
class="ant-back-top ant-back-top-rtl"
/>
`;
29 changes: 28 additions & 1 deletion components/back-top/__tests__/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,16 @@ describe('BackTop', () => {
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
window.scrollY = y;
window.pageYOffset = y;
document.documentElement.scrollTop = y;
});
window.scrollTo(0, 400);
expect(document.documentElement.scrollTop).toBe(400);
// trigger scroll manually
wrapper.instance().handleScroll();
await sleep();
wrapper.find('.ant-back-top').simulate('click');
await sleep(500);
expect(window.pageYOffset).toBe(0);
expect(document.documentElement.scrollTop).toBe(0);
scrollToSpy.mockRestore();
});

Expand All @@ -32,6 +34,7 @@ describe('BackTop', () => {
window.scrollY = y;
window.pageYOffset = y;
});
document.dispatchEvent(new Event('scroll'));
window.scrollTo(0, 400);
// trigger scroll manually
wrapper.instance().handleScroll();
Expand All @@ -40,4 +43,28 @@ describe('BackTop', () => {
expect(onClick).toHaveBeenCalled();
scrollToSpy.mockRestore();
});

it('should be able to update target', async () => {
const wrapper = mount(<BackTop />);
wrapper.instance().handleScroll = jest.fn();
const container = document.createElement('div');
wrapper.setProps({ target: () => container });
expect(wrapper.instance().handleScroll).toHaveBeenLastCalledWith({
target: container,
});
container.dispatchEvent(new Event('scroll'));
expect(wrapper.instance().handleScroll).toHaveBeenLastCalledWith(
expect.objectContaining({
currentTarget: container,
target: container,
}),
);
});

it('invalid target', async () => {
const onClick = jest.fn();
const wrapper = mount(<BackTop onClick={onClick} target={() => ({ documentElement: {} })} />);
wrapper.find('.ant-back-top').simulate('click');
expect(onClick).toHaveBeenCalled();
});
});
4 changes: 2 additions & 2 deletions components/back-top/demo/basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ The most basic usage.
import { BackTop } from 'antd';

ReactDOM.render(
<div>
<>
<BackTop />
Scroll down to see the bottom-right
<strong className="site-back-top-basic"> gray </strong>
button.
</div>,
</>,
mountNode,
);
```
Expand Down
42 changes: 21 additions & 21 deletions components/back-top/demo/custom.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
order: 1
iframe: 400
title:
zh-CN: 自定义样式
en-US: Custom style
Expand All @@ -16,31 +17,30 @@ You can customize the style of the button, just note the size limit: no more tha
```jsx
import { BackTop } from 'antd';

const style = {
height: 40,
width: 40,
lineHeight: '40px',
borderRadius: 4,
backgroundColor: '#1088e9',
color: '#fff',
textAlign: 'center',
fontSize: 14,
};

ReactDOM.render(
<div>
<div style={{ height: '600vh', padding: 8 }}>
<div>Scroll to bottom</div>
<div>Scroll to bottom</div>
<div>Scroll to bottom</div>
<div>Scroll to bottom</div>
<div>Scroll to bottom</div>
<div>Scroll to bottom</div>
<div>Scroll to bottom</div>
<BackTop>
<div className="ant-back-top-inner">UP</div>
<div style={style}>UP</div>
</BackTop>
Scroll down to see the bottom-right
<strong style={{ color: '#1088e9' }}> blue </strong>
button.
</div>,
mountNode,
);
```

```css
#components-back-top-demo-custom .ant-back-top {
bottom: 100px;
}
#components-back-top-demo-custom .ant-back-top-inner {
height: 40px;
width: 40px;
line-height: 40px;
border-radius: 4px;
background-color: #1088e9;
color: #fff;
text-align: center;
font-size: 20px;
}
```
Loading