diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js
index f3d708013d6cb..c9c61207f81e4 100644
--- a/src/components/TextInput/BaseTextInput.js
+++ b/src/components/TextInput/BaseTextInput.js
@@ -1,5 +1,5 @@
import _ from 'underscore';
-import React, {Component} from 'react';
+import React, {useState, useRef, useEffect, useCallback} from 'react';
import {Animated, View, AppState, Keyboard, StyleSheet} from 'react-native';
import Str from 'expensify-common/lib/str';
import RNTextInput from '../RNTextInput';
@@ -18,128 +18,184 @@ import getSecureEntryKeyboardType from '../../libs/getSecureEntryKeyboardType';
import CONST from '../../CONST';
import FormHelpMessage from '../FormHelpMessage';
import isInputAutoFilled from '../../libs/isInputAutoFilled';
-import * as Pressables from '../Pressable';
+import PressableWithoutFeedback from '../Pressable/PressableWithoutFeedback';
import withLocalize from '../withLocalize';
-const PressableWithoutFeedback = Pressables.PressableWithoutFeedback;
-class BaseTextInput extends Component {
- constructor(props) {
- super(props);
-
- const value = props.value || props.defaultValue || '';
- const activeLabel = props.forceActiveLabel || value.length > 0 || Boolean(props.prefixCharacter);
-
- this.state = {
- isFocused: false,
- labelTranslateY: new Animated.Value(activeLabel ? styleConst.ACTIVE_LABEL_TRANSLATE_Y : styleConst.INACTIVE_LABEL_TRANSLATE_Y),
- labelScale: new Animated.Value(activeLabel ? styleConst.ACTIVE_LABEL_SCALE : styleConst.INACTIVE_LABEL_SCALE),
- passwordHidden: props.secureTextEntry,
- textInputWidth: 0,
- textInputHeight: 0,
- prefixWidth: 0,
- selection: props.selection,
- height: variables.componentSizeLarge,
-
- // Value should be kept in state for the autoGrow feature to work - https://github.com/Expensify/App/pull/8232#issuecomment-1077282006
- value,
- };
-
- this.input = null;
- this.isLabelActive = activeLabel;
- this.onPress = this.onPress.bind(this);
- this.onFocus = this.onFocus.bind(this);
- this.onBlur = this.onBlur.bind(this);
- this.setValue = this.setValue.bind(this);
- this.togglePasswordVisibility = this.togglePasswordVisibility.bind(this);
- this.dismissKeyboardWhenBackgrounded = this.dismissKeyboardWhenBackgrounded.bind(this);
- this.storePrefixLayoutDimensions = this.storePrefixLayoutDimensions.bind(this);
- }
-
- componentDidMount() {
- if (this.props.disableKeyboard) {
- this.appStateSubscription = AppState.addEventListener('change', this.dismissKeyboardWhenBackgrounded);
+function BaseTextInput(props) {
+ const inputValue = props.value || props.defaultValue || '';
+ const initialActiveLabel = props.forceActiveLabel || inputValue.length > 0 || Boolean(props.prefixCharacter);
+
+ const [isFocused, setIsFocused] = useState(false);
+ const [passwordHidden, setPasswordHidden] = useState(props.secureTextEntry);
+ const [textInputWidth, setTextInputWidth] = useState(0);
+ const [textInputHeight, setTextInputHeight] = useState(0);
+ const [prefixWidth, setPrefixWidth] = useState(0);
+ const [height, setHeight] = useState(variables.componentSizeLarge);
+ const [width, setWidth] = useState();
+ const labelScale = useRef(new Animated.Value(initialActiveLabel ? styleConst.ACTIVE_LABEL_SCALE : styleConst.INACTIVE_LABEL_SCALE)).current;
+ const labelTranslateY = useRef(new Animated.Value(initialActiveLabel ? styleConst.ACTIVE_LABEL_TRANSLATE_Y : styleConst.INACTIVE_LABEL_TRANSLATE_Y)).current;
+
+ const input = useRef(null);
+ const isLabelActive = useRef(initialActiveLabel);
+
+ useEffect(() => {
+ if (!props.disableKeyboard) {
+ return;
}
+ const appStateSubscription = AppState.addEventListener('change', (nextAppState) => {
+ if (!nextAppState.match(/inactive|background/)) {
+ return;
+ }
+
+ Keyboard.dismiss();
+ });
+
+ return () => {
+ appStateSubscription.remove();
+ };
+ }, [props.disableKeyboard]);
+
+ // AutoFocus which only works on mount:
+ useEffect(() => {
// We are manually managing focus to prevent this issue: https://github.com/Expensify/App/issues/4514
- if (!this.props.autoFocus || !this.input) {
+ if (!props.autoFocus || !input.current) {
return;
}
- if (this.props.shouldDelayFocus) {
- this.focusTimeout = setTimeout(() => this.input.focus(), CONST.ANIMATED_TRANSITION);
+ let focusTimeout;
+ if (props.shouldDelayFocus) {
+ focusTimeout = setTimeout(() => input.current.focus(), CONST.ANIMATED_TRANSITION);
return;
}
- this.input.focus();
- }
+ input.current.focus();
- componentDidUpdate(prevProps) {
- // Activate or deactivate the label when value is changed programmatically from outside
- const inputValue = _.isUndefined(this.props.value) ? this.input.value : this.props.value;
- if ((_.isUndefined(inputValue) || this.state.value === inputValue) && _.isEqual(prevProps.selection, this.props.selection)) {
+ return () => {
+ if (!focusTimeout) {
+ return;
+ }
+ clearTimeout(focusTimeout);
+ };
+ // We only want this to run on mount
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ const animateLabel = useCallback(
+ (translateY, scale) => {
+ Animated.parallel([
+ Animated.spring(labelTranslateY, {
+ toValue: translateY,
+ duration: styleConst.LABEL_ANIMATION_DURATION,
+ useNativeDriver: true,
+ }),
+ Animated.spring(labelScale, {
+ toValue: scale,
+ duration: styleConst.LABEL_ANIMATION_DURATION,
+ useNativeDriver: true,
+ }),
+ ]).start();
+ },
+ [labelScale, labelTranslateY],
+ );
+
+ const activateLabel = useCallback(() => {
+ const value = props.value || '';
+
+ if (value.length < 0 || isLabelActive.current) {
return;
}
- // eslint-disable-next-line react/no-did-update-set-state
- this.setState({value: inputValue, selection: this.props.selection}, () => {
- if (this.state.value) {
- this.activateLabel();
- } else if (!this.state.isFocused) {
- this.deactivateLabel();
- }
- });
+ animateLabel(styleConst.ACTIVE_LABEL_TRANSLATE_Y, styleConst.ACTIVE_LABEL_SCALE);
+ isLabelActive.current = true;
+ }, [animateLabel, props.value]);
- // In some cases, When the value prop is empty, it is not properly updated on the TextInput due to its uncontrolled nature, thus manually clearing the TextInput.
- if (inputValue === '') {
- this.input.clear();
+ const deactivateLabel = useCallback(() => {
+ const value = props.value || '';
+
+ if (props.forceActiveLabel || value.length !== 0 || props.prefixCharacter) {
+ return;
}
- }
- componentWillUnmount() {
- if (this.focusTimeout) {
- clearTimeout(this.focusTimeout);
+ animateLabel(styleConst.INACTIVE_LABEL_TRANSLATE_Y, styleConst.INACTIVE_LABEL_SCALE);
+ isLabelActive.current = false;
+ }, [animateLabel, props.forceActiveLabel, props.prefixCharacter, props.value]);
+
+ const onFocus = (event) => {
+ if (props.onFocus) {
+ props.onFocus(event);
}
+ setIsFocused(true);
+ };
- if (!this.props.disableKeyboard || !this.appStateSubscription) {
- return;
+ const onBlur = (event) => {
+ if (props.onBlur) {
+ props.onBlur(event);
}
+ setIsFocused(false);
- this.appStateSubscription.remove();
- }
+ // If the text has been supplied by Chrome autofill, the value state is not synced with the value
+ // as Chrome doesn't trigger a change event. When there is autofill text, don't deactivate label.
+ if (!isInputAutoFilled(input.current)) {
+ deactivateLabel();
+ }
+ };
- onPress(event) {
- if (this.props.disabled) {
+ const onPress = (event) => {
+ if (props.disabled) {
return;
}
- if (this.props.onPress) {
- this.props.onPress(event);
+ if (props.onPress) {
+ props.onPress(event);
}
if (!event.isDefaultPrevented()) {
- this.input.focus();
+ input.current.focus();
}
- }
+ };
- onFocus(event) {
- if (this.props.onFocus) {
- this.props.onFocus(event);
- }
- this.setState({isFocused: true});
- this.activateLabel();
- }
+ const onLayout = useCallback(
+ (event) => {
+ if (!props.autoGrowHeight && props.multiline) {
+ return;
+ }
+
+ const layout = event.nativeEvent.layout;
+
+ setWidth((prevWidth) => (props.autoGrowHeight ? layout.width : prevWidth));
+ setHeight((prevHeight) => (!props.multiline ? layout.height : prevHeight));
+ },
+ [props.autoGrowHeight, props.multiline],
+ );
+
+ useEffect(() => {
+ // Handle side effects when the value gets changed programatically from the outside
- onBlur(event) {
- if (this.props.onBlur) {
- this.props.onBlur(event);
+ // In some cases, When the value prop is empty, it is not properly updated on the TextInput due to its uncontrolled nature, thus manually clearing the TextInput.
+ if (inputValue === '') {
+ input.current.clear();
}
- this.setState({isFocused: false});
- // If the text has been supplied by Chrome autofill, the value state is not synced with the value
- // as Chrome doesn't trigger a change event. When there is autofill text, don't deactivate label.
- if (!isInputAutoFilled(this.input)) {
- this.deactivateLabel();
+ if (inputValue) {
+ activateLabel();
}
- }
+ }, [activateLabel, inputValue]);
+
+ // We capture whether the input has a value or not in a ref.
+ // It gets updated when the text gets changed.
+ const hasValueRef = useRef(inputValue.length > 0);
+
+ // Activate or deactivate the label when the focus changes:
+ useEffect(() => {
+ // We can't use inputValue here directly, as it might contain
+ // the defaultValue, which doesn't get updated when the text changes.
+ // We can't use props.value either, as it might be undefined.
+ if (hasValueRef.current || isFocused) {
+ activateLabel();
+ } else if (!hasValueRef.current && !isFocused) {
+ deactivateLabel();
+ }
+ }, [activateLabel, deactivateLabel, inputValue, isFocused]);
/**
* Set Value & activateLabel
@@ -147,258 +203,202 @@ class BaseTextInput extends Component {
* @param {String} value
* @memberof BaseTextInput
*/
- setValue(value) {
- if (this.props.onInputChange) {
- this.props.onInputChange(value);
- }
- this.setState({value});
- Str.result(this.props.onChangeText, value);
- this.activateLabel();
- }
-
- activateLabel() {
- if (this.state.value.length < 0 || this.isLabelActive) {
- return;
+ const setValue = (value) => {
+ if (props.onInputChange) {
+ props.onInputChange(value);
}
- this.animateLabel(styleConst.ACTIVE_LABEL_TRANSLATE_Y, styleConst.ACTIVE_LABEL_SCALE);
- this.isLabelActive = true;
- }
-
- deactivateLabel() {
- if (this.props.forceActiveLabel || this.state.value.length !== 0 || this.props.prefixCharacter) {
- return;
- }
-
- this.animateLabel(styleConst.INACTIVE_LABEL_TRANSLATE_Y, styleConst.INACTIVE_LABEL_SCALE);
- this.isLabelActive = false;
- }
-
- dismissKeyboardWhenBackgrounded(nextAppState) {
- if (!nextAppState.match(/inactive|background/)) {
- return;
+ Str.result(props.onChangeText, value);
+ if (value && value.length > 0) {
+ hasValueRef.current = true;
+ activateLabel();
+ } else {
+ hasValueRef.current = false;
}
-
- Keyboard.dismiss();
- }
-
- animateLabel(translateY, scale) {
- Animated.parallel([
- Animated.spring(this.state.labelTranslateY, {
- toValue: translateY,
- duration: styleConst.LABEL_ANIMATION_DURATION,
- useNativeDriver: true,
- }),
- Animated.spring(this.state.labelScale, {
- toValue: scale,
- duration: styleConst.LABEL_ANIMATION_DURATION,
- useNativeDriver: true,
- }),
- ]).start();
- }
-
- togglePasswordVisibility() {
- this.setState((prevState) => ({passwordHidden: !prevState.passwordHidden}));
- }
-
- storePrefixLayoutDimensions(event) {
- this.setState({prefixWidth: Math.abs(event.nativeEvent.layout.width)});
- }
-
- render() {
- // eslint-disable-next-line react/forbid-foreign-prop-types
- const inputProps = _.omit(this.props, _.keys(baseTextInputPropTypes.propTypes));
- const hasLabel = Boolean(this.props.label.length);
- const isEditable = _.isUndefined(this.props.editable) ? !this.props.disabled : this.props.editable;
- const inputHelpText = this.props.errorText || this.props.hint;
- const placeholder = this.props.prefixCharacter || this.state.isFocused || !hasLabel || (hasLabel && this.props.forceActiveLabel) ? this.props.placeholder : null;
- const maxHeight = StyleSheet.flatten(this.props.containerStyles).maxHeight;
- const textInputContainerStyles = _.reduce(
- [
- styles.textInputContainer,
- ...this.props.textInputContainerStyles,
- this.props.autoGrow && StyleUtils.getWidthStyle(this.state.textInputWidth),
- !this.props.hideFocusedState && this.state.isFocused && styles.borderColorFocus,
- (this.props.hasError || this.props.errorText) && styles.borderColorDanger,
- this.props.autoGrowHeight && {scrollPaddingTop: 2 * maxHeight},
- ],
- (finalStyles, s) => ({...finalStyles, ...s}),
- {},
- );
- const isMultiline = this.props.multiline || this.props.autoGrowHeight;
-
- return (
- <>
-
- {
+ setPasswordHidden((prevState) => !prevState.passwordHidden);
+ }, []);
+
+ const storePrefixLayoutDimensions = useCallback((event) => {
+ setPrefixWidth(Math.abs(event.nativeEvent.layout.width));
+ }, []);
+
+ // eslint-disable-next-line react/forbid-foreign-prop-types
+ const inputProps = _.omit(props, _.keys(baseTextInputPropTypes.propTypes));
+ const hasLabel = Boolean(props.label.length);
+ const isEditable = _.isUndefined(props.editable) ? !props.disabled : props.editable;
+ const inputHelpText = props.errorText || props.hint;
+ const placeholder = props.prefixCharacter || isFocused || !hasLabel || (hasLabel && props.forceActiveLabel) ? props.placeholder : null;
+ const maxHeight = StyleSheet.flatten(props.containerStyles).maxHeight;
+ const textInputContainerStyles = StyleSheet.flatten([
+ styles.textInputContainer,
+ ...props.textInputContainerStyles,
+ props.autoGrow && StyleUtils.getWidthStyle(textInputWidth),
+ !props.hideFocusedState && isFocused && styles.borderColorFocus,
+ (props.hasError || props.errorText) && styles.borderColorDanger,
+ props.autoGrowHeight && {scrollPaddingTop: 2 * maxHeight},
+ ]);
+ const isMultiline = props.multiline || props.autoGrowHeight;
+
+ return (
+ <>
+
+
+
- {
- if (!this.props.autoGrowHeight && this.props.multiline) {
- return;
- }
-
- const layout = event.nativeEvent.layout;
-
- this.setState((prevState) => ({
- width: this.props.autoGrowHeight ? layout.width : prevState.width,
- height: !isMultiline ? layout.height : prevState.height,
- }));
- }}
- style={[
- textInputContainerStyles,
-
- // When autoGrow is on and minWidth is not supplied, add a minWidth to allow the input to be focusable.
- this.props.autoGrow && !textInputContainerStyles.minWidth && styles.mnw2,
- ]}
- >
- {hasLabel ? (
- <>
- {/* Adding this background to the label only for multiline text input,
- to prevent text overlapping with label when scrolling */}
- {isMultiline && (
-
- )}
-
+ {/* Adding this background to the label only for multiline text input,
+ to prevent text overlapping with label when scrolling */}
+ {isMultiline && (
+
- >
- ) : null}
-
- {Boolean(this.props.prefixCharacter) && (
-
-
- {this.props.prefixCharacter}
-
-
)}
- {
- if (typeof this.props.innerRef === 'function') {
- this.props.innerRef(ref);
- } else if (this.props.innerRef && _.has(this.props.innerRef, 'current')) {
- this.props.innerRef.current = ref;
- }
- this.input = ref;
- }}
- // eslint-disable-next-line
- {...inputProps}
- autoCorrect={this.props.secureTextEntry ? false : this.props.autoCorrect}
- placeholder={placeholder}
- placeholderTextColor={themeColors.placeholderText}
- underlineColorAndroid="transparent"
- style={[
- styles.flex1,
- styles.w100,
- this.props.inputStyle,
- (!hasLabel || isMultiline) && styles.pv0,
- this.props.prefixCharacter && StyleUtils.getPaddingLeft(this.state.prefixWidth + styles.pl1.paddingLeft),
- this.props.secureTextEntry && styles.secureInput,
-
- // Explicitly remove `lineHeight` from single line inputs so that long text doesn't disappear
- // once it exceeds the input space (See https://github.com/Expensify/App/issues/13802)
- !isMultiline && {height: this.state.height, lineHeight: undefined},
-
- // Stop scrollbar flashing when breaking lines with autoGrowHeight enabled.
- this.props.autoGrowHeight && StyleUtils.getAutoGrowHeightInputStyle(this.state.textInputHeight, maxHeight),
- ]}
- multiline={isMultiline}
- maxLength={this.props.maxLength}
- onFocus={this.onFocus}
- onBlur={this.onBlur}
- onChangeText={this.setValue}
- secureTextEntry={this.state.passwordHidden}
- onPressOut={this.props.onPress}
- showSoftInputOnFocus={!this.props.disableKeyboard}
- keyboardType={getSecureEntryKeyboardType(this.props.keyboardType, this.props.secureTextEntry, this.state.passwordHidden)}
- value={this.state.value}
- selection={this.state.selection}
- editable={isEditable}
- // FormSubmit Enter key handler does not have access to direct props.
- // `dataset.submitOnEnter` is used to indicate that pressing Enter on this input should call the submit callback.
- dataSet={{submitOnEnter: isMultiline && this.props.submitOnEnter}}
+
- {Boolean(this.props.secureTextEntry) && (
- e.preventDefault()}
- accessibilityLabel={this.props.translate('common.visible')}
+ >
+ ) : null}
+
+ {Boolean(props.prefixCharacter) && (
+
+
-
-
- )}
- {!this.props.secureTextEntry && Boolean(this.props.icon) && (
-
-
-
- )}
-
+ {props.prefixCharacter}
+
+
+ )}
+ {
+ if (typeof props.innerRef === 'function') {
+ props.innerRef(ref);
+ } else if (props.innerRef && _.has(props.innerRef, 'current')) {
+ // eslint-disable-next-line no-param-reassign
+ props.innerRef.current = ref;
+ }
+ input.current = ref;
+ }}
+ // eslint-disable-next-line
+ {...inputProps}
+ autoCorrect={props.secureTextEntry ? false : props.autoCorrect}
+ placeholder={placeholder}
+ placeholderTextColor={themeColors.placeholderText}
+ underlineColorAndroid="transparent"
+ style={[
+ styles.flex1,
+ styles.w100,
+ props.inputStyle,
+ (!hasLabel || isMultiline) && styles.pv0,
+ props.prefixCharacter && StyleUtils.getPaddingLeft(prefixWidth + styles.pl1.paddingLeft),
+ props.secureTextEntry && styles.secureInput,
+
+ // Explicitly remove `lineHeight` from single line inputs so that long text doesn't disappear
+ // once it exceeds the input space (See https://github.com/Expensify/App/issues/13802)
+ !isMultiline && {height, lineHeight: undefined},
+
+ // Stop scrollbar flashing when breaking lines with autoGrowHeight enabled.
+ props.autoGrowHeight && StyleUtils.getAutoGrowHeightInputStyle(textInputHeight, maxHeight),
+ ]}
+ multiline={isMultiline}
+ maxLength={props.maxLength}
+ onFocus={onFocus}
+ onBlur={onBlur}
+ onChangeText={setValue}
+ secureTextEntry={passwordHidden}
+ onPressOut={props.onPress}
+ showSoftInputOnFocus={!props.disableKeyboard}
+ keyboardType={getSecureEntryKeyboardType(props.keyboardType, props.secureTextEntry, passwordHidden)}
+ value={props.value}
+ selection={props.selection}
+ editable={isEditable}
+ defaultValue={props.defaultValue}
+ // FormSubmit Enter key handler does not have access to direct props.
+ // `dataset.submitOnEnter` is used to indicate that pressing Enter on this input should call the submit callback.
+ dataSet={{submitOnEnter: isMultiline && props.submitOnEnter}}
+ />
+ {Boolean(props.secureTextEntry) && (
+ e.preventDefault()}
+ accessibilityLabel={props.translate('common.visible')}
+ >
+
+
+ )}
+ {!props.secureTextEntry && Boolean(props.icon) && (
+
+
+
+ )}
-
- {!_.isEmpty(inputHelpText) && (
-
- )}
-
- {/*
- Text input component doesn't support auto grow by default.
- We're using a hidden text input to achieve that.
- This text view is used to calculate width or height of the input value given textStyle in this component.
- This Text component is intentionally positioned out of the screen.
- */}
- {(this.props.autoGrow || this.props.autoGrowHeight) && (
- // Add +2 to width so that the first digit of amount do not cut off on mWeb - https://github.com/Expensify/App/issues/8158.
- this.setState({textInputWidth: e.nativeEvent.layout.width + 2, textInputHeight: e.nativeEvent.layout.height})}
- >
- {this.state.value || this.props.placeholder}
-
+
+
+ {!_.isEmpty(inputHelpText) && (
+
)}
- >
- );
- }
+
+ {/*
+ Text input component doesn't support auto grow by default.
+ We're using a hidden text input to achieve that.
+ This text view is used to calculate width or height of the input value given textStyle in this component.
+ This Text component is intentionally positioned out of the screen.
+ */}
+ {(props.autoGrow || props.autoGrowHeight) && (
+ // Add +2 to width so that the first digit of amount do not cut off on mWeb - https://github.com/Expensify/App/issues/8158.
+ {
+ setTextInputWidth(e.nativeEvent.layout.width + 2);
+ setTextInputHeight(e.nativeEvent.layout.height);
+ }}
+ >
+ {props.value || props.placeholder}
+
+ )}
+ >
+ );
}
+BaseTextInput.displayName = 'BaseTextInput';
BaseTextInput.propTypes = baseTextInputPropTypes.propTypes;
BaseTextInput.defaultProps = baseTextInputPropTypes.defaultProps;
diff --git a/src/components/TextInput/baseTextInputPropTypes.js b/src/components/TextInput/baseTextInputPropTypes.js
index 2e278bab5d698..8a1b05a628c25 100644
--- a/src/components/TextInput/baseTextInputPropTypes.js
+++ b/src/components/TextInput/baseTextInputPropTypes.js
@@ -40,10 +40,18 @@ const propTypes = {
/** Disable the virtual keyboard */
disableKeyboard: PropTypes.bool,
- /** Autogrow input container length based on the entered text */
+ /**
+ * Autogrow input container length based on the entered text.
+ * Note: If you use this prop, the text input has to be controlled
+ * by a value prop.
+ */
autoGrow: PropTypes.bool,
- /** Autogrow input container height based on the entered text */
+ /**
+ * Autogrow input container height based on the entered text
+ * Note: If you use this prop, the text input has to be controlled
+ * by a value prop.
+ */
autoGrowHeight: PropTypes.bool,
/** Hide the focus styles on TextInput */
diff --git a/src/stories/TextInput.stories.js b/src/stories/TextInput.stories.js
index 64329ffed7150..098828c651984 100644
--- a/src/stories/TextInput.stories.js
+++ b/src/stories/TextInput.stories.js
@@ -60,32 +60,6 @@ PlaceholderInput.args = {
placeholder: 'My placeholder text',
};
-const AutoGrowInput = Template.bind({});
-AutoGrowInput.args = {
- label: 'Autogrow input',
- name: 'AutoGrow',
- placeholder: 'My placeholder text',
- autoGrow: true,
- textInputContainerStyles: [
- {
- minWidth: 150,
- },
- ],
-};
-
-const AutoGrowHeightInput = Template.bind({});
-AutoGrowHeightInput.args = {
- label: 'Autogrowheight input',
- name: 'AutoGrowHeight',
- placeholder: 'My placeholder text',
- autoGrowHeight: true,
- textInputContainerStyles: [
- {
- maxHeight: 115,
- },
- ],
-};
-
const PrefixedInput = Template.bind({});
PrefixedInput.args = {
label: 'Prefixed input',
@@ -126,5 +100,50 @@ HintAndErrorInput.args = {
hint: 'Type "Oops!" to see the error',
};
+// To use autoGrow we need to control the TextInput's value
+function AutoGrowSupportInput(args) {
+ const [value, setValue] = useState(args.value || '');
+ React.useEffect(() => {
+ setValue(args.value || '');
+ }, [args.value]);
+
+ return (
+
+ );
+}
+
+const AutoGrowInput = AutoGrowSupportInput.bind({});
+AutoGrowInput.args = {
+ label: 'Autogrow input',
+ name: 'AutoGrow',
+ placeholder: 'My placeholder text',
+ autoGrow: true,
+ textInputContainerStyles: [
+ {
+ minWidth: 150,
+ maxWidth: 500,
+ },
+ ],
+ value: '',
+};
+
+const AutoGrowHeightInput = AutoGrowSupportInput.bind({});
+AutoGrowHeightInput.args = {
+ label: 'Autogrowheight input',
+ name: 'AutoGrowHeight',
+ placeholder: 'My placeholder text',
+ autoGrowHeight: true,
+ textInputContainerStyles: [
+ {
+ maxHeight: 115,
+ },
+ ],
+};
+
export default story;
export {AutoFocus, DefaultInput, DefaultValueInput, ErrorInput, ForceActiveLabel, PlaceholderInput, AutoGrowInput, AutoGrowHeightInput, PrefixedInput, MaxLengthInput, HintAndErrorInput};