Skip to content

Commit

Permalink
feat: 모두의 정원 게시글 아이템 구현 (#340)
Browse files Browse the repository at this point in the history
* refactor: queryClient 데코레이터 리팩터링

* feat: 모두의 정원 게시글 아이템 구현

- garden 타입 추가

* design: 식물 이름 길이 최대 192px로 제한

* chore: alt 추가

* chore: as const 대신 ComponentProps 적용

* chore: 날짜 타입에 DateFormat 적용

* fix: createdAt의 Description, control 에서 7만개의 타입 가져오는 이슈 해결

* feat: 더보기, 줄바꿈 기능이 있는 박스 컴포넌트 추가

* design: 더보기 영역과 내용 영역 분리

* design: 더보기 영역과 내용 영역 분리

* feat: 모두의 정원 아이템과 사전 식물 상세 페이지에 더보기 컴포넌트 적용

* design: 날짜 폰트 변경

* refactor: 내용 영역 크기 줄임

* refactor: showState 상태 관련 로직 훅으로 분리

* chore: Fragment에 key 추가

* chore: transient props 적용

* chore: 사전 식물 이름 추가

* feat: 태그 위치 변경 및 사전 식물 태그 추가

* chore: p 태그 추가

* design: 더보기와의 간격 추가

* chore: <br /> 한줄 제거
  • Loading branch information
bassyu authored and Choi-JJunho committed Sep 20, 2023
1 parent 38ea733 commit 9c7da58
Show file tree
Hide file tree
Showing 11 changed files with 468 additions and 36 deletions.
16 changes: 10 additions & 6 deletions frontend/.storybook/decorators.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Decorator } from '@storybook/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import React from 'react';
import { RecoilRoot } from 'recoil';
import { ThemeProvider } from 'styled-components';
Expand All @@ -14,9 +15,12 @@ export const decorateGlobalStyle: Decorator = (Story) => (
</ThemeProvider>
);

export const decorateToastRoot: Decorator = (Story) => (
<>
<div id="toast-root"></div>
<Story />
</>
);
export const decorateQueryClient: Decorator = (Story) => {
const queryClient = new QueryClient();

return (
<QueryClientProvider client={queryClient}>
<Story />
</QueryClientProvider>
);
};
16 changes: 2 additions & 14 deletions frontend/.storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport';
import type { Preview } from '@storybook/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { initialize, mswLoader } from 'msw-storybook-addon';
import React from 'react';
import { withRouter } from 'storybook-addon-react-router-v6';
import { storybookHandlers } from '../src/mocks/storybookHandlers';
import { decorateGlobalStyle } from './decorators';
import { decorateGlobalStyle, decorateQueryClient } from './decorators';

initialize({
serviceWorker: {
Expand All @@ -24,18 +22,8 @@ const customViewPort = {
},
};

const queryClient = new QueryClient();

const preview: Preview = {
decorators: [
decorateGlobalStyle,
(Story) => (
<QueryClientProvider client={queryClient}>
<Story />
</QueryClientProvider>
),
withRouter,
],
decorators: [decorateGlobalStyle, decorateQueryClient, withRouter],
parameters: {
viewport: {
viewports: { ...INITIAL_VIEWPORTS, ...customViewPort },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { Meta, StoryObj } from '@storybook/react';
import SeeMoreContentBox from '.';

const meta: Meta<typeof SeeMoreContentBox> = {
component: SeeMoreContentBox,
};

export default meta;

type Story = StoryObj<typeof SeeMoreContentBox>;

export const ShortContent: Story = {
args: {
maxHeight: '32px',
children: '안녕하세요',
},
};

export const LongContent: Story = {
args: {
maxHeight: '32px',
children: `
제가 LA에 있을때는 말이죠 정말 제가 꿈에 무대인 메이저리그로 진출해서 가는 식당마다 싸인해달라 기자들은 항상 붙어다니며 취재하고 제가 그 머~ 어~ 대통령이 된 기분이였어요
그런데 17일만에 17일만에 마이너리그로 떨어졌어요 못던져서 그만두고 그냥 확 한국으로 가버리고 싶었어요
그래서 집에 가는길에 그 맥주6개 달린거 있잖아요 맥주6개 그걸 사가지고 집으로 갔어요
그전에는 술먹으면 야구 못하는줄 알았어요 그냥 한국으로
`,
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { styled } from 'styled-components';

export const Wrapper = styled.div<{ $hiddenOver: boolean; $maxHeight: string }>`
position: relative;
width: 100%;
height: ${({ $hiddenOver, $maxHeight }) => ($hiddenOver ? $maxHeight : 'max-content')};
`;

export const ContentBox = styled.div<{ $hiddenOver: boolean; $maxHeight: string }>`
overflow: hidden;
max-height: ${({ $hiddenOver, $maxHeight }) =>
$hiddenOver ? `calc(${$maxHeight} - 1.6rem)` : ''};
`;

export const SeeMoreButtonArea = styled.div`
position: absolute;
z-index: ${(props) => props.theme.zIndex.fixed};
bottom: 0;
display: flex;
justify-content: start;
width: 100%;
`;

export const SeeMoreButton = styled.button`
width: max-content;
height: 1.2rem;
font-size: 1.2rem;
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useState } from 'react';

type ShowState = 'NOT_OVER' | 'HIDDEN_OVER' | 'SHOW_OVER';

const useShowState = (maxHeight: `${number}px`) => {
const [showState, setShowState] = useState<ShowState>('NOT_OVER');

const wrapperRef = (instance: HTMLDivElement | null) => {
if (!instance) return;

const maxHeightNumber = Number(maxHeight.slice(0, -2));

if (showState === 'NOT_OVER' && instance.offsetHeight > maxHeightNumber) {
setShowState('HIDDEN_OVER');
}
};

const showOver = () => {
setShowState('SHOW_OVER');
};

return { showState, wrapperRef, showOver };
};

export default useShowState;
33 changes: 33 additions & 0 deletions frontend/src/components/@common/SeeMoreContentBox/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Fragment } from 'react';
import { ContentBox, SeeMoreButton, SeeMoreButtonArea, Wrapper } from './SeeMoreContentBox.styles';
import useShowState from './hooks/useShowState';

interface SeeMoreContentBoxProps {
children: string;
maxHeight?: `${number}px`;
}

const SeeMoreContentBox = ({ children: content, maxHeight = '64px' }: SeeMoreContentBoxProps) => {
const { showState, wrapperRef, showOver } = useShowState(maxHeight);
const paragraphList = content.trim().split(/(?:\r?\n)+/);

return (
<Wrapper ref={wrapperRef} $maxHeight={maxHeight} $hiddenOver={showState === 'HIDDEN_OVER'}>
<ContentBox $maxHeight={maxHeight} $hiddenOver={showState === 'HIDDEN_OVER'}>
{paragraphList.map((paragraph, index) => (
<Fragment key={paragraph}>
{index !== 0 && <br />}
<p>{paragraph}</p>
</Fragment>
))}
</ContentBox>
{showState === 'HIDDEN_OVER' && (
<SeeMoreButtonArea>
<SeeMoreButton onClick={showOver}>더보기</SeeMoreButton>
</SeeMoreButtonArea>
)}
</Wrapper>
);
};

export default SeeMoreContentBox;
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import PottedPlant from 'components/@common/Icons/PottedPlant';
import ThermometerSnow from 'components/@common/Icons/ThermometerSnow';
import ThermometerSun from 'components/@common/Icons/ThermometerSun';
import Warning from 'components/@common/Icons/Warning';
import SeeMoreContentBox from 'components/@common/SeeMoreContentBox';
import TagBox from 'components/dictionaryPlant/TagBox';
import TagSwitch from 'components/dictionaryPlant/TagSwitch';
import {
Expand Down Expand Up @@ -159,22 +160,9 @@ const DictionaryPlantContent = (props: DictionaryPlantExtendCycles) => {
<ManageInfoBox>
<p>특별 관리 정보</p>
<span>
{specialManageInfo !== NO_INFORMATION
? specialManageInfo
.trim()
.split(/(?:\r?\n)+/)
.map((paragraph, index) =>
index ? (
<>
<br />
<br />
{paragraph}
</>
) : (
paragraph
)
)
: '관련 정보가 없어요😇'}
<SeeMoreContentBox>
{specialManageInfo !== NO_INFORMATION ? specialManageInfo : '관련 정보가 없어요😇'}
</SeeMoreContentBox>
</span>
</ManageInfoBox>
</ContentBox>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type { Meta, StoryObj } from '@storybook/react';
import { ComponentProps } from 'react';
import GardenPostItem from '.';

const meta: Meta<typeof GardenPostItem> = {
component: GardenPostItem,
argTypes: {
createdAt: {
table: {
type: {
summary: 'DateFormat',
detail: 'YYYY-MM-DD 형식의 문자열',
},
},
control: 'text',
},
},
};

export default meta;

type Story = StoryObj<typeof GardenPostItem>;

const defaultArgs: ComponentProps<typeof GardenPostItem> = {
createdAt: '1999-12-16',
dictionaryPlantName: '연봉',
content: '한달차인데 저는 이거 이렇게 키우고있어요',
manageLevel: '초보자',
petPlant: {
imageUrl: 'https://images.unsplash.com/photo-1667342608690-828e1a839ead',
nickname: '초퍼',
location: '거실',
flowerpot: '플라스틱/유리/캔',
light: '창문 밖에서 해를 받아요',
wind: '창문이 없지만 바람이 통해요',
daySince: 32,
waterCycle: 7,
},
};

export const Default: Story = {
args: defaultArgs,
};

export const LargeContent: Story = {
args: {
...defaultArgs,
dictionaryPlantName: '아스파라거스 풀루모수스',
content: `
제가 LA에 있을때는 말이죠 정말 제가 꿈에 무대인 메이저리그로 진출해서 가는 식당마다 싸인해달라 기자들은 항상 붙어다니며 취재하고 제가 그 머~ 어~ 대통령이 된 기분이였어요
그런데 17일만에 17일만에 마이너리그로 떨어졌어요 못던져서 그만두고 그냥 확 한국으로 가버리고 싶었어요
그래서 집에 가는길에 그 맥주6개 달린거 있잖아요 맥주6개 그걸 사가지고 집으로 갔어요
그전에는 술먹으면 야구 못하는줄 알았어요 그냥 한국으로
`,
petPlant: {
...defaultArgs.petPlant,
nickname: '박찬호의 길고길고길고길고길고길고길고긴 식물이름',
},
},
};
Loading

0 comments on commit 9c7da58

Please sign in to comment.