diff --git a/src/components/introduce/IntroduceHeader.jsx b/src/components/introduce/IntroduceHeader.jsx index 6909f17..0c0de51 100644 --- a/src/components/introduce/IntroduceHeader.jsx +++ b/src/components/introduce/IntroduceHeader.jsx @@ -24,7 +24,7 @@ const IntroduceHeaderWrapper = styled.div` `; const IntroduceHeader = ({ - group, onApply, user, realTime, onApplyCancel, onChangeApplyFields, applyFields, + group, onApply, user, realTime, onApplyCancel, onChangeApplyFields, applyFields, clearForm, }) => { const [loginCheckModal, setLoginCheckModal] = useState(false); const [applyCancelModal, setApplyCancelModal] = useState(false); @@ -69,6 +69,7 @@ const IntroduceHeader = ({ const handleFormCancel = () => { setModalForm(false); + clearForm(); }; return ( diff --git a/src/components/introduce/IntroduceHeader.test.jsx b/src/components/introduce/IntroduceHeader.test.jsx index 2260338..124c174 100644 --- a/src/components/introduce/IntroduceHeader.test.jsx +++ b/src/components/introduce/IntroduceHeader.test.jsx @@ -9,6 +9,7 @@ import STUDY_GROUP from '../../../fixtures/study-group'; describe('IntroduceHeader', () => { const handleApply = jest.fn(); const handleApplyCancel = jest.fn(); + const handleClearForm = jest.fn(); beforeEach(() => { jest.clearAllMocks(); @@ -18,10 +19,11 @@ describe('IntroduceHeader', () => { )); @@ -214,7 +216,6 @@ describe('IntroduceHeader', () => { fireEvent.click(button); - // TODO: 이 부분은 추후 변경해야된다 현재 스터디 참여 신청서 모달창이 나타남. fireEvent.click(getByText('확인')); expect(handleApply).toBeCalled(); diff --git a/src/components/introduce/modals/ApplicationFormModal.jsx b/src/components/introduce/modals/ApplicationFormModal.jsx index 37c6947..9aa16cb 100644 --- a/src/components/introduce/modals/ApplicationFormModal.jsx +++ b/src/components/introduce/modals/ApplicationFormModal.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import styled from '@emotion/styled'; @@ -96,6 +96,13 @@ const ContentTextareaWrapper = styled.textarea` &:focus { border: 2px solid ${palette.teal[5]}; } + + ${({ error }) => error && css` + &::placeholder { + color: ${palette.warn[1]}; + } + border: 2px solid ${palette.warn[1]}; + `}; `; const StyledButton = styled(Button)` @@ -107,14 +114,37 @@ const StyledButton = styled(Button)` const ApplicationFormModal = ({ visible, onCancel, onConfirm, onChangeApply, fields, }) => { + const [error, setError] = useState(null); + const { reason, wantToGet } = fields; const handleChange = (e) => { const { name, value } = e.target; + setError(null); onChangeApply({ name, value }); }; + const handleCancel = () => { + setError(null); + onCancel(); + }; + + const handleConfirm = () => { + if (!reason.trim()) { + setError('reason'); + return; + } + + if (!wantToGet.trim()) { + setError('wantToGet'); + return; + } + + setError(null); + onConfirm(); + }; + if (!visible) { return null; } @@ -126,6 +156,7 @@ const ApplicationFormModal = ({
- 취소 - 확인 + 취소 + 확인
diff --git a/src/components/introduce/modals/ApplicationFormModal.test.jsx b/src/components/introduce/modals/ApplicationFormModal.test.jsx index bb9479a..7f023ad 100644 --- a/src/components/introduce/modals/ApplicationFormModal.test.jsx +++ b/src/components/introduce/modals/ApplicationFormModal.test.jsx @@ -9,6 +9,10 @@ describe('ApplicationFormModal', () => { const handleConfirm = jest.fn(); const handleChange = jest.fn(); + beforeEach(() => { + jest.clearAllMocks(); + }); + const renderApplicationFormModal = ({ visible, fields }) => render(( { )); context('with visible', () => { - const modal = { - visible: true, - fields: { - reason: '', - wantToGet: '', - }, - }; + context('with applyFields value', () => { + const modal = { + visible: true, + fields: { + reason: 'reason', + wantToGet: 'wantToGet', + }, + }; - it('renders Modal text', () => { - const { container } = renderApplicationFormModal(modal); + it('renders Modal text', () => { + const { container } = renderApplicationFormModal(modal); - expect(container).toHaveTextContent('스터디 참여 신청서 📚'); - expect(container).toHaveTextContent('신청하게 된 이유'); - expect(container).toHaveTextContent('스터디를 통해 얻고 싶은 것은 무엇인가요?'); - }); + expect(container).toHaveTextContent('스터디 참여 신청서 📚'); + expect(container).toHaveTextContent('신청하게 된 이유'); + expect(container).toHaveTextContent('스터디를 통해 얻고 싶은 것은 무엇인가요?'); + }); - it('calls confirm event action', () => { - const { getByText } = renderApplicationFormModal(modal); + it('calls confirm event action', () => { + const { getByText } = renderApplicationFormModal(modal); - const button = getByText('확인'); + const button = getByText('확인'); - fireEvent.click(button); + fireEvent.click(button); - expect(handleConfirm).toBeCalled(); - }); + expect(handleConfirm).toBeCalled(); + }); + + it('calls cancel event action', () => { + const { getByText } = renderApplicationFormModal(modal); - it('calls cancel event action', () => { - const { getByText } = renderApplicationFormModal(modal); + const button = getByText('취소'); - const button = getByText('취소'); + fireEvent.click(button); - fireEvent.click(button); + expect(handleCancel).toBeCalled(); + }); - expect(handleCancel).toBeCalled(); + it('change apply form fields', () => { + const { getByLabelText } = renderApplicationFormModal(modal); + + const input = getByLabelText('신청하게 된 이유'); + + fireEvent.change(input, { target: { name: 'reason', value: '내용' } }); + + expect(handleChange).toBeCalled(); + }); }); - it('change apply form fields', () => { - const { getByLabelText } = renderApplicationFormModal(modal); + context('without applyFields value', () => { + it("doesn't reason value", () => { + const modal = { + visible: true, + fields: { + reason: '', + wantToGet: 'wantToGet', + }, + }; + + const { getByText, getByLabelText } = renderApplicationFormModal(modal); + + const button = getByText('확인'); + + fireEvent.click(button); + + expect(handleConfirm).not.toBeCalled(); + + expect(getByLabelText('신청하게 된 이유')).toHaveStyle('border: 2px solid #ff8787;'); + }); + + it("doesn't wantToGet value", () => { + const modal = { + visible: true, + fields: { + reason: 'reason', + wantToGet: '', + }, + }; + + const { getByText, getByLabelText } = renderApplicationFormModal(modal); + + const button = getByText('확인'); - const input = getByLabelText('신청하게 된 이유'); + fireEvent.click(button); - fireEvent.change(input, { target: { name: 'reason', value: '내용' } }); + expect(handleConfirm).not.toBeCalled(); - expect(handleChange).toBeCalled(); + expect(getByLabelText('스터디를 통해 얻고 싶은 것은 무엇인가요?')).toHaveStyle('border: 2px solid #ff8787;'); + }); }); }); diff --git a/src/containers/introduce/IntroduceContainer.jsx b/src/containers/introduce/IntroduceContainer.jsx index 856f6d4..472a274 100644 --- a/src/containers/introduce/IntroduceContainer.jsx +++ b/src/containers/introduce/IntroduceContainer.jsx @@ -5,7 +5,7 @@ import { useDispatch, useSelector } from 'react-redux'; import { getAuth, getGroup } from '../../util/utils'; import { - changeApplyFields, deleteParticipant, loadStudyGroup, updateParticipant, + changeApplyFields, clearApplyFields, deleteParticipant, loadStudyGroup, updateParticipant, } from '../../reducers/groupSlice'; import StudyIntroduceForm from '../../components/introduce/StudyIntroduceForm'; @@ -41,6 +41,10 @@ const IntroduceContainer = ({ groupId }) => { dispatch(changeApplyFields({ name, value })); }, [dispatch]); + const clearApplyForm = useCallback(() => { + dispatch(clearApplyFields()); + }, [dispatch]); + if (!group) { return ( @@ -56,6 +60,7 @@ const IntroduceContainer = ({ groupId }) => { applyFields={applyFields} onApply={onApplyStudy} onApplyCancel={onApplyCancel} + clearForm={clearApplyForm} onChangeApplyFields={onChangeApplyFields} /> { given('group', () => (STUDY_GROUP)); given('user', () => ('user')); given('applyFields', () => ({ - reason: '', - wantToGet: '', + reason: 'reason', + wantToGet: 'wantToGet', })); it('click event dispatches action call updateParticipant', () => { @@ -101,7 +101,6 @@ describe('IntroduceContainer', () => { fireEvent.click(button); - // TODO: 이 부분은 추후 수정해야된다. 현재 스터디 참여 신청서 모달창으로 인해 테스트 fail되기 때문에 변경해놈 fireEvent.click(getByText('확인')); expect(dispatch).toBeCalledTimes(2); diff --git a/src/reducers/groupSlice.js b/src/reducers/groupSlice.js index a052471..7cf5185 100644 --- a/src/reducers/groupSlice.js +++ b/src/reducers/groupSlice.js @@ -76,6 +76,13 @@ const { actions, reducer } = createSlice({ draft.applyFields[name] = value; }); }, + + clearApplyFields(state) { + return { + ...state, + applyFields: applyInitialState, + }; + }, }, }); @@ -86,6 +93,7 @@ export const { clearWriteFields, successWrite, changeApplyFields, + clearApplyFields, } = actions; export const loadStudyGroups = (tag) => async (dispatch) => { @@ -142,6 +150,7 @@ export const updateParticipant = ({ reason, wantToGet }) => async (dispatch, get }); dispatch(setStudyGroup(newGroup)); + dispatch(clearApplyFields()); }; export const deleteParticipant = () => async (dispatch, getState) => { diff --git a/src/reducers/groupSlice.test.js b/src/reducers/groupSlice.test.js index 501007d..51262db 100644 --- a/src/reducers/groupSlice.test.js +++ b/src/reducers/groupSlice.test.js @@ -15,6 +15,7 @@ import reducer, { updateParticipant, deleteParticipant, changeApplyFields, + clearApplyFields, } from './groupSlice'; import STUDY_GROUPS from '../../fixtures/study-groups'; @@ -151,6 +152,24 @@ describe('reducer', () => { expect(state.applyFields.reason).toBe('참여합니다.'); }); }); + + describe('clearApplyFields', () => { + const initialState = { + applyFields: { + reason: '타이틀', + wantToGet: '내용', + }, + }; + + it('clears fields of application', () => { + const state = reducer(initialState, clearApplyFields()); + + const { applyFields: { reason, wantToGet } } = state; + + expect(reason).toBe(''); + expect(wantToGet).toBe(''); + }); + }); }); describe('async actions', () => { @@ -237,7 +256,7 @@ describe('async actions', () => { wantToGet: '원하는 것', }; - it('dispatches setStudyGroup', async () => { + it('dispatches setStudyGroup and clearApplyFields', async () => { await store.dispatch(updateParticipant(applyFields)); const actions = store.getActions(); @@ -250,6 +269,7 @@ describe('async actions', () => { confirm: false, }], })); + expect(actions[1]).toEqual(clearApplyFields()); }); });