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
54 changes: 54 additions & 0 deletions src/components/introduce/ApplyStatusButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from 'react';

import styled from '@emotion/styled';

const ApplyStatusButtonWrapper = styled.button``;

const ApplyStatusButton = ({
timeStatus, onApply, user, applyStatus,
}) => {
if (applyStatus) {
return (
<ApplyStatusButtonWrapper
type="button"
className="deadline"
>
신청 완료
</ApplyStatusButtonWrapper>
);
}

if (timeStatus) {
return (
<ApplyStatusButtonWrapper
type="button"
className="deadline"
>
모집 마감
</ApplyStatusButtonWrapper>
);
}

if (!user) {
return (
<ApplyStatusButtonWrapper
type="button"
className="deadline"
>
로그인 후 신청 가능합니다.
</ApplyStatusButtonWrapper>
);
}

return (
<ApplyStatusButtonWrapper
type="button"
className="apply"
onClick={onApply}
>
신청하기
</ApplyStatusButtonWrapper>
);
};

export default ApplyStatusButton;
54 changes: 54 additions & 0 deletions src/components/introduce/ApplyStatusButton.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React from 'react';

import { render } from '@testing-library/react';

import ApplyStatusButton from './ApplyStatusButton';

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

const renderApplyStatusButton = ({
applyStatus = false,
timeStatus = false,
user = true,
}) => render((
<ApplyStatusButton
applyStatus={applyStatus}
onApply={handleApply}
timeStatus={timeStatus}
user={user}
/>
));

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

expect(container).toHaveTextContent('신청 완료');
});
});

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

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

context('When not log in', () => {
it('renders "You can apply after logging in." text', () => {
const { container } = renderApplyStatusButton({ user: false });

expect(container).toHaveTextContent('로그인 후 신청 가능합니다.');
});
});

context('When it is possible to apply', () => {
it('renders "apply" text', () => {
const { container } = renderApplyStatusButton({});

expect(container).toHaveTextContent('신청하기');
});
});
});
26 changes: 11 additions & 15 deletions src/components/introduce/StudyIntroduceForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { isCheckedTimeStatus } from '../../util/utils';
import Tags from '../common/Tags';
import palette from '../../styles/palette';
import DateTimeChange from '../common/DateTimeChange';
import ApplyStatusButton from './ApplyStatusButton';

const StudyIntroduceWrapper = styled.div`
margin-top: 6em;
Expand Down Expand Up @@ -99,7 +100,9 @@ const IntroduceContent = styled.div`
padding: 1.5rem;
`;

const StudyIntroduceForm = ({ group, realTime }) => {
const StudyIntroduceForm = ({
group, realTime, onApply, user,
}) => {
const {
title, contents, tags, moderatorId, personnel, participants, applyEndDate,
} = group;
Expand All @@ -110,20 +113,13 @@ const StudyIntroduceForm = ({ group, realTime }) => {
<StudyIntroduceWrapper>
<IntroduceHeaderWrapper>
<h1>{title}</h1>
{isCheckedTimeStatus({ ...group, time: realTime, applyEndTime }) ? (
<button
type="button"
className="deadline"
>
모집마감
</button>
) : (
<button
type="button"
className="apply"
>
신청하기
</button>
{moderatorId !== user && (
<ApplyStatusButton
user={user}
onApply={onApply}
applyStatus={participants.includes(user)}
timeStatus={isCheckedTimeStatus({ ...group, time: realTime, applyEndTime })}
/>
)}
</IntroduceHeaderWrapper>
<ModeratorWrapper>
Expand Down
12 changes: 10 additions & 2 deletions src/components/introduce/StudyIntroduceForm.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import StudyIntroduceForm from './StudyIntroduceForm';
import STUDY_GROUP from '../../../fixtures/study-group';

describe('StudyIntroduceForm', () => {
const renderStudyIntroduceForm = ({ group, time }) => render((
const renderStudyIntroduceForm = ({ group, time, user = 'user' }) => render((
<MemoryRouter>
<StudyIntroduceForm
user={user}
group={group}
realTime={time}
/>
Expand All @@ -22,7 +23,6 @@ describe('StudyIntroduceForm', () => {
const { container } = renderStudyIntroduceForm({ group: STUDY_GROUP });

expect(container).toHaveTextContent('스터디를 소개합니다.2');
expect(container).toHaveTextContent('우리는 이것저것 합니다.2');
});

it('renders links of tags', () => {
Expand All @@ -31,6 +31,14 @@ describe('StudyIntroduceForm', () => {
expect(container.innerHTML).toContain('<a ');
});

context('When the author and the logged-in user have the same ID', () => {
it("doesn't renders apply button", () => {
const { container } = renderStudyIntroduceForm({ group: STUDY_GROUP, user: 'user2' });

expect(container).not.toHaveTextContent('신청하기');
});
});

context('When the study recruitment is closed', () => {
const time = Date.now();

Expand Down
19 changes: 14 additions & 5 deletions src/containers/introduce/IntroduceContainer.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { useEffect, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';

import { useInterval } from 'react-use';
import { useDispatch, useSelector } from 'react-redux';

import { get } from '../../util/utils';
import { loadStudyGroup } from '../../reducers/slice';
import { loadStudyGroup, updateStudyGroup } from '../../reducers/slice';

import StudyIntroduceForm from '../../components/introduce/StudyIntroduceForm';

Expand All @@ -13,16 +13,23 @@ const IntroduceContainer = ({ groupId }) => {

const dispatch = useDispatch();

const group = useSelector(get('group'));
const user = useSelector(get('user'));

useEffect(() => {
dispatch(loadStudyGroup(groupId));
}, []);

const group = useSelector(get('group'));
}, [dispatch, groupId]);

useInterval(() => {
setRealTime(Date.now());
}, 1000);

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

if (!group) {
return (
<div>로딩중..</div>
Expand All @@ -31,8 +38,10 @@ const IntroduceContainer = ({ groupId }) => {

return (
<StudyIntroduceForm
user={user}
group={group}
realTime={realTime}
onApply={onApplyStudy}
/>
);
};
Expand Down
39 changes: 38 additions & 1 deletion src/containers/introduce/IntroduceContainer.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import React from 'react';

import { useDispatch, useSelector } from 'react-redux';

import { render } from '@testing-library/react';
import { fireEvent, render } from '@testing-library/react';

import { MemoryRouter } from 'react-router-dom';

import STUDY_GROUP from '../../../fixtures/study-group';

import IntroduceContainer from './IntroduceContainer';

describe('IntroduceContainer', () => {
Expand All @@ -18,6 +20,7 @@ describe('IntroduceContainer', () => {

useSelector.mockImplementation((state) => state({
group: given.group,
user: given.user,
}));
});

Expand Down Expand Up @@ -65,4 +68,38 @@ describe('IntroduceContainer', () => {
expect(container).toHaveTextContent('로딩중..');
});
});

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

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

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

expect(button).not.toBeNull();

fireEvent.click(button);

expect(dispatch).toBeCalledTimes(2);
});
});

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

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

const button = getByText('로그인 후 신청 가능합니다.');

expect(button).not.toBeNull();

fireEvent.click(button);

expect(dispatch).toBeCalledTimes(1);
});
});
});
19 changes: 18 additions & 1 deletion src/reducers/slice.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
postUserLogin,
postUserLogout,
postUserRegister,
updateParticipants,
} from '../services/api';
import { removeItem, saveItem } from '../services/storage';

Expand Down Expand Up @@ -176,7 +177,10 @@ export const loadStudyGroup = (id) => async (dispatch) => {

const group = await getStudyGroup(id);

dispatch(setStudyGroup(group));
dispatch(setStudyGroup({
...group,
id,
}));
};

export const writeStudyGroup = () => async (dispatch, getState) => {
Expand All @@ -193,6 +197,19 @@ export const writeStudyGroup = () => async (dispatch, getState) => {
dispatch(clearWriteFields());
};

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

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

// TODO: 같은 유저가 들어가도 update 된다. validation 하기
await updateParticipants(newGroup);
Comment on lines +207 to +208
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

유저가 스터디에 신청을 완료했으면 버튼 disable 시키기


dispatch(setStudyGroup(newGroup));
};

export const requestRegister = () => async (dispatch, getState) => {
const { register: { userEmail, password } } = getState();

Expand Down
25 changes: 23 additions & 2 deletions src/reducers/slice.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import reducer, {
setUser,
logout,
requestLogout,
updateStudyGroup,
} from './slice';

import STUDY_GROUPS from '../../fixtures/study-groups';
Expand Down Expand Up @@ -332,7 +333,7 @@ describe('async actions', () => {
const actions = store.getActions();

expect(actions[0]).toEqual(setStudyGroup(null));
expect(actions[1]).toEqual(setStudyGroup(undefined));
expect(actions[1]).toEqual(setStudyGroup({ id: 1 }));
});
});

Expand All @@ -343,7 +344,7 @@ describe('async actions', () => {
});
});

it('dispatches clearWriteFields', async () => {
it('dispatches clearWriteFields and successWrite', async () => {
await store.dispatch(writeStudyGroup());

const actions = store.getActions();
Expand All @@ -353,6 +354,26 @@ describe('async actions', () => {
});
});

describe('updateStudyGroup', () => {
beforeEach(() => {
store = mockStore({
group: STUDY_GROUP,
user: 'example',
});
});

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

const actions = store.getActions();

expect(actions[0]).toEqual(setStudyGroup({
...STUDY_GROUP,
participants: [...STUDY_GROUP.participants, 'example'],
}));
});
});

describe('requestRegister', () => {
const register = {
userEmail: 'seungmin@naver.com',
Expand Down
Loading