From dac7aa1077bbc4a878bee5dd28cdbf28309889f8 Mon Sep 17 00:00:00 2001 From: jero_kang <81199414+inyeong-kang@users.noreply.github.com> Date: Wed, 2 Aug 2023 13:50:33 +0900 Subject: [PATCH] =?UTF-8?q?=EB=82=B4=20=EC=A0=95=EB=B3=B4=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20UI=20=EA=B5=AC=ED=98=84,=20Accordion=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84=20(#1?= =?UTF-8?q?79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: (#175) Accordion 컴포넌트 구현 및 스토리 작성 * feat: (#175) 내정보 페이지 UI/UX 구현 * chore: (#175) 불필요한 코드 삭제 * feat: (#175) 회원 탈퇴 모달 컴포넌트 구현 * feat: (#175) Layout 컴포넌트 추가, 반응형 디자인 구현 * fix: (#175) isPicked props 앞에 $ 기호 추가 --- .../common/Accordion/Accordion.stories.tsx | 112 ++++++++++++++++++ .../src/components/common/Accordion/index.tsx | 30 +++++ .../src/components/common/Accordion/style.ts | 62 ++++++++++ frontend/src/mocks/example/get.ts | 2 - frontend/src/pages/MyInfo/MyInfo.stories.tsx | 14 +++ frontend/src/pages/MyInfo/index.tsx | 83 +++++++++++++ frontend/src/pages/MyInfo/style.ts | 85 +++++++++++++ frontend/src/routes/router.tsx | 3 +- frontend/src/styles/theme.ts | 4 +- 9 files changed, 390 insertions(+), 5 deletions(-) create mode 100644 frontend/src/components/common/Accordion/Accordion.stories.tsx create mode 100644 frontend/src/components/common/Accordion/index.tsx create mode 100644 frontend/src/components/common/Accordion/style.ts create mode 100644 frontend/src/pages/MyInfo/MyInfo.stories.tsx create mode 100644 frontend/src/pages/MyInfo/index.tsx create mode 100644 frontend/src/pages/MyInfo/style.ts diff --git a/frontend/src/components/common/Accordion/Accordion.stories.tsx b/frontend/src/components/common/Accordion/Accordion.stories.tsx new file mode 100644 index 000000000..859d36c4f --- /dev/null +++ b/frontend/src/components/common/Accordion/Accordion.stories.tsx @@ -0,0 +1,112 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { styled } from 'styled-components'; + +import { useToggle } from '@hooks/useToggle'; + +import Modal from '../Modal'; +import SquareButton from '../SquareButton'; + +import Accordion from '.'; + +const meta: Meta = { + component: Accordion, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => ( + + Hello This is Content! + + ), +}; + +export const NicknameChange: Story = { + render: () => ( + + + + + 변경 + + + + ), +}; + +export const DeleteUserAccount = () => { + const { isOpen, openComponent, closeComponent } = useToggle(); + return ( + + + + 회원 탈퇴 + + + {isOpen && ( + + + 정말 탈퇴하시겠어요? + + 탈퇴 버튼 클릭 시,

계정은 삭제되며 복구되지 않아요. +
+ + + 탈퇴 + + + 취소 + + +
+
+ )} +
+ ); +}; + +const ButtonWrapper = styled.div` + width: 90px; + height: 50px; +`; + +const Input = styled.input` + width: 80%; + border: 1px solid #f2f2f2; + padding: 20px; +`; + +const ModalBody = styled.div` + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + gap: 30px; + + width: 90%; + margin: 40px 20px 0px 16px; + + font: var(--text-caption); +`; + +const ModalTitle = styled.div` + font: var(--text-title); +`; + +const ModalDescription = styled.div` + font: var(--text-body); +`; + +const ButtonListWrapper = styled.div` + display: flex; + justify-content: space-around; + gap: 20px; + + width: 90%; + height: 50px; + + margin-top: 20px; +`; diff --git a/frontend/src/components/common/Accordion/index.tsx b/frontend/src/components/common/Accordion/index.tsx new file mode 100644 index 000000000..e9a37472c --- /dev/null +++ b/frontend/src/components/common/Accordion/index.tsx @@ -0,0 +1,30 @@ +import { PropsWithChildren, useState } from 'react'; + +import chevronDown from '@assets/chevron-down.svg'; +import chevronUp from '@assets/chevron-up.svg'; + +import * as S from './style'; + +interface AccordionProps extends PropsWithChildren { + title: string; +} + +export default function Accordion({ title, children }: AccordionProps) { + const [isOpen, setIsOpen] = useState(false); + + const toggleAccordion = () => { + setIsOpen(!isOpen); + }; + + return ( + + + {title} + + + + {children} + + + ); +} diff --git a/frontend/src/components/common/Accordion/style.ts b/frontend/src/components/common/Accordion/style.ts new file mode 100644 index 000000000..a4b92b06b --- /dev/null +++ b/frontend/src/components/common/Accordion/style.ts @@ -0,0 +1,62 @@ +import styled, { keyframes } from 'styled-components'; + +export const Wrapper = styled.div` + width: 100%; + + font: var(--text-caption); +`; + +export const Title = styled.div` + display: flex; + justify-content: space-between; + + border: 1px solid #f2f2f2; + border-radius: 7px 7px 0 0; + padding: 16px; + + background-color: #ffffff; + + &:hover { + background-color: #f2f2f2; + } + cursor: pointer; +`; + +export const Content = styled.div<{ $isOpen: boolean }>` + display: ${props => (props.$isOpen ? 'flex' : 'none')}; + justify-content: space-between; + + border: 1px solid #f2f2f2; + border-radius: 0 0 7px 7px; + padding: 16px; + + opacity: ${props => (props.$isOpen ? 1 : 0)}; + animation: ${props => (props.$isOpen ? fadeIn : fadeOut)} 0.2s ease-in-out; +`; + +export const Image = styled.img<{ $isOpen: boolean }>` + width: 20px; + height: 20px; +`; + +const fadeIn = keyframes` + from { + opacity: 0; + height: 0; + } + to { + opacity: 1; + height: auto; + } +`; + +const fadeOut = keyframes` + from { + opacity: 1; + height: auto; + } + to { + opacity: 0; + height: 0; + } +`; diff --git a/frontend/src/mocks/example/get.ts b/frontend/src/mocks/example/get.ts index 7a1b5bf31..7e54b94c4 100644 --- a/frontend/src/mocks/example/get.ts +++ b/frontend/src/mocks/example/get.ts @@ -5,5 +5,3 @@ export const example = [ return res(ctx.status(200)); }), ]; - -window.console.log('husky pre-commit test'); diff --git a/frontend/src/pages/MyInfo/MyInfo.stories.tsx b/frontend/src/pages/MyInfo/MyInfo.stories.tsx new file mode 100644 index 000000000..f615a645c --- /dev/null +++ b/frontend/src/pages/MyInfo/MyInfo.stories.tsx @@ -0,0 +1,14 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import MyInfo from '.'; + +const meta: Meta = { + component: MyInfo, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => , +}; diff --git a/frontend/src/pages/MyInfo/index.tsx b/frontend/src/pages/MyInfo/index.tsx new file mode 100644 index 000000000..ac2a49bf3 --- /dev/null +++ b/frontend/src/pages/MyInfo/index.tsx @@ -0,0 +1,83 @@ +import { useNavigate } from 'react-router-dom'; + +import { User } from '@type/user'; + +import { useToggle } from '@hooks/useToggle'; + +import Accordion from '@components/common/Accordion'; +import UserProfile from '@components/common/Dashboard/UserProfile'; +import IconButton from '@components/common/IconButton'; +import Layout from '@components/common/Layout'; +import Modal from '@components/common/Modal'; +import NarrowTemplateHeader from '@components/common/NarrowTemplateHeader'; +import SquareButton from '@components/common/SquareButton'; + +import * as S from './style'; + +export default function MyInfo() { + const navigate = useNavigate(); + const { isOpen, openComponent, closeComponent } = useToggle(); + + // const { data: userInfo, error, isLoading, isError } = useUserInfo(); // 유저 정보 조회 관련 pr merge 필요 + const MOCK_USER_INFO: User = { + nickname: '우아한 코끼리', + postCount: 4, + voteCount: 128, + userPoint: 200, + }; + + return ( + + + + + { + navigate(-1); + }} + /> + + + + + + + + + + + 변경 + + + + + + + 회원 탈퇴 + + + {isOpen && ( + + + 정말 탈퇴하시겠어요? + + 탈퇴 버튼 클릭 시,

계정은 삭제되며 복구되지 않아요. +
+ + + 탈퇴 + + + 취소 + + +
+
+ )} +
+
+
+
+ ); +} diff --git a/frontend/src/pages/MyInfo/style.ts b/frontend/src/pages/MyInfo/style.ts new file mode 100644 index 000000000..d1f3f6a3f --- /dev/null +++ b/frontend/src/pages/MyInfo/style.ts @@ -0,0 +1,85 @@ +import { styled } from 'styled-components'; + +import { theme } from '@styles/theme'; + +export const Wrapper = styled.div` + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + gap: 30px; + + padding-top: 55px; + position: relative; + + @media (min-width: 768px) { + padding-top: 20px; + } + + @media (min-width: ${theme.breakpoint.md}) { + padding-top: 20px; + } +`; + +export const HeaderWrapper = styled.div` + width: 100%; + + position: fixed; + + z-index: ${theme.zIndex.header}; + + @media (min-width: ${theme.breakpoint.md}) { + display: none; + } +`; + +export const ProfileSection = styled.section` + width: 90%; +`; + +export const UserControlSection = styled.section` + width: 90%; +`; + +export const ButtonWrapper = styled.div` + width: 90px; + height: 50px; +`; + +export const Input = styled.input` + width: 80%; + border: 1px solid #f2f2f2; + padding: 20px; +`; + +export const ModalBody = styled.div` + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + gap: 30px; + + width: 90%; + margin: 40px 20px 0px 16px; + + font: var(--text-caption); +`; + +export const ModalTitle = styled.div` + font: var(--text-title); +`; + +export const ModalDescription = styled.div` + font: var(--text-body); +`; + +export const ButtonListWrapper = styled.div` + display: flex; + justify-content: space-around; + gap: 20px; + + width: 90%; + height: 50px; + + margin-top: 20px; +`; diff --git a/frontend/src/routes/router.tsx b/frontend/src/routes/router.tsx index 027e66222..8f01b522f 100644 --- a/frontend/src/routes/router.tsx +++ b/frontend/src/routes/router.tsx @@ -1,6 +1,7 @@ import { createBrowserRouter } from 'react-router-dom'; import Home from '@pages/Home'; +import MyInfo from '@pages/MyInfo'; import CreatePost from '@pages/post/CreatePost'; import EditPost from '@pages/post/EditPost'; import PostDetailPage from '@pages/post/PostDetail'; @@ -39,9 +40,9 @@ const router = createBrowserRouter([ { path: PATH.USER, children: [ + { path: 'myPage', element: }, { path: 'posts', element: }, { path: 'votes', element: }, - { path: 'myPage', element: }, ], }, ]); diff --git a/frontend/src/styles/theme.ts b/frontend/src/styles/theme.ts index a2a1966ee..ffc90c2b6 100644 --- a/frontend/src/styles/theme.ts +++ b/frontend/src/styles/theme.ts @@ -3,8 +3,8 @@ import { DefaultTheme } from 'styled-components'; const breakpoint = { /** @media (min-width: 576px) { ... } */ sm: '576px', - /** @media (min-width: 960px) { ... } */ - md: '960px', + /** @media (min-width: 768px) { ... } */ + md: '768px', /** @media (min-width: 1440px) { ... }*/ lg: '1440px', };