Skip to content

Commit 19ff84e

Browse files
authored
Merge pull request #136 from boostcampwm-2022/61-modal-컴포넌트-만들기
Modal 컴포넌트 만들기
2 parents d4a2a11 + 2e9eb2b commit 19ff84e

File tree

16 files changed

+256
-12
lines changed

16 files changed

+256
-12
lines changed

frontend/src/App.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { BrowserRouter } from 'react-router-dom';
66
import RootRoutes from '@routes/RootRoutes';
77
import globalStyle from '@styles/globalStyle';
88
import theme from '@styles/theme';
9+
import ModalManager from '@components/ModalManager';
910

1011
function App() {
1112
return (
@@ -16,8 +17,10 @@ function App() {
1617
<Global styles={globalStyle} />
1718
<Suspense fallback={<>spinner...</>}>
1819
<RootRoutes />
20+
<ModalManager />
1921
</Suspense>
2022
</div>
23+
<div id="popup-root"></div>
2124
</ThemeProvider>
2225
</BrowserRouter>
2326
</RecoilRoot>

frontend/src/components/@drawer/RecordDrawer/RecordDrawer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import DocsItem from '@components/@shared/DocsItem/DocsItem';
22
import { REST_TYPE } from '@constants/rest.constant';
3-
import { DocsItemDtoType } from '@customType/dto';
3+
import { DocsItemDtoType } from '@customType/DTO';
44
import axios from 'axios';
55
import React, { useEffect, useState } from 'react';
66

frontend/src/components/@shared/DocsItem/DocsItem.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22

3-
import { DocsItemDtoType } from '@customType/dto';
3+
import { DocsItemDtoType } from '@customType/DTO';
44
import { ReactComponent as DownloadIcon } from '@assets/icon/download.svg';
55
import { iconSmStyle } from '@styles/commonStyle';
66
import { createdAtStyle, docsItemWrapper, indexStyle, playTimeStyle } from './DocsItem.style';
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import React from 'react';
2+
import { Story } from '@storybook/react';
3+
import Modal from './Modal';
4+
5+
export default {
6+
component: Modal,
7+
title: '@shared/Modal',
8+
};
9+
10+
const Template: Story = (args) => (
11+
<Modal>
12+
<Modal.Title>타이틀</Modal.Title>
13+
<Modal.ButtonArea>
14+
<Modal.Button style="text" color="black">
15+
취소
16+
</Modal.Button>
17+
<Modal.Button>확인</Modal.Button>
18+
</Modal.ButtonArea>
19+
</Modal>
20+
);
21+
22+
export const Default = Template.bind({});
23+
Default.args = {};
24+
25+
const SingleButtonModalTemplate: Story = (args) => (
26+
<Modal>
27+
<Modal.Title>타이틀</Modal.Title>
28+
<Modal.ButtonArea>
29+
<Modal.Button style="contained" color="red">
30+
취소
31+
</Modal.Button>
32+
</Modal.ButtonArea>
33+
</Modal>
34+
);
35+
36+
export const SingleButtonModal = SingleButtonModalTemplate.bind({});
37+
SingleButtonModal.args = {};
38+
39+
const TextModalTemplate: Story = (args) => (
40+
<Modal>
41+
<Modal.Title>타이틀</Modal.Title>
42+
<Modal.ContentArea>
43+
<span>본문 텍스트입니다.</span>
44+
</Modal.ContentArea>
45+
<Modal.ButtonArea>
46+
<Modal.Button style="text" color="black">
47+
취소
48+
</Modal.Button>
49+
<Modal.Button onClick={() => alert('안녕')}>확인</Modal.Button>
50+
</Modal.ButtonArea>
51+
</Modal>
52+
);
53+
54+
export const TextModal = TextModalTemplate.bind({});
55+
TextModal.args = {};
56+
57+
const TextFieldModalTemplate: Story = (args) => (
58+
<Modal>
59+
<Modal.Title textAlign="center">타이틀</Modal.Title>
60+
<Modal.ContentArea>
61+
<Modal.TextField width={'100%'} textAlign={'center'} />
62+
</Modal.ContentArea>
63+
<Modal.ButtonArea>
64+
<Modal.Button style="text" color="black">
65+
취소
66+
</Modal.Button>
67+
<Modal.Button>확인</Modal.Button>
68+
</Modal.ButtonArea>
69+
</Modal>
70+
);
71+
72+
export const TextFieldModal = TextFieldModalTemplate.bind({});
73+
TextFieldModal.args = {};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { css } from '@emotion/react';
2+
import { flexColumn, flexRow } from '@styles/globalStyle';
3+
4+
export const ModalWrapperStyle = (theme) => css`
5+
${flexColumn({ gap: '32px' })};
6+
7+
width: 480px;
8+
background-color: ${theme.colors.white};
9+
padding: 40px 32px 32px 32px;
10+
11+
border: 1px solid ${theme.colors.gray2};
12+
border-radius: ${theme.borderRaduis};
13+
`;
14+
15+
export const ModalTitleStyle = (theme) => css`
16+
color: ${theme.colors.black};
17+
18+
font-size: ${theme.fontSize.large};
19+
font-weight: bold;
20+
`;
21+
22+
export const ModalButtonAreaStyle = (isArray) => css`
23+
${flexRow({ justifyContent: isArray ? 'space-between' : 'center' })};
24+
25+
width: 100%;
26+
`;
27+
28+
export const ModalContentAreaStyle = () => css`
29+
${flexColumn({ gap: '32px' })}
30+
31+
width: 100%;
32+
`;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React from 'react';
2+
import Button from '../Button/Button';
3+
import TextField from '../TextField/TextField';
4+
import {
5+
ModalButtonAreaStyle,
6+
ModalContentAreaStyle,
7+
ModalTitleStyle,
8+
ModalWrapperStyle,
9+
} from './Modal.style';
10+
11+
export interface ModalPropType {
12+
children: React.ReactNode | React.ReactNode[];
13+
}
14+
15+
const ModalTitle = ({ children }: any) => {
16+
return <span css={ModalTitleStyle}>{children}</span>;
17+
};
18+
19+
const ModalContentArea = ({ children }: ModalPropType) => {
20+
return <div css={ModalContentAreaStyle}>{children}</div>;
21+
};
22+
23+
const ModalButtonArea = ({ children }: ModalPropType) => {
24+
const isArray = Array.isArray(children);
25+
26+
return <div css={ModalButtonAreaStyle(isArray)}>{children}</div>;
27+
};
28+
29+
const ModalWrapper = ({ children }: ModalPropType) => {
30+
return <div css={ModalWrapperStyle}>{children}</div>;
31+
};
32+
33+
const Modal = Object.assign(ModalWrapper, {
34+
Title: ModalTitle,
35+
ContentArea: ModalContentArea,
36+
ButtonArea: ModalButtonArea,
37+
Button,
38+
TextField,
39+
});
40+
41+
export default Modal;

frontend/src/components/@shared/TextField/TextField.style.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
import { css } from '@emotion/react';
2+
import { flexColumn } from '@styles/globalStyle';
3+
4+
export const TextFieldWrapperStyle = (width) => css`
5+
${flexColumn({ gap: '4px' })};
26
3-
export const TextFieldStyle = (theme, width, error, disabled) => css`
47
width: ${width};
8+
`;
9+
10+
export const TextFieldStyle = (theme, error, disabled, textAlign) => css`
11+
width: 100%;
512
background-color: ${disabled ? theme.colors.gray3 : theme.colors.secondary};
613
color: ${error ? theme.colors.red : disabled ? theme.colors.gray2 : theme.colors.black};
714
padding: 12px;
815
16+
text-align: ${textAlign};
917
font-size: ${theme.fontSize.small};
1018
1119
border: 1px solid ${!disabled && error ? theme.colors.red : theme.colors.gray2};

frontend/src/components/@shared/TextField/TextField.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import { css } from '@emotion/react';
22
import { flexColumn } from '@styles/globalStyle';
33
import React from 'react';
4-
import { TextFieldHelperTextStyle, TextFieldStyle } from './TextField.style';
4+
import { TextFieldHelperTextStyle, TextFieldStyle, TextFieldWrapperStyle } from './TextField.style';
55

66
export interface TextFieldPropType {
77
width?: string;
88
placeholder?: string;
99
disabled?: boolean;
1010
readOnly?: boolean;
1111
error?: boolean;
12+
textAlign?: 'left' | 'center' | 'right';
1213
helperText?: string;
1314
onChange?: React.ChangeEventHandler<HTMLInputElement>;
1415
value?: string;
@@ -20,22 +21,23 @@ const TextField = ({
2021
disabled = false,
2122
readOnly = false,
2223
error = false,
24+
textAlign = 'left',
2325
helperText,
2426
onChange,
2527
value,
2628
}: TextFieldPropType) => {
2729
return (
28-
<div css={flexColumn({ gap: '4px' })}>
30+
<div css={TextFieldWrapperStyle(width)}>
2931
<input
30-
css={(theme) => TextFieldStyle(theme, width, error, disabled)}
32+
css={(theme) => TextFieldStyle(theme, error, disabled, textAlign)}
3133
type="text"
3234
placeholder={placeholder}
3335
disabled={disabled}
3436
readOnly={readOnly}
3537
onChange={onChange}
3638
value={value}
3739
/>
38-
{!disabled && helperText?.trim().length > 0 ? (
40+
{helperText?.trim().length > 0 ? (
3941
<span css={(theme) => TextFieldHelperTextStyle(theme, error)}>{helperText}</span>
4042
) : null}
4143
</div>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import React from 'react';
2+
import { useRecoilValue } from 'recoil';
3+
import { currentModalState } from '@store/currentModal.atom';
4+
import ReactDOM from 'react-dom';
5+
6+
const ModalManager = () => {
7+
const currentModal = useRecoilValue(currentModalState);
8+
9+
if (!currentModal) return;
10+
11+
const modalStore = {
12+
};
13+
14+
const ModalComponent = modalStore[currentModal.modal];
15+
16+
return ReactDOM.createPortal(
17+
<ModalComponent {...currentModal.props} />,
18+
document.querySelector('#popup-root')
19+
);
20+
};
21+
22+
export default ModalManager;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const MODAL_TYPE = {
2+
EnterRoomModal: 'EnterRoomModal',
3+
};

0 commit comments

Comments
 (0)