Repository: mui/material-ui (TypeScript)
Date: 2020-05-19 17:18:06+00:00
Task type: Bug Fix

Issue:
[Slide] Left and Up directions are broken when child is pre-translated
- [x] The issue is present in the latest release.
- [x] I have searched the [issues](https://github.com/mui-org/material-ui/issues) of this repository and believe that this is not a duplicate.

## Current Behavior 😯

The Slide component appears to break when direction is "left" or "up" and the child is pre-translated left or up.

## Expected Behavior 🤔

Slide should continue to function.

## Steps to Reproduce 🕹

Example here, change the "left" variable to -9 or greater to see the break.

https://codesandbox.io/s/nostalgic-smoke-847m4

## Context 🔦

I am trying to create a carousel component like this one: https://github.com/Learus/react-material-ui-carousel that supports multiple directions, but the slide animation breaks when sliding left to right.

I believe the culprit is lines 39 and 47 of Slide.js:

    return `translateX(${window.innerWidth}px) translateX(-${rect.left - offsetX}px)`;
    ...
    return `translateY(${window.innerHeight}px) translateY(-${rect.top - offsetY}px)`;

If `rect.left` is less than `offsetX` this will create a `translateX(--Npx)` transform string, which is invalid.

I believe the fix could be as simple as changing these lines to:

    return `translateX(${window.innerWidth}px) translateX(${offsetX - rect.left}px)`;
    ...
    return `translateY(${window.innerHeight}px) translateY(${offsetY - rect.top}px)`;

Which preserves the intent but removes the possibility of the double negative.

## Your Environment 🌎

| Tech        | Version |
| ----------- | ------- |
| Material-UI | v4.9.14  |
| React       | v16.12.0 |
| Browser     | Firefox v76 |



Issue discussion:

Solution patch:
diff --git a/packages/material-ui/src/Slide/Slide.js b/packages/material-ui/src/Slide/Slide.js
--- a/packages/material-ui/src/Slide/Slide.js
+++ b/packages/material-ui/src/Slide/Slide.js
@@ -36,7 +36,7 @@ function getTranslateValue(direction, node) {
   }
 
   if (direction === 'left') {
-    return `translateX(${window.innerWidth}px) translateX(-${rect.left - offsetX}px)`;
+    return `translateX(${window.innerWidth}px) translateX(${offsetX - rect.left}px)`;
   }
 
   if (direction === 'right') {
@@ -44,7 +44,7 @@ function getTranslateValue(direction, node) {
   }
 
   if (direction === 'up') {
-    return `translateY(${window.innerHeight}px) translateY(-${rect.top - offsetY}px)`;
+    return `translateY(${window.innerHeight}px) translateY(${offsetY - rect.top}px)`;
   }
 
   // direction === 'down'


Test patch:
diff --git a/packages/material-ui/src/Slide/Slide.test.js b/packages/material-ui/src/Slide/Slide.test.js
--- a/packages/material-ui/src/Slide/Slide.test.js
+++ b/packages/material-ui/src/Slide/Slide.test.js
@@ -185,6 +185,7 @@ describe('<Slide />', () => {
               right: 800,
               top: 200,
               bottom: 500,
+              ...props.rect,
             }));
           } catch (error) {
             // already stubbed
@@ -291,6 +292,48 @@ describe('<Slide />', () => {
 
         expect(nodeEnterTransformStyle).to.equal('translateX(-800px)');
       });
+
+      it('should set element transform in the `up` direction when element is offscreen', () => {
+        const childRef = React.createRef();
+        let nodeEnterTransformStyle;
+        const wrapper = mount(
+          <Slide
+            direction="up"
+            onEnter={(node) => {
+              nodeEnterTransformStyle = node.style.transform;
+            }}
+          >
+            <FakeDiv rect={{ top: -100 }} ref={childRef} />
+          </Slide>,
+        );
+
+        wrapper.setProps({ in: true });
+
+        expect(nodeEnterTransformStyle).to.equal(
+          `translateY(${global.innerHeight}px) translateY(100px)`,
+        );
+      });
+
+      it('should set element transform in the `left` direction when element is offscreen', () => {
+        const childRef = React.createRef();
+        let nodeEnterTransformStyle;
+        const wrapper = mount(
+          <Slide
+            direction="left"
+            onEnter={(node) => {
+              nodeEnterTransformStyle = node.style.transform;
+            }}
+          >
+            <FakeDiv rect={{ left: -100 }} ref={childRef} />
+          </Slide>,
+        );
+
+        wrapper.setProps({ in: true });
+
+        expect(nodeEnterTransformStyle).to.equal(
+          `translateX(${global.innerWidth}px) translateX(100px)`,
+        );
+      });
     });
 
     describe('handleExiting()', () => {