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
21 changes: 19 additions & 2 deletions src/components/introduce/ModeratorViewButton.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import React from 'react';
import React, { useState } from 'react';

import StyledApplyStatusButton from '../../styles/StyledApplyStatusButton';
import ParticipantListModal from './modals/ParticipantListModal';

const ModeratorViewButton = ({ group, user }) => {
const { moderatorId } = group;
const [ListModal, setListModal] = useState(false);

const { moderatorId, participants } = group;

const handleClick = () => {
setListModal(true);
};

const handelClose = () => {
setListModal(false);
};

if (moderatorId !== user) {
return null;
Expand All @@ -14,9 +25,15 @@ const ModeratorViewButton = ({ group, user }) => {
<StyledApplyStatusButton
type="button"
className="confirm"
onClick={handleClick}
>
스터디 참여 승인하기
</StyledApplyStatusButton>
<ParticipantListModal
visible={ListModal}
onClose={handelClose}
participants={participants.filter((_, index) => index !== 0)}
/>
</>
);
};
Expand Down
34 changes: 32 additions & 2 deletions src/components/introduce/ModeratorViewButton.test.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';

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

import ModeratorViewButton from './ModeratorViewButton';

Expand Down Expand Up @@ -31,12 +31,42 @@ describe('ModeratorViewButton', () => {
const group = {
...STUDY_GROUP,
moderatorId: 'user',
participants: [
{
confirm: true,
id: 'test1',
},
{
confirm: false,
id: 'test2',
},
],
};

it('nothing renders', () => {
it('renders button', () => {
const { container } = renderModeratorViewButton({ group, user: 'user' });

expect(container).toHaveTextContent('스터디 참여 승인하기');
});

it('click button and renders List of study applicants', () => {
const { getByText, container } = renderModeratorViewButton({ group, user: 'user' });
const button = getByText('스터디 참여 승인하기');

fireEvent.click(button);

expect(container).toHaveTextContent('스터디 신청자 목록');
});

it('Clicking the close button closes the modal window.', () => {
const { getByText, container } = renderModeratorViewButton({ group, user: 'user' });
const button = getByText('스터디 참여 승인하기');

fireEvent.click(button);

fireEvent.click(getByText('닫기'));

expect(container).not.toHaveTextContent('스터디 신청자 목록');
});
});
});
190 changes: 190 additions & 0 deletions src/components/introduce/modals/ParticipantListModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import React from 'react';

import styled from '@emotion/styled';

import { css } from '@emotion/react';

import Button from '../../../styles/Button';
import palette from '../../../styles/palette';

const ParticipantListModalWrapper = styled.div`
position: fixed;
z-index: 101;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.25);
display: flex;
justify-content: center;
align-items: center;

${(props) => props.visible && css`
&.animation {
animation-name: fade-in;
animation-fill-mode: both;
animation-duration: 0.3s;
}

@keyframes fade-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
`};
`;

const ModalBoxWrapper = styled.div`
display: flex;
flex-direction: column;
height: 550px;
width: 600px;
background: white;
padding: 1.5rem;
border-radius: 6px;
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.125);
h2 {
margin-top: 0;
margin-bottom: 1rem;
text-align: center;
}
.buttons {
display: flex;
justify-content: flex-end;
}
`;

const ParticipantTitleWrapper = styled.div`
display: grid;
grid-template-columns: 260px 190px 145px;
justify-items: center;
align-items: center;
margin: 0.5rem 0 0.3rem 0;

div {
font-size: 1.1rem;
font-weight: bold;
margin-bottom: 0.3rem;
}
`;

const ParticipantListWrapper = styled.div`
height: 100%;
width: 99%;
border: 2px solid ${palette.gray[3]};
border-radius: 4px;
margin-bottom: 1rem;
padding-top: 0.5rem;
overflow-y: auto;
`;

const ParticipantListBlock = styled.div`
display: grid;
grid-template-columns: 260px 186px 148px;
justify-items: center;
align-items: center;
margin-bottom: 0.5rem;
min-height: 0;
min-width: 0;
`;

const StyledButton = styled(Button)`
&:last-of-type {
margin-left: .7rem;
}
`;

const ConfirmButton = styled.button`
transition-duration: 0.08s;
transition-property: all;
transition-timing-function: ease-in-out;
transition-delay: initial;
font-size: 0.8rem;
padding: 0.15rem 0.7rem;
font-weight: bold;
outline: none;
border: none;
cursor: pointer;
background: none;
font-family: 'Noto Sans KR', sans-serif;
border-radius: 4px;
color: white;

${({ sky }) => sky && css`
background: #74c0fc;
border-bottom: 2px solid #4dabf7;
box-shadow: 0px 2px 4px #4dabf7;
&:hover{
box-shadow: none;
border-bottom: 2px solid #74c0fc;
}
`};

${({ confirm }) => confirm && css`
background: ${palette.teal[5]};
border-bottom: 2px solid ${palette.teal[6]};
box-shadow: 0px 2px 4px ${palette.teal[6]};
&:hover{
box-shadow: none;
border-bottom: 2px solid ${palette.teal[5]};
}
`};

${({ cancel }) => cancel && css`
background: ${palette.warn[1]};
border-bottom: 2px solid ${palette.warn[2]};
box-shadow: 0px 2px 4px ${palette.warn[2]};
&:hover{
box-shadow: none;
border-bottom: 2px solid ${palette.warn[1]};
}
`};
`;
Comment on lines +100 to +145
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

  • Button Styled Component를 따로 분리해주기.


const ParticipantListModal = ({ visible, onClose, participants }) => {
if (!visible) {
return null;
}

return (
<ParticipantListModalWrapper visible className="animation">
<ModalBoxWrapper>
<h2>스터디 신청자 목록 🙋‍♂️</h2>
<ParticipantTitleWrapper>
<div>신청자 이메일</div>
<div>신청서 보기</div>
<div>승인 여부</div>
</ParticipantTitleWrapper>
<ParticipantListWrapper>
{participants.length && participants.map(({ id, confirm }) => (
<ParticipantListBlock key={id}>
<div>{id}</div>
<div>
<ConfirmButton sky>
신청서 보기
</ConfirmButton>
</div>
<div>
{confirm === true ? (
<ConfirmButton cancel>취소하기</ConfirmButton>
) : (
<ConfirmButton confirm>승인하기</ConfirmButton>
)}
</div>
</ParticipantListBlock>
))}
<>
</>
</ParticipantListWrapper>
<div className="buttons">
<StyledButton onClick={onClose}>닫기</StyledButton>
</div>
</ModalBoxWrapper>
</ParticipantListModalWrapper>
);
};

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

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

import ParticipantListModal from './ParticipantListModal';

describe('ParticipantListModal', () => {
const handleClose = jest.fn();

const renderParticipantListModal = ({ visible, participants }) => render((
<ParticipantListModal
visible={visible}
participants={participants}
onClose={handleClose}
/>
));

context('with visible', () => {
const modal = {
visible: true,
participants: [{
confirm: false,
id: 'test',
}],
};

it('renders Modal text', () => {
const { container } = renderParticipantListModal(modal);

expect(container).toHaveTextContent('스터디 신청자 목록 🙋‍♂️');
expect(container).toHaveTextContent('신청서 보기');
expect(container).toHaveTextContent('test');
});

it('click button call close', () => {
const { getByText } = renderParticipantListModal(modal);

const button = getByText('닫기');

fireEvent.click(button);

expect(handleClose).toBeCalled();
});
});

context('without visible', () => {
const modal = {
visible: false,
};

it("doesn't renders Modal text", () => {
const { container } = renderParticipantListModal(modal);

expect(container).toBeEmptyDOMElement();
});
});
});