Skip to content

Commit

Permalink
공통 모달 컴포넌트 구현 (#28)
Browse files Browse the repository at this point in the history
* refactor: (#17) App.tsx에서 Example 컴포넌트 import
Button 컴포넌트 대신 Example 컴포넌트 import

* feat: (#17) Modal 컴포넌트 구현 및 스토리 작성
Modal 컴포넌트는 3가지 상태 - Default, Wide(디바이스 width가 넓은 경우), With Close Button(모달 안에 취소 버튼이 있는 경우)로 구성된다

* chore: (#17) Modal 컴포넌트 common 폴더로 이동

* chore: (#17) Modal 컴포넌트 css property 정렬

* design: (#17) Modal 컴포넌트 내 Body, Description css 수정

* refactor: (#17) 파일 간 순환참조 코드 삭제

* refactor: (#17) Modal 컴포넌트 onModalClose props에 setStateAction 대신 일반함수(closeModal) 전달하도록 수정

* chore: (#17) 오타 수정

* refactor: (#17) story에서만 사용하는 style들 stories.tsx 로 이동

* refactor: (#17) size props의 타입 분리 대신 컴포넌트 파일 내에 명시

* refactor: (#17) 모달 사이즈 상수화

* chore: (#17) 불필요한 export 삭제
  • Loading branch information
inyeong-kang authored Jul 12, 2023
1 parent 1c2bec7 commit f738b28
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 2 deletions.
4 changes: 2 additions & 2 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import Home from '@pages/Home';

import Button from '@components/Button';
import Example from '@components/Example';

const App = () => (
<>
<Button />
<Example />
<Home />
</>
);
Expand Down
133 changes: 133 additions & 0 deletions frontend/src/components/common/Modal/Modal.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import type { Meta } from '@storybook/react';

import { useState } from 'react';

import { styled } from 'styled-components';

import Modal from '.';

const meta: Meta<typeof Modal> = {
component: Modal,
};

export default meta;

export const Default = () => {
const [isOpen, setIsOpen] = useState(false);

const openModal = () => {
setIsOpen(true);
};

const closeModal = () => {
if (isOpen === true) setIsOpen(false);
};

return (
<>
<button onClick={openModal}>Open Modal</button>
{isOpen && (
<Modal size="sm" onModalClose={closeModal}>
<p>This is Default Modal</p>
</Modal>
)}
</>
);
};

export const Wide = () => {
const [isOpen, setIsOpen] = useState(false);

const openModal = () => {
setIsOpen(true);
};

const closeModal = () => {
if (isOpen === true) setIsOpen(false);
};

return (
<>
<button onClick={openModal}>Open Modal</button>
{isOpen && (
<Modal size="lg" onModalClose={closeModal}>
<p>This is Wide Modal</p>
</Modal>
)}
</>
);
};

export const WithCloseButton = () => {
const [isOpen, setIsOpen] = useState(false);

const openModal = () => {
setIsOpen(true);
};

const closeModal = () => {
if (isOpen === true) setIsOpen(false);
};

return (
<>
<button onClick={openModal}>Open Modal</button>
{isOpen && (
<Modal size="sm" onModalClose={closeModal}>
<>
<S.Header>
<p>Modal Title</p>
<S.CloseButton onClick={closeModal}>X</S.CloseButton>
</S.Header>
<S.Body>
<S.Description>This is Description</S.Description>
This is Content
</S.Body>
</>
</Modal>
)}
</>
);
};

const Header = styled.div`
display: flex;
justify-content: space-between;
alignt-items: center;
width: 100%;
border-bottom: 1px solid #f6f6f6;
padding: 10px;
font-size: 1.5rem;
font-weight: bold;
`;

const Body = styled.div`
display: flex;
flex-direction: column;
justify-content: start;
gap: 5px;
padding: 10px;
font-size: 1.4rem;
`;

const Description = styled.div`
color: gray;
font-size: 1.2rem;
`;

const CloseButton = styled.button`
width: 20px;
height: 20px;
`;

const S = {
Header,
Body,
Description,
CloseButton,
};
32 changes: 32 additions & 0 deletions frontend/src/components/common/Modal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React, { useEffect, useRef } from 'react';

import * as S from './style';

interface ModalProps {
onModalClose: () => void;
children: React.JSX.Element;
size: 'sm' | 'md' | 'lg';
}

export default function Modal({ onModalClose, children, size }: ModalProps) {
const BackDropRef = useRef<HTMLDivElement>(null);

useEffect(() => {
const handler = (e: MouseEvent) => {
if (e.target === BackDropRef.current) {
onModalClose();
}
};

document.addEventListener('click', handler);

return () => document.removeEventListener('click', handler);
}, [BackDropRef, onModalClose]);

return (
<S.All>
<S.Backdrop ref={BackDropRef}></S.Backdrop>
<S.Container size={size}>{children}</S.Container>
</S.All>
);
}
45 changes: 45 additions & 0 deletions frontend/src/components/common/Modal/style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { styled } from 'styled-components';

const modalSize: {
[key: string]: string;
} = {
sm: '290px',
md: '590px',
lg: '700px',
};

export const All = styled.div`
display: flex;
justify-content: center;
align-items: center;
`;

export const Backdrop = styled.div`
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba(0, 0, 0, 0.35);
`;

export const Container = styled.div<{ size: string }>`
display: grid;
grid-template-rows: 1fr 6fr;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: ${props => modalSize[props.size]};
height: 290px;
border-radius: 12px;
border: 2px solid #f6f6f6;
padding: 5px;
background-color: white;
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.5);
`;

0 comments on commit f738b28

Please sign in to comment.