Dropdown(Select & option)

장봄·2021년 1월 6일
3

⚙️ Dropdown Component

드롭다운은 html태그 중 select와 option태그를 이용해서 구현을 하면 쉽게 dropdown을 구현할 수 있습니다. 태그의 속성을 이용하면 option을 Group으로 묶어서 사용도 가능하고 미리 default값을 적용해 selet된 상태로 보여질 수도 있습니다.

기능적으로는 사용이 바로 가능하지만 디자인적인 측면에서는 전혀 멋지지 않습니다. 그래서 커스텀을 하거나 라이브러리의 기능을 가져와서 적용하는 경우가 많습니다.

라이브러리를 적용하지 않고 기본적으로 적용되는 css가 아닌 기업에서 요청받은 경우나 웹페이지에 어울리는 디자인으로 적용하려는 경우에는 쉽지 않았습니다.

select나 option을 커스텀하는 방법은 여러가지가 있습니다. 첫번째 방법은 태그에 바로 css를 적용하는 방법입니다. 하지만 이 방법을 적용하는 경우에도 어려운 경우가 많습니다. 두번째 방법은 select와 option태그를 display: none으로 적용해 보이지 않게 하고 div태그나 Label태그 등 을 이용해 새롭게 만드는 방법도 있습니다.

근무중인 기업에서 사용하기 위해 dropdown컴포넌트를 구현하는 과정에서 얻은 정보를 공유하려고 합니다.
커스텀을 하는 과정에서 select태그에서 기본적으로 적용하는 화살표 아이콘은 보이지 않게 css에 appearance: none;을 적용했습니다.

그리고 빈자리에 화살표 이미지를 svg icon component로 구현해서 컴포넌트에 배치했습니다. 하지만 여기서 문제가 발생했습니다. 아이콘을 누르면 option이 open되지 않고 focus만 적용되었습니다. 아이콘이 select태그와 형제관계여서 아이콘을 누르면 select를 선택할 수 없었습니다.

구글링을 열심히 했지만 원인을 알아내기가 힘들었습니다. 다른 코드에서는 select에 css로 background에 이미지를 적용해서 icon component가 아닌 이미지로 적용을 했습니다. 하지만 component를 적용해서 커스텀을 하고 싶었고 버그를 팀원들에게 공유했습니다.

import React from 'react';
import { weightType } from 'components/atoms/Label';
import { Color, Icon } from 'core';
import * as S from './style';

export type DropdownSizeType = 'medium' | 'small';

export interface DropdownProps {
  id?: string;
  className?: string;
  disable?: boolean;
  selected?: string;
  Children?: React.ReactNode;
  width?: string;
  sizing?: DropdownSizeType;
  weight?: weightType;
  margin?: string;
  letterSpacing?: string;
  backgroundColor?: string;
  onChange?: (event: React.ChangeEvent<HTMLSelectElement>) => void;
}

export const Dropdown: React.FC<DropdownProps> = ({
  id = 'Dropdown',
  className,
  disable = true,
  selected = '',
  Children,
  width = '366px',
  sizing = 'medium',
  weight = '500',
  margin,
  letterSpacing = '-0.64px',
  backgroundColor,
  onChange,
}) => {
  return (
    <S.WrapDropdown htmlFor={id}>
      <S.SelectDropdown
        id={id}
        className={className}
        disable={disable}
        selected={selected}
        width={width}
        sizing={sizing}
        weight={weight}
        margin={margin}
        letterSpacing={letterSpacing}
        backgroundColor={backgroundColor}
        disabled={disable ? false : true}
        onChange={onChange}
      >
        {Children}
      </S.SelectDropdown>
      <S.ContainerIcon>
        <Icon.IcDown
          fillColor={disable ? Color.GREY[600] : Color.GREY[500]}
          size={sizing === 'medium' ? 24 : 20}
        />
      </S.ContainerIcon>
    </S.WrapDropdown>
  );
};

다른 라이브러리에서는 어떻게 구현이 되었는지 확인하는 과정에서 material-ui 라이브러리의 코드를 분석했습니다. icon component를 적용했는데 기능은 작동을 하고 있어서 코드를 분석해보니 새로운 css를 발견했습니다.

/* 아이콘을 클릭시 select가 활성화될 수 있도록 icon component에 적용 */
  pointer-events: none;

pointer-events에 none을 적용하면 클릭을 할 수 없도록 비활성화 되어 icon은 보이지만 select을 가리지 않아 기능을 구현할 수 있었습니다.

SVG 콘텐츠에 pointer-events를 지정하지 않은 경우, visiblePainted 값과 동일한 방법을 사용합니다.

none 값의 경우 요소가 포인터 이벤트의 대상이 아님을 가리키는 동시에, 이벤트가 요소를 "뚫고" 들어가 "아래" 요소를 대상으로 하도록 만듭니다.

3시간동안 이러한 이슈를 해결하기 위해서 구글링을 했지만 원하는 결과를 찾지 못해서 미래의 누군가에게 필요한 정보일거라 생각이 들었습니다.

오늘 하루도 단짠단짠했습니다⭐️

📒 Reference

profile
즐겁게 배우고 꾸준히 블로깅하는 개발자입니다 ;>

3개의 댓글

comment-user-thumbnail
2021년 2월 4일

좋은 자료인거 같아요!
다만, Children 이 부분은 소문자로 카멜 케이스를 지향하는 것이 좋을 거 같아요 🔥
className 같은 부분도 String이 아닌 React.CSSProperties를 적용하면 더욱 더 효과 적이에요!

1개의 답글
comment-user-thumbnail
2023년 5월 2일

감사합니다. 비슷한 문제 덕분에 해결합니다..

답글 달기