-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: 모임 가입문제 유무 라디오 카드 구현 - tailwind config에 check content 추가 * feat: 모임 가입문제 스텝 구현 - 모임 가입문제 유무 fieldset - 모임 가입문제,정답 fieldset * feat: 모임 가입문제 스텝 스토리 작성 * refactor: JoinTypeFieldset 합성 컴포넌트로 리팩토링 * refactor: PropsWithChildren 타입 사용하도록 수정 * refactor: FormValue export명 수정
- Loading branch information
Showing
8 changed files
with
296 additions
and
0 deletions.
There are no files selected for viewing
46 changes: 46 additions & 0 deletions
46
src/stories/bookGroup/create/steps/SelectJoinTypeStep.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { Meta, StoryObj } from '@storybook/react'; | ||
import { FormProvider, useForm } from 'react-hook-form'; | ||
import { appLayoutMeta } from '@/stories/meta'; | ||
|
||
import { | ||
SelectJoinTypeStep, | ||
SelectJoinTypeStepFormValues, | ||
} from '@/v1/bookGroup/create/steps/SelectJoinTypeStep'; | ||
|
||
const meta: Meta<typeof SelectJoinTypeStep> = { | ||
title: 'bookGroup/create/steps/SelectJoinTypeStep', | ||
component: SelectJoinTypeStep, | ||
...appLayoutMeta, | ||
}; | ||
|
||
export default meta; | ||
|
||
type Story = StoryObj<typeof SelectJoinTypeStep>; | ||
|
||
const RenderSelectJoinTypeStep = () => { | ||
const methods = useForm<SelectJoinTypeStepFormValues>({ | ||
defaultValues: { | ||
hasJoinPasswd: 'false', | ||
}, | ||
mode: 'all', | ||
}); | ||
|
||
const onSubmit = () => { | ||
const { hasJoinPasswd, joinPasswd, joinQuestion } = methods.getValues(); | ||
alert( | ||
`가입 문제 유무: ${hasJoinPasswd}\n가입 문제: ${joinQuestion}\n정답: ${joinPasswd}` | ||
); | ||
}; | ||
|
||
return ( | ||
<FormProvider {...methods}> | ||
<form> | ||
<SelectJoinTypeStep onSubmit={onSubmit} /> | ||
</form> | ||
</FormProvider> | ||
); | ||
}; | ||
|
||
export const Default: Story = { | ||
render: () => <RenderSelectJoinTypeStep />, | ||
}; |
50 changes: 50 additions & 0 deletions
50
src/v1/bookGroup/create/steps/SelectJoinTypeStep/SelectJoinTypeStep.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { useFormContext } from 'react-hook-form'; | ||
|
||
import BottomActionButton from '@/v1/base/BottomActionButton'; | ||
|
||
import { JoinPasswordFieldset, JoinTypeFieldset } from './fields'; | ||
|
||
interface MoveFunnelStepProps { | ||
onPrevStep?: () => void; | ||
onNextStep?: () => void; | ||
onSubmit?: () => void; | ||
} | ||
|
||
export type JoinTypeStepFormValues = { | ||
hasJoinPasswd: 'true' | 'false'; | ||
joinQuestion?: string; | ||
joinPasswd?: string; | ||
}; | ||
|
||
export type JoinTypeStepFieldName = keyof JoinTypeStepFormValues; | ||
export type JoinTypeStepFieldProp = { name: JoinTypeStepFieldName }; | ||
|
||
const SelectJoinTypeStep = ({ onSubmit }: MoveFunnelStepProps) => { | ||
const { handleSubmit } = useFormContext<JoinTypeStepFormValues>(); | ||
|
||
return ( | ||
<article> | ||
<h2 className="mb-[3rem] text-lg font-bold">가입은 어떻게 받을까요?</h2> | ||
|
||
<section className="flex flex-col gap-[2rem]"> | ||
<JoinTypeFieldset> | ||
<JoinTypeFieldset.RadioCardField name="hasJoinPasswd" /> | ||
</JoinTypeFieldset> | ||
|
||
<JoinPasswordFieldset joinTypeFieldName="hasJoinPasswd"> | ||
<JoinPasswordFieldset.QuestionField name="joinQuestion" /> | ||
<JoinPasswordFieldset.AnswerField name="joinPasswd" /> | ||
</JoinPasswordFieldset> | ||
</section> | ||
|
||
<BottomActionButton | ||
type="submit" | ||
onClick={onSubmit && handleSubmit(onSubmit)} | ||
> | ||
독서모임 만들기 | ||
</BottomActionButton> | ||
</article> | ||
); | ||
}; | ||
|
||
export default SelectJoinTypeStep; |
119 changes: 119 additions & 0 deletions
119
src/v1/bookGroup/create/steps/SelectJoinTypeStep/fields/JoinPasswordFieldset.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import { PropsWithChildren } from 'react'; | ||
import { useFormContext, useWatch } from 'react-hook-form'; | ||
|
||
import ErrorMessage from '@/v1/base/ErrorMessage'; | ||
import Input from '@/v1/base/Input'; | ||
import InputLength from '@/v1/base/InputLength'; | ||
|
||
import { | ||
JoinTypeStepFieldName, | ||
JoinTypeStepFormValues, | ||
JoinTypeStepFieldProp, | ||
} from '../SelectJoinTypeStep'; | ||
|
||
type JoinPasswordFieldsetProps = { | ||
joinTypeFieldName: JoinTypeStepFieldName; | ||
}; | ||
|
||
const JoinPasswordFieldset = ({ | ||
joinTypeFieldName, | ||
children, | ||
}: PropsWithChildren<JoinPasswordFieldsetProps>) => { | ||
const { control } = useFormContext<JoinTypeStepFormValues>(); | ||
const hasJoinPassword = useWatch({ control, name: joinTypeFieldName }); | ||
|
||
const shouldRender = hasJoinPassword === 'true'; | ||
|
||
return ( | ||
<> | ||
{shouldRender && ( | ||
<fieldset className="flex flex-col gap-[1.5rem]">{children}</fieldset> | ||
)} | ||
</> | ||
); | ||
}; | ||
|
||
const JoinQuestionField = ({ name }: JoinTypeStepFieldProp) => { | ||
const { | ||
register, | ||
control, | ||
formState: { errors }, | ||
} = useFormContext<JoinTypeStepFormValues>(); | ||
|
||
const joinQuestion = useWatch({ control, name }); | ||
|
||
const questionLength = joinQuestion?.length; | ||
const error = errors[name]; | ||
|
||
return ( | ||
<label className="flex flex-col gap-[0.5rem]"> | ||
<p>가입 문제</p> | ||
<Input | ||
placeholder="모임에 가입하기 위한 적절한 문제를 작성해주세요" | ||
{...register(name, { | ||
required: '1 ~ 30글자의 가입 문제가 필요해요', | ||
maxLength: { | ||
value: 30, | ||
message: '1 ~ 30글자의 가입 문제를 작성해주세요', | ||
}, | ||
})} | ||
error={!!error} | ||
/> | ||
<div className="flex flex-row-reverse justify-between"> | ||
<InputLength | ||
currentLength={questionLength} | ||
isError={!!error} | ||
maxLength={30} | ||
/> | ||
<ErrorMessage>{error?.message}</ErrorMessage> | ||
</div> | ||
</label> | ||
); | ||
}; | ||
|
||
const JoinAnswerField = ({ name }: JoinTypeStepFieldProp) => { | ||
const { | ||
register, | ||
control, | ||
formState: { errors }, | ||
} = useFormContext<JoinTypeStepFormValues>(); | ||
|
||
const joinPasswd = useWatch({ control, name }); | ||
|
||
const passwordLength = joinPasswd?.length; | ||
const error = errors[name]; | ||
|
||
return ( | ||
<label className="flex flex-col gap-[0.5rem]"> | ||
<p>정답</p> | ||
<Input | ||
placeholder="띄어쓰기 없이 정답을 작성해주세요" | ||
{...register(name, { | ||
required: '띄어쓰기 없이 10글자 이하의 정답이 필요해요', | ||
maxLength: { | ||
value: 10, | ||
message: '띄어쓰기 없이 10글자 이하의 정답을 작성해주세요,', | ||
}, | ||
pattern: { | ||
value: /^[ㄱ-ㅎㅏ-ㅣ가-힣a-zA-Z0-9]+$/, | ||
message: '띄어쓰기 없이 한글, 영어, 숫자만 입력할 수 있어요', | ||
}, | ||
})} | ||
error={!!error} | ||
/> | ||
<div className="flex flex-row-reverse justify-between"> | ||
<InputLength | ||
currentLength={passwordLength} | ||
isError={!!error} | ||
maxLength={10} | ||
/> | ||
<ErrorMessage>{error?.message}</ErrorMessage> | ||
</div> | ||
</label> | ||
); | ||
}; | ||
|
||
JoinPasswordFieldset.QuestionField = JoinQuestionField; | ||
JoinPasswordFieldset.AnswerField = JoinAnswerField; | ||
|
||
export default JoinPasswordFieldset; |
37 changes: 37 additions & 0 deletions
37
src/v1/bookGroup/create/steps/SelectJoinTypeStep/fields/JoinTypeFieldset.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { useFormContext } from 'react-hook-form'; | ||
|
||
import { | ||
JoinTypeStepFormValues, | ||
JoinTypeStepFieldProp, | ||
} from '../SelectJoinTypeStep'; | ||
|
||
import JoinTypeRadioCard from './JoinTypeRadioCard'; | ||
|
||
const JoinTypeFieldset = ({ children }: { children?: React.ReactNode }) => { | ||
return <fieldset className="flex flex-col gap-[1rem]">{children}</fieldset>; | ||
}; | ||
|
||
const RadioCardField = ({ name }: JoinTypeStepFieldProp) => { | ||
const { register } = useFormContext<JoinTypeStepFormValues>(); | ||
|
||
return ( | ||
<> | ||
<JoinTypeRadioCard | ||
{...register(name)} | ||
id="no-password" | ||
value="false" | ||
label="문제 없이 가입할 수 있어요" | ||
/> | ||
<JoinTypeRadioCard | ||
{...register(name)} | ||
id="has-password" | ||
value="true" | ||
label="문제를 맞춰야 모임에 가입할 수 있어요" | ||
/> | ||
</> | ||
); | ||
}; | ||
|
||
JoinTypeFieldset.RadioCardField = RadioCardField; | ||
|
||
export default JoinTypeFieldset; |
39 changes: 39 additions & 0 deletions
39
src/v1/bookGroup/create/steps/SelectJoinTypeStep/fields/JoinTypeRadioCard.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { forwardRef, InputHTMLAttributes, Ref } from 'react'; | ||
|
||
const BASE_CLASSES = | ||
'flex h-[8rem] w-full cursor-pointer items-center justify-between rounded-[0.5rem] border-[0.1rem] bg-white px-[2.5rem] text-black-600 text-md'; | ||
|
||
const JoinTypeRadioCard = ( | ||
{ | ||
id, | ||
value, | ||
label, | ||
...props | ||
}: Omit<InputHTMLAttributes<HTMLInputElement>, 'className' | 'type'> & { | ||
label?: string; | ||
}, | ||
ref: Ref<HTMLInputElement> | ||
) => { | ||
const inputId = id || 'radio-card'; | ||
|
||
return ( | ||
<div> | ||
<input | ||
type="radio" | ||
id={inputId} | ||
value={value} | ||
className="peer hidden" | ||
ref={ref} | ||
{...props} | ||
/> | ||
<label | ||
className={`${BASE_CLASSES} after:h-[2.4rem] peer-checked:border-main-900 peer-checked:bg-main-900/[0.05] peer-checked:text-[#FF8B00] peer-checked:after:content-check`} | ||
htmlFor={inputId} | ||
> | ||
<p>{label}</p> | ||
</label> | ||
</div> | ||
); | ||
}; | ||
|
||
export default forwardRef(JoinTypeRadioCard); |
2 changes: 2 additions & 0 deletions
2
src/v1/bookGroup/create/steps/SelectJoinTypeStep/fields/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { default as JoinTypeFieldset } from './JoinTypeFieldset'; | ||
export { default as JoinPasswordFieldset } from './JoinPasswordFieldset'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export type { JoinTypeStepFormValues as SelectJoinTypeStepFormValues } from './SelectJoinTypeStep'; | ||
export { default as SelectJoinTypeStep } from './SelectJoinTypeStep'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters