Skip to content

Commit

Permalink
feat(apply): implement the function to preview the application form b…
Browse files Browse the repository at this point in the history
…efore final submission
  • Loading branch information
greenblues1190 authored Oct 13, 2022
1 parent 13e9305 commit e37d882
Show file tree
Hide file tree
Showing 28 changed files with 684 additions and 75 deletions.
6 changes: 3 additions & 3 deletions frontend/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { addDecorator } from "@storybook/react";
import axios from "axios";
import { initializeWorker, mswDecorator } from "msw-storybook-addon";
import { MemoryRouter } from "react-router-dom";
import "../src/api/api";
import "../src/App.css";
import { ModalProvider } from "../src/hooks/useModalContext";
import { RecruitmentContext } from "../src/hooks/useRecruitmentContext";
import { UserInfoContext } from "../src/hooks/useUserInfoContext";
import { recruitmentDummy, userInfoDummy } from "../src/mock/dummy";
Expand Down Expand Up @@ -35,9 +35,9 @@ export const decorators = [
>
<TokenProvider>
<UserInfoContext.Provider value={{ userInfo: userInfoDummy }}>
<MemoryRouter>
<ModalProvider>
<Story />
</MemoryRouter>
</ModalProvider>
</UserInfoContext.Provider>
</TokenProvider>
</RecruitmentContext.Provider>
Expand Down
10 changes: 6 additions & 4 deletions frontend/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
--pc-main-width: 800px;
--pc-main-width-narrow: 512px;

--tooltip-z-index: 1000;
--tooltip-dimmed-z-index: 999;
--header-z-index: 998;
--nav-z-index: 997;
--modal-window-z-index: 1000;
--modal-portal-z-index: 990;
--tooltip-z-index: 980;
--tooltip-dimmed-z-index: 970;
--header-z-index: 960;
--nav-z-index: 950;
}

* {
Expand Down
85 changes: 44 additions & 41 deletions frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,53 +22,56 @@ import SignUp from "./pages/SignUp/SignUp";
import RecruitmentProvider from "./provider/RecruitmentProvider";
import TokenProvider from "./provider/TokenProvider";
import UserInfoProvider from "./provider/UserInfoProvider";
import { ModalProvider } from "./hooks/useModalContext";

const App = () => {
return (
<TokenProvider>
<RecruitmentProvider>
<BrowserRouter>
<Header />
<main className="main">
<ScrollToTop>
<Routes>
<Route path={PATH.HOME} element={<Recruits />} />
<Route path={PATH.RECRUITS} element={<Recruits />} />
<Route path={PATH.SIGN_UP} element={<SignUp />} />
<Route path={PATH.LOGIN} element={<Login />} />
<Route path={PATH.FIND_PASSWORD} element={<PasswordFind />} />
<Route path={PATH.FIND_PASSWORD_RESULT} element={<PasswordFindResult />} />
<Route element={<PrivateRoute />}>
<Route path={PATH.APPLICATION_FORM} element={<ApplicationRegister />} />
<Route path={PATH.EDIT_PASSWORD} element={<PasswordEdit />} />
<Route path={PATH.MY_APPLICATION} element={<MyApplication />} />
<Route path={PATH.ASSIGNMENT} element={<AssignmentSubmit />} />
</Route>
<ModalProvider>
<BrowserRouter>
<Header />
<main className="main">
<ScrollToTop>
<Routes>
<Route path={PATH.HOME} element={<Recruits />} />
<Route path={PATH.RECRUITS} element={<Recruits />} />
<Route path={PATH.SIGN_UP} element={<SignUp />} />
<Route path={PATH.LOGIN} element={<Login />} />
<Route path={PATH.FIND_PASSWORD} element={<PasswordFind />} />
<Route path={PATH.FIND_PASSWORD_RESULT} element={<PasswordFindResult />} />
<Route element={<PrivateRoute />}>
<Route path={PATH.APPLICATION_FORM} element={<ApplicationRegister />} />
<Route path={PATH.EDIT_PASSWORD} element={<PasswordEdit />} />
<Route path={PATH.MY_APPLICATION} element={<MyApplication />} />
<Route path={PATH.ASSIGNMENT} element={<AssignmentSubmit />} />
</Route>

<Route element={<PrivateRoute />}>
<Route
path={PATH.MY_PAGE}
element={
<UserInfoProvider>
<MyPage />
</UserInfoProvider>
}
/>
<Route
path={PATH.EDIT_MY_PAGE}
element={
<UserInfoProvider>
<MyPageEdit />
</UserInfoProvider>
}
/>
</Route>
</Routes>
</ScrollToTop>
</main>
<Footer />
<InquiryFloatingButton />
</BrowserRouter>
<Route element={<PrivateRoute />}>
<Route
path={PATH.MY_PAGE}
element={
<UserInfoProvider>
<MyPage />
</UserInfoProvider>
}
/>
<Route
path={PATH.EDIT_MY_PAGE}
element={
<UserInfoProvider>
<MyPageEdit />
</UserInfoProvider>
}
/>
</Route>
</Routes>
</ScrollToTop>
</main>
<Footer />
<InquiryFloatingButton />
</BrowserRouter>
</ModalProvider>
</RecruitmentProvider>
</TokenProvider>
);
Expand Down
1 change: 1 addition & 0 deletions frontend/src/color.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@
--blue-gray: #5f7997;
--red: #ff0000;
--green: #0eaf7a;
--dim: rgba(0, 0, 0, 0.4);
}
25 changes: 25 additions & 0 deletions frontend/src/components/@common/ModalPortal/ModalPortal.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@keyframes dissolve {
from {
opacity: 0;
}

to {
opacity: 1;
}
}

.box {
position: fixed;
inset: 0;
display: flex;
justify-content: center;
align-items: center;
z-index: var(--modal-portal-z-index);
}

.dimmer-box {
position: fixed;
inset: 0;
background-color: var(--dim);
animation: dissolve 0.3s forwards;
}
47 changes: 47 additions & 0 deletions frontend/src/components/@common/ModalPortal/ModalPortal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
import { ModalContextValue } from "../../../hooks/useModalContext";
import styles from "./ModalPortal.module.css";

type ModalPortalProps = {
closeModal: ModalContextValue["closeModal"];
children: NonNullable<React.ReactNode>;
};

const ModalPortal = ({ children, closeModal }: ModalPortalProps) => {
const ref = useRef<Element | null>(null);
const [mounted, setMounted] = useState(false);

const handleKeyDown: React.KeyboardEventHandler<HTMLDivElement> = (e) => {
if (e.key === "Escape") {
closeModal();
}
};

useEffect(() => {
setMounted(true);

let modalRoot = document.getElementById("modal-root");

if (!modalRoot) {
modalRoot = document.createElement("div");
modalRoot.id = "modal-root";
document.body.appendChild(modalRoot);
}

ref.current = modalRoot;
}, []);

if (ref.current && mounted) {
return createPortal(
<div className={styles.box} onKeyDown={handleKeyDown}>
<div className={styles["dimmer-box"]} onClick={closeModal} />
{children}
</div>,
ref.current
);
}

return null;
};
export default ModalPortal;
57 changes: 57 additions & 0 deletions frontend/src/components/@common/ModalWindow/ModalWindow.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
@keyframes drag-up {
from {
transform: translateY(100px);
opacity: 0;
}

to {
transform: translateY(0);
opacity: 1;
}
}

@keyframes subtle-pop-up {
from {
transform: scale(0.9);
opacity: 0;
}

to {
transform: scale(1);
opacity: 1;
}
}

.box {
position: fixed;
margin: auto;
max-width: 40rem;
z-index: var(--modal-window-z-index);
display: flex;
flex-direction: column;
background-color: white;
overflow: hidden;
}

@media only screen and (max-width: 420px) {
.box {
bottom: 0;
padding: 1rem;
max-height: 80%;
min-height: 10rem;
width: 100%;
border-radius: 8px 8px 0 0;
animation: drag-up 0.3s forwards;
}
}

@media only screen and (min-width: 420px) {
.box {
left: 4rem;
right: 4rem;
padding: 1.5rem;
max-height: calc(100% - 8rem);
border-radius: 8px;
animation: subtle-pop-up 0.3s forwards;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { ComponentMeta, ComponentStory } from "@storybook/react";
import ModalWindow from "./ModalWindow";

export default {
title: "components/ModalWindow",
component: ModalWindow,
} as ComponentMeta<typeof ModalWindow>;

const Template: ComponentStory<typeof ModalWindow> = (args) => {
return <ModalWindow {...args} />;
};

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

Default.args = {
children: "modal text",
};

export const LongContent = Template.bind({});

LongContent.args = {
children: (
<>
<p>
대통령은 국무회의의 의장이 되고, 국무총리는 부의장이 된다. 군인·군무원·경찰공무원 기타
법률이 정하는 자가 전투·훈련등 직무집행과 관련하여 받은 손해에 대하여는 법률이 정하는
보상외에 국가 또는 공공단체에 공무원의 직무상 불법행위로 인한 배상은 청구할 수 없다.
국가원로자문회의의 의장은 직전대통령이 된다. 다만, 직전대통령이 없을 때에는 대통령이
지명한다. 훈장등의 영전은 이를 받은 자에게만 효력이 있고, 어떠한 특권도 이에 따르지
아니한다. 국정감사 및 조사에 관한 절차 기타 필요한 사항은 법률로 정한다. 국가는 모성의
보호를 위하여 노력하여야 한다. 대법원장과 대법관이 아닌 법관의 임기는 10년으로 하며, 법률이
정하는 바에 의하여 연임할 수 있다.
</p>

<p>
대통령은 법률안의 일부에 대하여 또는 법률안을 수정하여 재의를 요구할 수 없다.
저작자·발명가·과학기술자와 예술가의 권리는 법률로써 보호한다. 모든 국민은 근로의 의무를
진다. 국가는 근로의 의무의 내용과 조건을 민주주의원칙에 따라 법률로 정한다. 제1항의
해임건의는 국회재적의원 3분의 1 이상의 발의에 의하여 국회재적의원 과반수의 찬성이 있어야
한다. 국가는 농·어민과 중소기업의 자조조직을 육성하여야 하며, 그 자율적 활동과 발전을
보장한다. 한 회계연도를 넘어 계속하여 지출할 필요가 있을 때에는 정부는 연한을 정하여
계속비로서 국회의 의결을 얻어야 한다. 근로조건의 기준은 인간의 존엄성을 보장하도록 법률로
정한다.
</p>
</>
),
};
20 changes: 20 additions & 0 deletions frontend/src/components/@common/ModalWindow/ModalWindow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import classNames from "classnames";
import styles from "./ModalWindow.module.css";

type ModalWindowProps = Omit<React.HTMLAttributes<HTMLDivElement>, "role" | "aria-label">;

const ModalWindow = ({ className, children, ...props }: ModalWindowProps) => {
return (
<div
tabIndex={-1}
role="dialog"
aria-label="dialogTitle"
className={classNames(styles.box, className)}
{...props}
>
{children}
</div>
);
};

export default ModalWindow;
Loading

0 comments on commit e37d882

Please sign in to comment.