c-h-w-h / cds

🧊 차가운 디자인 시스템

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Refactor: Dropdown Props 변경

prayinforrain opened this issue · comments

♻️ 리팩토링 사항

  • Dropdown의 Props 타입을 변경할 예정이에요.
    • id는 HTML에서 id가 필수가 아닌 점을 생각해 optional로 두었는데, 드롭다운이 작동하기 위해 필수 props에요.
    • dropdownLabel 역시 접근성을 위해 필수 props가 되어야 해요. 그리고 이미 Dropdown이라는 네임스페이스 아래에 있으니 label이라는 이름으로 변경할거에요.

📖 참고 사항

image
피드백 감사합니다 ^ㅁ^

commented

구구절절한 채팅 추가합니다 ... 우재님은 고민에 빠지셨어요

image

image

image

image

image

기나긴 토론을 했어요 세영님 너무 감사드려요

보기

J149_이우재:
@J040_김세영
Tabs 보면서 떠오른건데요, 어제 제가 얘기했던 id문제 Tabs처럼 label이라는 이름으로 받아서 알아서 id 생성해서 넣어버리면 괜찮을까요? 사실 어제 어떻게 결론냈는지 까먹었는데 현빈님이 쓰신 방법 되게 괜찮은 것 같아요
어제 고민했던 이유가, 드롭다운은 특정 상황에서 id로 트리거 찾아서 {TriggerElement}.click()으로 드롭다운 열리도록 하고싶을 수 있을 것 같은데 이것도 고민이에요

J040_김세영:
어제 결론이 없었던 것 같은데요 ...! Tabs랑 비슷하게 하는것도 방법일 것 같아요.

드롭다운은 특정 상황에서 id로 트리거 찾아서 {TriggerElement}.click()으로 드롭다운 열리도록 하고싶을 수 있을 것 같은데 이것도 고민이에요

이것도 그냥 id는 이렇게 생성된다고 문서에 적어두면 안되는건가요?

J149_이우재:
흠 그럴까요.. 근데 저처럼 문서를 얕게 읽는 사람이라면 HTML의 id에 대한 배경지식때문에 아는 내용이네 하고 휙 넘겼다가 이거왜안돼 할까봐 ㅋㅋㅋㅠㅠ 적고보니까 거기까지 고려할 사항은 아닌거같긴 하네요
암튼 결정되면 다시 얘기하던가 PR올리던가 할게요 일단 지금 생각은 label이에요 (편집됨)

J040_김세영:
저 지금 작업하면서 드롭다운 css 아주 살짝 건드려서 ...ㅎㅎ 천천히 하세요 컨플릭트 있을까봐 무서우니까

J149_이우재:
그럼 안하고 생각만 하고 있어볼게요!

J040_김세영:
label 사용하면 id에 대한 배경지식 < 이런건 사실 상관없어보이는데 음

J040_김세영:
저도 생각해볼게요 더 !!

J040_김세영:
저도 id 붙이는거 있는데 사실 타스랑 싸우느라 아직 거기까지 못갔어요

J040_김세영:
그리고 뭔가 id로 트리거 찾아서 > 이거 자체가 리액트 레벨에서 컨트롤하는게 아니라 직접 DOM 접근하는 느낌이라 ..ㅎㅎ 좋은 방식인지 잘 모르겠어요

J149_이우재:
저도 그렇게 생각은 하는데.. 저 시나리오에서 어떻게 사용할 수 있도록 열어줄지 당장 생각나는 방법이 없어요

J040_김세영:
사용자가 아닌 외부 컴포넌트에서 드롭다운 열고싶은 용례가 혹시 있나요? ref를 넘기고 useImperativeHandle로 click 인터페이스 정의해두는 것도 방법이라고 생각되는데 용례에 적합한지 잘 모르겠어요

J149_이우재:
첫 사용자에게 서비스 사용법을 알려주기 위해 드롭다운을 열어서 설명과 함께 보여준다거나.. 그런 상황이 있을 것 같아요. 약간 프로그래머스 IDE 처음 들어가면 나오는 것처럼?

J040_김세영:
아하 ... 그런거면 뭔가 간단하게 한 컴포넌트 레벨에서 ref 내릴 수 있는게 아닌 것 같아서 querySelector로 가져오는게 낫겠네요

J040_김세영:
ㅜㅜ 어렵잖아요

J149_이우재:
그리고 지금

<Dropdown.Trigger>
  <button/>
</Dropdown.Trigger>

이런식으로 들어가는데 그냥 children으로 접근해서 저 element에 온클릭 리스너를 달아주는 식으로 하는게 더 정확하지 않나.. 그런 생각도 들어요 ㅠㅠ

J040_김세영:
한번만 다시 설명해주세요 ...ㅎㅎ

J149_이우재:
지금 저렇게 코드를 쓰면

<div> // 이 div에 Trigger의 onClick이 달림
  <button/>
</div>

이런 결과가 되는게 조금.. 말로 표현하기 애매한 찝찝함이 있어서
children에 접근해서 온클릭을 다는게 낫지 않을까 하는 생각이에요... (편집됨)

J040_김세영:
방법이 없지 않나요 ...? 껍데기가 없을 수 있으면 더 좋겠지만 .. 전 Trigger라는 요소의 역할이 눌렀을때 드롭다운을 드러나게 한다 니까 지금 방식에서도 찝찝한 부분이 거기에 있진 않은거같아요. 껍데기가 하나 더 생기는게 좀 별로일뿐 ..? (편집됨)

J149_이우재:
Trigger에 props로 컴포넌트를 받거나 children에 접근해서 자식 노드에 onClick을 달거나
두가지 방법이 있을 것 같아요. 물론 aria태그로 role 지정할수 있긴 한데 div에 억지로 aira태그 달아서 버튼인척 하지 말라는 글을 봐가지구

J040_김세영:
props로 컴포넌트를 받거나 > 이게 JSXConstructor까지만 받는거죠 ..? 사용처에서 원하는 props를 또 받아줘야해서 그렇게 좋은 방법은 아닌 것 같아요.
children에 접근해서 자식 노드에 onClick을 달거나 > 이건 어떻게 하는거에요 ..?
차라리 cloneElement 사용하는게 나을지도 ...

J040_김세영:
접근성 관련된 부분은 aria-controls 사용하면 괜찮을 것 같은데 어떻게 생각하세요 ..!!

J149_이우재:
aria-controls를 달더라도 지금의 방식에서는 이 링크의 2번 문단같은 문제가 생길 것 같아요. tabindex를 지정한다고 하더라도 Trigger의 하위에 올 button 요소는 개발자가 직접 tabindex를 -1로 지정해야 하기 때문에 결국 접근성 고려에 대한 책임을 지게 돼요.
저는 차라리 <Dropdown.Trigger Element={<button/>}/>의 형태로 받아서 버튼이 직접적으로 버튼의 역할을 하도록 사용하는게 더 바람직해보여요. 당장 트리거가 받는 props도 따로 없고.. (편집됨)

J040_김세영:
Trigger의 하위에 올 button 요소는 개발자가 직접 tabindex를 -1로 지정해야 하기 때문에 > onClick 핸들러가 Trigger에 달려있는데 그렇게 안해도 되지않나요 ?? <Dropdown.Trigger Element={<button/>}/> 이렇게 받아도 해당 Element에 onClick 붙이려면 clone 같은거 해야하는건 똑같은 것 같은데 아닌가요 ...??

J040_김세영:
Dropdown 단에서는 키보드 컨트롤 달아줘야하는게 Trigger에 tabIndex 넣고 엔터 키에 open 해주면 되는거 맞나요?

J149_이우재:

해당 Element에 onClick 붙이려면 clone 같은거 해야하는건 똑같은 것 같은데 아닌가요 ...??

import React from "react";

function MyButton() {
  return <button type="button">dd</button>;
}

function Trigger({Element} : {Element: React.ElementType}) {
    // ...
    return <Element onClick={() => {console.log('dd')}}/>
}

// 실제 사용
function Usecase() {
    return <Dropdown.Trigger Element={MyButton}/>
}

이런식으로 onClick을 추가할 수 있는데.. 지금 체크하려고 보니까 React.ElementType이 EmotionJSX.Element와 타입이 안맞아서 우리 프로젝트에서는 에러를 표시하네요

Dropdown 단에서는 키보드 컨트롤 달아줘야하는게 Trigger에 tabIndex 넣고 엔터 키에 open 해주면 되는거 맞나요?

맞습니당

J149_이우재:
tabindex를 -1로 지정해줘야한다는 이야기는

<Dropdown.Trigger>
  <button/>
</Dropdown.Trigger>

위에처럼 쓰면 button태그도 기본적으로 focusable이기때문에 불필요한 탭인덱스를 빼야해서 추가 옵션을 넣어주어야 한다는 의미였어요! 제가 탭인덱스에 대해 잘못 이해한게 아니라면 기본 상태로는 Trigger와 button이 둘 다 탭인덱스를 갖게 될 것 같아서요

J149_이우재:
ㅠㅠ죄송해요 혼자 고민해야하는데 세영님 붙들고 질질 끄는것같아요

J040_김세영:
헉 아니에요 같이 고민해보자고 같이 하는건데요 ㅋㅋㅋㅋㅋㅋ키 저녁만 먹고 코멘트 달게요 !!!!

J040_김세영:

기본 상태로는 Trigger와 button이 둘 다 탭인덱스를 갖게 될 것 같아서요

아아 이게 맞네요. <Dropdown.Trigger Element={<button/>}/> 이렇게 얘기하셔서 방법이 cloneElement밖에 없다고 생각했었어요.
근데 엘레먼트 자체를 props로 받는건 생각을 좀 더 해봐야 할 것 같아요. 기존 컴포넌트에 전달하고싶은 props가 있는 경우에 직접 붙이는게 아니라 객체 형태로 전달해야 하는거 아닌가요?
불편하기도 하고, 다른 컴파운드 컴포넌트들이 사용하는 인터페이스대로 통일하는게 좋을 것 같다는 의견이에요.
상황을 정리해보면

  • children을 감싸는 div가 있을 때: tabbable 요소가 들어오는 케이스를 고려해 tabIndex 조정
  • 없을 때: onClick 핸들러
    어떤 경우든 children으로 받은 컴포넌트에 추가 속성이 필요하다면 접근성 이슈를 개발적 허용..ㅋㅋ 으로 남겨두거나, 레거시지만 cloneElement를 사용하는것 중에 선택하면 될 것 같아요.
    아니면 Trigger 형태의 자유도를 좀 줄여서 기본 요소를 버튼으로 고정하고 children을 텍스트나 svg만 받는건 어떨까요 ..? 컴파운드의 의미가 없나요 ...? > 는 이걸 쓰고 Select 개발하려고 갔더니 바로 안된다는걸 깨달았어요 ...ㅋ ㅋㅋㅋㅋㅋㅋ ㅠㅜ
    하나 더 생각났어요 상위 요소에서 focus 있는 상태에서 tab 눌렀을 때 이동 안시키면 되는거니까 keydown 핸들러로 어떻게 안될까요 ..? Trigger div 내부에 tabbable 요소 있는 경우요! (편집됨)

J149_이우재:
저 댓글을 남기고 지금까지 저 형태가 불가능하다는걸 탐구하고 왔어요.. 결국 cloneElement가 제일 쉬운 길인 것 같아요. 아니면 children을 받아서 children에 직접 DOM 요소로 접근해서 onClick, aria 속성 등 붙이기..

기존 컴포넌트에 전달하고싶은 props가 있는 경우에 직접 붙이는게 아니라 객체 형태로 전달해야 하는거 아닌가요?

이거 다시 설명해주실 수 있나요?
마지막 줄은 저는 아직 잘 모르겠어요. interactive한 요소는 focus 의사 코드로 스타일링을 할 일이 있을텐데, 이걸 위해서는 Trigger의 children에 직접 focus가 갈 수 있어야 해요. 그럼 결국 children에 접근한다는 찝찝함은 똑같이 가져가면서 불필요한 div만 한 겹 더 생기는 꼴이라고 생각해요.