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

feat: ✨ LevelModal 컴포넌트 이관 #98

Merged
merged 10 commits into from
Jul 26, 2022
4 changes: 2 additions & 2 deletions src/assets/icon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ export { default as Logo } from './logo.svg';
export { default as Photo } from './photo.svg';
export { default as NavHome } from './nav_home.svg';
export { default as NavBeer } from './nav_beer.svg';
export { default as NavMyPage } from './nav_MyPage.svg';
export { default as NavMyPage } from './nav_mypage.svg';
export { default as NavTravel } from './nav_travel.svg';
export { default as NavHomeActive } from './nav_home_active.svg';
export { default as NavBeerActive } from './nav_beer_active.svg';
export { default as NavMyPageActive } from './nav_MyPage_active.svg';
export { default as NavMyPageActive } from './nav_mypage_active.svg';
export { default as NavTravelActive } from './nav_travel_active.svg';
export { default as Plus } from './plus.svg';
export { default as Save } from './save.svg';
Expand Down
File renamed without changes
File renamed without changes
141 changes: 141 additions & 0 deletions src/components/Modal/Modal.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import React, { useState } from 'react';
import Button from '@/components/commons/Button';

import Modal from './Modal';

export default {
title: 'Components/Modal',
component: Modal,
argTypes: {
title: { control: 'text', name: 'title' },
description: { control: 'text', name: 'description' },
withCloseButton: { control: 'boolean' },
noMoreSee: { control: 'boolean' },
},
args: { withCloseButton: true, noMoreSee: true },
} as ComponentMeta<typeof Modal>;

const Template: ComponentStory<typeof Modal> = (args) => {
const [isModalOpen, setIsModalOpen] = useState(false);

const openModal = () => setIsModalOpen(true);
const closeModal = () => setIsModalOpen(false);

return <Modal openModal={openModal} closeModal={closeModal} {...args} />;
};

export const Default = Template.bind({});
Default.args = {
open: true,
header: '헤더입니다',
title: '타이틀입니다',
withCloseButton: true,
noMoreSee: true,
description: '설명설명설명설명설명설명설명설명',
buttons: (
<Button type="primary" width="large">
기록할 맥주 검색하러 가기
</Button>
),
};

export const 로그아웃: ComponentStory<typeof Modal> = () => {
const [isModalOpen, setIsModalOpen] = useState(true);

const openModal = () => setIsModalOpen(true);
const closeModal = () => setIsModalOpen(false);

return (
<Modal
open={isModalOpen}
openModal={openModal}
closeModal={closeModal}
buttons={
<>
<Button type="grey" onClick={closeModal}>
취소
</Button>
<Button type="primary" onClick={closeModal}>
확인
</Button>
</>
}
title="로그아웃 하시겠어요?"
/>
);
};

export const 회원탈퇴: ComponentStory<typeof Modal> = () => {
const [isModalOpen, setIsModalOpen] = useState(true);

const openModal = () => setIsModalOpen(true);
const closeModal = () => setIsModalOpen(false);

return (
<Modal
open={isModalOpen}
openModal={openModal}
closeModal={closeModal}
buttons={
<>
<Button type="grey" onClick={closeModal}>
취소
</Button>
<Button type="primary" onClick={closeModal}>
확인
</Button>
</>
}
title="정말 회원 탈퇴 하시겠어요?"
description="회원 탈퇴 시 저장된 모든 정보가 삭제됩니다."
/>
);
};

export const 닉네임_수정: ComponentStory<typeof Modal> = () => {
const [isModalOpen, setIsModalOpen] = useState(true);

const openModal = () => setIsModalOpen(true);
const closeModal = () => setIsModalOpen(false);

return (
<Modal
open={isModalOpen}
openModal={openModal}
closeModal={closeModal}
withCloseButton
header="닉네임 수정하기"
buttons={
<Button type="primary" width="large" onClick={closeModal}>
완료
</Button>
}
/>
);
};

export const 기록할_맥주_검색_하러가기: ComponentStory<typeof Modal> = () => {
const [isModalOpen, setIsModalOpen] = useState(true);

const openModal = () => setIsModalOpen(true);
const closeModal = () => setIsModalOpen(false);

return (
<Modal
open={isModalOpen}
openModal={openModal}
closeModal={closeModal}
title="어떤 맥주를 기록하시겠어요?"
description="맥주를 선택하면 기록을 시작할 수 있어요."
withCloseButton
buttons={
<Button type="primary" width="large" onClick={closeModal}>
기록할 맥주 검색하러 가기
</Button>
}
noMoreSee
/>
);
};

122 changes: 122 additions & 0 deletions src/components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import styled from '@emotion/styled';
import React, { ReactNode } from 'react';

import ModalLayout from '@/components/layouts/ModalLayout';
import Icon from '@/components/commons/Icon';

interface ModalProps {
openModal?: () => void;
closeModal?: () => void;
open: boolean;
setOpen?: () => void;
Copy link
Contributor

Choose a reason for hiding this comment

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

openModal과 setOpen은 불필요한 prop인 것 같고, 코드상에 사용되는 부분이 없어보이는데
전달받아야하는 이유가 있나요??

Copy link
Contributor Author

@hy57in hy57in Jul 26, 2022

Choose a reason for hiding this comment

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

Modal 컴포넌트를 옮기는 과정에서 중복되는 prop이 생긴것 같아 정리하였습니다!
불필요하게 중복되는 prop은 제거하고, 네이밍을 통일했습니다
a292574

buttons?: ReactNode;
header?: string;
title?: string;
description?: string | ReactNode;
withCloseButton?: boolean;
noMoreSee?: boolean;
disabledDimClick?: boolean;
}

const Modal = (props: ModalProps) => {
const {
closeModal,
open,
buttons,
header,
title,
description,
withCloseButton,
noMoreSee,
disabledDimClick,
} = props;

return (
<ModalLayout open={open} {...(!disabledDimClick && { onClose: closeModal })}>
<StyledModal open={open}>
<Header>
{withCloseButton ? (
<button type="button" aria-label="닫기">
<Icon name="Close" size={24} onClick={closeModal} />
</button>
) : (
<Icon name="Close" size={24} color="white" />
)}
{header}
<Icon name="Close" size={24} color="white" />
</Header>
{title && <Title>{title}</Title>}
{description && <Description>{description}</Description>}
{buttons && <ButtonContainer>{buttons}</ButtonContainer>}
{noMoreSee && <NoMoreSee onClick={closeModal}>다시 보지 않기</NoMoreSee>}
Copy link
Contributor

Choose a reason for hiding this comment

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

다시보지 않기의 onClick 이벤트가 closeModal만 들어가는것이 맞을까용..?
스토리지에 상태 저장 등 다른 로직이 포함될 여지가 있어보여서 onNoMoreSee Prop을 추가하거나
아예 공통 Modal 컴포넌트에서는 이부분이 없어도 될것같아요 (필요할때만 children으로 추가)

Copy link
Contributor Author

@hy57in hy57in Jul 26, 2022

Choose a reason for hiding this comment

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

넵 noMoreSee prop은 공통 Modal에 있기에 적합하지 않다는 의견에 동의합니다
noMoreSee 는 홈 화면의 모달에서만 사용되니 Modal 컴포넌트를 래핑한 홈 화면 전용 모달을 따로 만드는게 좋을 것 같아요

이 PR은 LevelModal 컴포넌트를 만들면서 필요한 Modal 컴포넌트를 이관한 것이니, 홈 화면 전용 모달을 만들때 NoMoreSee 부분을 리팩토링하면 좋을 것 같아요

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 Author

Choose a reason for hiding this comment

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

#104 (comment) 추가했어요!

</StyledModal>
</ModalLayout>
);
};

export default Modal;

const StyledModal = styled.div<{ open: boolean }>`
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
width: calc(100% - 76px);
padding: 16px 16px 20px 16px;
border-radius: 12px;
background-color: ${(p) => p.theme.color.white};

${(p) => !p.open && `display:none;`}
`;

const Header = styled.div`
position: absolute;
top: 0;
left: 0;
width: 100%;
padding: 14px;
font-weight: 700;
font-size: 16px;
line-height: 19px;
color: #323232;
display: flex;
justify-content: space-between;
`;

const Title = styled.p`
width: 100%;
margin: 46px 0 8px 0;
text-align: center;
color: #323232;
${(p) => p.theme.fonts.SubTitle2};
`;

const Description = styled.p`
width: 100%;
text-align: center;
color: ${({ theme }) => theme.color.grey4};
${(p) => p.theme.fonts.Body1};
`;

const ButtonContainer = styled.div`
width: 100%;
display: flex;
justify-content: center;
gap: 13px;
margin: 30px 0 10px 0;
`;

const NoMoreSee = styled.div`
font-weight: 600;
font-size: 13px;
line-height: 16px;
border-bottom: 1px solid #c7c7c7;
color: ${({ theme }) => theme.color.grey3};
height: fit-content;
margin-top: 10px;
cursor: pointer;
`;
1 change: 1 addition & 0 deletions src/components/Modal/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './Modal';
5 changes: 3 additions & 2 deletions src/components/mypage/Bio/Bio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ const Bio = ({
<LevelImage src={userLevel?.imageUrl} alt={userLevel?.tier.toString()} />
<NickName>
{nickname}
<ModifyIcon name="Modify" size={24} onClick={openModifyModal} />
<button type="button" aria-label="수정">
<ModifyIcon name="Modify" size={24} onClick={openModifyModal} />
</button>
</NickName>
<Email>{email}</Email>
</StyledBio>
Expand Down Expand Up @@ -88,7 +90,6 @@ const NickName = styled.div`
`;

const ModifyIcon = styled(Icon)`
cursor: pointer;
position: absolute;
left: calc(100% + 12px);
`;
Expand Down
58 changes: 58 additions & 0 deletions src/components/mypage/LevelModal/LevelModal.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { ILevel } from '@/types';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { useState } from 'react';

import LevelModal from './LevelModal';

const LEVEL_DATA: ILevel[] = [
{
id: 1,
tier: 1,
imageUrl: 'https://cdn.pixabay.com/photo/2016/04/24/14/19/paper-1349664_1280.png',
req: 0,
},
{
id: 2,
tier: 2,
imageUrl: 'https://cdn.pixabay.com/photo/2016/04/24/14/19/paper-1349664_1280.png',
req: 1,
},
{
id: 3,
tier: 3,
imageUrl: 'https://cdn.pixabay.com/photo/2016/04/24/14/19/paper-1349664_1280.png',
req: 5,
},
{
id: 4,
tier: 4,
imageUrl: 'https://cdn.pixabay.com/photo/2016/04/24/14/19/paper-1349664_1280.png',
req: 12,
},
{
id: 5,
tier: 5,
imageUrl: 'https://cdn.pixabay.com/photo/2016/04/24/14/19/paper-1349664_1280.png',
req: 20,
},
];

export default {
title: 'Components/mypage/LevelModal',
component: LevelModal,
} as ComponentMeta<typeof LevelModal>;

const Template: ComponentStory<typeof LevelModal> = () => {
const [isOpen, setIsOpen] = useState(true);

return (
<LevelModal
isLevelModalOpen={isOpen}
openLevelModal={() => setIsOpen(true)}
closeLevelModal={() => setIsOpen(false)}
levels={LEVEL_DATA}
/>
);
};

export const Default = Template.bind({});
Loading