From f3569eea578320c4ad51e449a950acafdfd7ebd6 Mon Sep 17 00:00:00 2001 From: yumin-kim4757 Date: Thu, 2 Jul 2026 16:29:25 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat(ui):=20Tab=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84=20(#58)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/index.ts | 1 + .../src/components/tab/Tab.tsx | 51 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 packages/timo-design-system/src/components/tab/Tab.tsx diff --git a/packages/timo-design-system/src/components/index.ts b/packages/timo-design-system/src/components/index.ts index 579ad40..8a8dae7 100644 --- a/packages/timo-design-system/src/components/index.ts +++ b/packages/timo-design-system/src/components/index.ts @@ -3,3 +3,4 @@ export { Color } from "./color/Color"; export { Typography } from "./typography/Typography"; export { Tag } from "./tag/Tag"; export { PriorityIcon } from "./priority-icon/PriorityIcon"; +export { Tab } from "./tab/Tab"; diff --git a/packages/timo-design-system/src/components/tab/Tab.tsx b/packages/timo-design-system/src/components/tab/Tab.tsx new file mode 100644 index 0000000..68a0aeb --- /dev/null +++ b/packages/timo-design-system/src/components/tab/Tab.tsx @@ -0,0 +1,51 @@ +import { cn } from "@lib"; + +import type { ReactNode } from "react"; + +type TabVariant = "default" | "selected"; + +const TAB_VARIANT_CLASS_NAME: Record = { + default: "text-timo-gray-800 hover:bg-timo-gray-500", + selected: "bg-timo-blue-65 text-timo-blue-300", +}; + +export interface TabProps { + label: string; + icon: ReactNode; + hoverIcon?: ReactNode; + isSelected?: boolean; + onClick: () => void; +} + +export const Tab = ({ + label, + icon, + hoverIcon, + isSelected = false, + onClick, +}: TabProps) => { + const baseButtonClassName = + "group flex w-45 items-center gap-2 rounded-8 px-3 py-1"; + + const variant: TabVariant = isSelected ? "selected" : "default"; + + const hasHoverIcon = hoverIcon && !isSelected; + + return ( + + ); +}; From 4fd99541b9f77d9c87a1f0845379b9dc3653429c Mon Sep 17 00:00:00 2001 From: yumin-kim4757 Date: Thu, 2 Jul 2026 16:29:43 +0900 Subject: [PATCH 2/3] =?UTF-8?q?docs(ui):=20Tab=20Storybook=20=EC=98=88?= =?UTF-8?q?=EC=8B=9C=20=EC=B6=94=EA=B0=80=20(#58)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/tab/Tab.stories.tsx | 184 ++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 packages/timo-design-system/src/components/tab/Tab.stories.tsx diff --git a/packages/timo-design-system/src/components/tab/Tab.stories.tsx b/packages/timo-design-system/src/components/tab/Tab.stories.tsx new file mode 100644 index 0000000..15c14fa --- /dev/null +++ b/packages/timo-design-system/src/components/tab/Tab.stories.tsx @@ -0,0 +1,184 @@ +import { + ChartHoverIcon, + ChartOffIcon, + ChartOnIcon, + HomeHoverIcon, + HomeOffIcon, + HomeOnIcon, + SettingHoverIcon, + SettingOffIcon, + SettingOnIcon, + TimerHoverIcon, + TimerOffIcon, + TimerOnIcon, + TodayHoverIcon, + TodayOffIcon, + TodayOnIcon, +} from "@icons"; +import { useState } from "react"; + +import { Tab } from "./Tab"; + +import type { Meta, StoryObj } from "@storybook/react"; + +const meta = { + title: "Components/Tab", + component: Tab, + parameters: { + layout: "centered", + }, + argTypes: { + label: { + control: "text", + }, + isSelected: { + control: "boolean", + }, + }, + args: { + label: "홈", + icon: , + hoverIcon: , + isSelected: false, + onClick: () => {}, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const AllStates: Story = { + parameters: { + controls: { disable: true }, + }, + render: () => ( +
+
+ } + isSelected + onClick={() => {}} + /> + } + hoverIcon={} + onClick={() => {}} + /> + } + onClick={() => {}} + /> +
+ +
+ } + isSelected + onClick={() => {}} + /> + } + hoverIcon={} + onClick={() => {}} + /> + } + onClick={() => {}} + /> +
+ +
+ } + isSelected + onClick={() => {}} + /> + } + hoverIcon={} + onClick={() => {}} + /> + } + onClick={() => {}} + /> +
+ +
+ } + isSelected + onClick={() => {}} + /> + } + hoverIcon={} + onClick={() => {}} + /> + } + onClick={() => {}} + /> +
+ +
+ } + isSelected + onClick={() => {}} + /> + } + hoverIcon={} + onClick={() => {}} + /> + } + onClick={() => {}} + /> +
+
+ ), +}; + +const InteractiveHomeTab = () => { + const [isSelected, setIsSelected] = useState(false); + + return ( + + ) : ( + + ) + } + hoverIcon={} + isSelected={isSelected} + onClick={() => setIsSelected((prevIsSelected) => !prevIsSelected)} + /> + ); +}; + +export const Interactive: Story = { + parameters: { + controls: { disable: true }, + }, + render: () => , +}; From 15391bd6403baf2c2c3ecd3c0cfd1c3a7809d585 Mon Sep 17 00:00:00 2001 From: yumin-kim4757 Date: Fri, 3 Jul 2026 00:53:31 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix(ui):=20Tab=20Storybook=20=EC=95=A1?= =?UTF-8?q?=EC=85=98=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=97=B0=EA=B2=B0=20?= =?UTF-8?q?(#58)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/tab/Tab.stories.tsx | 55 ++++++------------- .../src/components/tab/Tab.tsx | 2 +- 2 files changed, 17 insertions(+), 40 deletions(-) diff --git a/packages/timo-design-system/src/components/tab/Tab.stories.tsx b/packages/timo-design-system/src/components/tab/Tab.stories.tsx index 15c14fa..207ecb5 100644 --- a/packages/timo-design-system/src/components/tab/Tab.stories.tsx +++ b/packages/timo-design-system/src/components/tab/Tab.stories.tsx @@ -20,6 +20,7 @@ import { useState } from "react"; import { Tab } from "./Tab"; import type { Meta, StoryObj } from "@storybook/react"; +import type { ComponentProps } from "react"; const meta = { title: "Components/Tab", @@ -34,13 +35,15 @@ const meta = { isSelected: { control: "boolean", }, + onClick: { + action: "clicked", + }, }, args: { label: "홈", icon: , hoverIcon: , isSelected: false, - onClick: () => {}, }, } satisfies Meta; @@ -58,19 +61,13 @@ export const AllStates: Story = { label="홈" icon={} isSelected - onClick={() => {}} /> } hoverIcon={} - onClick={() => {}} - /> - } - onClick={() => {}} /> + } />
@@ -78,19 +75,13 @@ export const AllStates: Story = { label="오늘" icon={} isSelected - onClick={() => {}} /> } hoverIcon={} - onClick={() => {}} - /> - } - onClick={() => {}} /> + } />
@@ -98,19 +89,13 @@ export const AllStates: Story = { label="집중 모드" icon={} isSelected - onClick={() => {}} /> } hoverIcon={} - onClick={() => {}} - /> - } - onClick={() => {}} /> + } />
@@ -118,19 +103,13 @@ export const AllStates: Story = { label="통계" icon={} isSelected - onClick={() => {}} /> } hoverIcon={} - onClick={() => {}} - /> - } - onClick={() => {}} /> + } />
@@ -138,29 +117,24 @@ export const AllStates: Story = { label="설정" icon={} isSelected - onClick={() => {}} /> } hoverIcon={} - onClick={() => {}} - /> - } - onClick={() => {}} /> + } />
), }; -const InteractiveHomeTab = () => { +const InteractiveHomeTab = (args: ComponentProps) => { const [isSelected, setIsSelected] = useState(false); return ( { } hoverIcon={} isSelected={isSelected} - onClick={() => setIsSelected((prevIsSelected) => !prevIsSelected)} + onClick={() => { + args.onClick?.(); + setIsSelected((prevIsSelected) => !prevIsSelected); + }} /> ); }; @@ -180,5 +157,5 @@ export const Interactive: Story = { parameters: { controls: { disable: true }, }, - render: () => , + render: (args) => , }; diff --git a/packages/timo-design-system/src/components/tab/Tab.tsx b/packages/timo-design-system/src/components/tab/Tab.tsx index 68a0aeb..d1273df 100644 --- a/packages/timo-design-system/src/components/tab/Tab.tsx +++ b/packages/timo-design-system/src/components/tab/Tab.tsx @@ -14,7 +14,7 @@ export interface TabProps { icon: ReactNode; hoverIcon?: ReactNode; isSelected?: boolean; - onClick: () => void; + onClick?: () => void; } export const Tab = ({