UNPKG

materialuiupgraded

Version:

Material-UI's workspace package

511 lines (470 loc) 18.4 kB
import React from 'react'; import { assert } from 'chai'; import { spy, stub } from 'sinon'; import ReactDOM from 'react-dom'; import { createMount, unwrap } from '../test-utils'; import Paper from '../Paper'; import Drawer from '../Drawer'; import SwipeableDrawer, { reset } from './SwipeableDrawer'; import SwipeArea from './SwipeArea'; import createMuiTheme from '../styles/createMuiTheme'; function fireBodyMouseEvent(name, properties) { const event = document.createEvent('MouseEvents'); event.initEvent(name, true, true); Object.keys(properties).forEach(key => { event[key] = properties[key]; }); document.body.dispatchEvent(event); } describe('<SwipeableDrawer />', () => { const SwipeableDrawerNaked = unwrap(SwipeableDrawer); let mount; let findDOMNodeStub; before(() => { mount = createMount(); // mock the drawer DOM node, since jsdom doesn't do layouting but its size is required const findDOMNode = ReactDOM.findDOMNode; findDOMNodeStub = stub(ReactDOM, 'findDOMNode').callsFake(arg => { if (arg instanceof Paper) { // mock the drawer's DOM node return { clientWidth: 250, clientHeight: 250, style: {} }; } return findDOMNode(arg); }); }); after(() => { findDOMNodeStub.restore(); mount.cleanUp(); }); it('should render a Drawer and a SwipeArea', () => { const wrapper = mount( <SwipeableDrawerNaked onOpen={() => {}} onClose={() => {}} open={false} theme={createMuiTheme()} />, ); assert.strictEqual(wrapper.childAt(0).type(), Drawer); assert.strictEqual( wrapper .childAt(1) .childAt(0) .type(), SwipeArea, ); wrapper.unmount(); }); it('should hide the SwipeArea if swipe to open is disabled', () => { const wrapper = mount( <SwipeableDrawerNaked onOpen={() => {}} onClose={() => {}} open={false} theme={createMuiTheme()} disableSwipeToOpen />, ); assert.strictEqual(wrapper.children().length, 1); wrapper.unmount(); }); it('should hide the SwipeArea if discovery is disabled', () => { const wrapper = mount( <SwipeableDrawerNaked onOpen={() => {}} onClose={() => {}} open={false} theme={createMuiTheme()} disableDiscovery />, ); assert.strictEqual(wrapper.children().length, 1); wrapper.unmount(); }); it('should accept user custom style', () => { const customStyle = { style: { backgroundColor: 'hotpink' } }; const wrapper = mount( <SwipeableDrawerNaked onOpen={() => {}} onClose={() => {}} open={false} theme={createMuiTheme()} PaperProps={customStyle} />, ); assert.strictEqual(wrapper.props().PaperProps, customStyle); }); describe('swipe to open', () => { let wrapper; let instance; beforeEach(() => { wrapper = mount( <SwipeableDrawerNaked onOpen={() => {}} onClose={() => {}} open={false} theme={createMuiTheme()} > <h1>Hello</h1> </SwipeableDrawerNaked>, ); instance = wrapper.instance(); }); afterEach(() => { reset(); if (wrapper.length > 0) { wrapper.unmount(); } }); const bodyWidth = document.body.offsetWidth; const windowHeight = window.innerHeight; const tests = [ { anchor: 'left', openTouches: [ { pageX: 0, clientY: 0 }, { pageX: 20, clientY: 0 }, { pageX: 180, clientY: 0 }, ], closeTouches: [ { pageX: 200, clientY: 0 }, { pageX: 180, clientY: 0 }, { pageX: 10, clientY: 0 }, ], edgeTouch: { pageX: 10, clientY: 50 }, ignoreTouch: { pageX: 100, clientY: 0 }, }, { anchor: 'right', openTouches: [ { pageX: bodyWidth, clientY: 0 }, { pageX: bodyWidth - 20, clientY: 0 }, { pageX: bodyWidth - 180, clientY: 0 }, ], closeTouches: [ { pageX: bodyWidth - 200, clientY: 0 }, { pageX: bodyWidth - 180, clientY: 0 }, { pageX: bodyWidth - 10, clientY: 0 }, ], edgeTouch: { pageX: bodyWidth - 10, clientY: 50 }, ignoreTouch: { pageX: bodyWidth - 100, clientY: 0 }, }, { anchor: 'top', openTouches: [ { pageX: 0, clientY: 0 }, { pageX: 0, clientY: 20 }, { pageX: 0, clientY: 180 }, ], closeTouches: [ { pageX: 0, clientY: 200 }, { pageX: 0, clientY: 180 }, { pageX: 0, clientY: 10 }, ], edgeTouch: { pageX: 50, clientY: 10 }, ignoreTouch: { pageX: 0, clientY: 100 }, }, { anchor: 'bottom', openTouches: [ { pageX: 0, clientY: windowHeight }, { pageX: 0, clientY: windowHeight - 20 }, { pageX: 0, clientY: windowHeight - 180 }, ], closeTouches: [ { pageX: 0, clientY: windowHeight - 200 }, { pageX: 0, clientY: windowHeight - 180 }, { pageX: 0, clientY: windowHeight - 10 }, ], edgeTouch: { pageX: 50, clientY: windowHeight - 10 }, ignoreTouch: { pageX: 0, clientY: windowHeight - 100 }, }, ]; tests.forEach(params => { describe(`anchor=${params.anchor}`, () => { beforeEach(() => { wrapper.setProps({ anchor: params.anchor }); }); it('should open and close when swiping', () => { // mock the internal setPosition function that moves the drawer while swiping instance.setPosition = spy(); // simulate open swipe const handleOpen = spy(); wrapper.setProps({ onOpen: handleOpen }); fireBodyMouseEvent('touchstart', { touches: [params.openTouches[0]] }); assert.strictEqual(wrapper.state().maybeSwiping, true); fireBodyMouseEvent('touchmove', { touches: [params.openTouches[1]] }); assert.strictEqual(instance.isSwiping, true); fireBodyMouseEvent('touchmove', { touches: [params.openTouches[2]] }); fireBodyMouseEvent('touchend', { changedTouches: [params.openTouches[2]] }); assert.strictEqual(handleOpen.callCount, 1); assert.strictEqual(instance.setPosition.callCount, 3); // simulate close swipe instance.setPosition.resetHistory(); const handleClose = spy(); wrapper.setProps({ open: true, onClose: handleClose }); fireBodyMouseEvent('touchstart', { touches: [params.closeTouches[0]] }); assert.strictEqual(wrapper.state().maybeSwiping, true); fireBodyMouseEvent('touchmove', { touches: [params.closeTouches[1]] }); assert.strictEqual(instance.isSwiping, true); fireBodyMouseEvent('touchmove', { touches: [params.closeTouches[2]] }); fireBodyMouseEvent('touchend', { changedTouches: [params.closeTouches[2]] }); assert.strictEqual(handleClose.callCount, 1); assert.strictEqual(instance.setPosition.callCount, 2); }); it('should stay closed when not swiping far enough', () => { // simulate open swipe that doesn't swipe far enough const handleOpen = spy(); wrapper.setProps({ onOpen: handleOpen }); fireBodyMouseEvent('touchstart', { touches: [params.openTouches[0]] }); assert.strictEqual(wrapper.state().maybeSwiping, true); fireBodyMouseEvent('touchmove', { touches: [params.openTouches[1]] }); assert.strictEqual(instance.isSwiping, true); fireBodyMouseEvent('touchend', { changedTouches: [params.openTouches[1]] }); assert.strictEqual(handleOpen.callCount, 0); }); it('should stay opened when not swiping far enough', () => { // simulate close swipe that doesn't swipe far enough const handleClose = spy(); wrapper.setProps({ open: true, onClose: handleClose }); fireBodyMouseEvent('touchstart', { touches: [params.closeTouches[0]] }); assert.strictEqual(wrapper.state().maybeSwiping, true); fireBodyMouseEvent('touchmove', { touches: [params.closeTouches[1]] }); assert.strictEqual(instance.isSwiping, true); fireBodyMouseEvent('touchend', { changedTouches: [params.closeTouches[1]] }); assert.strictEqual(handleClose.callCount, 0); }); it('should ignore swiping in the wrong direction if discovery is disabled', () => { wrapper.setProps({ disableDiscovery: true }); fireBodyMouseEvent('touchstart', { touches: [params.openTouches[0]] }); if (['left', 'right'].includes(params.anchor)) { fireBodyMouseEvent('touchmove', { touches: [ { pageX: params.openTouches[0].pageX, clientY: params.openTouches[0].clientY + 50, }, ], }); } else { fireBodyMouseEvent('touchmove', { touches: [ { pageX: params.openTouches[0].pageX + 50, clientY: params.openTouches[0].clientY, }, ], }); } assert.strictEqual(instance.isSwiping, null, 'should not be swiping'); }); it('should slide in a bit when touching near the edge', () => { // mock the internal setPosition function that moves the drawer while swiping instance.setPosition = spy(); const handleOpen = spy(); const handleClose = spy(); wrapper.setProps({ onOpen: handleOpen, onClose: handleClose }); fireBodyMouseEvent('touchstart', { touches: [params.edgeTouch] }); assert.strictEqual(wrapper.state().maybeSwiping, true); assert.strictEqual(instance.setPosition.callCount, 1); fireBodyMouseEvent('touchend', { changedTouches: [params.edgeTouch] }); assert.strictEqual(handleOpen.callCount, 0); assert.strictEqual(handleClose.callCount, 0); }); it('should makes the drawer stay hidden', () => { // mock the internal setPosition function that moves the drawer while swiping instance.setPosition = spy(); const handleOpen = spy(); const handleClose = spy(); wrapper.setProps({ disableDiscovery: true, onOpen: handleOpen, onClose: handleClose, }); fireBodyMouseEvent('touchstart', { touches: [params.edgeTouch] }); assert.strictEqual(wrapper.state().maybeSwiping, true); assert.strictEqual(instance.setPosition.callCount, 1); fireBodyMouseEvent('touchend', { changedTouches: [params.edgeTouch] }); assert.strictEqual(handleOpen.callCount, 0); assert.strictEqual(handleClose.callCount, 0); }); it('should let user scroll the page', () => { // mock the internal setPosition function that moves the drawer while swiping instance.setPosition = spy(); const handleOpen = spy(); const handleClose = spy(); wrapper.setProps({ open: false, disableDiscovery: true, onOpen: handleOpen, onClose: handleClose, }); fireBodyMouseEvent('touchstart', { touches: [params.ignoreTouch] }); assert.strictEqual(wrapper.state().maybeSwiping, false); assert.strictEqual(instance.setPosition.callCount, 0); fireBodyMouseEvent('touchend', { changedTouches: [params.ignoreTouch] }); assert.strictEqual(handleOpen.callCount, 0); assert.strictEqual(handleClose.callCount, 0); }); }); }); it('should abort when the SwipeableDrawer is closed', () => { wrapper.setProps({ open: true, }); assert.strictEqual(instance.isSwiping, null); fireBodyMouseEvent('touchstart', { touches: [{ pageX: 0, clientY: 0 }] }); assert.strictEqual(instance.isSwiping, null); fireBodyMouseEvent('touchmove', { touches: [{ pageX: 10, clientY: 0 }] }); assert.strictEqual(instance.isSwiping, true); assert.strictEqual(wrapper.state().maybeSwiping, true); wrapper.setProps({ open: false, }); assert.strictEqual(wrapper.state().maybeSwiping, false); }); it('should wait for a clear signal to determin this.isSwiping', () => { assert.strictEqual(instance.isSwiping, null); fireBodyMouseEvent('touchstart', { touches: [{ pageX: 0, clientY: 0 }] }); assert.strictEqual(instance.isSwiping, null); fireBodyMouseEvent('touchmove', { touches: [{ pageX: 3, clientY: 0 }] }); assert.strictEqual(instance.isSwiping, null); fireBodyMouseEvent('touchmove', { touches: [{ pageX: 10, clientY: 0 }] }); assert.strictEqual(instance.isSwiping, true); }); it('removes event listeners on unmount', () => { fireBodyMouseEvent('touchstart', { touches: [{ pageX: 0, clientY: 0 }] }); wrapper.unmount(); // trigger setState warning if listeners aren't cleaned. fireBodyMouseEvent('touchmove', { touches: [{ pageX: 180, clientY: 0 }] }); // trigger setState warning if swipe handling is not cleaned, too fireBodyMouseEvent('touchstart', { touches: [{ pageX: 0, clientY: 0 }] }); }); it('toggles swipe handling when the variant is changed', () => { // variant is 'temporary' by default spy(instance, 'removeTouchStart'); wrapper.setProps({ variant: 'persistent' }); assert.strictEqual(instance.removeTouchStart.callCount, 1); spy(instance, 'listenTouchStart'); wrapper.setProps({ variant: 'temporary' }); assert.strictEqual(instance.listenTouchStart.callCount, 1); }); }); describe('disableSwipeToOpen', () => { it('should not support swipe to open if disableSwipeToOpen is set', () => { const wrapper = mount( <SwipeableDrawerNaked onOpen={() => {}} onClose={() => {}} open={false} theme={createMuiTheme()} > <h1>Hello</h1> </SwipeableDrawerNaked>, ); // simulate open swipe wrapper.setProps({ disableSwipeToOpen: true }); fireBodyMouseEvent('touchstart', { touches: [{ pageX: 0, clientY: 0 }] }); assert.strictEqual( wrapper.state().maybeSwiping, false, 'should not be listening for open swipe', ); wrapper.unmount(); }); it('should support swipe to close if disableSwipeToOpen is set', () => { const wrapper = mount( <SwipeableDrawerNaked onOpen={() => {}} onClose={() => {}} open={false} theme={createMuiTheme()} > <h1>Hello</h1> </SwipeableDrawerNaked>, ); // simulate close swipe wrapper.setProps({ disableSwipeToOpen: true, open: true }); fireBodyMouseEvent('touchstart', { touches: [{ pageX: 0, clientY: 0 }] }); assert.strictEqual( wrapper.state().maybeSwiping, true, 'should not be listening for open swipe', ); wrapper.unmount(); }); }); describe('lock', () => { it('should handle a single swipe at the time', () => { const handleOpen = spy(); const wrapper = mount( <div> <SwipeableDrawerNaked onOpen={handleOpen} onClose={() => {}} open={false} theme={createMuiTheme()} > <h1>Hello1</h1> </SwipeableDrawerNaked> <SwipeableDrawerNaked onOpen={handleOpen} onClose={() => {}} open={false} theme={createMuiTheme()} > <h1>Hello2</h1> </SwipeableDrawerNaked> </div>, ); fireBodyMouseEvent('touchstart', { touches: [{ pageX: 0, clientY: 0 }] }); fireBodyMouseEvent('touchmove', { touches: [{ pageX: 20, clientY: 0 }] }); fireBodyMouseEvent('touchmove', { touches: [{ pageX: 180, clientY: 0 }] }); fireBodyMouseEvent('touchend', { changedTouches: [{ pageX: 180, clientY: 0 }] }); assert.strictEqual(handleOpen.callCount, 1, 'should call onOpen once, not twice'); wrapper.unmount(); }); }); it('does not crash when updating the parent component while swiping', () => { const wrapper = mount( <SwipeableDrawerNaked onOpen={() => {}} onClose={() => {}} open={false} theme={createMuiTheme()} > <h1>Hello</h1> </SwipeableDrawerNaked>, ); fireBodyMouseEvent('touchstart', { touches: [{ pageX: 0, clientY: 0 }] }); // simulate paper ref being null because of the drawer being updated wrapper.instance().handlePaperRef(null); fireBodyMouseEvent('touchmove', { touches: [{ pageX: 20, clientY: 0 }] }); }); describe('no backdrop', () => { it('does not crash when backdrop is hidden while swiping', () => { mount( <SwipeableDrawerNaked onClose={() => {}} onOpen={() => {}} open={false} theme={createMuiTheme()} hideBackdrop />, ); fireBodyMouseEvent('touchstart', { touches: [{ pageX: 0, clientY: 0 }] }); }); it('does not crash when backdrop props are empty while swiping', () => { mount( <SwipeableDrawerNaked onClose={() => {}} onOpen={() => {}} open={false} theme={createMuiTheme()} BackdropProps={{}} />, ); fireBodyMouseEvent('touchstart', { touches: [{ pageX: 0, clientY: 0 }] }); }); }); });