diff --git a/packages/material-ui/src/Drawer/Drawer.js b/packages/material-ui/src/Drawer/Drawer.js index 2bf59265ce3658..97fd81242328a5 100644 --- a/packages/material-ui/src/Drawer/Drawer.js +++ b/packages/material-ui/src/Drawer/Drawer.js @@ -88,14 +88,12 @@ const oppositeDirection = { bottom: 'up', }; -export function isHorizontal(props) { - return ['left', 'right'].indexOf(props.anchor) !== -1; +export function isHorizontal(anchor) { + return ['left', 'right'].indexOf(anchor) !== -1; } -export function getAnchor(props) { - return props.theme.direction === 'rtl' && isHorizontal(props) - ? oppositeDirection[props.anchor] - : props.anchor; +export function getAnchor(theme, anchor) { + return theme.direction === 'rtl' && isHorizontal(anchor) ? oppositeDirection[anchor] : anchor; } const defaultTransitionDuration = { enter: duration.enteringScreen, exit: duration.leavingScreen }; @@ -130,7 +128,7 @@ const Drawer = React.forwardRef(function Drawer(props, ref) { mounted.current = true; }, []); - const anchor = getAnchor({ anchor: anchorProp, theme }); + const anchor = getAnchor(theme, anchorProp); const drawer = ( ', () => { describe('isHorizontal', () => { it('should recognize left and right as horizontal swiping directions', () => { - assert.strictEqual(isHorizontal({ anchor: 'left' }), true); - assert.strictEqual(isHorizontal({ anchor: 'right' }), true); - assert.strictEqual(isHorizontal({ anchor: 'top' }), false); - assert.strictEqual(isHorizontal({ anchor: 'bottom' }), false); + assert.strictEqual(isHorizontal('left'), true); + assert.strictEqual(isHorizontal('right'), true); + assert.strictEqual(isHorizontal('top'), false); + assert.strictEqual(isHorizontal('bottom'), false); }); }); describe('getAnchor', () => { it('should return the anchor', () => { - const theme = createMuiTheme({ - direction: 'ltr', - }); + const theme = { direction: 'ltr' }; - assert.strictEqual(getAnchor({ anchor: 'left', theme }), 'left'); - assert.strictEqual(getAnchor({ anchor: 'right', theme }), 'right'); - assert.strictEqual(getAnchor({ anchor: 'top', theme }), 'top'); - assert.strictEqual(getAnchor({ anchor: 'bottom', theme }), 'bottom'); + assert.strictEqual(getAnchor(theme, 'left'), 'left'); + assert.strictEqual(getAnchor(theme, 'right'), 'right'); + assert.strictEqual(getAnchor(theme, 'top'), 'top'); + assert.strictEqual(getAnchor(theme, 'bottom'), 'bottom'); }); it('should switch left/right if RTL is enabled', () => { - const theme = createMuiTheme({ - direction: 'rtl', - }); + const theme = { direction: 'rtl' }; - assert.strictEqual(getAnchor({ anchor: 'left', theme }), 'right'); - assert.strictEqual(getAnchor({ anchor: 'right', theme }), 'left'); + assert.strictEqual(getAnchor(theme, 'left'), 'right'); + assert.strictEqual(getAnchor(theme, 'right'), 'left'); }); }); }); diff --git a/packages/material-ui/src/SwipeableDrawer/SwipeArea.js b/packages/material-ui/src/SwipeableDrawer/SwipeArea.js index 5baa0e74c6eb41..26be2eb142152c 100644 --- a/packages/material-ui/src/SwipeableDrawer/SwipeArea.js +++ b/packages/material-ui/src/SwipeableDrawer/SwipeArea.js @@ -43,7 +43,7 @@ const SwipeArea = React.forwardRef(function SwipeArea(props, ref) { className={clsx(classes.root, classes[`anchor${capitalize(anchor)}`], className)} ref={ref} style={{ - [isHorizontal(props) ? 'width' : 'height']: width, + [isHorizontal(anchor) ? 'width' : 'height']: width, }} {...other} /> diff --git a/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.js b/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.js index e97022ac541b0f..4dc4123b701d0f 100644 --- a/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.js +++ b/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.js @@ -85,11 +85,11 @@ class SwipeableDrawer extends React.Component { } getMaxTranslate() { - return isHorizontal(this.props) ? this.paperRef.clientWidth : this.paperRef.clientHeight; + return isHorizontal(this.props.anchor) ? this.paperRef.clientWidth : this.paperRef.clientHeight; } getTranslate(current) { - const start = isHorizontal(this.props) ? this.startX : this.startY; + const start = isHorizontal(this.props.anchor) ? this.startX : this.startY; return Math.min( Math.max(this.props.open ? start - current : this.getMaxTranslate() + start - current, 0), this.getMaxTranslate(), @@ -99,9 +99,9 @@ class SwipeableDrawer extends React.Component { setPosition(translate, options = {}) { const { mode = null, changeTransition = true } = options; - const anchor = getAnchor(this.props); + const anchor = getAnchor(this.props.theme, this.props.anchor); const rtlTranslateMultiplier = ['right', 'bottom'].indexOf(anchor) !== -1 ? 1 : -1; - const transform = isHorizontal(this.props) + const transform = isHorizontal(this.props.anchor) ? `translate(${rtlTranslateMultiplier * translate}px, 0)` : `translate(0, ${rtlTranslateMultiplier * translate}px)`; const drawerStyle = this.paperRef.style; @@ -147,7 +147,7 @@ class SwipeableDrawer extends React.Component { } const { disableDiscovery, disableSwipeToOpen, open, swipeAreaWidth } = this.props; - const anchor = getAnchor(this.props); + const anchor = getAnchor(this.props.theme, this.props.anchor); const currentX = anchor === 'right' ? document.body.offsetWidth - event.touches[0].pageX @@ -161,7 +161,7 @@ class SwipeableDrawer extends React.Component { if (disableSwipeToOpen || event.target !== this.swipeAreaRef.current) { return; } - if (isHorizontal(this.props)) { + if (isHorizontal(this.props.anchor)) { if (currentX > swipeAreaWidth) { return; } @@ -196,8 +196,8 @@ class SwipeableDrawer extends React.Component { // the ref may be null when a parent component updates while swiping if (!this.paperRef) return; - const anchor = getAnchor(this.props); - const horizontalSwipe = isHorizontal(this.props); + const anchor = getAnchor(this.props.theme, this.props.anchor); + const horizontalSwipe = isHorizontal(this.props.anchor); const currentX = anchor === 'right' @@ -284,9 +284,9 @@ class SwipeableDrawer extends React.Component { this.isSwiping = null; - const anchor = getAnchor(this.props); + const anchor = getAnchor(this.props.theme, this.props.anchor); let current; - if (isHorizontal(this.props)) { + if (isHorizontal(this.props.anchor)) { current = anchor === 'right' ? document.body.offsetWidth - event.changedTouches[0].pageX diff --git a/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.test.js b/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.test.js index 4be27586c6fd25..c14775b8fea65f 100644 --- a/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.test.js +++ b/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.test.js @@ -1,13 +1,13 @@ import React from 'react'; import { assert } from 'chai'; -import { spy, stub } from 'sinon'; -import { createMount, describeConformance, unwrap } from '@material-ui/core/test-utils'; +import { spy } from 'sinon'; +import { createMount, describeConformance } from '@material-ui/core/test-utils'; import PropTypes from 'prop-types'; import consoleErrorMock from 'test/utils/consoleErrorMock'; import Drawer from '../Drawer'; import SwipeableDrawer, { reset } from './SwipeableDrawer'; import SwipeArea from './SwipeArea'; -import createMuiTheme from '../styles/createMuiTheme'; +import { useForkRef } from '../utils/reactHelpers'; function fireBodyMouseEvent(name, properties = {}) { const event = document.createEvent('MouseEvents'); @@ -33,25 +33,54 @@ function fireSwipeAreaMouseEvent(wrapper, name, properties = {}) { return event; } -describe('', () => { - const SwipeableDrawerNaked = unwrap(SwipeableDrawer); - let mount; +const FakePaper = React.forwardRef(function FakeWidthPaper(props, ref) { + const paperRef = React.useRef(null); + const handleRef = useForkRef(ref, paperRef); - function mockDrawerDOMNode(wrapper) { - wrapper.find('SwipeableDrawer').forEach(drawer => { - // like .value() but prevent reassignment - stub(drawer.instance(), 'paperRef') - .set(() => {}) - .get(() => { - return { - clientWidth: 250, - clientHeight: 250, - style: {}, - }; - }); - }); + React.useEffect(() => { + // For jsdom + Object.defineProperty(paperRef.current, 'clientWidth', { value: 250 }); + Object.defineProperty(paperRef.current, 'clientHeight', { value: 250 }); + }); + + return ( +
+ ); +}); + +const NullPaper = React.forwardRef(function NullPaper(props, ref) { + const [hidden, setHidden] = React.useState(false); + + React.useEffect(() => { + const handleTouchStart = () => { + setHidden(true); + }; + + document.addEventListener('touchstart', handleTouchStart); + + return () => { + document.removeEventListener('touchstart', handleTouchStart); + }; + }, []); + + if (hidden) { + return null; } + return
; +}); + +describe('', () => { + let mount; + before(() => { // test are mostly asserting on implementation details mount = createMount({ strict: undefined }); @@ -70,69 +99,46 @@ describe('', () => { })); it('should render a Drawer and a SwipeArea', () => { - const wrapper = mount( - {}} - onClose={() => {}} - open={false} - theme={createMuiTheme()} - />, - ).find('SwipeableDrawer'); // unwrap withForwardedRef - assert.strictEqual(wrapper.childAt(0).type(), Drawer); - assert.strictEqual( - wrapper - .childAt(1) - .childAt(0) - .type(), - SwipeArea, - ); + const wrapper = mount( {}} onClose={() => {}} open={false} />); // unwrap withForwardedRef + assert.strictEqual(wrapper.find(Drawer).exists(), true); + assert.strictEqual(wrapper.find(SwipeArea).exists(), true); }); it('should hide the SwipeArea if swipe to open is disabled', () => { const wrapper = mount( - {}} - onClose={() => {}} - open={false} - theme={createMuiTheme()} - disableSwipeToOpen - />, - ).find('SwipeableDrawer'); - assert.strictEqual(wrapper.children().length, 1); + {}} onClose={() => {}} open={false} disableSwipeToOpen />, + ); + assert.strictEqual(wrapper.find(SwipeArea).exists(), false); }); it('should accept user custom style', () => { const customStyle = { style: { backgroundColor: 'hotpink' } }; const wrapper = mount( - {}} onClose={() => {}} open={false} - theme={createMuiTheme()} PaperProps={customStyle} />, - ).find('SwipeableDrawer'); + ); assert.strictEqual(wrapper.props().PaperProps, customStyle); }); describe('swipe to open', () => { let wrapper; - let instance; beforeEach(() => { wrapper = mount( - {}} onClose={() => {}} open={false} - theme={createMuiTheme()} + PaperProps={{ component: FakePaper }} >

Hello

-
, +
, ); - instance = wrapper.find('SwipeableDrawer').instance(); - mockDrawerDOMNode(wrapper); }); afterEach(() => { @@ -215,32 +221,22 @@ describe('', () => { 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 }); fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [params.openTouches[0]] }); - assert.strictEqual(instance.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 }); fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [params.closeTouches[0]] }); - assert.strictEqual(instance.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', () => { @@ -248,9 +244,7 @@ describe('', () => { const handleOpen = spy(); wrapper.setProps({ onOpen: handleOpen }); fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [params.openTouches[0]] }); - assert.strictEqual(instance.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); }); @@ -260,9 +254,7 @@ describe('', () => { const handleClose = spy(); wrapper.setProps({ open: true, onClose: handleClose }); fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [params.closeTouches[0]] }); - assert.strictEqual(instance.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); }); @@ -290,28 +282,22 @@ describe('', () => { ], }); } - assert.strictEqual(instance.isSwiping, null, 'should not be swiping'); + assert.strictEqual(wrapper.find('[role="presentation"]').exists(), false); }); 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 }); fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [params.edgeTouch] }); - assert.strictEqual(instance.state.maybeSwiping, true); - assert.strictEqual(instance.setPosition.callCount, 1); + wrapper.update(); + assert.strictEqual(wrapper.find('[role="presentation"]').exists(), true); 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({ @@ -320,17 +306,12 @@ describe('', () => { onClose: handleClose, }); fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [params.edgeTouch] }); - assert.strictEqual(instance.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({ @@ -340,8 +321,6 @@ describe('', () => { onClose: handleClose, }); fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [params.ignoreTouch] }); - assert.strictEqual(instance.state.maybeSwiping, false); - assert.strictEqual(instance.setPosition.callCount, 0); fireBodyMouseEvent('touchend', { changedTouches: [params.ignoreTouch] }); assert.strictEqual(handleOpen.callCount, 0); assert.strictEqual(handleClose.callCount, 0); @@ -350,29 +329,19 @@ describe('', () => { }); it('should abort when the SwipeableDrawer is closed', () => { + const handleClose = spy(); wrapper.setProps({ open: true, + onClose: handleClose, }); - assert.strictEqual(instance.isSwiping, null); - fireSwipeAreaMouseEvent(wrapper, '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(instance.state.maybeSwiping, true); + fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [{ pageX: 250, clientY: 0 }] }); + fireBodyMouseEvent('touchmove', { touches: [{ pageX: 180, clientY: 0 }] }); wrapper.setProps({ open: false, }); - assert.strictEqual(instance.state.maybeSwiping, false); - }); - - it('should wait for a clear signal to determine this.isSwiping', () => { - assert.strictEqual(instance.isSwiping, null); - fireSwipeAreaMouseEvent(wrapper, '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); + wrapper.update(); + fireBodyMouseEvent('touchend', { changedTouches: [{ pageX: 10, clientY: 0 }] }); + assert.strictEqual(handleClose.callCount, 0); }); it('removes event listeners on unmount', () => { @@ -386,60 +355,61 @@ describe('', () => { it('toggles swipe handling when the variant is changed', () => { // variant is 'temporary' by default - spy(instance, 'removeTouchStart'); + assert.strictEqual(wrapper.find(SwipeArea).exists(), true); wrapper.setProps({ variant: 'persistent' }); - assert.strictEqual(instance.removeTouchStart.callCount, 1); + assert.strictEqual(wrapper.find(SwipeArea).exists(), false); - spy(instance, 'listenTouchStart'); wrapper.setProps({ variant: 'temporary' }); - assert.strictEqual(instance.listenTouchStart.callCount, 1); + wrapper.update(); + assert.strictEqual(wrapper.find(SwipeArea).exists(), true); }); }); describe('disableSwipeToOpen', () => { it('should not support swipe to open if disableSwipeToOpen is set', () => { + const handleOpen = spy(); const wrapper = mount( - {}} + {}} open={false} - theme={createMuiTheme()} + PaperProps={{ component: FakePaper }} >

Hello

-
, +
, ); // simulate open swipe wrapper.setProps({ disableSwipeToOpen: true }); - fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [{ pageX: 0, clientY: 0 }] }); - assert.strictEqual( - wrapper.find('SwipeableDrawer').instance().state.maybeSwiping, - false, - 'should not be listening for open swipe', - ); + assert.strictEqual(wrapper.find('[role="presentation"]').exists(), false); + fireBodyMouseEvent('touchstart', { touches: [{ pageX: 10, clientY: 0 }] }); + fireBodyMouseEvent('touchmove', { touches: [{ pageX: 150, clientY: 0 }] }); + fireBodyMouseEvent('touchend', { changedTouches: [{ pageX: 250, clientY: 0 }] }); + assert.strictEqual(handleOpen.callCount, 0); + assert.strictEqual(wrapper.find('[role="presentation"]').exists(), false); wrapper.unmount(); }); it('should support swipe to close if disableSwipeToOpen is set', () => { + const handleClose = spy(); const wrapper = mount( - {}} - onClose={() => {}} - open={false} - theme={createMuiTheme()} + onClose={handleClose} + open + PaperProps={{ component: FakePaper }} >

Hello

-
, +
, ); // simulate close swipe - wrapper.setProps({ disableSwipeToOpen: true, open: true }); - fireBodyMouseEvent('touchstart', { touches: [{ pageX: 0, clientY: 0 }] }); - assert.strictEqual( - wrapper.find('SwipeableDrawer').instance().state.maybeSwiping, - true, - 'should not be listening for open swipe', - ); + wrapper.setProps({ disableSwipeToOpen: true }); + assert.strictEqual(wrapper.find('[role="presentation"]').exists(), true); + fireBodyMouseEvent('touchstart', { touches: [{ pageX: 250, clientY: 0 }] }); + fireBodyMouseEvent('touchmove', { touches: [{ pageX: 150, clientY: 0 }] }); + fireBodyMouseEvent('touchend', { changedTouches: [{ pageX: 10, clientY: 0 }] }); + assert.strictEqual(handleClose.callCount, 1); wrapper.unmount(); }); }); @@ -449,30 +419,29 @@ describe('', () => { const handleOpen = spy(); const wrapper = mount(
- {}} open={false} - theme={createMuiTheme()} + PaperProps={{ component: FakePaper }} >

Hello1

-
- + {}} open={false} - theme={createMuiTheme()} + PaperProps={{ component: FakePaper }} >

Hello2

-
+
, ); - mockDrawerDOMNode(wrapper); - fireSwipeAreaMouseEvent(wrapper.find(SwipeableDrawerNaked).at(0), 'touchstart', { + fireSwipeAreaMouseEvent(wrapper.find(SwipeableDrawer).at(0), 'touchstart', { touches: [{ pageX: 0, clientY: 0 }], }); - fireSwipeAreaMouseEvent(wrapper.find(SwipeableDrawerNaked).at(1), 'touchstart', { + fireSwipeAreaMouseEvent(wrapper.find(SwipeableDrawer).at(1), 'touchstart', { touches: [{ pageX: 0, clientY: 0 }], }); fireBodyMouseEvent('touchmove', { touches: [{ pageX: 20, clientY: 0 }] }); @@ -484,47 +453,31 @@ describe('', () => { it('does not crash when updating the parent component while swiping', () => { const wrapper = mount( - {}} onClose={() => {}} open={false} - theme={createMuiTheme()} + PaperProps={{ component: NullPaper }} >

Hello

-
, +
, ); fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [{ pageX: 0, clientY: 0 }] }); // simulate paper ref being null because of the drawer being updated - wrapper - .find('SwipeableDrawer') - .instance() - .handlePaperRef(null); fireBodyMouseEvent('touchmove', { touches: [{ pageX: 20, clientY: 0 }] }); }); describe('no backdrop', () => { it('does not crash when backdrop is hidden while swiping', () => { const wrapper = mount( - {}} - onOpen={() => {}} - open={false} - theme={createMuiTheme()} - hideBackdrop - />, + {}} onOpen={() => {}} open={false} hideBackdrop />, ); fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [{ pageX: 0, clientY: 0 }] }); }); it('does not crash when backdrop props are empty while swiping', () => { const wrapper = mount( - {}} - onOpen={() => {}} - open={false} - theme={createMuiTheme()} - BackdropProps={{}} - />, + {}} onOpen={() => {}} open={false} BackdropProps={{}} />, ); fireSwipeAreaMouseEvent(wrapper, 'touchstart', { touches: [{ pageX: 0, clientY: 0 }] }); });