Repository: codypearce/material-bread
Pull request #207 (2019-06-20 23:59:59)

Label positioning fix
Fixed: 

- #206 the focused/filled label positioning by basing the size of the text off a font size animation instead of a scale.  The scale was causing us to have to calculate exactly how far each label based on width needed to be translated, but this seems to just work.  Also changed some of the positioning to better fit material, please check me there.

- The label was wrapped in a View such that pointer events can be ignored.  Previously, this was causing taps to be ignored on the label text and focus would not occur.

- The labelStyle passed in from the TextField sub class was not being applied.


Patch:
diff --git a/.eslintrc b/.eslintrc
index d62b7fa..b194f20 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -19,5 +19,12 @@
     "react/display-name": 0,
     "react/no-typos": 0
   },
-  "plugins": ["prettier", "react", "import"]
+  "plugins": ["prettier", "react", "import"],
+  "settings": {
+    "import/resolver": {
+      "node": {
+        "extensions": [".js", ".ios.js", ".android.js", ".web.js", ".native.js"]
+      }
+    }
+  }
 }
diff --git a/src/Components/TextField/TextFieldFilled/TextFieldFilled.js b/src/Components/TextField/TextFieldFilled/TextFieldFilled.js
index 84441d7..794be97 100644
--- a/src/Components/TextField/TextFieldFilled/TextFieldFilled.js
+++ b/src/Components/TextField/TextFieldFilled/TextFieldFilled.js
@@ -80,7 +80,7 @@ class TextFieldFilled extends Component {
     const { prefix } = this.props;
 
     return (
-      <View style={{ position: 'absolute', left: 16, top: 26 }}>
+      <View style={{ position: 'absolute', left: 16, top: 26, zIndex: 1 }}>
         {React.cloneElement(prefix, {
           color: prefix.props.color ? prefix.props.color : 'rgba(0, 0, 0, .57)',
           fontSize: prefix.props.fontSize ? prefix.props.fontSize : 16,
diff --git a/src/Components/TextField/TextFieldLabel/TextFieldLabel.constants.native.js b/src/Components/TextField/TextFieldLabel/TextFieldLabel.constants.native.js
new file mode 100644
index 0000000..9f3aa89
--- /dev/null
+++ b/src/Components/TextField/TextFieldLabel/TextFieldLabel.constants.native.js
@@ -0,0 +1,14 @@
+export const outlinedStops = {
+  initial: 20,
+  active: -12,
+};
+
+export const outlinedStopsDense = {
+  initial: 8,
+  active: -12,
+};
+
+export const nonOutlinedStops = {
+  initial: 20,
+  active: 5,
+};
diff --git a/src/Components/TextField/TextFieldLabel/TextFieldLabel.constants.web.js b/src/Components/TextField/TextFieldLabel/TextFieldLabel.constants.web.js
new file mode 100644
index 0000000..de70257
--- /dev/null
+++ b/src/Components/TextField/TextFieldLabel/TextFieldLabel.constants.web.js
@@ -0,0 +1,14 @@
+export const outlinedStops = {
+  initial: 18,
+  active: -8,
+};
+
+export const outlinedStopsDense = {
+  initial: 10,
+  active: -8,
+};
+
+export const nonOutlinedStops = {
+  initial: 18,
+  active: 5,
+};
diff --git a/src/Components/TextField/TextFieldLabel/TextFieldLabel.js b/src/Components/TextField/TextFieldLabel/TextFieldLabel.js
index 910f3c5..79a2f0c 100644
--- a/src/Components/TextField/TextFieldLabel/TextFieldLabel.js
+++ b/src/Components/TextField/TextFieldLabel/TextFieldLabel.js
@@ -1,8 +1,13 @@
 import React, { Component } from 'react';
 import PropTypes from 'prop-types';
-import { Animated, Easing } from 'react-native';
+import { Animated, Easing, StyleSheet } from 'react-native';
 import withTheme from '../../../Theme/withTheme';
 import styles from './TextFieldLabel.styles';
+import {
+  nonOutlinedStops,
+  outlinedStops,
+  outlinedStopsDense,
+} from './TextFieldLabel.constants';
 
 class TextFieldLabel extends Component {
   constructor(props) {
@@ -24,8 +29,14 @@ class TextFieldLabel extends Component {
   };
 
   state = {
-    translateYAnimation: new Animated.Value(20),
-    scaleAnimation: new Animated.Value(1),
+    translateYAnimation: new Animated.Value(
+      this.props.type === 'outlined'
+        ? outlinedStops.initial
+        : nonOutlinedStops.initial,
+    ),
+    fontSizeAnimation: new Animated.Value(
+      this.props.value || this.props.focused ? 1 : 0,
+    ),
     animationDuration: 200,
     animationEasing: Easing.ease,
     canAnimate: true,
@@ -35,7 +46,9 @@ class TextFieldLabel extends Component {
     const { type, dense, prefix, value } = this.props;
 
     if (type == 'outlined' && dense) {
-      this.setState({ translateYAnimation: new Animated.Value(11) });
+      this.setState({
+        translateYAnimation: new Animated.Value(outlinedStopsDense.initial),
+      });
     }
 
     if (prefix) this._handlePrefix();
@@ -51,6 +64,7 @@ class TextFieldLabel extends Component {
 
   componentDidUpdate(prevProps) {
     const { focused, type } = this.props;
+
     if (focused !== prevProps.focused) {
       if (type == 'outlined') {
         this._handleLabelOutlinedAnimation();
@@ -61,16 +75,18 @@ class TextFieldLabel extends Component {
   }
 
   _handlePrefix() {
-    const { type } = this.props;
-    let translateYAnimation = 10;
+    const { type, dense } = this.props;
+    let translateYAnimation = nonOutlinedStops.active;
     if (type == 'outlined') {
-      translateYAnimation = -10;
+      translateYAnimation = dense
+        ? outlinedStopsDense.active
+        : outlinedStops.active;
     }
 
     this.setState({
       canAnimate: false,
       translateYAnimation: new Animated.Value(translateYAnimation),
-      scaleAnimation: new Animated.Value(0.75),
+      fontSizeAnimation: new Animated.Value(1),
     });
   }
 
@@ -78,15 +94,16 @@ class TextFieldLabel extends Component {
     const { focused, value } = this.props;
     const {
       translateYAnimation,
-      scaleAnimation,
       animationEasing,
       animationDuration,
       canAnimate,
+      fontSizeAnimation,
     } = this.state;
     if (!canAnimate) return;
 
-    let position = focused || value ? 10 : 20;
-    let scale = focused || value ? 0.75 : 1;
+    let position =
+      focused || value ? nonOutlinedStops.active : nonOutlinedStops.initial;
+    const fontVal = focused || value ? 1 : 0;
 
     Animated.parallel([
       Animated.timing(translateYAnimation, {
@@ -94,8 +111,8 @@ class TextFieldLabel extends Component {
         duration: animationDuration,
         easing: animationEasing,
       }),
-      Animated.timing(scaleAnimation, {
-        toValue: scale,
+      Animated.timing(fontSizeAnimation, {
+        toValue: fontVal,
         duration: animationDuration,
         easing: animationEasing,
       }),
@@ -106,17 +123,22 @@ class TextFieldLabel extends Component {
     const { focused, value, dense } = this.props;
     const {
       translateYAnimation,
-      scaleAnimation,
       animationEasing,
       animationDuration,
+      fontSizeAnimation,
       canAnimate,
     } = this.state;
     if (!canAnimate) return;
 
-    let position = focused || value ? -10 : 20;
-    let scale = focused || value ? 0.75 : 1;
+    let position =
+      focused || value ? outlinedStops.active : outlinedStops.initial;
+    const fontVal = focused || value ? 1 : 0;
 
-    if (dense) position = focused || value ? -10 : 11;
+    if (dense)
+      position =
+        focused || value
+          ? outlinedStopsDense.active
+          : outlinedStopsDense.initial;
 
     Animated.parallel([
       Animated.timing(translateYAnimation, {
@@ -124,8 +146,8 @@ class TextFieldLabel extends Component {
         duration: animationDuration,
         easing: animationEasing,
       }),
-      Animated.timing(scaleAnimation, {
-        toValue: scale,
+      Animated.timing(fontSizeAnimation, {
+        toValue: fontVal,
         duration: animationDuration,
         easing: animationEasing,
       }),
@@ -142,10 +164,17 @@ class TextFieldLabel extends Component {
       leadingIcon,
       prefix,
       theme,
+      style,
+      dense,
     } = this.props;
-    const { translateYAnimation, scaleAnimation } = this.state;
+    const { translateYAnimation, fontSizeAnimation } = this.state;
 
-    const translateX = type == 'flat' ? -1 : 11;
+    let translateX = 12;
+    if (type === 'flat') {
+      translateX = -1;
+    } else if (type === 'outlined') {
+      translateX = 10;
+    }
 
     if (!labelColor) labelColor = 'rgba(0, 0, 0, 0.54)';
 
@@ -163,24 +192,45 @@ class TextFieldLabel extends Component {
       marginLeft = 6;
     }
 
+    const baseFontSize =
+      (StyleSheet.flatten(style) || {}).fontSize ||
+      theme.subtitleOne.fontSize ||
+      16;
+
+    const fontStyle = {
+      fontSize: fontSizeAnimation.interpolate({
+        inputRange: [0, 1],
+        outputRange: [baseFontSize, baseFontSize * (dense ? 0.65 : 0.75)],
+      }),
+    };
+
     return (
-      <Animated.Text
+      <Animated.View
         style={[
-          styles.label,
+          styles.container,
           {
-            color: labelColor,
-            backgroundColor: type == 'outlined' ? 'white' : 'transparent',
-            paddingHorizontal: type == 'outlined' ? 4 : 0,
             marginLeft: marginLeft,
-            transform: [
-              { scale: scaleAnimation },
-              { translateY: translateYAnimation },
-              { translateX: translateX },
-            ],
           },
-        ]}>
-        {label}
-      </Animated.Text>
+        ]}
+        pointerEvents="none">
+        <Animated.Text
+          style={[
+            styles.label,
+            {
+              color: labelColor,
+              backgroundColor: type == 'outlined' ? 'white' : 'transparent',
+              paddingHorizontal: type == 'outlined' ? 4 : 0,
+              transform: [
+                { translateY: translateYAnimation },
+                { translateX: translateX },
+              ],
+            },
+            style,
+            fontStyle,
+          ]}>
+          {label}
+        </Animated.Text>
+      </Animated.View>
     );
   }
 }
diff --git a/src/Components/TextField/TextFieldLabel/TextFieldLabel.styles.js b/src/Components/TextField/TextFieldLabel/TextFieldLabel.styles.js
index 78a6f7c..b4ae5df 100644
--- a/src/Components/TextField/TextFieldLabel/TextFieldLabel.styles.js
+++ b/src/Components/TextField/TextFieldLabel/TextFieldLabel.styles.js
@@ -1,9 +1,7 @@
 import { StyleSheet } from 'react-native';
 
 const styles = StyleSheet.create({
-  label: {
-    position: 'absolute',
-    zIndex: 10,
-  },
+  container: { position: 'absolute', zIndex: 10 },
+  label: {},
 });
 export default styles;
diff --git a/src/Components/TextField/TextFieldOutline/TextFieldOutline.js b/src/Components/TextField/TextFieldOutline/TextFieldOutline.js
index 73b3044..8555646 100644
--- a/src/Components/TextField/TextFieldOutline/TextFieldOutline.js
+++ b/src/Components/TextField/TextFieldOutline/TextFieldOutline.js
@@ -4,7 +4,7 @@ import { View, TextInput, Platform } from 'react-native';
 import withTheme from '../../../Theme/withTheme';
 import TextFieldLabel from '../TextFieldLabel/TextFieldLabel';
 import TextFieldHelperText from '../TextFieldHelperText/TextFieldHelperText';
-import styles from './TextFieldOutline.styles';
+import styles, { OUTLINED_LEFT_PADDING } from './TextFieldOutline.styles';
 
 class TextFieldOutlined extends Component {
   constructor(props) {
@@ -141,7 +141,7 @@ class TextFieldOutlined extends Component {
       height = 40;
     }
 
-    let paddingLeft = leadingIcon ? 44 : 12;
+    let paddingLeft = leadingIcon ? 44 : OUTLINED_LEFT_PADDING;
     if (prefix) paddingLeft = 32;
 
     const platformStyles = Platform.OS == 'web' ? { outlineWidth: 0 } : {};
diff --git a/src/Components/TextField/TextFieldOutline/TextFieldOutline.styles.js b/src/Components/TextField/TextFieldOutline/TextFieldOutline.styles.js
index 8cb0ac6..16a03cb 100644
--- a/src/Components/TextField/TextFieldOutline/TextFieldOutline.styles.js
+++ b/src/Components/TextField/TextFieldOutline/TextFieldOutline.styles.js
@@ -1,10 +1,12 @@
 import { StyleSheet } from 'react-native';
 
+export const OUTLINED_LEFT_PADDING = 14;
+
 const styles = StyleSheet.create({
   containerStyle: {},
   textField: {
     height: 56,
-    paddingHorizontal: 12,
+    paddingHorizontal: OUTLINED_LEFT_PADDING,
   },
   outlinedInput: {
     borderWidth: 1,


Test patch:
diff --git a/src/Components/TextField/TextFieldFilled/__snapshots__/TextFieldFilled.test.js.snap b/src/Components/TextField/TextFieldFilled/__snapshots__/TextFieldFilled.test.js.snap
index a195b20..a82d778 100644
--- a/src/Components/TextField/TextFieldFilled/__snapshots__/TextFieldFilled.test.js.snap
+++ b/src/Components/TextField/TextFieldFilled/__snapshots__/TextFieldFilled.test.js.snap
@@ -14,29 +14,35 @@ exports[`TextFieldFilled Renders 1`] = `
     ]
   }
 >
-  <Text
+  <View
+    pointerEvents="none"
     style={
       Object {
-        "backgroundColor": "transparent",
-        "color": "rgba(0, 0, 0, 0.54)",
         "marginLeft": 0,
-        "paddingHorizontal": 0,
         "position": "absolute",
-        "transform": Array [
-          Object {
-            "scale": 1,
-          },
-          Object {
-            "translateY": 20,
-          },
-          Object {
-            "translateX": 11,
-          },
-        ],
         "zIndex": 10,
       }
     }
-  />
+  >
+    <Text
+      style={
+        Object {
+          "backgroundColor": "transparent",
+          "color": "rgba(0, 0, 0, 0.54)",
+          "fontSize": 16,
+          "paddingHorizontal": 0,
+          "transform": Array [
+            Object {
+              "translateY": 20,
+            },
+            Object {
+              "translateX": 12,
+            },
+          ],
+        }
+      }
+    />
+  </View>
   <TextInput
     allowFontScaling={true}
     onContentSizeChange={[Function]}
diff --git a/src/Components/TextField/TextFieldFlat/__snapshots__/TextFieldFlat.test.js.snap b/src/Components/TextField/TextFieldFlat/__snapshots__/TextFieldFlat.test.js.snap
index bbb6b36..f3ab646 100644
--- a/src/Components/TextField/TextFieldFlat/__snapshots__/TextFieldFlat.test.js.snap
+++ b/src/Components/TextField/TextFieldFlat/__snapshots__/TextFieldFlat.test.js.snap
@@ -14,29 +14,35 @@ exports[`TextFieldFlat Renders 1`] = `
     ]
   }
 >
-  <Text
+  <View
+    pointerEvents="none"
     style={
       Object {
-        "backgroundColor": "transparent",
-        "color": "rgba(0, 0, 0, 0.54)",
         "marginLeft": 0,
-        "paddingHorizontal": 0,
         "position": "absolute",
-        "transform": Array [
-          Object {
-            "scale": 1,
-          },
-          Object {
-            "translateY": 20,
-          },
-          Object {
-            "translateX": -1,
-          },
-        ],
         "zIndex": 10,
       }
     }
-  />
+  >
+    <Text
+      style={
+        Object {
+          "backgroundColor": "transparent",
+          "color": "rgba(0, 0, 0, 0.54)",
+          "fontSize": 16,
+          "paddingHorizontal": 0,
+          "transform": Array [
+            Object {
+              "translateY": 20,
+            },
+            Object {
+              "translateX": -1,
+            },
+          ],
+        }
+      }
+    />
+  </View>
   <TextInput
     allowFontScaling={true}
     onContentSizeChange={[Function]}
diff --git a/src/Components/TextField/TextFieldLabel/__snapshots__/TextFieldLabel.test.js.snap b/src/Components/TextField/TextFieldLabel/__snapshots__/TextFieldLabel.test.js.snap
index cd388d1..0c777ab 100644
--- a/src/Components/TextField/TextFieldLabel/__snapshots__/TextFieldLabel.test.js.snap
+++ b/src/Components/TextField/TextFieldLabel/__snapshots__/TextFieldLabel.test.js.snap
@@ -1,27 +1,33 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`TextFieldLabel Renders 1`] = `
-<Text
+<View
+  pointerEvents="none"
   style={
     Object {
-      "backgroundColor": "transparent",
-      "color": "rgba(0, 0, 0, 0.54)",
       "marginLeft": 0,
-      "paddingHorizontal": 0,
       "position": "absolute",
-      "transform": Array [
-        Object {
-          "scale": 1,
-        },
-        Object {
-          "translateY": 20,
-        },
-        Object {
-          "translateX": 11,
-        },
-      ],
       "zIndex": 10,
     }
   }
-/>
+>
+  <Text
+    style={
+      Object {
+        "backgroundColor": "transparent",
+        "color": "rgba(0, 0, 0, 0.54)",
+        "fontSize": 16,
+        "paddingHorizontal": 0,
+        "transform": Array [
+          Object {
+            "translateY": 20,
+          },
+          Object {
+            "translateX": 12,
+          },
+        ],
+      }
+    }
+  />
+</View>
 `;
diff --git a/src/Components/TextField/TextFieldOutline/__snapshots__/TextFieldOutline.test.js.snap b/src/Components/TextField/TextFieldOutline/__snapshots__/TextFieldOutline.test.js.snap
index f7779ba..424b840 100644
--- a/src/Components/TextField/TextFieldOutline/__snapshots__/TextFieldOutline.test.js.snap
+++ b/src/Components/TextField/TextFieldOutline/__snapshots__/TextFieldOutline.test.js.snap
@@ -13,29 +13,35 @@ exports[`TextFieldOutline Renders 1`] = `
     ]
   }
 >
-  <Text
+  <View
+    pointerEvents="none"
     style={
       Object {
-        "backgroundColor": "white",
-        "color": "rgba(0, 0, 0, 0.54)",
         "marginLeft": 0,
-        "paddingHorizontal": 4,
         "position": "absolute",
-        "transform": Array [
-          Object {
-            "scale": 1,
-          },
-          Object {
-            "translateY": 20,
-          },
-          Object {
-            "translateX": 11,
-          },
-        ],
         "zIndex": 10,
       }
     }
-  />
+  >
+    <Text
+      style={
+        Object {
+          "backgroundColor": "white",
+          "color": "rgba(0, 0, 0, 0.54)",
+          "fontSize": 16,
+          "paddingHorizontal": 4,
+          "transform": Array [
+            Object {
+              "translateY": 20,
+            },
+            Object {
+              "translateX": 10,
+            },
+          ],
+        }
+      }
+    />
+  </View>
   <TextInput
     allowFontScaling={true}
     onContentSizeChange={[Function]}
@@ -44,7 +50,7 @@ exports[`TextFieldOutline Renders 1`] = `
       Array [
         Object {
           "height": 56,
-          "paddingHorizontal": 12,
+          "paddingHorizontal": 14,
         },
         Object {
           "borderRadius": 4,
@@ -56,7 +62,7 @@ exports[`TextFieldOutline Renders 1`] = `
           "height": 56,
           "minHeight": 56,
           "paddingBottom": 0,
-          "paddingLeft": 12,
+          "paddingLeft": 14,
           "paddingRight": 0,
           "paddingTop": 0,
         },