-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #40 from DaleStudy/37
Icon 컴포넌트
- Loading branch information
Showing
17 changed files
with
399 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
import { vstack } from "../../../styled-system/patterns"; | ||
import { Heading } from "../Heading/Heading"; | ||
import { Text } from "../Text/Text"; | ||
import { Icon } from "./Icon"; | ||
|
||
export default { | ||
component: Icon, | ||
parameters: { | ||
layout: "centered", | ||
}, | ||
args: { | ||
name: "user", | ||
}, | ||
} satisfies Meta<typeof Icon>; | ||
|
||
export const Basic: StoryObj<typeof Icon> = { | ||
args: { | ||
tone: "accent", | ||
muted: true, | ||
size: "xl", | ||
}, | ||
}; | ||
|
||
export const Sizes: StoryObj<typeof Icon> = { | ||
render: (args) => { | ||
return ( | ||
<div className={vstack({ gap: "6" })}> | ||
<Icon {...args} size="xs" /> | ||
<Icon {...args} size="sm" /> | ||
<Icon {...args} size="md" /> | ||
<Icon {...args} size="lg" /> | ||
<Icon {...args} size="xl" /> | ||
</div> | ||
); | ||
}, | ||
argTypes: { | ||
size: { | ||
control: false, | ||
}, | ||
}, | ||
args: { | ||
tone: "accent", | ||
muted: true, | ||
}, | ||
}; | ||
|
||
export const Tones: StoryObj<typeof Icon> = { | ||
render: (args) => { | ||
return ( | ||
<div className={vstack({ gap: "6" })}> | ||
<Icon {...args} tone="neutral" /> | ||
<Icon {...args} tone="accent" /> | ||
<Icon {...args} tone="danger" /> | ||
<Icon {...args} tone="warning" /> | ||
</div> | ||
); | ||
}, | ||
argTypes: { | ||
tone: { | ||
control: false, | ||
}, | ||
}, | ||
args: { | ||
muted: true, | ||
}, | ||
}; | ||
|
||
export const Contrasts: StoryObj<typeof Icon> = { | ||
render: (args) => { | ||
return ( | ||
<div className={vstack({ gap: "6" })}> | ||
<Text {...args} muted> | ||
낮은 <Icon name="moon" /> 명암비 | ||
</Text> | ||
<Text {...args}> | ||
높은 <Icon name="sun" /> 명암비 | ||
</Text> | ||
</div> | ||
); | ||
}, | ||
argTypes: { | ||
name: { | ||
control: false, | ||
}, | ||
muted: { | ||
control: false, | ||
}, | ||
}, | ||
}; | ||
|
||
export const WithHeading: StoryObj<typeof Icon> = { | ||
render: (args) => { | ||
return ( | ||
<Heading level={2}> | ||
<Icon {...args} name="user" /> | ||
프로필 | ||
</Heading> | ||
); | ||
}, | ||
argTypes: { | ||
name: { | ||
control: false, | ||
}, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { composeStories } from "@storybook/react"; | ||
import { render } from "@testing-library/react"; | ||
import { expect, test } from "vitest"; | ||
import * as stories from "./Icon.stories"; | ||
|
||
const { Basic } = composeStories(stories); | ||
|
||
test("renders an svg element", () => { | ||
const { container } = render(<Basic />); | ||
|
||
expect(container.querySelector("svg")).toBeInTheDocument(); | ||
}); | ||
|
||
test.each([ | ||
["xs", "w_1em h_1em"], | ||
["sm", "w_1.25em h_1.25em"], | ||
["md", "w_1.5em h_1.5em"], | ||
["lg", "w_1.875em h_1.875em"], | ||
["xl", "w_2.25em h_2.25em"], | ||
] as const)('applies the correct class for size="%s"', (size, className) => { | ||
const { container } = render(<Basic size={size} />); | ||
|
||
expect(container.querySelector("svg")).toHaveClass(className); | ||
}); | ||
|
||
test.each([ | ||
["neutral", "c_text"], | ||
["accent", "c_text.accent"], | ||
["danger", "c_text.danger"], | ||
["warning", "c_text.warning"], | ||
] as const)('applies the correct class for tone="%s"', (tone, className) => { | ||
const { container } = render(<Basic tone={tone} muted={false} />); | ||
|
||
expect(container.querySelector("svg")).toHaveClass(className); | ||
}); | ||
|
||
test.each([ | ||
[false, "c_text"], | ||
[true, "c_text.muted"], | ||
] as const)("applies the correct class for muted={%s}", (muted, className) => { | ||
const { container } = render(<Basic tone="neutral" muted={muted} />); | ||
|
||
expect(container.querySelector("svg")).toHaveClass(className); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import { css, cva } from "../../../styled-system/css"; | ||
import type { Tone } from "../../tokens/colors"; | ||
import { type IconName, icons } from "../../tokens/iconography"; | ||
export interface IconProps { | ||
/** 이름 */ | ||
name: IconName; | ||
/** 색조 */ | ||
tone?: Tone; | ||
/** 크기 */ | ||
size?: "xs" | "sm" | "md" | "lg" | "xl"; | ||
/** 명암비 낮출지 */ | ||
muted?: boolean; | ||
} | ||
|
||
/** | ||
* - `name` 속성으로 어떤 모양의 아이콘을 사용할지 지정할 수 있습니다. | ||
* - 아이콘의 기본 크기는 부모 요소에서 설정한 글자 크기의 1.5배이며, `size` 속성을 통해서 크기를 변경할 수 있습니다. | ||
* - 아이콘의 기본 색상은 부모 요소에서 설정한 글자 색상과 동일하며, `tone` 속성과 `muted` 속성을 통해서 색상을 변경할 수 있습니다. | ||
*/ | ||
export const Icon = ({ | ||
name, | ||
size, | ||
tone, | ||
muted = false, | ||
...rest | ||
}: IconProps) => { | ||
const Tag = icons[name]; | ||
|
||
return ( | ||
<Tag | ||
className={css( | ||
sizeStyles.raw({ size }), | ||
colorStyles.raw({ tone, muted }), | ||
css.raw({ | ||
display: "inline-block", | ||
}) | ||
)} | ||
{...rest} | ||
/> | ||
); | ||
}; | ||
|
||
const sizeStyles = cva({ | ||
variants: { | ||
size: { | ||
xs: { | ||
width: "1em", | ||
height: "1em", | ||
}, | ||
sm: { | ||
width: "1.25em", | ||
height: "1.25em", | ||
}, | ||
md: { | ||
width: "1.5em", | ||
height: "1.5em", | ||
}, | ||
lg: { | ||
width: "1.875em", | ||
height: "1.875em", | ||
}, | ||
xl: { | ||
width: "2.25em", | ||
height: "2.25em", | ||
}, | ||
}, | ||
}, | ||
defaultVariants: { | ||
size: "md", | ||
}, | ||
}); | ||
|
||
const colorStyles = cva({ | ||
compoundVariants: [ | ||
{ | ||
muted: false, | ||
tone: "neutral", | ||
css: { color: "text" }, | ||
}, | ||
{ | ||
muted: false, | ||
tone: "accent", | ||
css: { color: "text.accent" }, | ||
}, | ||
{ | ||
muted: false, | ||
tone: "danger", | ||
css: { color: "text.danger" }, | ||
}, | ||
{ | ||
muted: false, | ||
tone: "warning", | ||
css: { color: "text.warning" }, | ||
}, | ||
{ | ||
muted: true, | ||
tone: "neutral", | ||
css: { color: "text.muted" }, | ||
}, | ||
{ | ||
muted: true, | ||
tone: "accent", | ||
css: { color: "text.muted.accent" }, | ||
}, | ||
{ | ||
muted: true, | ||
tone: "danger", | ||
css: { color: "text.muted.danger" }, | ||
}, | ||
{ | ||
muted: true, | ||
tone: "warning", | ||
css: { color: "text.muted.warning" }, | ||
}, | ||
], | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { Icon as Text } from "./Icon"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { IconGallery, IconItem } from "@storybook/blocks"; | ||
import { icons } from "./iconography"; | ||
|
||
# Iconography | ||
|
||
> 달레 UI는 [Lucide](https://lucide.dev/)와 [Simple Icons](https://simpleicons.org/)의 아이콘을 선별적으로 사용하고 있습니다. | ||
> 번들 크기를 최적화하기 위해서 아이콘은 필요할 때 마다 요청을 받아서 추가됩니다. | ||
<IconGallery> | ||
{Object.entries(icons).map(([name, Icon]) => ( | ||
<IconItem name={name}> | ||
<Icon /> | ||
</IconItem> | ||
))} | ||
</IconGallery> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { | ||
Check, | ||
ChevronDown, | ||
ChevronLeft, | ||
ChevronRight, | ||
CircleAlert, | ||
Clock, | ||
Info, | ||
MessageCircle, | ||
Menu, | ||
Moon, | ||
Search, | ||
Sun, | ||
Star, | ||
User, | ||
X, | ||
} from "lucide-react"; | ||
import type { FunctionComponent, ComponentProps, SVGProps } from "react"; | ||
import Discord from "../assets/Discord.svg?react"; | ||
import GitHub from "../assets/GitHub.svg?react"; | ||
import LinkedIn from "../assets/LinkedIn.svg?react"; | ||
import Medium from "../assets/Medium.svg?react"; | ||
import YouTube from "../assets/YouTube.svg?react"; | ||
|
||
function createBrandIcon(Icon: FunctionComponent<SVGProps<SVGSVGElement>>) { | ||
return (args: ComponentProps<typeof Icon>) => ( | ||
<Icon {...args} fill="currentColor" /> | ||
); | ||
} | ||
|
||
export const icons = { | ||
check: Check, | ||
chevronDown: ChevronDown, | ||
chevronLeft: ChevronLeft, | ||
chevronRight: ChevronRight, | ||
circleAlert: CircleAlert, | ||
clock: Clock, | ||
info: Info, | ||
chat: MessageCircle, | ||
menu: Menu, | ||
moon: Moon, | ||
search: Search, | ||
sun: Sun, | ||
star: Star, | ||
user: User, | ||
x: X, | ||
Discord: createBrandIcon(Discord), | ||
GitHub: createBrandIcon(GitHub), | ||
LinkedIn: createBrandIcon(LinkedIn), | ||
Medium: createBrandIcon(Medium), | ||
YouTube: createBrandIcon(YouTube), | ||
}; | ||
|
||
export type IconName = keyof typeof icons; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
/// <reference types="vite/client" /> | ||
/// <reference types="vite-plugin-svgr/client" /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters