Skip to content

Commit

Permalink
Merge pull request #7984 from parasharrajat/textinput-final
Browse files Browse the repository at this point in the history
Rename TextInputFocusable to `Composer` ⌨️  & add Stories For TextInputs and fix bugs
  • Loading branch information
NikkiWines authored Mar 11, 2022
2 parents f916215 + ee1ecba commit a613b2b
Show file tree
Hide file tree
Showing 16 changed files with 255 additions and 63 deletions.
2 changes: 2 additions & 0 deletions .storybook/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import Onyx from 'react-native-onyx';
import '../assets/css/fonts.css';
import ComposeProviders from '../src/components/ComposeProviders';
import HTMLEngineProvider from '../src/components/HTMLEngineProvider';
import OnyxProvider from '../src/components/OnyxProvider';
import {LocaleContextProvider} from '../src/components/withLocalize';
import ONYXKEYS from '../src/ONYXKEYS';
Expand All @@ -16,6 +17,7 @@ const decorators = [
components={[
OnyxProvider,
LocaleContextProvider,
HTMLEngineProvider,
]}
>
<Story />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const defaultProps = {
forwardedRef: null,
};

class TextInputFocusable extends React.Component {
class Composer extends React.Component {
componentDidMount() {
// This callback prop is used by the parent component using the constructor to
// get a ref to the inner textInput element e.g. if we do
Expand Down Expand Up @@ -76,11 +76,11 @@ class TextInputFocusable extends React.Component {
}
}

TextInputFocusable.displayName = 'TextInputFocusable';
TextInputFocusable.propTypes = propTypes;
TextInputFocusable.defaultProps = defaultProps;
Composer.displayName = 'Composer';
Composer.propTypes = propTypes;
Composer.defaultProps = defaultProps;

export default React.forwardRef((props, ref) => (
/* eslint-disable-next-line react/jsx-props-no-spreading */
<TextInputFocusable {...props} forwardedRef={ref} />
<Composer {...props} forwardedRef={ref} />
));
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const defaultProps = {
},
};

class TextInputFocusable extends React.Component {
class Composer extends React.Component {
componentDidMount() {
// This callback prop is used by the parent component using the constructor to
// get a ref to the inner textInput element e.g. if we do
Expand Down Expand Up @@ -88,10 +88,10 @@ class TextInputFocusable extends React.Component {
}
}

TextInputFocusable.propTypes = propTypes;
TextInputFocusable.defaultProps = defaultProps;
Composer.propTypes = propTypes;
Composer.defaultProps = defaultProps;

export default React.forwardRef((props, ref) => (
/* eslint-disable-next-line react/jsx-props-no-spreading */
<TextInputFocusable {...props} forwardedRef={ref} />
<Composer {...props} forwardedRef={ref} />
));
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,10 @@ const IMAGE_EXTENSIONS = {
};

/**
* Enable Markdown parsing.
* On web we like to have the Text Input field always focused so the user can easily type a new chat
*/
class TextInputFocusable extends React.Component {
class Composer extends React.Component {
constructor(props) {
super(props);

Expand Down Expand Up @@ -197,7 +198,6 @@ class TextInputFocusable extends React.Component {
* Handles all types of drag-N-drop events on the composer
*
* @param {Object} e native Event
* @memberof TextInputFocusable
*/
dragNDropListener(e) {
let isOriginComposer = false;
Expand Down Expand Up @@ -237,7 +237,6 @@ class TextInputFocusable extends React.Component {
* Manually place the pasted HTML into Composer
*
* @param {String} html - pasted HTML
* @memberof TextInputFocusable
*/
handlePastedHTML(html) {
const parser = new ExpensiMark();
Expand Down Expand Up @@ -285,14 +284,14 @@ class TextInputFocusable extends React.Component {
.then((x) => {
const extension = IMAGE_EXTENSIONS[x.type];
if (!extension) {
throw new Error(this.props.translate('textInputFocusable.noExtentionFoundForMimeType'));
throw new Error(this.props.translate('composer.noExtentionFoundForMimeType'));
}

return new File([x], `pasted_image.${extension}`, {});
})
.then(this.props.onPasteFile)
.catch(() => {
const errorDesc = this.props.translate('textInputFocusable.problemGettingImageYouPasted');
const errorDesc = this.props.translate('composer.problemGettingImageYouPasted');
Growl.error(errorDesc);

/*
Expand Down Expand Up @@ -366,10 +365,10 @@ class TextInputFocusable extends React.Component {
}
}

TextInputFocusable.propTypes = propTypes;
TextInputFocusable.defaultProps = defaultProps;
Composer.propTypes = propTypes;
Composer.defaultProps = defaultProps;

export default withLocalize(React.forwardRef((props, ref) => (
/* eslint-disable-next-line react/jsx-props-no-spreading */
<TextInputFocusable {...props} forwardedRef={ref} />
<Composer {...props} forwardedRef={ref} />
)));
4 changes: 2 additions & 2 deletions src/components/EmojiPicker/EmojiPickerMenu/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import themeColors from '../../../styles/themes/default';
import emojis from '../../../../assets/emojis';
import EmojiPickerMenuItem from '../EmojiPickerMenuItem';
import Text from '../../Text';
import TextInputFocusable from '../../TextInputFocusable';
import Composer from '../../Composer';
import withWindowDimensions, {windowDimensionsPropTypes} from '../../withWindowDimensions';
import withLocalize, {withLocalizePropTypes} from '../../withLocalize';
import compose from '../../../libs/compose';
Expand Down Expand Up @@ -432,7 +432,7 @@ class EmojiPickerMenu extends Component {
>
{!this.props.isSmallScreenWidth && (
<View style={[styles.pt4, styles.ph4, styles.pb1]}>
<TextInputFocusable
<Composer
textAlignVertical="top"
placeholder={this.props.translate('common.search')}
placeholderTextColor={themeColors.textSupporting}
Expand Down
45 changes: 27 additions & 18 deletions src/components/TextInput/BaseTextInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ class BaseTextInput extends Component {
constructor(props) {
super(props);

this.value = props.value || props.defaultValue || '';
const activeLabel = props.forceActiveLabel || this.value.length > 0 || props.prefixCharacter;
const value = props.value || props.defaultValue || '';
const activeLabel = props.forceActiveLabel || value.length > 0 || props.prefixCharacter;

this.state = {
isFocused: false,
Expand All @@ -29,6 +29,7 @@ class BaseTextInput extends Component {
passwordHidden: props.secureTextEntry,
textInputWidth: 0,
prefixWidth: 0,
value,
};

this.input = null;
Expand Down Expand Up @@ -61,12 +62,13 @@ class BaseTextInput extends Component {
componentDidUpdate() {
// Activate or deactivate the label when value is changed programmatically from outside
// Only update when value prop is provided
if (this.props.value === undefined || this.value === this.props.value) {
if (this.props.value === undefined || this.state.value === this.props.value) {
return;
}

this.value = this.props.value;
this.input.setNativeProps({text: this.value});
// eslint-disable-next-line react/no-did-update-set-state
this.setState({value: this.props.value});
this.input.setNativeProps({text: this.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 (this.props.value === '') {
Expand Down Expand Up @@ -124,13 +126,13 @@ class BaseTextInput extends Component {
if (this.props.onInputChange) {
this.props.onInputChange(value);
}
this.value = value;
this.setState({value});
Str.result(this.props.onChangeText, value);
this.activateLabel();
}

activateLabel() {
if (this.value.length < 0 || this.isLabelActive) {
if (this.state.value.length < 0 || this.isLabelActive) {
return;
}

Expand All @@ -142,7 +144,7 @@ class BaseTextInput extends Component {
}

deactivateLabel() {
if (this.props.forceActiveLabel || this.value.length !== 0 || this.props.prefixCharacter) {
if (this.props.forceActiveLabel || this.state.value.length !== 0 || this.props.prefixCharacter) {
return;
}

Expand Down Expand Up @@ -187,6 +189,14 @@ class BaseTextInput extends Component {
const hasLabel = Boolean(this.props.label.length);
const inputHelpText = this.props.errorText || this.props.hint;
const formHelpStyles = this.props.errorText ? styles.formError : styles.formHelp;
const placeholder = (this.props.prefixCharacter || this.state.isFocused || !hasLabel || (hasLabel && this.props.forceActiveLabel)) ? this.props.placeholder : null;
const textInputContainerStyles = _.reduce([
styles.textInputContainer,
...this.props.textInputContainerStyles,
this.props.autoGrow && StyleUtils.getAutoGrowTextInputStyle(this.state.textInputWidth),
!this.props.hideFocusedState && this.state.isFocused && styles.borderColorFocus,
(this.props.hasError || this.props.errorText) && styles.borderColorDanger,
], (finalStyles, s) => ({...finalStyles, ...s}), {});

return (
<>
Expand All @@ -200,11 +210,10 @@ class BaseTextInput extends Component {
<TouchableWithoutFeedback onPress={this.onPress} focusable={false}>
<View
style={[
styles.textInputContainer,
...this.props.textInputContainerStyles,
this.props.autoGrow && StyleUtils.getAutoGrowTextInputStyle(this.state.textInputWidth),
!this.props.hideFocusedState && this.state.isFocused && styles.borderColorFocus,
(this.props.hasError || this.props.errorText) && styles.borderColorDanger,
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 ? (
Expand Down Expand Up @@ -241,8 +250,8 @@ class BaseTextInput extends Component {
}}
// eslint-disable-next-line
{...inputProps}
defaultValue={this.value}
placeholder={(this.props.prefixCharacter || this.state.isFocused || !this.props.label) ? this.props.placeholder : null}
defaultValue={this.state.value}
placeholder={placeholder}
placeholderTextColor={themeColors.placeholderText}
underlineColorAndroid="transparent"
style={[
Expand Down Expand Up @@ -292,7 +301,7 @@ class BaseTextInput extends Component {
)}
{!_.isNull(this.props.maxLength) && (
<Text style={[formHelpStyles, styles.flex1, styles.textAlignRight]}>
{this.value.length}
{this.state.value.length}
/
{this.props.maxLength}
</Text>
Expand All @@ -308,10 +317,10 @@ class BaseTextInput extends Component {
*/}
{this.props.autoGrow && (
<Text
style={[...this.props.inputStyle, styles.hiddenElementOutsideOfWindow]}
style={[...this.props.inputStyle, styles.hiddenElementOutsideOfWindow, styles.visibilityHidden]}
onLayout={e => this.setState({textInputWidth: e.nativeEvent.layout.width})}
>
{this.props.value || this.props.placeholder}
{this.state.value || this.props.placeholder}
</Text>
)}
</>
Expand Down
1 change: 0 additions & 1 deletion src/components/TextInput/baseTextInputPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ const propTypes = {
prefixCharacter: PropTypes.string,

/** Form props */

/** Indicates that the input is being used with the Form component */
isFormInput: PropTypes.bool,

Expand Down
2 changes: 1 addition & 1 deletion src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export default {
attachmentTooLarge: 'Attachment too large',
sizeExceeded: 'Attachment size is larger than 50 MB limit.',
},
textInputFocusable: {
composer: {
noExtentionFoundForMimeType: 'No extension found for mime type',
problemGettingImageYouPasted: 'There was a problem getting the image you pasted',
},
Expand Down
2 changes: 1 addition & 1 deletion src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export default {
attachmentTooLarge: 'Archivo adjunto demasiado grande',
sizeExceeded: 'El archivo adjunto supera el límite de 50 MB.',
},
textInputFocusable: {
composer: {
noExtentionFoundForMimeType: 'No se encontró una extension para este tipo de contenido',
problemGettingImageYouPasted: 'Ha ocurrido un problema al obtener la imagen que has pegado',
},
Expand Down
4 changes: 2 additions & 2 deletions src/pages/home/report/ReportActionCompose.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {withOnyx} from 'react-native-onyx';
import lodashIntersection from 'lodash/intersection';
import styles from '../../../styles/styles';
import themeColors from '../../../styles/themes/default';
import TextInputFocusable from '../../../components/TextInputFocusable';
import Composer from '../../../components/Composer';
import ONYXKEYS from '../../../ONYXKEYS';
import Icon from '../../../components/Icon';
import * as Expensicons from '../../../components/Icon/Expensicons';
Expand Down Expand Up @@ -514,7 +514,7 @@ class ReportActionCompose extends React.Component {
</>
)}
</AttachmentPicker>
<TextInputFocusable
<Composer
autoFocus={this.shouldFocusInputOnScreenFocus || _.size(this.props.reportActions) === 1}
multiline
ref={this.setTextInputRef}
Expand Down
4 changes: 2 additions & 2 deletions src/pages/home/report/ReportActionItemMessageEdit.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import _ from 'underscore';
import ExpensiMark from 'expensify-common/lib/ExpensiMark';
import reportActionPropTypes from './reportActionPropTypes';
import styles from '../../../styles/styles';
import TextInputFocusable from '../../../components/TextInputFocusable';
import Composer from '../../../components/Composer';
import * as Report from '../../../libs/actions/Report';
import * as ReportScrollManager from '../../../libs/ReportScrollManager';
import toggleReportActionComposeView from '../../../libs/toggleReportActionComposeView';
Expand Down Expand Up @@ -158,7 +158,7 @@ class ReportActionItemMessageEdit extends React.Component {
return (
<View style={styles.chatItemMessage}>
<View style={[styles.chatItemComposeBox, styles.flexRow, styles.chatItemComposeBoxColor]}>
<TextInputFocusable
<Composer
multiline
ref={(el) => {
this.textInput = el;
Expand Down
Loading

0 comments on commit a613b2b

Please sign in to comment.