Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/common/DateTimeChange.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const DateTimeChange = ({ group, page, time }) => {
if (isCheckedTimeStatus(valid)) {
return (
<DateTimeStatus status="introduceDeadline">
모집마감
모집 마감
</DateTimeStatus>
);
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/common/DateTimeChange.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ describe('DateTimeChange', () => {

const { container } = renderDateTimeChange({ group, page, time });

expect(container).toHaveTextContent('모집마감');
expect(container).toHaveTextContent('모집 마감');
});
});

Expand All @@ -153,7 +153,7 @@ describe('DateTimeChange', () => {

const { container } = renderDateTimeChange({ group, page, time });

expect(container).toHaveTextContent('모집마감');
expect(container).toHaveTextContent('모집 마감');
});
});
});
Expand Down
30 changes: 23 additions & 7 deletions src/components/introduce/ApplyStatusButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ const ApplyStatusButtonWrapper = styled.button`
color: ${palette.gray[5]};
}

&.apply-cancel {
cursor: pointer;
background: ${palette.orange[4]};
color: white;
&:hover {
background: ${palette.orange[3]};
}
}

&.apply-complete {
background: ${palette.gray[1]};
border: 2px solid #a5d8ff;
Expand All @@ -32,20 +41,27 @@ const ApplyStatusButtonWrapper = styled.button`
color: white;
cursor: pointer;
background: ${palette.teal[5]};
&:hover{
&:hover {
background: ${palette.teal[4]};
}
}

&.no-login{
cursor: not-allowed;
color: ${palette.gray[5]};
}
`;

const ApplyStatusButton = ({
timeStatus, onApply, applyStatus,
timeStatus, onApply, applyStatus, onCancel,
}) => {
if (!timeStatus && applyStatus) {
return (
<ApplyStatusButtonWrapper
type="button"
className="apply-cancel"
onClick={onCancel}
>
신청 취소
</ApplyStatusButtonWrapper>
);
}

if (applyStatus) {
return (
<ApplyStatusButtonWrapper
Expand Down
10 changes: 9 additions & 1 deletion src/components/introduce/ApplyStatusButton.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,17 @@ describe('ApplyStatusButton', () => {
/>
));

context('When the applicant applies before the application deadline', () => {
it('renders Cancel application', () => {
const { container } = renderApplyStatusButton({ applyStatus: true });

expect(container).toHaveTextContent('신청 취소');
});
});

context('When the study application is completed', () => {
it('renders application completed', () => {
const { container } = renderApplyStatusButton({ applyStatus: true });
const { container } = renderApplyStatusButton({ applyStatus: true, timeStatus: true });

expect(container).toHaveTextContent('신청 완료');
});
Expand Down
3 changes: 2 additions & 1 deletion src/components/introduce/IntroduceHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const IntroduceHeaderWrapper = styled.div`
`;

const IntroduceHeader = ({
group, onApply, user, realTime,
group, onApply, user, realTime, onApplyCancel,
}) => {
const [modal, setModal] = useState(false);

Expand Down Expand Up @@ -56,6 +56,7 @@ const IntroduceHeader = ({
<>
<ApplyStatusButton
user={user}
onCancel={onApplyCancel}
onApply={handleApply}
applyStatus={participants.includes(user)}
timeStatus={isCheckedTimeStatus({ ...group, time: realTime, applyEndTime })}
Expand Down
32 changes: 32 additions & 0 deletions src/components/introduce/IntroduceHeader.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import STUDY_GROUP from '../../../fixtures/study-group';

describe('IntroduceHeader', () => {
const handleApply = jest.fn();
const handleApplyCancel = jest.fn();

beforeEach(() => {
jest.clearAllMocks();
Expand All @@ -19,6 +20,7 @@ describe('IntroduceHeader', () => {
group={group}
realTime={time}
onApply={handleApply}
onApplyCancel={handleApplyCancel}
/>
));

Expand All @@ -28,6 +30,36 @@ describe('IntroduceHeader', () => {
expect(container).toHaveTextContent('스터디를 소개합니다.2');
});

context(`When the application date is earlier than the
deadline date and the application deadline is not reached`, () => {
const time = Date.now();

const nowDate = new Date();
const tomorrow = nowDate.setDate(nowDate.getDate() + 1);

const group = {
...STUDY_GROUP,
applyEndDate: tomorrow,
participants: [
'user2',
'user',
],
personnel: 3,
};

it('Call the cancel application button.', () => {
const { getByText } = renderIntroduceHeader({ group, user: 'user', time });

const button = getByText('신청 취소');

expect(button).not.toBeNull();

fireEvent.click(button);

expect(handleApplyCancel).toBeCalled();
});
});

context('When the author and the logged-in user have the same ID', () => {
it("doesn't renders apply button", () => {
const { container } = renderIntroduceHeader({ group: STUDY_GROUP, user: 'user2' });
Expand Down
11 changes: 9 additions & 2 deletions src/containers/introduce/IntroduceContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useInterval } from 'react-use';
import { useDispatch, useSelector } from 'react-redux';

import { getAuth, getGroup } from '../../util/utils';
import { loadStudyGroup, updateStudyGroup } from '../../reducers/groupSlice';
import { deleteParticipant, loadStudyGroup, updateParticipant } from '../../reducers/groupSlice';

import StudyIntroduceForm from '../../components/introduce/StudyIntroduceForm';
import GroupContentLoader from '../../components/introduce/GroupsContentLoader';
Expand All @@ -27,9 +27,15 @@ const IntroduceContainer = ({ groupId }) => {
}, 1000);

const onApplyStudy = useCallback(() => {
dispatch(updateStudyGroup());
dispatch(updateParticipant());
}, [dispatch]);

const onApplyCancel = useCallback(() => {
if (user) {
dispatch(deleteParticipant());
}
}, [dispatch, user]);

if (!group) {
return (
<GroupContentLoader />
Expand All @@ -43,6 +49,7 @@ const IntroduceContainer = ({ groupId }) => {
group={group}
realTime={realTime}
onApply={onApplyStudy}
onApplyCancel={onApplyCancel}
/>
<StudyIntroduceForm
group={group}
Expand Down
41 changes: 38 additions & 3 deletions src/containers/introduce/IntroduceContainer.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe('IntroduceContainer', () => {
});
});

context('without group', () => {
context('without group ', () => {
given('group', () => (null));

it('renders "loading.." text', () => {
Expand All @@ -73,13 +73,15 @@ describe('IntroduceContainer', () => {
});
});

describe('with user', () => {
context('with group & user', () => {
given('group', () => (STUDY_GROUP));
given('user', () => ('user'));

it('click event dispatches action call updateStudyGroup', () => {
it('click event dispatches action call updateParticipant', () => {
const { getByText } = renderIntroduceContainer(1);

expect(dispatch).toBeCalledTimes(1);

const button = getByText('신청하기');

expect(button).not.toBeNull();
Expand All @@ -89,4 +91,37 @@ describe('IntroduceContainer', () => {
expect(dispatch).toBeCalledTimes(2);
});
});

describe(`When the application date is earlier than the deadline
date and the application deadline is not reached`, () => {
const nowDate = new Date();
const tomorrow = nowDate.setDate(nowDate.getDate() + 1);

const group = {
...STUDY_GROUP,
applyEndDate: tomorrow,
participants: [
'user2',
'user',
],
personnel: 3,
};

given('group', () => (group));
given('user', () => ('user'));

it('click event dispatches action call deleteParticipant', () => {
const { getByText } = renderIntroduceContainer(1);

expect(dispatch).toBeCalledTimes(1);

const button = getByText('신청 취소');

expect(button).not.toBeNull();

fireEvent.click(button);

expect(dispatch).toBeCalledTimes(2);
});
});
});
32 changes: 26 additions & 6 deletions src/reducers/groupSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
getStudyGroup,
getStudyGroups,
postStudyGroup,
updateParticipants,
updatePostParticipant,
deletePostParticipant,
} from '../services/api';

const writeInitialState = {
Expand Down Expand Up @@ -112,14 +113,33 @@ export const writeStudyGroup = () => async (dispatch, getState) => {
dispatch(clearWriteFields());
};

export const updateStudyGroup = () => async (dispatch, getState) => {
const { groupReducer, authReducer } = getState();
export const updateParticipant = () => async (dispatch, getState) => {
const { groupReducer: { group }, authReducer: { user } } = getState();

const newGroup = produce(groupReducer.group, (draft) => {
draft.participants.push(authReducer.user);
const newGroup = produce(group, (draft) => {
draft.participants.push(user);
});

await updateParticipants(newGroup);
await updatePostParticipant({
user,
id: group.id,
});

dispatch(setStudyGroup(newGroup));
};

export const deleteParticipant = () => async (dispatch, getState) => {
const { groupReducer: { group }, authReducer: { user } } = getState();

const newGroup = {
...group,
participants: group.participants.filter((participant) => participant !== user),
};

await deletePostParticipant({
user,
id: group.id,
});

dispatch(setStudyGroup(newGroup));
};
Expand Down
40 changes: 37 additions & 3 deletions src/reducers/groupSlice.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import reducer, {
writeStudyGroup,
clearWriteFields,
successWrite,
updateStudyGroup,
updateParticipant,
deleteParticipant,
} from './groupSlice';

import STUDY_GROUPS from '../../fixtures/study-groups';
Expand Down Expand Up @@ -196,7 +197,7 @@ describe('async actions', () => {
});
});

describe('updateStudyGroup', () => {
describe('updateParticipant', () => {
beforeEach(() => {
store = mockStore({
groupReducer: {
Expand All @@ -209,7 +210,7 @@ describe('async actions', () => {
});

it('dispatches setStudyGroup', async () => {
await store.dispatch(updateStudyGroup());
await store.dispatch(updateParticipant());

const actions = store.getActions();

Expand All @@ -219,4 +220,37 @@ describe('async actions', () => {
}));
});
});

describe('deleteParticipant', () => {
const group = {
id: 1,
participants: [
'user2',
'example',
],
};
const user = 'example';

beforeEach(() => {
store = mockStore({
groupReducer: {
group,
},
authReducer: {
user,
},
});
});

it('dispatches setStudyGroup', async () => {
await store.dispatch(deleteParticipant());

const actions = store.getActions();

expect(actions[0]).toEqual(setStudyGroup({
...group,
participants: group.participants.filter((participant) => participant !== user),
}));
});
});
});
4 changes: 3 additions & 1 deletion src/services/__mocks__/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ export const postUserLogin = jest.fn();

export const postUserLogout = jest.fn();

export const updateParticipants = jest.fn();
export const updatePostParticipant = jest.fn();

export const deletePostParticipant = jest.fn();
Loading