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
JavaScript
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;