Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed Input styles for Storybook web #21

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
38,765 changes: 685 additions & 38,080 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"@storybook/react-native-server": "^6.5.4",
"@testing-library/react-native": "^7.2.0",
"@types/jest": "^25.2.3",
"@types/react": "^17.0.2",
"@types/react-native": "^0.63.2",
"@types/react-test-renderer": "^16.9.2",
"@typescript-eslint/eslint-plugin": "^5.45.0",
Expand Down Expand Up @@ -103,6 +104,9 @@
],
"*.{js,jsx,ts,tsx}": [
"npm run test:commit"
],
"*.{ts,tsx}": [
"tsc --noEmit"
]
},
"dependencies": {
Expand Down
22 changes: 12 additions & 10 deletions src/components/Input/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,29 +50,29 @@ describe('Input', () => {

expect(mockSetInputState).toHaveBeenCalledWith('focus');
});
it('as it has valid label, placeholder and user focuses out of the input without value', () => {
it('as it has valid label, placeholder and user finishes typing on input without value', () => {
const mockSetInputState = jest.fn();
useStateSpy.mockReturnValueOnce(['', mockSetInputState]);

const InputComp = create(<Input label="Test" placeholder="Test" />).root;

const TextInputComp = InputComp.findByType(TextInput);
const {onBlur} = TextInputComp.props;
const {onEndEditing} = TextInputComp.props;

onBlur();
onEndEditing({nativeEvent: {}});

expect(mockSetInputState).toHaveBeenCalledWith('incomplete');
});
it('as it has valid label, placeholder and user focuses out of the input with value', () => {
it('as it has valid label, placeholder and user finishes typing on input with value', () => {
const mockSetInputState = jest.fn();
useStateSpy.mockReturnValueOnce(['', mockSetInputState]);

const InputComp = create(<Input label="Test" placeholder="Test" value="Test" />).root;

const TextInputComp = InputComp.findByType(TextInput);
const {onBlur} = TextInputComp.props;
const {onEndEditing} = TextInputComp.props;

onBlur();
onEndEditing({nativeEvent: {text: 'valid value'}});

expect(mockSetInputState).toHaveBeenCalledWith('complete');
});
Expand All @@ -83,11 +83,12 @@ describe('Input', () => {
const InputComp = create(<Input label="Test" placeholder="Test" />).root;

const TextInputComp = InputComp.findByType(TextInput);
const {onChange} = TextInputComp.props;
const {onChange, onEndEditing} = TextInputComp.props;

onEndEditing({nativeEvent: {text: 'valid value'}});
onChange();

expect(mockSetInputState).toBeCalledTimes(0);
expect(mockSetInputState).toBeCalledTimes(1);
});
it('as it has valid label, placeholder and user submits', () => {
const mockSetInputState = jest.fn();
Expand All @@ -96,11 +97,12 @@ describe('Input', () => {
const InputComp = create(<Input label="Test" placeholder="Test" />).root;

const TextInputComp = InputComp.findByType(TextInput);
const {onSubmitEditing} = TextInputComp.props;
const {onSubmitEditing, onEndEditing} = TextInputComp.props;

onEndEditing({nativeEvent: {text: 'valid value'}});
onSubmitEditing();

expect(mockSetInputState).toBeCalledTimes(0);
expect(mockSetInputState).toBeCalledTimes(1);
});
});
});
37 changes: 26 additions & 11 deletions src/components/Input/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import React, {LegacyRef, useEffect, useState} from 'react';
import {TextInput, StyleSheet, View, Text, KeyboardType, TextStyle} from 'react-native';
import {
TextInput,
StyleSheet,
View,
Text,
KeyboardType,
TextStyle,
NativeSyntheticEvent,
TextInputEndEditingEventData,
} from 'react-native';
import {
Status,
getBorderColor,
getInputColor,
getInputInitialState,
getLabelColor,
getStatusMessageColor,
Expand Down Expand Up @@ -40,6 +48,8 @@ interface InputProps {
style?: TextStyle;
}

type InputState = 'incomplete' | 'complete' | 'focus';

const Input = React.forwardRef<TextInput, InputProps>(
(
{
Expand All @@ -62,10 +72,10 @@ const Input = React.forwardRef<TextInput, InputProps>(
}: InputProps,
ref: LegacyRef<TextInput>
) => {
const [inputState, setInputState] = useState('incomplete');
const [inputState, setInputState] = useState<InputState>('incomplete');

useEffect(() => {
setInputState(getInputInitialState(value.toString()));
setInputState(getInputInitialState(value?.toString()));
}, [value]);

if (!label || !placeholder) {
Expand All @@ -77,8 +87,12 @@ const Input = React.forwardRef<TextInput, InputProps>(
return onFocus();
};

const onBlurHandler = () => {
if (value) {
const onEndEditingHandler = ({
nativeEvent,
}: NativeSyntheticEvent<TextInputEndEditingEventData>) => {
const {text = ''} = nativeEvent;

if (text) {
setInputState('complete');
return onBlur();
}
Expand All @@ -88,7 +102,7 @@ const Input = React.forwardRef<TextInput, InputProps>(
};

const hasMessage = !!statusMessage;
const isLabelVisible = inputState === 'focus' && !value;
const isLabelVisible = !disabled && !readOnly && (inputState !== 'incomplete' || hasMessage);

const validBorderColor = getBorderColor({inputState, hasMessage, status, inputColor});
const validLabelColor = getLabelColor({
Expand All @@ -97,8 +111,8 @@ const Input = React.forwardRef<TextInput, InputProps>(
inputColor,
inputState,
statusMessage,
status,
});
const validInputTextColor = getInputColor({hasMessage, inputState, status, valueColor});
const validStatusMessageColor = getStatusMessageColor(status);

const styles = StyleSheet.create({
Expand All @@ -117,10 +131,10 @@ const Input = React.forwardRef<TextInput, InputProps>(
letterSpacing: 0,
lineHeight: 19,
position: 'absolute',
bottom: raiseLabel({disabled, hasMessage, inputState}) ? 25 : 5,
bottom: raiseLabel({disabled, hasMessage, inputState}) ? 25 : 0,
},
input: {
color: validInputTextColor,
color: valueColor,
fontSize: 16,
letterSpacing: 0,
lineHeight: 19,
Expand All @@ -140,13 +154,14 @@ const Input = React.forwardRef<TextInput, InputProps>(
style={[styles.input, style]}
ref={ref}
onFocus={onFocusHandler}
onBlur={onBlurHandler}
onEndEditing={onEndEditingHandler}
onChange={onChange}
onSubmitEditing={onSubmitEditing}
placeholder={placeholder}
editable={!(readOnly || disabled)}
selectionColor={inputColor}
keyboardType={keyboardType}
value={value?.toString()}
{...props}
/>
</View>
Expand Down
93 changes: 56 additions & 37 deletions src/components/Input/utils/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
getInputInitialState,
getBorderColor,
getLabelColor,
getInputColor,
raiseLabel,
showStatusMessage,
getStatusMessageColor,
Expand Down Expand Up @@ -68,77 +67,97 @@ describe('getLabelColor', () => {
const disabled = true;
const readOnly = false;
const inputState = 'inputState';
const result = getLabelColor({disabled, readOnly, inputColor, inputState, statusMessage});
const status = 'success';
const result = getLabelColor({
disabled,
readOnly,
inputColor,
inputState,
statusMessage,
status,
});
expect(result).toBe(palette.grey[500]);
});

it('should return palette.grey[500] when readOnly is true', () => {
const disabled = false;
const readOnly = true;
const inputState = 'inputState';
const result = getLabelColor({disabled, readOnly, inputColor, inputState, statusMessage});
const status = 'success';
const result = getLabelColor({
disabled,
readOnly,
inputColor,
inputState,
statusMessage,
status,
});
expect(result).toBe(palette.grey[500]);
});

it('should return inputColor when inputState is "focus"', () => {
const disabled = false;
const readOnly = false;
const inputState = 'focus';
const result = getLabelColor({disabled, readOnly, inputColor, inputState, statusMessage});
const status = 'success';
const result = getLabelColor({
disabled,
readOnly,
inputColor,
inputState,
statusMessage,
status,
});
expect(result).toBe(inputColor);
});

it('should return palette.primary.main when statusMessage is not empty', () => {
it('should return palette.status.main when statusMessage is not empty', () => {
const disabled = false;
const readOnly = false;
const inputState = 'notFocus';
const result = getLabelColor({disabled, readOnly, inputColor, inputState, statusMessage});
expect(result).toBe(palette.primary.main);
const status = 'success';
const result = getLabelColor({
disabled,
readOnly,
inputColor,
inputState,
statusMessage,
status,
});
expect(result).toBe(palette[status].main);
});

it('should return palette.grey[500] when statusMessage is empty', () => {
it('should return palette.grey[500] when statusMessage is not empty but there is no main on palette', () => {
const disabled = false;
const readOnly = false;
const inputState = 'notFocus';
const emptyStatusMessage = '';
const status = 'environment';
const result = getLabelColor({
disabled,
readOnly,
inputColor,
inputState,
statusMessage: emptyStatusMessage,
statusMessage,
status,
});
expect(result).toBe(palette.grey[500]);
});
});

describe('getInputColor', () => {
const valueColor = 'valueColor';

it('should return colorPalette.main when hasMessage is true and inputState is not "focus" and "main" property exists in colorPalette', () => {
const hasMessage = true;
const inputState = 'notFocus';
const status: Status = 'error';
const result = getInputColor({hasMessage, inputState, status, valueColor});
expect(result).toBe(palette.error.main);
});

it('should return palette.error.main when hasMessage is true and inputState is not "focus" and "main" property does not exist in colorPalette', () => {
const hasMessage = true;
it('should return palette.grey[500] when statusMessage is empty', () => {
const disabled = false;
const readOnly = false;
const inputState = 'notFocus';
const status: Status = 'grey';
const result = getInputColor({hasMessage, inputState, status, valueColor});
expect(result).toBe(palette.error.main);
});

it('should return valueColor when hasMessage is false or inputState is "focus"', () => {
const hasMessage = false;
const inputState = 'focus';
const status: Status = 'error';
const result1 = getInputColor({hasMessage, inputState, status, valueColor});
const result2 = getInputColor({hasMessage: true, inputState: 'notFocus', status, valueColor});
expect(result1).toBe(valueColor);
expect(result2).toBe(palette.error.main);
const emptyStatusMessage = '';
const status = 'success';
const result = getLabelColor({
disabled,
readOnly,
inputColor,
inputState,
statusMessage: emptyStatusMessage,
status,
});
expect(result).toBe(palette.grey[500]);
});
});

Expand Down
18 changes: 2 additions & 16 deletions src/components/Input/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,7 @@ interface getLabelColorProps {
inputColor: string;
inputState: string;
statusMessage: string;
}

interface getInputColorProps {
hasMessage: boolean;
inputState: string;
status: Status;
valueColor: string;
}

interface raiseLabelProps {
Expand Down Expand Up @@ -63,6 +57,7 @@ export const getLabelColor = ({
inputColor,
inputState,
statusMessage,
status,
}: getLabelColorProps) => {
if (disabled || readOnly) {
return palette.grey[500];
Expand All @@ -73,22 +68,13 @@ export const getLabelColor = ({
}

if (statusMessage) {
return palette.primary.main;
}

return palette.grey[500];
};

export const getInputColor = ({hasMessage, inputState, status, valueColor}: getInputColorProps) => {
if (hasMessage && inputState !== 'focus') {
const colorPalette = palette[status];
if ('main' in colorPalette) {
return colorPalette.main;
}
return palette.error.main;
}

return valueColor;
return palette.grey[500];
};

export const raiseLabel = ({disabled, hasMessage, inputState}: raiseLabelProps) =>
Expand Down
3 changes: 2 additions & 1 deletion src/components/Text/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, {ReactElement} from 'react';
import {
StyleProp,
StyleSheet,
Text as TextComponent,
TextProps as TextComponentProps,
Expand All @@ -8,7 +9,7 @@ import {

interface TextProps extends TextComponentProps {
children?: ReactElement | string;
style?: TextStyle | TextStyle[];
style?: StyleProp<TextStyle>;
}

const Text = ({children, style, ...props}: TextProps) => {
Expand Down