diff --git a/src/components/CheckboxWithLabel.js b/src/components/CheckboxWithLabel.js index 8ee5c6712a6e..96f85fb25d91 100644 --- a/src/components/CheckboxWithLabel.js +++ b/src/components/CheckboxWithLabel.js @@ -1,11 +1,10 @@ import React from 'react'; import PropTypes from 'prop-types'; import {View, TouchableOpacity} from 'react-native'; -import _ from 'underscore'; import styles from '../styles/styles'; import Checkbox from './Checkbox'; import Text from './Text'; -import InlineErrorText from './InlineErrorText'; +import FormHelpMessage from './FormHelpMessage'; const requiredPropsCheck = (props) => { if (!props.label && !props.LabelComponent) { @@ -77,8 +76,6 @@ class CheckboxWithLabel extends React.Component { this.isChecked = props.value || props.defaultValue || props.isChecked; this.LabelComponent = props.LabelComponent; - this.defaultStyles = [styles.flexRow, styles.alignItemsCenter]; - this.wrapperStyles = _.isArray(props.style) ? [...this.defaultStyles, ...props.style] : [...this.defaultStyles, props.style]; this.toggleCheckbox = this.toggleCheckbox.bind(this); } @@ -90,8 +87,8 @@ class CheckboxWithLabel extends React.Component { render() { return ( - <> - + + )} - - {this.props.errorText} - - + + ); } } diff --git a/src/components/FormAlertWrapper.js b/src/components/FormAlertWrapper.js index 921a76c771fc..ed6c16810485 100644 --- a/src/components/FormAlertWrapper.js +++ b/src/components/FormAlertWrapper.js @@ -3,16 +3,14 @@ import {View} from 'react-native'; import PropTypes from 'prop-types'; import React from 'react'; import {withNetwork} from './OnyxProvider'; -import Icon from './Icon'; -import * as Expensicons from './Icon/Expensicons'; import RenderHTML from './RenderHTML'; -import TextLink from './TextLink'; import Text from './Text'; -import colors from '../styles/colors'; +import TextLink from './TextLink'; import compose from '../libs/compose'; import networkPropTypes from './networkPropTypes'; import styles from '../styles/styles'; import withLocalize, {withLocalizePropTypes} from './withLocalize'; +import FormHelpMessage from './FormHelpMessage'; const propTypes = { /** Wrapped child components */ @@ -52,38 +50,35 @@ const defaultProps = { // // This component takes other components as a child prop. It will then render any wrapped components as a function using "render props", // and passes it a (bool) isOffline parameter. Child components can then use the isOffline variable to determine offline behavior. -const FormAlertWrapper = props => ( - - {props.isAlertVisible && ( - - - - {!_.isEmpty(props.message) && props.isMessageHtml && ${props.message}`} />} - - {!_.isEmpty(props.message) && !props.isMessageHtml && {props.message}} - - {_.isEmpty(props.message) && ( - <> - - {`${props.translate('common.please')} `} - - - {props.translate('common.fixTheErrors')} - - - {` ${props.translate('common.inTheFormBeforeContinuing')}.`} - - - )} - - - )} - {props.children(props.network.isOffline)} - -); +const FormAlertWrapper = (props) => { + let children; + if (_.isEmpty(props.message)) { + children = ( + + {`${props.translate('common.please')} `} + + {props.translate('common.fixTheErrors')} + + {` ${props.translate('common.inTheFormBeforeContinuing')}.`} + + ); + } else if (props.isMessageHtml) { + children = ${props.message}`} />; + } + return ( + + {props.isAlertVisible && ( + + {children} + + )} + {props.children(props.network.isOffline)} + + ); +}; FormAlertWrapper.propTypes = propTypes; FormAlertWrapper.defaultProps = defaultProps; diff --git a/src/components/FormHelpMessage.js b/src/components/FormHelpMessage.js new file mode 100644 index 000000000000..3a78b279c962 --- /dev/null +++ b/src/components/FormHelpMessage.js @@ -0,0 +1,56 @@ +import React from 'react'; +import _ from 'underscore'; +import PropTypes from 'prop-types'; +import {View} from 'react-native'; +import Icon from './Icon'; +import * as Expensicons from './Icon/Expensicons'; +import Text from './Text'; +import colors from '../styles/colors'; +import styles from '../styles/styles'; +import stylePropTypes from '../styles/stylePropTypes'; + +const propTypes = { + /** Error or hint text. Ignored when children is not empty */ + message: PropTypes.string, + + /** Children to render next to dot indicator */ + children: PropTypes.node, + + /** Indicates whether to show error or hint */ + isError: PropTypes.bool, + + /** Container style props */ + style: stylePropTypes, +}; + +const defaultProps = { + message: '', + children: null, + isError: true, + style: [], +}; + +const FormHelpMessage = (props) => { + if (_.isEmpty(props.message) && _.isEmpty(props.children)) { + return null; + } + + return ( + + {props.isError && } + + {props.children || ( + + {props.message} + + )} + + + ); +}; + +FormHelpMessage.propTypes = propTypes; +FormHelpMessage.defaultProps = defaultProps; +FormHelpMessage.displayName = 'FormHelpMessage'; + +export default FormHelpMessage; diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js index 459e904a0bba..c644db26d153 100755 --- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js +++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js @@ -29,7 +29,7 @@ const customHTMLElementModels = { }), 'muted-text': defaultHTMLElementModels.div.extend({ tagName: 'muted-text', - mixedUAStyles: styles.mutedTextLabel, + mixedUAStyles: {...styles.formError, ...styles.mb0}, }), comment: defaultHTMLElementModels.div.extend({ tagName: 'comment', diff --git a/src/components/Picker/index.js b/src/components/Picker/index.js index 4dc676966827..d9b6c00a2635 100644 --- a/src/components/Picker/index.js +++ b/src/components/Picker/index.js @@ -5,7 +5,7 @@ import PropTypes from 'prop-types'; import BasePicker from './BasePicker'; import Text from '../Text'; import styles from '../../styles/styles'; -import InlineErrorText from '../InlineErrorText'; +import FormHelpMessage from '../FormHelpMessage'; const propTypes = { /** Picker label */ @@ -95,9 +95,7 @@ class Picker extends PureComponent { onInputChange={this.onInputChange} /> - - {this.props.errorText} - + ); } diff --git a/src/components/RadioButtonWithLabel.js b/src/components/RadioButtonWithLabel.js index 630981970c2c..7665cb3f9159 100644 --- a/src/components/RadioButtonWithLabel.js +++ b/src/components/RadioButtonWithLabel.js @@ -5,7 +5,7 @@ import _ from 'underscore'; import styles from '../styles/styles'; import RadioButton from './RadioButton'; import Text from './Text'; -import InlineErrorText from './InlineErrorText'; +import FormHelpMessage from './FormHelpMessage'; const propTypes = { /** Whether the radioButton is checked */ @@ -75,9 +75,7 @@ const RadioButtonWithLabel = (props) => { {LabelComponent && ()} - - {props.errorText} - + ); }; diff --git a/src/components/TextInput/BaseTextInput.js b/src/components/TextInput/BaseTextInput.js index 2f970bbb12e4..fe29ff79bf25 100644 --- a/src/components/TextInput/BaseTextInput.js +++ b/src/components/TextInput/BaseTextInput.js @@ -18,6 +18,7 @@ import variables from '../../styles/variables'; import Checkbox from '../Checkbox'; import getSecureEntryKeyboardType from '../../libs/getSecureEntryKeyboardType'; import CONST from '../../CONST'; +import FormHelpMessage from '../FormHelpMessage'; class BaseTextInput extends Component { constructor(props) { @@ -198,7 +199,6 @@ class BaseTextInput extends Component { const inputProps = _.omit(this.props, _.keys(baseTextInputPropTypes.propTypes)); 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, @@ -305,9 +305,7 @@ class BaseTextInput extends Component { {!_.isEmpty(inputHelpText) && ( - - {inputHelpText} - + )} {/* diff --git a/src/stories/FormAlertWithSubmitButton.stories.js b/src/stories/FormAlertWithSubmitButton.stories.js index 9747c447cf0d..eb58b7b59322 100644 --- a/src/stories/FormAlertWithSubmitButton.stories.js +++ b/src/stories/FormAlertWithSubmitButton.stories.js @@ -17,7 +17,9 @@ const Template = args => ; // Arguments can be passed to the component by binding // See: https://storybook.js.org/docs/react/writing-stories/introduction#using-args const Default = Template.bind({}); -Default.args = { +const HtmlError = Template.bind({}); + +const defaultArgs = { isAlertVisible: true, onSubmit: () => {}, buttonText: 'Submit', @@ -26,7 +28,12 @@ Default.args = { }, }; +Default.args = defaultArgs; +const html = 'This is a test. None of

these strings

should display as
HTML
.'; +HtmlError.args = {...defaultArgs, isMessageHtml: true, message: html}; + export default story; export { Default, + HtmlError, };