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

[FE] Input 컴포넌트 관련한 기능 수정, useDynamic-* 훅 관련 오류 수정, 네이밍 통일, 의미를 명확하게 담도록 네이밍 수정 #183

Merged
merged 46 commits into from
Aug 4, 2024
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
7ff3e92
chore: hdesign v0.1.37 배포
Todari Jul 30, 2024
add91ca
remove: 사용하지 않는 코드 제거
Todari Jul 30, 2024
d1570e9
feat: v0.1.44 배포
Todari Jul 31, 2024
7c3f5f7
refactor: event/create 페이지 input 작동 방식 변경 및 새로운 input 적용
Todari Jul 31, 2024
9dcb6ec
design: Input outline boxshadow로 변경 및 우선순위 조정
Todari Jul 31, 2024
b44b5e0
feat: 초기 멤버 설정 modal 내의 input 동작 방식 변경
Todari Jul 31, 2024
9d4d9c9
feat: LabelInput, LabelGroupInput 등 다양한 Input Component 생성
Todari Jul 31, 2024
28c1759
fix: 공백이 존재하는 input 제거
soi-ha Aug 1, 2024
537905b
style: lint 적용
soi-ha Aug 1, 2024
976d797
style: lint 적용
Todari Aug 1, 2024
7701d7b
chore: v0.1.47 배포
Todari Aug 1, 2024
261742d
feat: v0.1.49 배포
Todari Aug 1, 2024
014363e
feat: v0.1.51 배포
Todari Aug 1, 2024
6547b10
feat: DynamicInput 수정 및 유효성 검사 추가
soi-ha Aug 1, 2024
5aca949
chore: 충돌 병합
pakxe Aug 2, 2024
35be82d
feat: v0.1.52 디자인시스템 배포
Todari Aug 2, 2024
35ff11f
fix: 다음 입력을 기대하는 인풋이 뜨도록 하고 중간 인풋을 삭제했을 때 input 엘리먼트 자체가 사라지도록 구현
pakxe Aug 2, 2024
4b16db6
chore: 병합
pakxe Aug 2, 2024
c729152
chore: 충볼 병합합
pakxe Aug 2, 2024
b86d143
rename: SerPurchase -> AddBillActionListModalContent로 이름 변경
pakxe Aug 2, 2024
0df0ce2
rename: UpdateParticipants -> AddMemberActionListModalContent 로 이름 변경
pakxe Aug 2, 2024
9a40e2c
rename: SetPurchase -> SetActionListModal 로 이름 변경
pakxe Aug 2, 2024
e91c87f
rename: SetInitialParticipants -> SetInitialMemberListModal 로 이름 변경
pakxe Aug 2, 2024
f767e00
feat: enter가 눌렸을 때 실행할 로직을 훅 안으로 이동
pakxe Aug 2, 2024
895b8f2
fix: 0, 1번만 있을 때 2개의 빈 잇풋 엘리먼트가 남아버리는 문제 해결, handleBlur -> deleteEmpt…
pakxe Aug 2, 2024
2f9d538
feat: canSubmit 상태를 관리하는 로직을 함수로 분리
pakxe Aug 2, 2024
a1af31c
refactor: 현재 변화중인 targetInput을 가져오는 반복되는 로직을 분리, 선언되어있는 함수 순서를 useEff…
pakxe Aug 2, 2024
ec0a285
rename: pages안의 파일에 전부 -Page 를 붙여 컴포넌트 성격을 잘 드러낼 수 있도록 이름 변경
pakxe Aug 2, 2024
a4942e8
fix: 인덱스틀 사용해 인풋 쌍을 관리하도록 수정. 인덱스를 사용함에 따라 모든 함수도 인덱스를 사용하도록 수정
pakxe Aug 2, 2024
d2419dd
feat: pair당 하나의 인덱스를 갖지만, input element는 두 개이므로 정확한 input element특정을 …
pakxe Aug 2, 2024
0a6c763
rename: 해당 파일에 이미 도메인과 깊게 얽힌 코드가 내장되어 있으므로 의미를 더 드러내는 이름으로 변경. useDyn…
pakxe Aug 2, 2024
da8070f
chore: Modal 폴더 내의 이름 변경으로 인해 생긴 import 변동 사항
pakxe Aug 2, 2024
7fb5f31
fix: 함수가 state를 사용하지 않도록 수정
pakxe Aug 2, 2024
0c31cab
feat: type.d.ts에 있던 내용을 옮겨옴
pakxe Aug 2, 2024
e00b2b6
chore: import 경로 수정
pakxe Aug 2, 2024
9be9ed0
chore: package-lock 업데이트
pakxe Aug 2, 2024
8dca946
design: Input에 css props 적용
pakxe Aug 4, 2024
fcc18c3
refactor: 리뷰 반영
Todari Aug 4, 2024
d8468cb
refactor: 리뷰 반영
Todari Aug 4, 2024
5a10d81
style: lint 적용
Todari Aug 4, 2024
2a848b9
Merge branch 'fe-dev' into feature/#182
Todari Aug 4, 2024
332f11f
fix: Toast 2번 import 되던 오류 수정
Todari Aug 4, 2024
4e32498
chore: yml workflow 수정
Todari Aug 4, 2024
95b0391
chore: client pr workflow 수정
Todari Aug 4, 2024
c16864c
chore: client workflow 수정
Todari Aug 4, 2024
48d1355
chore: workflow 수정
Todari Aug 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions HDesign/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion HDesign/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "haengdong-design",
"version": "0.1.36",
"version": "0.1.52",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

벌써 버전이 52까지;;;; 와우

"description": "",
"main": "./dist/index.js",
"module": "./dist/index.js",
Expand Down
31 changes: 22 additions & 9 deletions HDesign/src/components/Input/Input.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
import type {Meta, StoryObj} from '@storybook/react';

import React, {useEffect, useState} from 'react';

import Input from '@components/Input/Input';

const meta = {
title: 'Components/Input',
component: Input,
tags: ['autodocs'],
parameters: {
// layout: 'centered',
},
argTypes: {
value: {
description: '',
control: {type: 'text'},
},
inputType: {
// TODO: (@cookie) 스토리북 라디오버튼 보이도록 설정해야 함
control: {type: 'radio'},
},
},
args: {
disabled: false,
placeholder: 'placeholder',
},
} satisfies Meta<typeof Input>;
Expand All @@ -29,4 +23,23 @@ export default meta;

type Story = StoryObj<typeof meta>;

export const Playground: Story = {};
export const Playground: Story = {
render: ({...args}) => {
const [value, setValue] = useState('');
const [isError, setIsError] = useState(false);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.value.length < 4) {
setValue(event.target.value);
setIsError(false);
} else {
event.target.value = value;
setIsError(true);
}
};
const handleBlur = () => {
console.log('blur');
};

return <Input value={value} onChange={e => handleChange(e)} isError={isError} onBlur={handleBlur} {...args} />;
},
};
11 changes: 7 additions & 4 deletions HDesign/src/components/Input/Input.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {Theme} from '@theme/theme.type';

import {InputType} from './Input.type';

const inputBoxBackgroundColorByInputType = (theme: Theme, inputType: InputType = 'input') => {
const getBackgroundColorStyle = (theme: Theme, inputType: InputType = 'input') => {
switch (inputType) {
case 'input':
return theme.colors.lightGrayContainer;
Expand All @@ -17,6 +17,9 @@ const inputBoxBackgroundColorByInputType = (theme: Theme, inputType: InputType =
}
};

const getBorderStyle = (isFocus: boolean, theme: Theme, isError?: boolean) =>
isError ? `0 0 0 1px ${theme.colors.error} inset` : isFocus ? `0 0 0 1px ${theme.colors.primary} inset` : 'none';

export const inputBoxStyle = (
theme: Theme,
inputType: InputType = 'input',
Expand All @@ -26,13 +29,13 @@ export const inputBoxStyle = (
css({
display: 'flex',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flex 컴포넌트를 사용해봐도 좋을 것 같아요!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

props로 속성을 넘기는게 좋을 것 같았는데 적용해야할 스타일이 너무 길어지면 결국 css를 먹이는게 나을 것 같기도 하고용.. 지금 생각이 난 방법은 플렉스 방향, justify, align 정도만 props로 받고 그 외는 css로 받으면 어떤가 싶기도 한데요. 음.. 어떻게 해야할지 고민 해볼게요..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아래 코드처럼 작성하는건 어떻게 생각하시나용? 위에서 말한 방법과 용어만 다를 뿐 방법은 똑같습니다.

const Flex = ({ align, direction, split, children, style }) => {
  return (
    <FlexContainer
      style={{ alignItems: align_items(align), flexDirection: flex_direction(direction), ...style }}
    >

justifyContent: 'space-between',

gap: '1rem',
padding: '0.75rem 1rem',
borderRadius: '1rem',
backgroundColor: inputBoxBackgroundColorByInputType(theme, inputType),
backgroundColor: getBackgroundColorStyle(theme, inputType),

boxSizing: 'border-box',
outline: isFocus ? `1px solid ${theme.colors.primary}` : isError ? `1px solid ${theme.colors.error}` : 'none',
boxShadow: getBorderStyle(isFocus, theme, isError),
});

export const inputStyle = (theme: Theme) =>
Expand Down
33 changes: 20 additions & 13 deletions HDesign/src/components/Input/Input.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
/** @jsxImportSource @emotion/react */
import React, {forwardRef, useImperativeHandle, useRef, useState} from 'react';
import React, {forwardRef, useImperativeHandle, useRef} from 'react';

import IconButton from '@components/IconButton/IconButton';
import {InputProps} from '@components/Input/Input.type';
import {inputBoxStyle, inputStyle} from '@components/Input/Input.style';
import {useInput} from '@components/Input/useInput';
import {useTheme} from '@/theme/HDesignProvider';

import {useTheme} from '@theme/HDesignProvider';
import IconButton from '../IconButton/IconButton';

import {useInput} from './useInput';
import {InputProps} from './Input.type';
import {inputBoxStyle, inputStyle} from './Input.style';

export const Input: React.FC<InputProps> = forwardRef<HTMLInputElement, InputProps>(function Input(
{value: propsValue, onChange, inputType, isError, ...htmlProps}: InputProps,
{value: propsValue, onChange, onFocus, onBlur, inputType, isError, placeholder, ...htmlProps}: InputProps,
ref,
) {
const {theme} = useTheme();
const inputRef = useRef<HTMLInputElement>(null);

useImperativeHandle(ref, () => inputRef.current!);

const {value, hasFocus, handleChange, handleClickDelete, toggleFocus} = useInput({propsValue, onChange, inputRef});
const inputRef = useRef<HTMLInputElement>(null);
const {value, handleChange, hasFocus, handleClickDelete, handleBlur, handleFocus, handleKeyDown} = useInput({
propsValue,
onChange,
onBlur,
onFocus,
inputRef,
});

return (
<div css={inputBoxStyle(theme, inputType, hasFocus, isError)}>
Expand All @@ -26,8 +31,10 @@ export const Input: React.FC<InputProps> = forwardRef<HTMLInputElement, InputPro
ref={inputRef}
value={value}
onChange={handleChange}
onFocus={toggleFocus}
onBlur={toggleFocus}
onBlur={handleBlur}
onFocus={handleFocus}
placeholder={inputRef.current === document.activeElement ? '' : placeholder}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

activeElement라는 것이 있는지 처음 알았어요👍👍

onKeyDown={handleKeyDown}
{...htmlProps}
/>
{value && hasFocus && <IconButton iconType="inputDelete" onClick={handleClickDelete} />}
Expand Down
57 changes: 42 additions & 15 deletions HDesign/src/components/Input/useInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,66 @@ import {RefObject, useEffect, useState} from 'react';
interface UseInputProps<T> {
propsValue: T;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

훅 내부에서 as 키워드를 사용해 강제로 string으로 타입 변환을 시켜주고 있는 것 같아요. 그래서 T type을 string으로 고정시켜서 제네릭을 없애는 방향으로 가는 것은 어떨까요?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋은 것 같아요~

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generic을 없애는 방법 말고, 전체적으로 제너릭을 받을 수 있는 코드로 수정했습니다~

onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;
onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;
inputRef: RefObject<HTMLInputElement>;
autoFocus?: boolean;
}

export const useInput = <T>({propsValue, onChange, inputRef}: UseInputProps<T>) => {
const [value, setValue] = useState(propsValue || '');
const [hasFocus, setHasFocus] = useState(false);
export const useInput = <T>({propsValue, onChange, onBlur, onFocus, inputRef, autoFocus}: UseInputProps<T>) => {
const [value, setValue] = useState<T>(propsValue);
const [hasFocus, setHasFocus] = useState(inputRef.current === document.activeElement);

useEffect(() => {
setValue(propsValue || '');
}, [propsValue]);
setHasFocus(inputRef.current === document.activeElement);
}, []);

const handleClickDelete = () => {
setValue('');

if (inputRef.current) {
inputRef.current.focus();
}
useEffect(() => {
setValue(propsValue);
}, [value]);

const handleClickDelete = (event: React.MouseEvent) => {
event.preventDefault();
setValue('' as T);
if (onChange) {
onChange({target: {value: ''}} as React.ChangeEvent<HTMLInputElement>);
}
if (inputRef.current) {
inputRef.current.focus();
}
};

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
setValue(e.target.value as T);
if (onChange) {
onChange(e);
}
};

const toggleFocus = () => {
setHasFocus(!hasFocus);
const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
setHasFocus(false);
if (onBlur) {
onBlur(e);
}
};

const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {
setHasFocus(true);
if (onFocus) {
onFocus(e);
}
};

const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.nativeEvent.isComposing) return;

if (event.key === 'Enter' || event.key === 'Escape') {
setHasFocus(false);
if (inputRef.current) {
inputRef.current.blur();
}
}
};

return {value, hasFocus, handleChange, handleClickDelete, toggleFocus};
return {value, handleChange, hasFocus, handleClickDelete, handleBlur, handleFocus, handleKeyDown};
};
62 changes: 62 additions & 0 deletions HDesign/src/components/LabelGroupInput/Element.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/** @jsxImportSource @emotion/react */

import {forwardRef, useEffect, useImperativeHandle, useRef, useState} from 'react';

import Input from '../Input/Input';

import {ElementProps} from './Element.type';
import {useGroupInputContext} from './GroupInputContext';

const Element: React.FC<ElementProps> = forwardRef<HTMLInputElement, ElementProps>(function Element(
{elementKey, value: propsValue, onChange, onBlur, onFocus, isError, ...htmlProps}: ElementProps,

ref,
) {
useImperativeHandle(ref, () => inputRef.current!);
const inputRef = useRef<HTMLInputElement>(null);
const {setHasAnyFocus, values, setValues, hasAnyErrors, setHasAnyErrors} = useGroupInputContext();

useEffect(() => {
setValues({...values, [elementKey]: `${propsValue}`});
}, [propsValue]);

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = e.target.value;
setValues({...values, [elementKey]: newValue});
if (onChange) {
onChange(e);
}
};

useEffect(() => {
setHasAnyErrors({...hasAnyErrors, [elementKey]: isError ?? false});
}, [isError]);

const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
setHasAnyFocus(false);
if (onBlur) {
onBlur(e);
}
};

const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {
setHasAnyFocus(true);
if (onFocus) {
onFocus(e);
}
};

return (
<Input
ref={inputRef}
isError={isError}
value={propsValue}
onChange={handleChange}
onBlur={handleBlur}
onFocus={handleFocus}
{...htmlProps}
/>
);
});

export default Element;
10 changes: 10 additions & 0 deletions HDesign/src/components/LabelGroupInput/Element.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface ElementStyleProps {}

export interface ElementCustomProps {
elementKey: string;
isError?: boolean;
}

export type ElementOptionProps = ElementStyleProps & ElementCustomProps;

export type ElementProps = React.ComponentProps<'input'> & ElementOptionProps;
32 changes: 32 additions & 0 deletions HDesign/src/components/LabelGroupInput/GroupInputContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React, {createContext, PropsWithChildren, useContext, useState} from 'react';

interface GroupInputContextProps {
hasAnyFocus: boolean;
setHasAnyFocus: React.Dispatch<React.SetStateAction<boolean>>;
values: {[key: string]: string};
setValues: React.Dispatch<React.SetStateAction<{[key: string]: string}>>;
hasAnyErrors: {[key: string]: boolean};
setHasAnyErrors: React.Dispatch<React.SetStateAction<{[key: string]: boolean}>>;
}

const GroupInputContext = createContext<GroupInputContextProps | undefined>(undefined);

export const useGroupInputContext = () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사용하는 측에서는 그룹인풋을 사용하는 바텀시트안에서 provider를 감싸면 되는 구조인거죠?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아뇨~ GroupInput 자체가 Provider를 포함하고 있어서 사용측에서 감쌀 필요가 없습니다
LabelGroupInput.tsx 파일을 참고하세요
image

const context = useContext(GroupInputContext);
if (!context) {
throw new Error('useGroupInputContext must be used within an GroupInputProvider');
}
return context;
};

export const GroupInputProvider: React.FC<PropsWithChildren> = ({children}: React.PropsWithChildren) => {
const [hasAnyFocus, setHasAnyFocus] = useState(false);
const [values, setValues] = useState<{[key: string]: string}>({});
const [hasAnyErrors, setHasAnyErrors] = useState<{[key: string]: boolean}>({});

return (
<GroupInputContext.Provider value={{hasAnyFocus, setHasAnyFocus, values, setValues, hasAnyErrors, setHasAnyErrors}}>
{children}
</GroupInputContext.Provider>
);
};
Loading
Loading