[React + Typescript + emotion] Badge 컴포넌트 제작

2024. 8. 9. 14:26

이번 프로젝트에서 Badge를 은근 많이 썼었다.

 

디자인 시스템을 봤을 때 이런 조건들이 있어서 css에서 삼항연산자를 많이 써야 하겠는데..?라는 생각을 했다.

1. disable
- 클릭 불가능
- 색 여러개 사용 가능성 있음
- 사이즈 여러개 사용 가능성 있음
2. clickable
- 기본은 흰색 -> 클릭하면 유색 -> 다시 클릭하면 흰색
3. tag
- 색 여러개 사용 가능성 있음
- 지울 수 있는 close 아이콘 추가

선택가능 여부인 isSelected와 사이즈+컬러로 variant를 설정하고, 

모든 Badge에는 label이 필수라 label도 넣어주고, icon도 옵셔널로 넣어줬다.

// Badge.tsx
import React from "react";

import * as S from "./Badge.styled";

interface BadgeProps {
  className?: string;
  isSelected?: boolean;
  variant: "smPink" | "smNavy" | "mdNavy" | "mdWhite";
  label: string;
  icon?: React.ReactNode;
  handleClick?: (e: React.MouseEvent) => void;
}

const Badge = ({
  className,
  label,
  isSelected,
  handleClick,
  variant,
  icon,
}: BadgeProps) => {
  return (
    <S.Badge
      className={className}
      isSelected={isSelected}
      isClickable={!!handleClick}
      onClick={handleClick}
      variant={variant}
    >
      {label}
      {icon && icon}
    </S.Badge>
  );
};

export default Badge;

 

고려사항이 많다보니 삼항연산자가 좀 늘어난..

추후 추가 개발 시에 혹시나 badge부분의 옵션이 더 늘어난다면...리팩토링할 때 어떻게 해결할지 생각해 봐야겠다.

// Badge.styled.ts
import { css } from "@emotion/react";
import styled from "@emotion/styled";

export const Badge = styled.button<{
  isSelected?: boolean;
  isClickable: boolean;
  variant: "smPink" | "smNavy" | "mdNavy" | "mdWhite";
}>`
  ${({ isSelected, isClickable, theme, variant }) => css`
    ${variant === "smPink" || variant === "smNavy"
      ? theme.fonts.caption_regular_10
      : isSelected || variant === "mdNavy"
      ? theme.fonts.caption_bold_12
      : theme.fonts.caption_regular_12};
    height: ${variant === "smPink" || variant === "smNavy" ? "18px" : "38px"};
    width: fit-content;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid
      ${variant === "smPink"
        ? theme.colors.pink900
        : (isSelected && isClickable) ||
          variant === "smNavy" ||
          variant === "mdNavy"
        ? theme.colors.navy900
        : theme.colors.gray600};
    border-radius: ${variant === "smPink" || variant === "smNavy"
      ? "4px"
      : "20px"};
    padding: ${variant === "smPink" || variant === "smNavy"
      ? "3px 6px"
      : "9px 16px"};
    background-color: ${variant === "smPink"
      ? theme.colors.pink900
      : isSelected || variant === "smNavy" || variant === "mdNavy"
      ? theme.colors.navy900
      : theme.colors.white};
    color: ${variant === "mdWhite" && !isSelected
      ? theme.colors.black
      : theme.colors.white};
    cursor: ${!isClickable && "default"};

    &:hover {
      background-color: ${isClickable && theme.colors.navy900};
      color: ${isClickable && theme.colors.white};
      border: ${isClickable && `1px solid ${theme.colors.navy900}`};
    }
  `}
`;

disable + smPink/smNavy
disabled
tag
tag + navy
clickable

 

BELATED ARTICLES

more