Skip to content

Commit

Permalink
feat: ProfileRegistrationForm 컴포넌트 개발
Browse files Browse the repository at this point in the history
  • Loading branch information
ppyom committed Nov 28, 2024
1 parent 5a071c5 commit 2adc06d
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 0 deletions.
74 changes: 74 additions & 0 deletions src/components/organisms/ProfileRegistrationForm/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { Controller, useForm } from "react-hook-form";
import { Image, ImageUpload, Text, TextButton } from "components/atoms";
import { LabeledInput } from "components/molecules";
import type { IUser } from "types";
import { ProfileImageWrapper, ProfileRegistrationFormWrapper } from "./styled";

interface IProfileRegistrationFormProps {
/** Submit 이벤트 발생 시 실행할 함수 */
onSubmit: (data: IUser) => void;
/** User 정보 (가입 => undefined, 프로필 수정 => 로그인한 유저) */
user?: IUser;
}

export const ProfileRegistrationForm = ({
onSubmit,
user,
}: IProfileRegistrationFormProps) => {
const { control, handleSubmit, setValue } = useForm<IUser>({
mode: "onChange",
defaultValues: {
nickname: user?.nickname || "",
profile: user?.profile,
},
});
return (
<ProfileRegistrationFormWrapper onSubmit={handleSubmit(onSubmit)}>
<Controller
name="profile"
control={control}
render={({ field: { value } }) => (
<ProfileImageWrapper>
<Image type="round" url={value} alt="유저 프로필 사진" />
<ImageUpload
onFileChange={(file) =>
setValue("profile", URL.createObjectURL(file))
}
/>
</ProfileImageWrapper>
)}
/>
<Controller
name="nickname"
control={control}
rules={{
required: "닉네임은 필수 입력 항목입니다.",
}}
render={({ field: { value }, fieldState: { invalid }, formState }) => (
<>
<LabeledInput
id="profile-nickname"
label="닉네임"
value={value}
setValue={(value) => {
setValue("nickname", value);
}}
placeholder="닉네임을 입력해주세요."
/>
{invalid && (
<Text
variant="button"
content={formState.errors.nickname?.message || ""}
/>
)}
</>
)}
/>
<TextButton
size="l"
text="등록 완료"
onClick={() => handleSubmit(onSubmit)}
/>
</ProfileRegistrationFormWrapper>
);
};
54 changes: 54 additions & 0 deletions src/components/organisms/ProfileRegistrationForm/stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { Meta, StoryObj } from "@storybook/react";
import { ProfileRegistrationForm } from ".";

const meta: Meta<typeof ProfileRegistrationForm> = {
title: "Organisms/ProfileRegistrationForm",
component: ProfileRegistrationForm,
tags: ["autodocs"],
decorators: (story) => (
<div style={{ width: "375px", height: "680px", padding: "1rem 0" }}>
{story()}
</div>
),
};

type Story = StoryObj<typeof meta>;

export const Default: Story = {
parameters: {
docs: {
description: {
story: "유저 정보가 없는 상태의 Form입니다.",
},
},
},
args: {
onSubmit: (data) => {
console.log("onSubmit");
console.log(data);
},
},
};

export const HasUser: Story = {
parameters: {
docs: {
description: {
story: "유저 정보가 있는 상태의 Form입니다.",
},
},
},
args: {
user: {
id: "1",
nickname: "ppyom",
profile: "https://github.com/ppyom.png",
},
onSubmit: (data) => {
console.log("onSubmit");
console.log(data);
},
},
};

export default meta;
38 changes: 38 additions & 0 deletions src/components/organisms/ProfileRegistrationForm/styled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import styled from "@emotion/styled";
import { ImageUploadWrapper } from "components/atoms/ImageUpload/styled";
import { InputWrapper } from "components/atoms/Input/styled";
import { TextButtonWrapper } from "components/atoms/Button/TextButton/styled";
import { LabeledInputWrapper } from "components/molecules/LabeledInput/styled";

export const ProfileImageWrapper: ReturnType<typeof styled.div> = styled.div`
max-width: 200px;
width: calc(100% - 2rem);
border-radius: 50%;
overflow: hidden;
position: relative;
${ImageUploadWrapper} {
position: absolute;
top: 0;
}
`;

export const ProfileRegistrationFormWrapper: ReturnType<typeof styled.form> =
styled.form`
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
gap: 1rem;
${LabeledInputWrapper} {
flex: 1;
justify-content: start;
width: calc(100% - 2rem);
${InputWrapper} {
width: 100%;
}
}
${TextButtonWrapper} {
width: calc(100% - 2rem);
}
`;
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from "./map";
export * from "./selectOption";
export * from "./navMenu";
export * from "./notification";
export * from "./user";
5 changes: 5 additions & 0 deletions src/types/user.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface IUser {
id: string;
nickname: string;
profile: string | undefined;
}

0 comments on commit 2adc06d

Please sign in to comment.