-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
내 정보 페이지 UI 구현, Accordion 컴포넌트 구현 (#179)
* feat: (#175) Accordion 컴포넌트 구현 및 스토리 작성 * feat: (#175) 내정보 페이지 UI/UX 구현 * chore: (#175) 불필요한 코드 삭제 * feat: (#175) 회원 탈퇴 모달 컴포넌트 구현 * feat: (#175) Layout 컴포넌트 추가, 반응형 디자인 구현 * fix: (#175) isPicked props 앞에 $ 기호 추가
- Loading branch information
1 parent
9bcbe4d
commit dac7aa1
Showing
9 changed files
with
390 additions
and
5 deletions.
There are no files selected for viewing
112 changes: 112 additions & 0 deletions
112
frontend/src/components/common/Accordion/Accordion.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<typeof Accordion> = { | ||
component: Accordion, | ||
}; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof Accordion>; | ||
|
||
export const Default: Story = { | ||
render: () => ( | ||
<Accordion title="Click Title to Open Content"> | ||
<span>Hello This is Content!</span> | ||
</Accordion> | ||
), | ||
}; | ||
|
||
export const NicknameChange: Story = { | ||
render: () => ( | ||
<Accordion title="닉네임 변경"> | ||
<Input placeholder="새로운 닉네임을 입력해주세요" /> | ||
<ButtonWrapper> | ||
<SquareButton aria-label="닉네임 변경" theme="fill"> | ||
변경 | ||
</SquareButton> | ||
</ButtonWrapper> | ||
</Accordion> | ||
), | ||
}; | ||
|
||
export const DeleteUserAccount = () => { | ||
const { isOpen, openComponent, closeComponent } = useToggle(); | ||
return ( | ||
<Accordion title="회원 탈퇴"> | ||
<ButtonWrapper> | ||
<SquareButton onClick={openComponent} aria-label="회원 탈퇴" theme="blank"> | ||
회원 탈퇴 | ||
</SquareButton> | ||
</ButtonWrapper> | ||
{isOpen && ( | ||
<Modal size="sm" onModalClose={closeComponent}> | ||
<ModalBody> | ||
<ModalTitle>정말 탈퇴하시겠어요?</ModalTitle> | ||
<ModalDescription> | ||
탈퇴 버튼 클릭 시, <br></br>계정은 삭제되며 복구되지 않아요. | ||
</ModalDescription> | ||
<ButtonListWrapper> | ||
<SquareButton aria-label="회원 탈퇴" theme="fill"> | ||
탈퇴 | ||
</SquareButton> | ||
<SquareButton onClick={closeComponent} aria-label="회원 탈퇴" theme="blank"> | ||
취소 | ||
</SquareButton> | ||
</ButtonListWrapper> | ||
</ModalBody> | ||
</Modal> | ||
)} | ||
</Accordion> | ||
); | ||
}; | ||
|
||
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; | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<S.Wrapper aria-label="Accordion"> | ||
<S.Title onClick={toggleAccordion} aria-controls={`${title}에 대한 내용`}> | ||
{title} | ||
<S.Image src={isOpen ? chevronUp : chevronDown} alt="" $isOpen={isOpen} /> | ||
</S.Title> | ||
<S.Content id={`${title}에 대한 내용`} $isOpen={isOpen}> | ||
{children} | ||
</S.Content> | ||
</S.Wrapper> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,5 +5,3 @@ export const example = [ | |
return res(ctx.status(200)); | ||
}), | ||
]; | ||
|
||
window.console.log('husky pre-commit test'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
import MyInfo from '.'; | ||
|
||
const meta: Meta<typeof MyInfo> = { | ||
component: MyInfo, | ||
}; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof MyInfo>; | ||
|
||
export const Default: Story = { | ||
render: () => <MyInfo />, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<Layout isSidebarVisible={true}> | ||
<S.Wrapper> | ||
<S.HeaderWrapper> | ||
<NarrowTemplateHeader> | ||
<IconButton | ||
category="back" | ||
onClick={() => { | ||
navigate(-1); | ||
}} | ||
/> | ||
</NarrowTemplateHeader> | ||
</S.HeaderWrapper> | ||
<S.ProfileSection> | ||
<UserProfile userInfo={MOCK_USER_INFO} /> | ||
</S.ProfileSection> | ||
<S.UserControlSection> | ||
<Accordion title="닉네임 변경"> | ||
<S.Input placeholder="새로운 닉네임을 입력해주세요" /> | ||
<S.ButtonWrapper> | ||
<SquareButton aria-label="닉네임 변경" theme="fill"> | ||
변경 | ||
</SquareButton> | ||
</S.ButtonWrapper> | ||
</Accordion> | ||
<Accordion title="회원 탈퇴"> | ||
<S.ButtonWrapper> | ||
<SquareButton onClick={openComponent} aria-label="회원 탈퇴" theme="blank"> | ||
회원 탈퇴 | ||
</SquareButton> | ||
</S.ButtonWrapper> | ||
{isOpen && ( | ||
<Modal size="sm" onModalClose={closeComponent}> | ||
<S.ModalBody> | ||
<S.ModalTitle>정말 탈퇴하시겠어요?</S.ModalTitle> | ||
<S.ModalDescription> | ||
탈퇴 버튼 클릭 시, <br></br>계정은 삭제되며 복구되지 않아요. | ||
</S.ModalDescription> | ||
<S.ButtonListWrapper> | ||
<SquareButton aria-label="회원 탈퇴" theme="fill"> | ||
탈퇴 | ||
</SquareButton> | ||
<SquareButton onClick={closeComponent} aria-label="회원 탈퇴" theme="blank"> | ||
취소 | ||
</SquareButton> | ||
</S.ButtonListWrapper> | ||
</S.ModalBody> | ||
</Modal> | ||
)} | ||
</Accordion> | ||
</S.UserControlSection> | ||
</S.Wrapper> | ||
</Layout> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
`; |
Oops, something went wrong.