Skip to content

Commit

Permalink
✨ feat: list 컴포넌트 구현 및 스토리북 추가 #15
Browse files Browse the repository at this point in the history
  • Loading branch information
froggy1014 committed Aug 3, 2024
1 parent 5ad0ad1 commit c1e1f28
Show file tree
Hide file tree
Showing 6 changed files with 341 additions and 0 deletions.
51 changes: 51 additions & 0 deletions src/components/common/List/BasicTile.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { Meta, StoryObj } from "@storybook/react";

import BasicTile from "./BasicTile";

const meta: Meta<typeof BasicTile> = {
title: "List/BasicTile",
component: BasicTile,
parameters: {
layout: "centered",
},

argTypes: {
active: {
control: {
type: "boolean",
},
},
label: {
control: {
type: "text",
},
},
},
};

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {
render: (args) => (
<div className="w-[360px]">
<BasicTile {...args} />
</div>
),
args: {
active: false,
label: "👫👭 가족",
},
};

export const Active: Story = {
render: (args) => (
<div className="w-[360px]">
<BasicTile {...args} />
</div>
),
args: {
active: true,
label: "👫👭 가족",
},
};
33 changes: 33 additions & 0 deletions src/components/common/List/BasicTile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { FC, HtmlHTMLAttributes } from "react";

// import { X_ICON } from "@/components/icons";
import { cn } from "@/utils/cn";

export interface Props
extends Omit<HtmlHTMLAttributes<HTMLButtonElement>, "children"> {
active: boolean;
label: string;
}

const BasicTile: FC<Props> = ({ active, label, ...props }) => {
return (
<button
type="button"
className={cn(
"w-full h-[54px] duration-300 rounded-[6px] p-[16px]",
"flex items-center justify-center",
active
? "border-[1px] border-primary-01 bg-primary-05"
: "bg-gray-scale-50",
props.className,
)}
{...props}
>
<span className="w-full text-left text-caption2-medium text-gray-scale-600">
{label}
</span>
</button>
);
};

export default BasicTile;
87 changes: 87 additions & 0 deletions src/components/common/List/FestivalTile.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import type { Meta, StoryObj } from "@storybook/react";
import React from "react";

import { HeartIcon } from "@/components/icons";

import IconButton from "../Button/IconButton/IconButton";
import DateTag from "../Tag/DateTag/DateTag";
import FestivalTile from "./FestivalTile";

const meta: Meta<typeof FestivalTile> = {
title: "List/FestivalTile",
component: FestivalTile,
parameters: {
layout: "centered",
},

argTypes: {
festival: {
src: "/images/festival.png",
title: "서울 치맥 페스티벌",
location: "서울 광진구",
duration: "8월 1일 ~ 8월 4일",
dday: "D-2",
},
},
};

export default meta;
type Story = StoryObj<typeof meta>;

export const HomeFestivalTile: Story = {
render: (args) => (
<div className="w-[400px]">
<FestivalTile {...args} />
</div>
),
args: {
festival: {
src: "/images/festival.png",
title: "서울 치맥 페스티벌",
location: "서울 광진구",
duration: "8월 1일 ~ 8월 4일",
dday: "D-2",
},
icon: <DateTag label="D-2" />,
},
};

export const UnScrabedFestivalTile: Story = {
render: (args) => (
<div className="w-[400px]">
<FestivalTile {...args} />
</div>
),
args: {
festival: {
src: "/images/festival.png",
title: "서울 치맥 페스티벌",
location: "서울 광진구",
duration: "8월 1일 ~ 8월 4일",
dday: "D-2",
},
icon: (
<IconButton active={false} icon={<HeartIcon width={24} height={24} />} />
),
},
};

export const ScrabedFestivalTile: Story = {
render: (args) => (
<div className="w-[400px]">
<FestivalTile {...args} />
</div>
),
args: {
festival: {
src: "/images/festival.png",
title: "서울 치맥 페스티벌",
location: "서울 광진구",
duration: "8월 1일 ~ 8월 4일",
dday: "D-2",
},
icon: (
<IconButton active={true} icon={<HeartIcon width={24} height={24} />} />
),
},
};
54 changes: 54 additions & 0 deletions src/components/common/List/FestivalTile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import Image from "next/image";
import { FC, HtmlHTMLAttributes } from "react";

import { cn } from "@/utils/cn";

interface Festival {
src: string;
title: string;
location: string;
duration: string;
dday: string;
}

export interface Props
extends Omit<HtmlHTMLAttributes<HTMLButtonElement>, "children"> {
festival: Festival;
icon: React.ReactNode;
}

const FestivalTile: FC<Props> = ({ festival, icon, ...props }) => {
return (
<button
type="button"
className={cn(
"w-full h-auto duration-300 rounded-[8px] p-[12px]",
"flex items-start justify-between gap-[16px]",
"bg-gray-scale-0",
props.className,
)}
{...props}
>
<div className="relative h-[84px] min-w-[80px]">
<Image src={festival.src} alt={festival.title} fill />
</div>

<div className="flex w-full items-start justify-between py-[8px]">
<div className="flex flex-col items-start">
<span className="text-caption1-regular text-gray-scale-600">
{festival.location}
</span>
<span className="text-subtitle-bold text-gray-scale-700">
{festival.title}
</span>
<span className="text-caption1-medium text-gray-scale-700">
{festival.duration}
</span>
</div>
{icon}
</div>
</button>
);
};

export default FestivalTile;
52 changes: 52 additions & 0 deletions src/components/common/List/ReviewTile.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { Meta, StoryObj } from "@storybook/react";
import React from "react";

import ReviewTile from "./ReviewTile";

const meta: Meta<typeof ReviewTile> = {
title: "List/ReviewTile",
component: ReviewTile,
parameters: {
layout: "centered",
},
};

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {
render: (args) => (
<div className="w-[360px]">
<ReviewTile {...args} />
</div>
),
args: {
festivalReview: {
src: "/images/festivalReview.png",
title: "서울 치맥 페스티벌",
content:
"완전 더워요... 선크림 필수.. 빨간 트럭에서파는 치킨 추천합니당 바삭하고 맛있어요. 바삭하고 맛있어요. 바삭하고 맛있어요.",
duration: "8월 1일 ~ 8월 4일",
rating: 4,
keywords: ["✨쾌적해요", "✨쾌적해요"],
},
},
};

export const WithOutPhoto: Story = {
render: (args) => (
<div className="w-[360px]">
<ReviewTile {...args} />
</div>
),
args: {
festivalReview: {
title: "서울 치맥 페스티벌",
content:
"완전 더워요... 선크림 필수.. 빨간 트럭에서파는 치킨 추천합니당 바삭하고 맛있어요. 바삭하고 맛있어요. 바삭하고 맛있어요.",
duration: "8월 1일 ~ 8월 4일",
rating: 4,
keywords: ["✨쾌적해요", "✨쾌적해요"],
},
},
};
64 changes: 64 additions & 0 deletions src/components/common/List/ReviewTile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import Image from "next/image";
import { FC, HtmlHTMLAttributes } from "react";

import Ratings from "@/components/rating/Ratings";
import { cn } from "@/utils/cn";

import ReviewTag from "../Tag/ReviewTag/ReviewTag";

interface FestivalReview {
src?: string;
title: string;
content: string;
duration: string;
keywords: string[];
rating: number;
}

export interface Props
extends Omit<HtmlHTMLAttributes<HTMLDivElement>, "children"> {
festivalReview: FestivalReview;
}

const ReviewTile: FC<Props> = ({ festivalReview, ...props }) => {
const firstTwoKeywords = festivalReview.keywords.slice(0, 2);

return (
<div
className={cn(
"w-full h-full duration-300 rounded-[8px] p-[16px]",
"flex items-start justify-between gap-[12px]",
"bg-gray-scale-0",
props.className,
)}
{...props}
>
{!!festivalReview.src && (
<div className="relative h-[104px] min-w-[80px]">
<Image src={festivalReview.src} alt={festivalReview.title} fill />
</div>
)}

<div className="flex h-full w-full flex-col items-start justify-between gap-[8px] py-[4px]">
<div className="flex h-full w-full flex-col items-start gap-[6px]">
<div className="flex h-full w-full items-center justify-between">
<span className="line-clamp-1 text-body2-bold text-gray-scale-700">
{festivalReview.title}
</span>
<Ratings rating={festivalReview.rating} />
</div>
<span className="line-clamp-2 text-body1-regular text-gray-scale-600">
{festivalReview.content}
</span>
</div>
<div className="flex gap-[8px]">
{firstTwoKeywords.map((keyword) => (
<ReviewTag key={keyword} label={keyword} />
))}
</div>
</div>
</div>
);
};

export default ReviewTile;

0 comments on commit c1e1f28

Please sign in to comment.