From 38bb82d18267dd849ae6bb56cb7f6ae0af1ce2b7 Mon Sep 17 00:00:00 2001 From: saseungmin Date: Sat, 5 Dec 2020 16:05:34 +0900 Subject: [PATCH 1/2] [Improve] Change CSS for tags in write form --- src/components/common/Tags.jsx | 66 ++++++++++++++++++++++++-- src/components/common/Tags.test.jsx | 22 +++++++-- src/components/write/TagItem.jsx | 15 ------ src/components/write/TagItem.test.jsx | 26 ---------- src/components/write/TagList.jsx | 20 +++----- src/components/write/TagList.test.jsx | 2 +- src/components/write/TagsForm.test.jsx | 2 +- 7 files changed, 88 insertions(+), 65 deletions(-) delete mode 100644 src/components/write/TagItem.jsx delete mode 100644 src/components/write/TagItem.test.jsx diff --git a/src/components/common/Tags.jsx b/src/components/common/Tags.jsx index 5517459..b42a102 100644 --- a/src/components/common/Tags.jsx +++ b/src/components/common/Tags.jsx @@ -1,16 +1,17 @@ import React from 'react'; -import styled from '@emotion/styled'; - import { Link } from 'react-router-dom'; +import styled from '@emotion/styled'; +import { css } from '@emotion/react'; + import palette from '../../styles/palette'; const TagsWrapper = styled.div` margin-top: 1rem; `; -const TagStyledLink = styled(Link)` +const TagStyledWrapper = ({ div }) => css` display: inline-flex; align-items: center; padding-left: 1em; @@ -25,20 +26,75 @@ const TagStyledLink = styled(Link)` &:hover { color: ${palette.teal[5]}; } + + ${div && css` + height: 2.5em; + border-radius: .5em; + margin-right: 0.3rem; + &:hover { + color: ${palette.teal[7]}; + } + `}; `; -const Tags = ({ tags }) => { +const TagWrapper = styled.div` + display: inline-flex; + align-items: center; +`; + +const TagSpanWrapper = styled.span` + height: 2.4em; + margin-right: .5rem; + font-weight: bold; + color: ${palette.warn[2]}; + cursor: pointer; + &:hover { + color: ${palette.warn[0]}; + } +`; + +const TagStyledDiv = styled.div` + ${TagStyledWrapper} +`; +const TagStyledLink = styled(Link)` + ${TagStyledWrapper} +`; + +const Tags = ({ tags, type, onRemove }) => { if (!tags || !tags.length) { return null; } + if (type === 'introduce') { + return ( + + {tags.map((tag) => ( + + + {`#${tag}`} + + onRemove(tag)} + > + x + + + ))} + + ); + } + return ( {tags.map((tag) => ( {`#${tag}`} diff --git a/src/components/common/Tags.test.jsx b/src/components/common/Tags.test.jsx index de58794..135a791 100644 --- a/src/components/common/Tags.test.jsx +++ b/src/components/common/Tags.test.jsx @@ -7,9 +7,10 @@ import { render } from '@testing-library/react'; import Tags from './Tags'; describe('Tags', () => { - const renderTags = (tags) => render(( + const renderTags = ({ tags, type }) => render(( @@ -18,7 +19,7 @@ describe('Tags', () => { context('with tags', () => { const tags = ['JavaScript', 'C', 'Python']; it('renders tags name', () => { - const { container } = renderTags(tags); + const { container } = renderTags({ tags }); tags.forEach((tag) => { expect(container).toHaveTextContent(tag); @@ -32,9 +33,24 @@ describe('Tags', () => { it('nothing renders tags name', () => { const tags = []; - const { container } = renderTags(tags); + const { container } = renderTags({ tags }); expect(container).toBeEmptyDOMElement(); }); }); + + context('with type introduce', () => { + const tags = ['JavaScript', 'C', 'Python']; + const type = 'introduce'; + + it('renders tags name', () => { + const { container } = renderTags({ tags, type }); + + tags.forEach((tag) => { + expect(container).toHaveTextContent(tag); + + expect(container.innerHTML).not.toContain(' ( - - {`#${tag}`} - -); - -export default TagItem; diff --git a/src/components/write/TagItem.test.jsx b/src/components/write/TagItem.test.jsx deleted file mode 100644 index 7255236..0000000 --- a/src/components/write/TagItem.test.jsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; - -import { render } from '@testing-library/react'; - -import TagItem from './TagItem'; - -describe('TagItem', () => { - const handleRemove = jest.fn(); - - const renderTagItem = (tag) => render(( - - )); - - const tag = 'JavaScript'; - - describe('render Tag Item contents text', () => { - it('renders tag text', () => { - const { container } = renderTagItem(tag); - - expect(container).toHaveTextContent('#JavaScript'); - }); - }); -}); diff --git a/src/components/write/TagList.jsx b/src/components/write/TagList.jsx index 380a92a..a14980a 100644 --- a/src/components/write/TagList.jsx +++ b/src/components/write/TagList.jsx @@ -1,10 +1,6 @@ import React from 'react'; -import styled from '@emotion/styled'; - -import TagItem from './TagItem'; - -const TagListWrapper = styled.div``; +import Tags from '../common/Tags'; const TagList = ({ tags, onRemove }) => { const handleRemove = (removeTag) => { @@ -14,15 +10,11 @@ const TagList = ({ tags, onRemove }) => { }; return ( - - {tags.map((tag) => ( - handleRemove(tag)} - /> - ))} - + ); }; diff --git a/src/components/write/TagList.test.jsx b/src/components/write/TagList.test.jsx index 367148a..01dd427 100644 --- a/src/components/write/TagList.test.jsx +++ b/src/components/write/TagList.test.jsx @@ -43,7 +43,7 @@ describe('TagList', () => { tags.forEach((tag) => { expect(getByText(`#${tag}`)).not.toBeNull(); - fireEvent.click(getByText(`#${tag}`)); + fireEvent.click(getByText(`#${tag}`).nextElementSibling); }); expect(handleRemove).toBeCalledTimes(2); }); diff --git a/src/components/write/TagsForm.test.jsx b/src/components/write/TagsForm.test.jsx index b8d9b28..e7450f8 100644 --- a/src/components/write/TagsForm.test.jsx +++ b/src/components/write/TagsForm.test.jsx @@ -115,7 +115,7 @@ describe('TagsForm', () => { const { getByText, container } = renderTagsForm(tags); tags.forEach((tag) => { - fireEvent.click(getByText(`#${tag}`)); + fireEvent.click(getByText(`#${tag}`).nextElementSibling); expect(container).not.toHaveTextContent(`#${tag}`); }); From 98e93ba738d35b88356deac3d8c2513b44484d23 Mon Sep 17 00:00:00 2001 From: saseungmin Date: Sun, 6 Dec 2020 14:20:19 +0900 Subject: [PATCH 2/2] [Improve] Write Page css - Applying a styled component using emotion in Write page --- src/App.test.jsx | 2 +- src/components/auth/AuthForm.jsx | 5 +- src/components/common/Header.jsx | 1 + src/components/write/TagsForm.jsx | 23 +++- src/components/write/TagsForm.test.jsx | 3 +- src/components/write/WriteButtons.jsx | 76 ++++++++++--- src/components/write/WriteEditor.jsx | 62 +++++++---- src/components/write/WriteForm.jsx | 102 ++++++++++++++---- .../write/TagsFormContainer.test.jsx | 3 +- src/index.css | 1 + src/pages/WritePage.jsx | 11 +- src/pages/WritePage.test.jsx | 7 +- src/styles/Button.jsx | 8 +- 13 files changed, 227 insertions(+), 77 deletions(-) diff --git a/src/App.test.jsx b/src/App.test.jsx index 496f11c..b6fe691 100644 --- a/src/App.test.jsx +++ b/src/App.test.jsx @@ -76,7 +76,7 @@ describe('App', () => { it('renders the study write page', () => { const { container } = renderApp({ path: '/write' }); - expect(container).toHaveTextContent('스터디 그룹 개설하기'); + expect(container).toHaveTextContent('내용을 작성해주세요.'); }); }); diff --git a/src/components/auth/AuthForm.jsx b/src/components/auth/AuthForm.jsx index 16c3742..28f6a52 100644 --- a/src/components/auth/AuthForm.jsx +++ b/src/components/auth/AuthForm.jsx @@ -56,10 +56,7 @@ const InputWrapper = styled.input` transition-delay: initial; padding: 8px 12px; border: 2px solid #D7E2EB; - &:focus{ - border: 2px solid ${palette.teal[5]}; - } - &:hover{ + &:focus, &.hover { border: 2px solid ${palette.teal[5]}; } width: 400px; diff --git a/src/components/common/Header.jsx b/src/components/common/Header.jsx index f77b4d2..c041f49 100644 --- a/src/components/common/Header.jsx +++ b/src/components/common/Header.jsx @@ -13,6 +13,7 @@ const HeaderWrapper = styled.div` width: 100%; background: white; box-shadow: 0px 2px 4px ${palette.teal[2]}; + z-index: 100; `; const Wrapper = styled(Responsive)` diff --git a/src/components/write/TagsForm.jsx b/src/components/write/TagsForm.jsx index 0be2449..deb1646 100644 --- a/src/components/write/TagsForm.jsx +++ b/src/components/write/TagsForm.jsx @@ -2,9 +2,23 @@ import React, { useEffect, useState } from 'react'; import styled from '@emotion/styled'; +import palette from '../../styles/palette'; + import TagList from './TagList'; -const TagsFormWrapper = styled.div``; +const TagInputWrapper = styled.input` + height: 30px; + padding: 5px; + border-radius: 0.25rem; + font-size: 1rem; + line-height: 20px; + color: #5f5f5f; + border: 2px solid #D7E2EB; + &:focus, &.hover { + border: 2px solid ${palette.teal[5]}; + } + width: 220px; +`; const TagsForm = ({ onChange, tags }) => { const [tag, setTag] = useState(''); @@ -44,9 +58,8 @@ const TagsForm = ({ onChange, tags }) => { }, [tags]); return ( - -

태그

- + { tags={inputTags} onRemove={handleRemove} /> -
+ ); }; diff --git a/src/components/write/TagsForm.test.jsx b/src/components/write/TagsForm.test.jsx index e7450f8..c30182a 100644 --- a/src/components/write/TagsForm.test.jsx +++ b/src/components/write/TagsForm.test.jsx @@ -20,10 +20,9 @@ describe('TagsForm', () => { describe('render Tag Form Container contents text', () => { it('renders tag form text', () => { - const { getByPlaceholderText, container } = renderTagsForm([]); + const { getByPlaceholderText } = renderTagsForm([]); expect(getByPlaceholderText('태그를 입력하세요')).not.toBeNull(); - expect(container).toHaveTextContent('태그'); }); }); diff --git a/src/components/write/WriteButtons.jsx b/src/components/write/WriteButtons.jsx index c3968e0..01b8d47 100644 --- a/src/components/write/WriteButtons.jsx +++ b/src/components/write/WriteButtons.jsx @@ -1,27 +1,71 @@ import React from 'react'; import styled from '@emotion/styled'; +import { css } from '@emotion/react'; -const WriteButtonsWrapper = styled.div``; +import palette from '../../styles/palette'; + +import Button from '../../styles/Button'; + +const WriteButtonsWrapper = styled.div` + margin-top: 3rem; + + ${(props) => props.error && css` + margin-top: 2rem; + `}; +`; + +const ButtonWrapper = styled.div` + display: flex; + flex-direction: row; + justify-content: space-between; +`; + +const ErrorWrapper = styled.div` + margin-top: 2rem; + font-weight: bold; + font-size: 1rem; + color: ${palette.warn[2]}; +`; + +const CancelButton = styled(Button)` + padding: 0.45rem 1rem; + background: white; + color: ${palette.warn[1]}; + &:hover { + color: white; + background: ${palette.warn[1]}; + } +`; + +const SubmitButton = styled(Button)` + padding: 0.45rem 5rem; +`; const WriteButtons = ({ error, onSubmit, onCancel }) => ( - + <> {error && ( -
{error}
+ {error} )} - - -
+ + + + 등록하기 + + + 취소 + + + + ); export default WriteButtons; diff --git a/src/components/write/WriteEditor.jsx b/src/components/write/WriteEditor.jsx index 5b7a318..1f0e2c6 100644 --- a/src/components/write/WriteEditor.jsx +++ b/src/components/write/WriteEditor.jsx @@ -6,9 +6,30 @@ import { EditorState, convertToRaw } from 'draft-js'; import draftToHtml from 'draftjs-to-html'; import { Editor } from 'react-draft-wysiwyg'; +import palette from '../../styles/palette'; + import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css'; -const WriteEditorWrapper = styled.div``; +const WriteEditorWrapper = styled.div` + margin-top: 1rem; + .toolbar { + padding: 6px 5px; + box-shadow: rgba(0, 0, 0, 0.04) 0px 0px 5px 0px; + } + + .editor { + margin-left: 1rem; + } +`; + +const SpaceBlock = styled.div` + background: ${palette.gray[3]}; + height: 2px; + width: 100%; + margin-top: 1rem; + margin-bottom: 1rem; + border-radius: 1px; +`; const WriteEditor = ({ onChange }) => { const [editorState, setEditorState] = useState(EditorState.createEmpty()); @@ -22,23 +43,28 @@ const WriteEditor = ({ onChange }) => { }; return ( - - - + <> + + + + + ); }; diff --git a/src/components/write/WriteForm.jsx b/src/components/write/WriteForm.jsx index 955d85e..19d8a59 100644 --- a/src/components/write/WriteForm.jsx +++ b/src/components/write/WriteForm.jsx @@ -2,7 +2,74 @@ import React from 'react'; import styled from '@emotion/styled'; -const WriteFormWrapper = styled.div``; +import palette from '../../styles/palette'; + +const WriteFormWrapper = styled.div` + display: flex; + flex-direction: column; +`; + +const WriteTitleInputWrapper = styled.input` + width: 100%; + font-size: 2.75rem; + font-weight: bold; + color: ${palette.gray[7]}; + line-height: 1.5; +`; + +const DateInputWrapper = styled.input` + margin-left: 0.5rem; + padding: 0.5rem; + border-bottom: 2px solid ${palette.gray[5]}; + font-size: 1rem; + width: fit-content; + &:focus, &:hover{ + border-bottom: 2px solid ${palette.gray[7]}; + } +`; + +const NumberInputWrapper = styled.input` + margin-left: 0.5rem; + padding: 0.5rem; + border-bottom: 2px solid ${palette.gray[5]}; + font-size: 1rem; + width: 50px; + &:focus, &:hover{ + border-bottom: 2px solid ${palette.gray[7]}; + } +`; + +const SpaceWrapper = styled.div` + background: ${palette.violet[3]}; + height: 6px; + width: 8rem; + margin-top: 1rem; + margin-bottom: 1rem; + border-radius: 1px; +`; + +const LabelWrapper = styled.label` + font-size: 1.3rem; + font-weight: bold; + margin-right: 1rem; + color: ${palette.gray[7]}; + ::before { + content: '*'; + display: inline-block; + vertical-align: top; + font-weight: 400; + color: ${palette.warn[1]}; + margin: 0 0.125rem 0 0; + font-size: 1.25rem; + line-height: 1.25rem; + } +`; + +const WriteDivBlock = styled.div` + display: flex; + flex-direction: column; + margin-top: 1rem; +`; const WriteForm = ({ onChange, fields }) => { const { @@ -17,28 +84,27 @@ const WriteForm = ({ onChange, fields }) => { return ( -
- -
-
- - + + + 모집 마감 날짜 + -
-
- - + + 참여 인원 수 + { onChange={handleChange} id="participants-number" /> -
+
); }; diff --git a/src/containers/write/TagsFormContainer.test.jsx b/src/containers/write/TagsFormContainer.test.jsx index 38ee916..1562a1f 100644 --- a/src/containers/write/TagsFormContainer.test.jsx +++ b/src/containers/write/TagsFormContainer.test.jsx @@ -29,10 +29,9 @@ describe('TagsFormContainer', () => { describe('render Tag Form Container contents text', () => { it('renders tag form text', () => { - const { getByPlaceholderText, container } = renderTagsFormContainer(); + const { getByPlaceholderText } = renderTagsFormContainer(); expect(getByPlaceholderText('태그를 입력하세요')).not.toBeNull(); - expect(container).toHaveTextContent('태그'); }); }); diff --git a/src/index.css b/src/index.css index c859bcb..b4984a2 100644 --- a/src/index.css +++ b/src/index.css @@ -22,6 +22,7 @@ body { } input { + border: none; outline:none; } diff --git a/src/pages/WritePage.jsx b/src/pages/WritePage.jsx index b305edb..47fa9a0 100644 --- a/src/pages/WritePage.jsx +++ b/src/pages/WritePage.jsx @@ -1,5 +1,7 @@ import React from 'react'; +import styled from '@emotion/styled'; + import Responsive from '../styles/Responsive'; import TagFormContainer from '../containers/write/TagsFormContainer'; @@ -7,13 +9,16 @@ import WriteButtonsContainer from '../containers/write/WriteButtonsContainer'; import WriteEditorContainer from '../containers/write/WriteEditorContainer'; import WriteFormContainer from '../containers/write/WriteFormContainer'; +const WritePageWrapper = styled(Responsive)` + margin-top: 4rem; +`; + const IntroducePage = () => ( - -

스터디 그룹 개설하기

+ -
+ ); export default IntroducePage; diff --git a/src/pages/WritePage.test.jsx b/src/pages/WritePage.test.jsx index 90e4900..02d240c 100644 --- a/src/pages/WritePage.test.jsx +++ b/src/pages/WritePage.test.jsx @@ -31,10 +31,10 @@ describe('WritePage', () => { )); describe('render Write Page contents text', () => { - it('renders Write Page Title', () => { + it('renders Write Editor placeholder text', () => { const { container } = renderWritePage(); - expect(container).toHaveTextContent('스터디 그룹 개설하기'); + expect(container).toHaveTextContent('내용을 작성해주세요.'); }); it('renders write form tag', () => { @@ -44,10 +44,9 @@ describe('WritePage', () => { }); it('renders tag form text', () => { - const { getByPlaceholderText, container } = renderWritePage(); + const { getByPlaceholderText } = renderWritePage(); expect(getByPlaceholderText('태그를 입력하세요')).not.toBeNull(); - expect(container).toHaveTextContent('태그'); }); it('renders buttons', () => { diff --git a/src/styles/Button.jsx b/src/styles/Button.jsx index c66975c..ccb4aaf 100644 --- a/src/styles/Button.jsx +++ b/src/styles/Button.jsx @@ -32,22 +32,22 @@ const ButtonWrapper = ({ warn, success }) => css` color: white; padding: 0.15rem 0.9rem; background: ${palette.warn[1]}; - border: 1px solid ${palette.warn[1]}; + border: 2px solid ${palette.warn[1]}; &:hover { background: white; color: ${palette.warn[1]}; - border: 1px solid ${palette.warn[1]}; + border: 2px solid ${palette.warn[1]}; } `} ${success && css` color: white; background: ${palette.teal[5]}; - border: 1px solid ${palette.teal[5]}; + border: 2px solid ${palette.teal[5]}; &:hover { background: white; color: ${palette.teal[5]}; - border: 1px solid ${palette.teal[5]}; + border: 2px solid ${palette.teal[5]}; } `} `;