From aa663e149d57cb0d6aabdb850e0134671632b144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=96=8C?= Date: Tue, 16 Jul 2019 19:19:11 +0800 Subject: [PATCH] feat: Re-bind scroll events when components change, add test cases --- components/anchor/Anchor.tsx | 15 ++- components/anchor/__tests__/Anchor.test.js | 116 ++++++++++++++++++++- 2 files changed, 129 insertions(+), 2 deletions(-) diff --git a/components/anchor/Anchor.tsx b/components/anchor/Anchor.tsx index daa097d7c5..289ec857dd 100644 --- a/components/anchor/Anchor.tsx +++ b/components/anchor/Anchor.tsx @@ -145,6 +145,8 @@ export default class Anchor extends React.Component { }; private inkNode: HTMLSpanElement; + // scroll scope's container + private scrollContainer: HTMLElement | Window; private links: string[] = []; private scrollEvent: any; @@ -174,7 +176,8 @@ export default class Anchor extends React.Component { componentDidMount() { const { getContainer } = this.props as AnchorDefaultProps; - this.scrollEvent = addEventListener(getContainer(), 'scroll', this.handleScroll); + this.scrollContainer = getContainer(); + this.scrollEvent = addEventListener(this.scrollContainer, 'scroll', this.handleScroll); this.handleScroll(); } @@ -185,6 +188,16 @@ export default class Anchor extends React.Component { } componentDidUpdate() { + if (this.scrollEvent) { + const { getContainer } = this.props as AnchorDefaultProps; + const currentContainer = getContainer(); + if (this.scrollContainer !== currentContainer) { + this.scrollContainer = currentContainer; + this.scrollEvent.remove(); + this.scrollEvent = addEventListener(this.scrollContainer, 'scroll', this.handleScroll); + this.handleScroll(); + } + } this.updateInk(); } diff --git a/components/anchor/__tests__/Anchor.test.js b/components/anchor/__tests__/Anchor.test.js index 911e99ca4d..6f10fd803e 100644 --- a/components/anchor/__tests__/Anchor.test.js +++ b/components/anchor/__tests__/Anchor.test.js @@ -4,6 +4,8 @@ import Anchor from '..'; const { Link } = Anchor; +const delay = timeout => new Promise(resolve => setTimeout(resolve, timeout)); + describe('Anchor Render', () => { it('Anchor render perfectly', () => { const wrapper = mount( @@ -62,7 +64,7 @@ describe('Anchor Render', () => { wrapper.instance().handleScrollTo('##API'); expect(wrapper.instance().state.activeLink).toBe('##API'); expect(scrollToSpy).not.toHaveBeenCalled(); - await new Promise(resolve => setTimeout(resolve, 1000)); + await delay(1000); expect(scrollToSpy).toHaveBeenCalled(); }); @@ -130,4 +132,116 @@ describe('Anchor Render', () => { expect(event).not.toBe(undefined); expect(link).toEqual({ href, title }); }); + + it('Different function returns the same DOM', async () => { + let root = document.getElementById('root'); + if (!root) { + root = document.createElement('div', { id: 'root' }); + root.id = 'root'; + document.body.appendChild(root); + } + mount(
Hello
, { attachTo: root }); + const getContainerA = () => { + return document.getElementById('API'); + }; + const getContainerB = () => { + return document.getElementById('API'); + }; + + const wrapper = mount( + + + , + ); + const removeListenerSpy = jest.spyOn(wrapper.instance().scrollEvent, 'remove'); + await delay(1000); + wrapper.setProps({ getContainer: getContainerB }); + expect(removeListenerSpy).not.toHaveBeenCalled(); + }); + + it('Different function returns different DOM', async () => { + let root = document.getElementById('root'); + if (!root) { + root = document.createElement('div', { id: 'root' }); + root.id = 'root'; + document.body.appendChild(root); + } + mount( +
+
Hello
+
World
+
, + { attachTo: root }, + ); + const getContainerA = () => { + return document.getElementById('API1'); + }; + const getContainerB = () => { + return document.getElementById('API2'); + }; + const wrapper = mount( + + + + , + ); + const removeListenerSpy = jest.spyOn(wrapper.instance().scrollEvent, 'remove'); + expect(removeListenerSpy).not.toHaveBeenCalled(); + await delay(1000); + wrapper.setProps({ getContainer: getContainerB }); + expect(removeListenerSpy).toHaveBeenCalled(); + }); + + it('Same function returns the same DOM', () => { + let root = document.getElementById('root'); + if (!root) { + root = document.createElement('div', { id: 'root' }); + root.id = 'root'; + document.body.appendChild(root); + } + mount(
Hello
, { attachTo: root }); + const getContainer = () => document.getElementById('API'); + const wrapper = mount( + + + , + ); + wrapper.find('a[href="#API"]').simulate('click'); + wrapper.instance().handleScroll(); + expect(wrapper.instance().state).not.toBe(null); + }); + + it('Same function returns different DOM', async () => { + let root = document.getElementById('root'); + if (!root) { + root = document.createElement('div', { id: 'root' }); + root.id = 'root'; + document.body.appendChild(root); + } + mount( +
+
Hello
+
World
+
, + { attachTo: root }, + ); + const holdContainer = { + container: document.getElementById('API1'), + }; + const getContainer = () => { + return holdContainer.container; + }; + const wrapper = mount( + + + + , + ); + const removeListenerSpy = jest.spyOn(wrapper.instance().scrollEvent, 'remove'); + expect(removeListenerSpy).not.toHaveBeenCalled(); + await delay(1000); + holdContainer.container = document.getElementById('API2'); + wrapper.setProps({ 'data-only-trigger-re-render': true }); + expect(removeListenerSpy).toHaveBeenCalled(); + }); });