UNPKG

maisonsport-common-ui

Version:

Suite of styled-components to be consumed by the React-Native App and by the Web (via React-Native for Web)

228 lines (213 loc) 5.88 kB
import React, { createRef, forwardRef, useEffect, useState, } from 'react'; import PropTypes from 'prop-types'; import styled, { ThemeProvider } from 'styled-components/native'; import { color, layout, space, typography, fontFamily, } from 'styled-system'; import composeRefs from '@seznam/compose-react-refs'; import Box from '../../atoms/Box'; import Touchable from '../../atoms/Touchable'; import Text from '../../atoms/Text'; import Theme from '../../theme'; export const inputParentID = 'input-parent'; export const errorMessageTestID = 'input-error-message'; export const placeholderErrorIconID = 'input-placeholder-error-icon'; export const inputContainerID = 'input-container'; export const inputIconID = 'input-icon'; export const inputID = 'text-input'; export const inputColors = { error: 'red', default: 'lightGrey' }; export const inputMargins = { error: 1, default: 0 }; const StyledTextInput = styled.TextInput.attrs(({ theme }) => ({ placeholderTextColor: theme.colors.darkGrey, }))` ${color} ${typography} ${layout} ${space} ${fontFamily} `; StyledTextInput.defaultProps = { color: 'black', backgroundColor: inputColors.default, fontFamily: 0, fontWeight: 3, fontSize: 2, height: '100%', flex: 1, width: 1, placeholderStyle: { font: 0, }, }; const TextInput = forwardRef(({ noWrapTheme, icon, iconRight, error, errorMessages, ErrorIconComponent, marginBottom, marginTop, marginLeft, marginRight, backgroundColor, ...props }, externalRef) => { const inputRef = createRef(); const [componentHasError, setComponentHasError] = useState(false); useEffect(() => { setComponentHasError(error || !!errorMessages.filter(Boolean).length); }, [error, errorMessages.filter(Boolean).length]); if (backgroundColor) inputColors.default = backgroundColor; function getErrorMessages() { return errorMessages.filter(Boolean).map((message) => ( <Box key={message} flexDirection="row" marginBottom={1} alignItems="center"> <Box width="7.5px" height="7.5px" backgroundColor={inputColors.error} borderRadius="7.5px" /> <Text testID={errorMessageTestID} noWrapTheme variant="p" color={inputColors.error} fontWeight={4} marginLeft={1} textAlign="left" > {message} </Text> </Box> )); } function getRightSideIcon() { const Container = ({ onPress = () => {}, children }) => ( <Touchable noWrapTheme accessible={false} backgroundColor={inputColors.default} width="8%" height={1} justifyContent="center" alignItems="center" onPress={onPress} activeOpacity={1} tabIndex={-1} style={{ cursor: 'text' }} > {children} </Touchable> ); let rightSideIcon = null; if (componentHasError) { rightSideIcon = ( <Container onPress={() => inputRef?.current?.focus()} > <ErrorIconComponent width={15} height={15} stroke={Theme.colors[inputColors.error]} /> </Container> ); } else if (iconRight) { rightSideIcon = ( <Container pointerEvents="box-none"> {iconRight} </Container> ); } return rightSideIcon; } return ( <ThemeProvider theme={Theme}> <Box testID={`${props.placeholder || ''}_${inputParentID}`} noWrapTheme marginTop={marginTop} marginRight={marginRight} marginBottom={marginBottom} marginLeft={marginLeft} > <Box testID={inputContainerID} noWrapTheme height={props.height || 'textInputHeight'} backgroundColor={inputColors.default} borderColor={componentHasError ? inputColors.error : inputColors.default} borderWidth="medium" borderRadius={4} flexDirection="row" justifyContent="space-between" alignItems="center" marginBottom={componentHasError ? inputMargins.error : inputMargins.default} > {icon && ( <Box testID={inputIconID} noWrapTheme accessible={false} width="8%" height={1} backgroundColor={inputColors.default} justifyContent="center" alignItems="center" activeOpacity={1} tabIndex={-1} > {icon} </Box> )} <StyledTextInput testID={inputID} paddingLeft={1} autoCapitalize={(props?.type && ['email'].includes(props.type)) ? 'none' : 'sentences'} // eslint-disable-next-line react/jsx-props-no-spreading {...props} ref={composeRefs(inputRef, externalRef)} /> {getRightSideIcon()} </Box> {componentHasError && ( <Box marginY={1}> {getErrorMessages()} </Box> )} </Box> </ThemeProvider> ); }); TextInput.defaultProps = { type: null, icon: null, iconRight: null, error: false, errorMessages: [], ErrorIconComponent: () => ( <Text fontWeight="black" color={inputColors.error} testID={placeholderErrorIconID} > X </Text> ), backgroundColor: null, }; TextInput.propTypes = { type: PropTypes.string, icon: PropTypes.node, iconRight: PropTypes.node, error: PropTypes.bool, // eslint-disable-next-line react/forbid-prop-types errorMessages: PropTypes.array, ErrorIconComponent: PropTypes.func, backgroundColor: PropTypes.string, }; TextInput.displayName = 'TextInput'; export default TextInput;