-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #42 from jhLim97/transaction-detail
[Sprint 1] 거래 내역을 추가 및 수정하는 모달에 대한 UI 구현
- Loading branch information
Showing
15 changed files
with
421 additions
and
9 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
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
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
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,13 @@ | ||
import { useRecoilState } from 'recoil'; | ||
|
||
import { modalState } from '../../recoil/modal/atom'; | ||
|
||
export const useModal = () => { | ||
const [modal, setModal] = useRecoilState(modalState); | ||
|
||
const closeModal = () => { | ||
setModal({ current: null, props: null }); | ||
}; | ||
|
||
return [modal, closeModal]; | ||
}; |
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,24 @@ | ||
import React from 'react'; | ||
|
||
import TransactionUpdateForm from '../TransactionUpdateForm'; | ||
import { useModal } from './hooks'; | ||
import { Wrapper, BackgroundDim } from './style'; | ||
|
||
const TransactionModal = () => { | ||
const [modal, closeModal] = useModal(); | ||
|
||
if (modal.props == null) return null; | ||
return ( | ||
<Wrapper active={modal.current === 'transaction'} data-testid="modal"> | ||
<BackgroundDim data-testid="dim" /> | ||
<TransactionUpdateForm | ||
transaction={modal.props} | ||
onUpdate={() => null} | ||
onDelete={() => null} | ||
onCancle={closeModal} | ||
/> | ||
</Wrapper> | ||
); | ||
}; | ||
|
||
export default TransactionModal; |
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,18 @@ | ||
import styled from 'styled-components'; | ||
|
||
export const Wrapper = styled.div` | ||
display: ${(props) => (props.active ? 'block' : 'none')}; | ||
`; | ||
|
||
export const BackgroundDim = styled.div` | ||
position: fixed; | ||
z-index: 1; | ||
top: 0; | ||
left: 0; | ||
width: 100vw; | ||
height: 100vh; | ||
background-color: ${({ theme }) => theme.color.black}; | ||
opacity: 0.57; | ||
`; |
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,49 @@ | ||
import React from 'react'; | ||
import { RecoilRoot } from 'recoil'; | ||
import { ThemeProvider } from 'styled-components'; | ||
import { render, screen } from '../../test-utils'; | ||
import '@testing-library/jest-dom'; | ||
|
||
import TransactionModal from '.'; | ||
import { modalState } from '../../recoil/modal/atom'; | ||
import theme from '../../styles/theme'; | ||
|
||
const TEST_DATA = { | ||
category: '카페/간식', | ||
color: '#D092E2', | ||
content: '녹차 스무디', | ||
method: '현금', | ||
sign: '-', | ||
cost: '5700', | ||
date: '2022-01-28', | ||
}; | ||
|
||
describe('TransactionModal 컴포넌트 테스트', () => { | ||
const initializeState = ({ set }) => { | ||
set(modalState, { | ||
current: 'transaction', | ||
props: TEST_DATA, | ||
}); | ||
}; | ||
|
||
it('배경 Dim, 입력 폼이 표시된다.', () => { | ||
render( | ||
<RecoilRoot initializeState={initializeState}> | ||
<ThemeProvider theme={theme}> | ||
<TransactionModal /> | ||
</ThemeProvider> | ||
</RecoilRoot>, | ||
); | ||
|
||
const BackgroundDim = screen.getByTestId('dim'); | ||
const transactionForm = screen.getByRole('form', { name: /transaction/i }); | ||
expect(BackgroundDim).toBeInTheDocument(); | ||
expect(transactionForm).toBeInTheDocument(); | ||
}); | ||
|
||
it('현재 선택된 modal의 props 속성이 없으면 모달이 표시되지 않는다.', () => { | ||
render(<TransactionModal />); | ||
const modalDiv = screen.queryByTestId('modal'); | ||
expect(modalDiv).not.toBeInTheDocument(); | ||
}); | ||
}); |
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,94 @@ | ||
import React from 'react'; | ||
|
||
import back from '../../../public/assets/back-button.svg'; | ||
import { Wrapper, Element, BackImg, Input, ButtonContainer, DecisionButton } from './style'; | ||
|
||
const TransactionUpdateForm = ({ transaction, onUpdate, onDelete, onCancle }) => { | ||
const shapedForm = (elements) => { | ||
const data = {}; | ||
Array.from(elements).forEach((element) => { | ||
if (element.value) { | ||
data[element.id] = element.value; | ||
} | ||
}); | ||
return data; | ||
}; | ||
|
||
const handleUpdateSubmit = (e) => { | ||
e.preventDefault(); | ||
|
||
const form = e.target; | ||
const data = shapedForm(form.elements); | ||
onUpdate(data); | ||
}; | ||
|
||
return ( | ||
<Wrapper aria-label="transactionUpdate" onSubmit={handleUpdateSubmit}> | ||
<Element> | ||
<button type="button" aria-label="back" onClick={onCancle}> | ||
<BackImg src={back} /> | ||
</button> | ||
</Element> | ||
<Element> | ||
<label htmlFor="date">날짜</label> | ||
<Input | ||
type="text" | ||
id="date" | ||
placeholder="YYYY-MM-DD" | ||
autoComplete="off" | ||
defaultValue={transaction.date} | ||
/> | ||
</Element> | ||
<Element> | ||
<label htmlFor="category">카테고리</label> | ||
<Input | ||
type="text" | ||
id="category" | ||
placeholder="입력하세요." | ||
autoComplete="off" | ||
defaultValue={transaction.category} | ||
/> | ||
</Element> | ||
<Element> | ||
<label htmlFor="content">내용</label> | ||
<Input | ||
type="text" | ||
id="content" | ||
placeholder="입력하세요." | ||
autoComplete="off" | ||
defaultValue={transaction.content} | ||
/> | ||
</Element> | ||
<Element> | ||
<label htmlFor="method">결제수단</label> | ||
<Input | ||
type="text" | ||
id="method" | ||
placeholder="입력하세요." | ||
autoComplete="off" | ||
defaultValue={transaction.method} | ||
/> | ||
</Element> | ||
<Element> | ||
<label htmlFor="cost">금액</label> | ||
<Input | ||
type="text" | ||
id="cost" | ||
placeholder="숫자만 기입하세요.(ex 3000)" | ||
autoComplete="off" | ||
defaultValue={transaction.cost} | ||
/> | ||
</Element> | ||
<ButtonContainer> | ||
<DecisionButton type="button" action="delete" onClick={onDelete}> | ||
삭제 | ||
</DecisionButton> | ||
<DecisionButton type="submit" action="update"> | ||
수정 | ||
</DecisionButton> | ||
</ButtonContainer> | ||
</Wrapper> | ||
); | ||
}; | ||
|
||
export default TransactionUpdateForm; |
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,88 @@ | ||
import styled from 'styled-components'; | ||
|
||
import { MAX_MOBILE_DEVICE } from '../../utils/constant/device-size'; | ||
|
||
export const Wrapper = styled.form` | ||
position: fixed; | ||
z-index: 2; | ||
width: 700px; | ||
height: 500px; | ||
top: 50%; | ||
left: 50%; | ||
transform: translate(-50%, -50%); | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: center; | ||
align-items: center; | ||
background-color: ${({ theme }) => theme.color.white}; | ||
box-shadow: ${({ theme }) => theme.shadow.thick}; | ||
@media screen and (max-width: ${MAX_MOBILE_DEVICE}px) { | ||
width: 100vw; | ||
height: 100vh; | ||
top: 0; | ||
left: 0; | ||
transform: none; | ||
justify-content: space-evenly; | ||
box-shadow: none; | ||
} | ||
`; | ||
|
||
export const Element = styled.div` | ||
margin-bottom: 10px; | ||
width: 250px; | ||
display: flex; | ||
flex-direction: column; | ||
font-weight: bold; | ||
@media screen and (max-width: ${MAX_MOBILE_DEVICE}px) { | ||
width: 80vw; | ||
height: 12vh; | ||
justify-content: center; | ||
} | ||
`; | ||
|
||
export const BackImg = styled.img` | ||
float: left; | ||
`; | ||
|
||
export const Input = styled.input` | ||
margin-top: 5px; | ||
padding: 10px; | ||
border: 1px solid ${({ theme }) => theme.color.brigtenL1Gray}; | ||
border-radius: 10px; | ||
background: ${({ theme }) => theme.color.whiteSmoke}; | ||
@media screen and (max-width: ${MAX_MOBILE_DEVICE}px) { | ||
height: 80%; | ||
} | ||
`; | ||
|
||
export const ButtonContainer = styled.div` | ||
padding-left: 0px; | ||
width: 250px; | ||
display: flex; | ||
flex-direction: row; | ||
justify-content: space-between; | ||
@media screen and (max-width: ${MAX_MOBILE_DEVICE}px) { | ||
width: 80vw; | ||
height: 12vh; | ||
} | ||
`; | ||
|
||
export const DecisionButton = styled.button` | ||
font-weight: bold; | ||
font-size: ${({ theme }) => theme.fontSize.default}; | ||
color: ${(props) => (props.action === 'update' ? '#0990d6' : '#ff000f')}; | ||
`; |
Oops, something went wrong.