본문 바로가기
프로젝트/Prolog

[Prolog 시즌3] SelectBox (dropdown) 리팩토링 2

by g2hans 2021. 8. 23.

지난 글에 이어서, select(dropdown menu) 리팩토링 2편입니다.

 

[Prolog 시즌3] SelectBox (dropdown) 리팩토링 - 접근성 UP

🌸 프로젝트를 하면서 정리한 글입니다. 피드백은 언제나 격한 환영 🌸 전에 UX 공부하다가 '드롭다운과 스크롤 디자인'이라는 주제의 글을 본 적이 있다. 기억에 남는 이야기로 드롭다운의 경

dev-hyun.tistory.com

피드백은 언제나 환영입니다. 🌸


🛠 Custom Select Tag(dropdown menu) 리팩토링

디토랑 나랑 한 작업 (이 포스팅부터 select, select box, dropdown을 모두 드롭다운이라고 쓰겠다)은 다음 4가지이고, 여기서 조금씩 코드만 수정했다. 그래서 사실상 리팩토링이 기능적으로 들어간 부분은 거의 없고, 이 글은 그냥 뭘 했는지 정리하는 글이다.

 

1. 윈도우에서 스크롤바가 너무 안 예쁘니깐 스크롤바 커스텀 

2. 선택을 하고 나서 드롭다운 메뉴가 닫히게 설정

3. 드롭다운 메뉴 바깥 부분을 선택해도 메뉴가 닫히게 설정

4. 다시 드롭다운 메뉴를 클릭하면, 리스트 중 선택된 메뉴로 자동 스크롤

 

👩‍🎨 스크롤바 커스텀하기

스크롤바를 커스텀하는 방법은 생각보다 어렵지 않다. 현재 우리가 스크롤 커스텀을 한 타겟은 webkit을 사용하는 브라우저이다.

 

::-webkit-scrollbar - CSS: Cascading Style Sheets | MDN

The ::-webkit-scrollbar CSS pseudo-element affects the style of an element's scrollbar when it has overflow:scroll; set.

developer.mozilla.org

 

여러 가지 속성 중에 전체 스크롤바 영역(Scrollbar), 스크롤바가 움직일 수 있는 영역(scrollbar-track), 현재 스크롤 위치(scrollbar-track)를 커스텀하면 됐다. 우리는 scrollbar가 좀 더 귀엽게 보이게 하기 위해 border-radius 속성을 추가했고, 너무 아래위의 여백이 없으면 답답해 보여서 scrollbar-track에 위아래 margin을 넣어주었다. 

 

또 스크롤바 영역이 따로 잡히기 때문에 li와 같은 색으로 배경색이 지정이 되면, 선택이 된 option이 있을 경우 디자인이 안 예쁘다는 문제점이 있어서, 일부러 ul의 배경색을 회색으로, li의 배경색은 흰색으로 하여 스크롤바 영역을 구분 지어줬다.

 

 

그렇게 완성된 코드는 다음과 같다. SelectItem에 있는 first-child, last-child의 border-radius는 가장자리의 border 때문에 hover 하거나 선택되었을 때 ul의 border-radius 밖으로 튀어나가는 것을 막기 위함이다.

 

const SelectItemList = styled.ul`
  position: absolute;
  top: 0;
  width: 100%;
  max-height: ${({ maxHeight }) => maxHeight ?? `${maxHeight}`};
  overflow-y: auto;

  list-style: none;
  z-index: 7;

  color: ${COLOR.DARK_BLUE_800};
  background-color: ${COLOR.LIGHT_GRAY_200};

  border: 2px solid ${COLOR.DARK_BLUE_800};
  border-radius: 1rem;

  & {
    ::-webkit-scrollbar {
      width: 1rem;
    }

    ::-webkit-scrollbar-track {
      margin: 0.5rem 0;
      border-radius: 1rem;
    }

    ::-webkit-scrollbar-thumb {
      background-color: ${COLOR.DARK_BLUE_800};
      border-radius: 1rem;
    }
  }
`;

const SelectItem = styled.li`
  display: flex;
  align-items: center;
  padding: 1rem 2rem;
  font-size: 1.6rem;
  cursor: pointer;
  background-color: ${COLOR.WHITE};

  &:hover {
    background-color: ${COLOR.LIGHT_GRAY_200};
  }

  &:first-of-type:hover {
    border-top-right-radius: 1rem;
    border-top-left-radius: 1rem;
  }

  &:last-child:hover {
    border-bottom-right-radius: 1rem;
    border-bottom-left-radius: 1rem;
  }

  ${({ isSelected }) =>
    isSelected &&
    `background-color: ${COLOR.LIGHT_BLUE_200};
    
    &:first-of-type {
      border-top-left-radius: 1rem;
    }

    &:last-child {
      border-bottom-left-radius: 1rem;
    }`}
`;

 

👩‍🎨 드롭다운 메뉴 닫기

드롭다운 메뉴를 닫기 위해서 useState와 useEffect를 통해 지역 상태 관리를 했다. 드롭다운 메뉴가 열렸을 때 선택된 영역이 드롭다운 메뉴가 아니라면  닫힌다. 모달과 다르게 따로 화면 전체에 딤드영역, 선택영역을 나누어 분리한게 아니라서 document에 이벤트를 추가했다. 아래는 selectBox 여닫는 기능을 커스텀 훅으로 분리한 코드이다. targetRef는 select box 전체(label)이다. select box 영역에서 option을 선택했을 때는 훅의 setIsSelectBoxOpened 를 통해서 닫아주면 된다! (이렇게 생각해보니 그냥 뭘 클릭하든지 닫히는 애네..?)

 

const useCustomSelectBox = ({ targetRef }) => {
  const [isSelectBoxOpened, setIsSelectBoxOpened] = useState(false);

  useEffect(() => {
    const onCloseOptionList = (event) => {
      if (!targetRef.current?.contains(event.target)) {
        setIsSelectBoxOpened(false);
      }
    };

    document.addEventListener('click', onCloseOptionList);

    return () => {
      document.removeEventListener('click', onCloseOptionList);
    };
  }, [isSelectBoxOpened, targetRef]);

  return [isSelectBoxOpened, setIsSelectBoxOpened];
};

 

📜  선택한 영역으로 자동스크롤

드롭다운 메뉴의 가장 끝에 있는 option을 선택했을때, 밑에까지 내려가서 확인하는 건 좀 별로다. 내가 선택한 영역으로 바로 이동을 해서 앞뒤에 무엇이 있는지 확인하는 게 UX 적으로 좋을 것 같아서 스크롤이 되는 기능을 추가했다. 로직 순서는 다음과 같다.

 

1. 전체 스크롤 영역에 대한 높이를 구한다. (🖇 scrollHeight)    💡 scrollHeight는 요소 콘텐츠의 총 높이를 나타내며, 바깥으로 넘쳐서 보이지 않는 콘텐츠도 포함합니다.

2. option의 갯수로 스크롤 영역을 나누면 각 option의 높이가 나온다.

3. 선택된 option의 index를 구한다.

4. option의 높이 * index를 하여 그 위치로 스크롤한다.

    💡 스크롤은 option의 top에 맞추어 화면에 보여준다.

 

 

 

글을 쓰다보니 정리가 되는 부분도 있고, 계속해서 리팩토링 해야하는 부분이 생긴다. 이래서 리팩토링은 끝이 없다는걸까?

 

🖇 전체코드 : https://github.com/woowacourse/prolog/tree/main/frontend/src/components/SelectBox

 

댓글