Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 커뮤니티 리스트에 사용되는 FeedCard 구현 #1037

Merged
merged 16 commits into from
Nov 12, 2023
Merged
Show file tree
Hide file tree
Changes from 15 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
4 changes: 4 additions & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ const config: StorybookConfig = {

return config;
},
babel: (options) => ({
...options,
presets: [...(options.presets ?? []), '@emotion/babel-preset-css-prop'],
}),
staticDirs: ['../public'],
docs: {
autodocs: false,
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"@radix-ui/react-tooltip": "^1.0.5",
"@sopt-makers/colors": "^3.0.0",
"@tanstack/react-query": "^5.4.3",
"@toss/emotion-utils": "^1.1.10",
"await-to-js": "^3.0.0",
"axios": "^0.27.2",
"cmdk": "^0.2.0",
Expand Down Expand Up @@ -72,6 +73,7 @@
"@cloudflare/workers-types": "^3.18.0",
"@commitlint/cli": "^16.2.3",
"@commitlint/config-conventional": "^16.2.1",
"@emotion/babel-preset-css-prop": "^11.11.0",
"@mdx-js/react": "^1.6.22",
"@next/bundle-analyzer": "^13.1.1",
"@storybook/addon-actions": "^7.0.23",
Expand Down
33 changes: 24 additions & 9 deletions src/components/common/Checkbox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@ import { forwardRef, InputHTMLAttributes } from 'react';
import IconCheck from '@/public/icons/icon-check.svg';
import { MOBILE_MEDIA_QUERY } from '@/styles/mediaQuery';

interface CheckboxProps extends InputHTMLAttributes<HTMLInputElement> {
interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {
checked?: boolean;
size?: 'small' | 'medium' | 'large';
}

const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(({ checked = false, ...props }, ref) => {
const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(({ checked = false, size = 'medium', ...props }, ref) => {
return (
<StyledLabel>
<input ref={ref} type='checkbox' {...props} />
<StyledCheckbox checked={checked}>{checked && <IconCheck />}</StyledCheckbox>
<StyledCheckbox checked={checked} size={size}>
{checked && <IconCheck />}
</StyledCheckbox>
</StyledLabel>
);
});
Expand Down Expand Up @@ -45,19 +48,31 @@ const StyledCheckbox = styled.span<CheckboxProps>`
background-color: transparent;
width: 22.5px;
height: 22.5px;
${({ checked }) =>
checked &&
css`
border: 1px solid ${colors.blue300};
background-color: ${colors.success};
`}

& > svg {
width: 14px;
height: 9px;
color: ${colors.gray10};
}

${({ size }) =>
size === 'small' &&
css`
width: 16px;
height: 16px;
& > svg {
width: 10px;
height: 6.5px;
}
`}

${({ checked }) =>
checked &&
css`
border: 1px solid ${colors.blue300};
background-color: ${colors.success};
`}

@media ${MOBILE_MEDIA_QUERY} {
width: 17.5px;
height: 17.5px;
Expand Down
147 changes: 138 additions & 9 deletions src/components/community/list/FeedList.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import styled from '@emotion/styled';
import { FC } from 'react';
import { colors } from '@sopt-makers/colors';
import { ComponentProps, FC } from 'react';

import CategorySelect from '@/components/community/list/CategorySelect';
import { FeedDetailLink } from '@/components/community/queryParam';
import { colors } from '@sopt-makers/colors';
import FeedCard from '@/components/feed/main/FeedCard';

interface FeedListProps {}

Expand Down Expand Up @@ -36,13 +37,74 @@ const FeedList: FC<FeedListProps> = ({}) => {
]}
/>
</CategoryArea>
<div>
{[...new Array(100)].map((_, idx) => (
<div key={idx} style={{ height: '80px' }}>
<FeedDetailLink feedId={`feed${idx}`}>Feed {idx}</FeedDetailLink>
</div>
))}
</div>
{/* TODO: api 나오면 변경 */}
<FeedDetailLink feedId={`feed${0}`}>
<FeedCard {...defaultProps}>
<FeedCard.Comment>
{COMMENTS.map((comment) => (
<FeedCard.CommentItem key={comment.id} name={comment.name} comment={comment.comment} />
))}
</FeedCard.Comment>
</FeedCard>
<FeedCard {...defaultProps} isBlindWriter={true}>
<FeedCard.Comment>
{COMMENTS.map((comment) => (
<FeedCard.CommentItem key={comment.id} name={comment.name} comment={comment.comment} />
))}
</FeedCard.Comment>
</FeedCard>
<FeedCard
{...defaultProps}
title='CSS 질문 있습니다!'
content='Flexbox container 안의 자식 item이 container의 width를 넘어가는데 왜 그런지 아시나요?'
isQuestion
>
<FeedCard.Comment>
{QURESTION_COMMENTS.map((comment) => (
<FeedCard.CommentItem key={comment.id} name={comment.name} comment={comment.comment} />
))}
</FeedCard.Comment>
</FeedCard>
<FeedCard {...defaultProps}>
<FeedCard.Comment>
{[
{
id: 1,
name: 'Evan Kim',
comment: 'LGTM!',
},
].map((comment) => (
<FeedCard.CommentItem key={comment.id} name={comment.name} comment={comment.comment} />
))}
</FeedCard.Comment>
</FeedCard>
<FeedCard
{...defaultProps}
title='이번에 당근 리브랜딩'
content='너무 귀엽지 않나요? 당근이 배경화면 공유합니다'
>
<FeedCard.Image>
{IMAGES.map((image) => (
<FeedCard.ImageItem key={image} src={image} />
))}
</FeedCard.Image>
<FeedCard.Comment>
{IMAGES_COMMENTS.map((comment) => (
<FeedCard.CommentItem key={comment.id} name={comment.name} comment={comment.comment} />
))}
</FeedCard.Comment>
</FeedCard>
<FeedCard
{...defaultProps}
title='제목이 겁나 길어서 두 줄로 줄바꿈 될 뿐만 아니라, 말줄임표까지 생기게 하는게 제 목표에요. 당근이는 귀엽지만, 그래도 춘식이에 비하면 그냥 토끼옷을 입은 강아지일 뿐이에요.'
>
<FeedCard.Comment>
{COMMENTS.map((comment) => (
<FeedCard.CommentItem key={comment.id} name={comment.name} comment={comment.comment} />
))}
</FeedCard.Comment>
</FeedCard>
</FeedDetailLink>
</ContainerInner>
</Container>
);
Expand All @@ -69,3 +131,70 @@ const CategoryArea = styled.div`
top: 0;
background-color: ${colors.background};
`;

// dummy
const COMMENTS = [
{
id: 1,
name: '주수한',
comment: '저도 같이 가고 싶어요!',
},
{
id: 2,
name: '이정연',
comment: '도요 도요 저도요!',
},
{
id: 3,
name: '남부장',
comment: '어허, 나는 왜 빼려고 하는가 허허',
},
];

const defaultProps: ComponentProps<typeof FeedCard> = {
name: '이준호',
info: 'Frontend Developer @Toss',
profileImage:
'https://s3.ap-northeast-2.amazonaws.com/sopt-makers-internal//prod/image/project/f7ca1cbd-87e4-4dd3-92cf-e2420afee237-IMG_9435.JPG',
createdAt: '2023.10.23.11:00:11',
title: '다음주 일요일에 클라이밍하러가실 분',
content:
'처음해봐도 괜찮으니까 편하게 오세요! 장소는 클라이밍파크 신논현점 생각하고 있어요. 2시쯤 만나서 하고, 끝나고 같이 고기 먹어요~',
hits: 23,
commentLength: COMMENTS.length,
};

const 당근_배경 =
'https://github.com/sopt-makers/sopt-playground-frontend/assets/26808056/ab298948-935c-41e9-801f-420ba4482a10';
const 노트북_당근 =
'https://github.com/sopt-makers/sopt-playground-frontend/assets/26808056/33e150c1-e8f9-4d02-bf32-1fe1f1e7cc8b';
const 폰배경_당근 =
'https://github.com/sopt-makers/sopt-playground-frontend/assets/26808056/0b168abb-8f7f-4562-874a-b179a1ddd012';

const IMAGES = [당근_배경, 노트북_당근, 폰배경_당근];
const IMAGES_COMMENTS = [
{
id: 1,
name: '이화정',
comment: '너무 귀여워요!! 당근 너무 좋아요',
},
{
id: 2,
name: '민솔',
comment: '배고파요...',
},
];

const QURESTION_COMMENTS = [
{
id: 1,
name: '박건영',
comment:
'flexbox의 자식 컴포넌트의 기본 min-width값은 min-width: auto 에요. 따라서 컨테이너의 width를 넘어가지 않을 것으로 예상되지만, flex-basis: auto보다 min-width: auto가 먼저 적용되어 자식 컴포넌트가 옆으로 길어질 경우 컨테이너의 너비를 초과하게 돼요. 이를 해결하려면 min-width: 0 을 주어야 해요!',
},
{
id: 2,
name: '남부장',
comment: '커사원 잘했네.',
},
];
104 changes: 102 additions & 2 deletions src/components/community/page/FeedHomePage.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { colors } from '@sopt-makers/colors';
import { FC } from 'react';
import { FC, useState } from 'react';

import Responsive from '@/components/common/Responsive';
import DesktopCommunityLayout from '@/components/community/layout/DesktopCommunityLayout';
import MobileCommunityLayout from '@/components/community/layout/MobileCommunityLayout';
import FeedList from '@/components/community/list/FeedList';
import { FeedDetailLink, useFeedDetailParam } from '@/components/community/queryParam';
import DetailFeedCard from '@/components/feed/detail/DetailFeedCard';

const CommunityPage: FC = () => {
const [feed] = useFeedDetailParam();
const [value, setValue] = useState('');
const [isBlind, setIsBlind] = useState(false);

const feedList = <FeedList />;

Expand All @@ -24,7 +27,49 @@ const CommunityPage: FC = () => {
return (
<>
<Responsive only='desktop'>
<DesktopCommunityLayout isDetailOpen={isDetailOpen} listSlot={feedList} detailSlot={feedDetail} />
<DesktopCommunityLayout
isDetailOpen={feed !== ''}
listSlot={feedList}
detailSlot={
<DetailFeedCard>
<DetailFeedCard.Header category='파트' tag='기획' />
<DetailFeedCard.Body>
<DetailFeedCard.Main>
<DetailFeedCard.Top
name='이준호'
profileImage='https://s3.ap-northeast-2.amazonaws.com/sopt-makers-internal//prod/image/project/f7ca1cbd-87e4-4dd3-92cf-e2420afee237-IMG_9435.JPG'
info='Frontend Developer @Toss'
createdAt='2023.10.23.11:00:11'
/>
<DetailFeedCard.Content
isQuestion
title='YC의 스타트업을 위한 필수 조언'
hits={23}
commentLength={COMMENTS.length}
content='라면을 끓일때 엄청난 비법이 필요하기보단 정량의 물을 넣고, 레시피에 주어진 시간만큼 끓이는 것이 중요하듯 기본적인 조언이지만 스타트업 경영과 성장 역시도 필수적인 것을 지키지 않았을 때 크리티컬한 만큼'
images={IMAGES}
/>
</DetailFeedCard.Main>
<DetailFeedCard.Divider />
{COMMENTS.map((comment) => (
<DetailFeedCard.Comment
key={comment.id}
name={comment.name}
image={comment.image}
info={comment.info}
comment={comment.comment}
/>
))}
</DetailFeedCard.Body>
<DetailFeedCard.Input
value={value}
onChange={setValue}
isBlindChecked={isBlind}
onChangeIsBlindChecked={setIsBlind}
/>
</DetailFeedCard>
}
/>
</Responsive>
<Responsive only='mobile'>
<MobileCommunityLayout isDetailOpen={isDetailOpen} listSlot={feedList} detailSlot={feedDetail} />
Expand All @@ -34,3 +79,58 @@ const CommunityPage: FC = () => {
};

export default CommunityPage;

const COMMENTS = [
{
id: 1,
name: '이정연',
image:
'https://s3.ap-northeast-2.amazonaws.com/sopt-makers-internal//prod/image/project/f7ca1cbd-87e4-4dd3-92cf-e2420afee237-IMG_9435.JPG',
info: '28기 디자인 파트',
comment:
'라면을 끓일때 엄청난 비법이 필요하기보단 정량의 물을 넣고, 레시피에 주어진 시간만큼 끓이는 것이 중요하듯 기본적인 조언이지만 스타트업 경영과 성장 역시도 필수적인 것을 지키지 않았을 때 크리티컬한 만큼',
},
{
id: 1,
name: '이정연',
image:
'https://s3.ap-northeast-2.amazonaws.com/sopt-makers-internal//prod/image/project/f7ca1cbd-87e4-4dd3-92cf-e2420afee237-IMG_9435.JPG',
info: '28기 디자인 파트',
comment:
'라면을 끓일때 엄청난 비법이 필요하기보단 정량의 물을 넣고, 레시피에 주어진 시간만큼 끓이는 것이 중요하듯 기본적인 조언이지만 스타트업 경영과 성장 역시도 필수적인 것을 지키지 않았을 때 크리티컬한 만큼',
},
{
id: 1,
name: '이정연',
image:
'https://s3.ap-northeast-2.amazonaws.com/sopt-makers-internal//prod/image/project/f7ca1cbd-87e4-4dd3-92cf-e2420afee237-IMG_9435.JPG',
info: '28기 디자인 파트',
comment:
'라면을 끓일때 엄청난 비법이 필요하기보단 정량의 물을 넣고, 레시피에 주어진 시간만큼 끓이는 것이 중요하듯 기본적인 조언이지만 스타트업 경영과 성장 역시도 필수적인 것을 지키지 않았을 때 크리티컬한 만큼',
},
{
id: 1,
name: '이정연',
image:
'https://s3.ap-northeast-2.amazonaws.com/sopt-makers-internal//prod/image/project/f7ca1cbd-87e4-4dd3-92cf-e2420afee237-IMG_9435.JPG',
info: '28기 디자인 파트',
comment:
'라면을 끓일때 엄청난 비법이 필요하기보단 정량의 물을 넣고, 레시피에 주어진 시간만큼 끓이는 것이 중요하듯 기본적인 조언이지만 스타트업 경영과 성장 역시도 필수적인 것을 지키지 않았을 때 크리티컬한 만큼',
},
{
id: 1,
name: '이정연',
image:
'https://s3.ap-northeast-2.amazonaws.com/sopt-makers-internal//prod/image/project/f7ca1cbd-87e4-4dd3-92cf-e2420afee237-IMG_9435.JPG',
info: '28기 디자인 파트',
comment:
'라면을 끓일때 엄청난 비법이 필요하기보단 정량의 물을 넣고, 레시피에 주어진 시간만큼 끓이는 것이 중요하듯 기본적인 조언이지만 스타트업 경영과 성장 역시도 필수적인 것을 지키지 않았을 때 크리티컬한 만큼',
},
];

const 당근데탑큰것 =
'https://github.com/sopt-makers/sopt-playground-frontend/assets/26808056/37aeca19-f53c-4a96-a42a-a6bfe1cc5f5b';
const 당근데탑작은것 =
'https://github.com/sopt-makers/sopt-playground-frontend/assets/26808056/45efc303-1449-4cc3-b4a0-b475af17ae57';

const IMAGES = [당근데탑큰것, 당근데탑작은것, 당근데탑큰것];
Loading
Loading