-
Notifications
You must be signed in to change notification settings - Fork 1
refactor: JDS 버튼 컴포넌트 vanilla-extract 마이그레이션 #432
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
Changes from all commits
7616d39
1123025
2defd4a
1303bd3
2c6e72c
88ddd9f
451d4b7
21440fa
c050a71
2b50a35
941a592
d9e3948
b8579d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import { createVar, style } from "@vanilla-extract/css"; | ||
|
|
||
| export const gapVar = createVar(); | ||
|
|
||
| export const flexRow = style({ | ||
| display: "flex", | ||
| flexDirection: "row", | ||
| alignItems: "center", | ||
| gap: gapVar, | ||
| }); | ||
|
|
||
| export const flexColumn = style({ | ||
| display: "flex", | ||
| flexDirection: "column", | ||
| gap: gapVar, | ||
| }); | ||
|
|
||
| export const label = style({ | ||
| width: "100px", | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,38 @@ | ||
| import styled from "@emotion/styled"; | ||
| import { assignInlineVars } from "@vanilla-extract/dynamic"; | ||
| import type { ComponentPropsWithoutRef } from "react"; | ||
|
|
||
| /** | ||
| * Storybook 전용 레이아웃 컴포넌트 | ||
| * Stories 파일에서 반복되는 레이아웃 스타일을 재사용하기 위한 유틸리티 | ||
| */ | ||
| import { flexColumn, flexRow, gapVar, label } from "./layout.css"; | ||
|
|
||
| export const FlexRow = styled.div<{ gap?: string }>(({ gap = "16px" }) => ({ | ||
| display: "flex", | ||
| flexDirection: "row", | ||
| gap, | ||
| alignItems: "center", | ||
| })); | ||
| type WithGap = { gap?: string }; | ||
|
|
||
| export const FlexColumn = styled.div<{ gap?: string }>(({ gap = "24px" }) => ({ | ||
| display: "flex", | ||
| flexDirection: "column", | ||
| gap, | ||
| })); | ||
| export function FlexRow({ | ||
| gap = "16px", | ||
| style, | ||
| ...props | ||
| }: ComponentPropsWithoutRef<"div"> & WithGap) { | ||
| return ( | ||
| <div | ||
| className={flexRow} | ||
| style={{ ...assignInlineVars({ [gapVar]: gap }), ...style }} | ||
| {...props} | ||
| /> | ||
| ); | ||
| } | ||
|
|
||
| export const Label = styled.span({ | ||
| width: "100px", | ||
| }); | ||
| export function FlexColumn({ | ||
| gap = "24px", | ||
| style, | ||
| ...props | ||
| }: ComponentPropsWithoutRef<"div"> & WithGap) { | ||
| return ( | ||
| <div | ||
| className={flexColumn} | ||
| style={{ ...assignInlineVars({ [gapVar]: gap }), ...style }} | ||
| {...props} | ||
| /> | ||
| ); | ||
| } | ||
|
|
||
| export function Label({ ...props }: ComponentPropsWithoutRef<"span">) { | ||
| return <span className={label} {...props} />; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,11 @@ | ||
| import { mergeProps } from "@react-aria/utils"; | ||
| import { clsx } from "clsx"; | ||
| import type { BlockButtonBasicProps, BlockButtonFeedbackProps } from "components"; | ||
| import { Icon } from "components"; | ||
| import { usePressable } from "hooks"; | ||
| import { forwardRef } from "react"; | ||
|
|
||
| import { iconSizeMap, StyledBlockButton } from "./blockButton.styles"; | ||
| import { basicRoot, feedbackRoot, iconSizeMap } from "./blockButton.css"; | ||
|
|
||
| const BlockButtonBasic = forwardRef<HTMLButtonElement, BlockButtonBasicProps>( | ||
| ( | ||
|
|
@@ -14,27 +17,25 @@ const BlockButtonBasic = forwardRef<HTMLButtonElement, BlockButtonBasicProps>( | |
| prefixIcon, | ||
| suffixIcon, | ||
| disabled = false, | ||
| className, | ||
| ...restProps | ||
| }, | ||
| ref, | ||
| forwardedRef, | ||
| ) => { | ||
| //Todo: 아이콘 사이즈도 전부 스타일의 theme 단위에서 해결하면 좋을듯(Theme 구조 추가 필요) | ||
| const { ref, pressableProps } = usePressable(forwardedRef, { disabled }); | ||
| const iconSize = iconSizeMap[size]; | ||
|
|
||
| return ( | ||
| <StyledBlockButton | ||
| <button | ||
| ref={ref} | ||
| $hierarchy={hierarchy} | ||
| $variant={variant} | ||
| $size={size} | ||
| $disabled={disabled} | ||
| disabled={disabled} | ||
| {...restProps} | ||
| {...mergeProps(pressableProps, restProps)} | ||
| data-part='root' | ||
| className={clsx(basicRoot({ hierarchy, variant, size }), className)} | ||
| > | ||
| {prefixIcon && <Icon name={prefixIcon} size={iconSize} />} | ||
| {children} | ||
| {suffixIcon && <Icon name={suffixIcon} size={iconSize} />} | ||
|
Comment on lines
35
to
37
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (선택) 해당 컴포넌트 자체에서는 문제가 없는데, 다만 이 컴포넌트(+LabelButton 포함) 의 경우 디자인 에셋에서 icon과 label의 색상이 동일하게 배합되어있어 실질적으로 문제는 없습니다! |
||
| </StyledBlockButton> | ||
| </button> | ||
| ); | ||
| }, | ||
| ); | ||
|
|
@@ -50,25 +51,25 @@ const BlockButtonFeedback = forwardRef<HTMLButtonElement, BlockButtonFeedbackPro | |
| prefixIcon, | ||
| suffixIcon, | ||
| disabled = false, | ||
| className, | ||
| ...restProps | ||
| }, | ||
| ref, | ||
| forwardedRef, | ||
| ) => { | ||
| const { ref, pressableProps } = usePressable(forwardedRef, { disabled }); | ||
| const iconSize = iconSizeMap[size]; | ||
|
|
||
| return ( | ||
| <StyledBlockButton | ||
| <button | ||
| ref={ref} | ||
| $intent={intent} | ||
| $size={size} | ||
| $disabled={disabled} | ||
| disabled={disabled} | ||
| {...restProps} | ||
| {...mergeProps(pressableProps, restProps)} | ||
| data-part='root' | ||
| className={clsx(feedbackRoot({ intent, size }), className)} | ||
| > | ||
| {prefixIcon && <Icon name={prefixIcon} size={iconSize} />} | ||
| {children} | ||
| {suffixIcon && <Icon name={suffixIcon} size={iconSize} />} | ||
| </StyledBlockButton> | ||
| </button> | ||
| ); | ||
| }, | ||
| ); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(참고) 커스텀 가능성이 크지 않은 prop이긴 하지만, 타입 상으로 만일 사용자가
data-part={custom}속성을 component에 주입했을 때root가 항상 덮어쓴다는 문제점이 있어 보여요.최상단 요소임을 명시하는 값이니 외부에서 주입하지 못하도록 아래처럼
data-part를 타입에서 막아 버리는 것도 방법이 될 수 있어 보이네요.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 컴포넌트들이 항상 루트 요소로 사용된다는 것이 불변 사항이라면 타입 레벨에서 외부 주입을 제한하는 것도 괜찮은 방향인 것 같네요!
다만 리액트에서
data-*속성을Omit으로 완전히 제거하기 어려워never타입으로 제한했습니다 (b8579d3)