-
Notifications
You must be signed in to change notification settings - Fork 1
Refactor/dropdown: Dropdown 컴포넌트 리팩토링 #71
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
Conversation
@@ -148,11 +142,15 @@ const Menu = ({ children }: ChildrenProps) => { | |||
return ( | |||
<MenuWrapper | |||
ref={menuRef} | |||
aria-labelledby={id} | |||
aria-labelledby={`${label}-Trigger`} | |||
id={`${label}-Dropdown`} |
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.
저희 이런애들도 컨벤션 정해두고 맞춰서 써요 ㅎㅎ
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.
다음주 회의때 얘기해볼게요~!
src/components/Dropdown/index.tsx
Outdated
const Trigger = ({ Element }: { Element: ReactElement }) => { | ||
const context = useContext(DropdownContext); | ||
const triggerRef = useRef<HTMLDivElement | null>(null); | ||
|
||
if (!context) return <></>; | ||
|
||
const { isOpen, setIsOpen, dropdownLabel, id, setTriggerSize } = context; | ||
const { isOpen, setIsOpen, label, setTriggerSize } = context; | ||
|
||
useEffect(() => { | ||
if (!triggerRef.current || !setTriggerSize) return; | ||
const $trigger = document.getElementById(`${label}-Trigger`); | ||
if (!$trigger) return; | ||
setTriggerSize({ | ||
width: triggerRef.current.offsetWidth, | ||
height: triggerRef.current.offsetHeight, | ||
width: $trigger.offsetWidth, | ||
height: $trigger.offsetHeight, | ||
}); | ||
}, [triggerRef.current]); | ||
|
||
return ( | ||
<div | ||
onClick={(e: ReactMouseEvent) => { | ||
e.stopPropagation(); | ||
setIsOpen(!isOpen); | ||
}} | ||
aria-expanded={isOpen} | ||
aria-haspopup="true" | ||
aria-label={dropdownLabel} | ||
id={id} | ||
ref={triggerRef} | ||
> | ||
{children} | ||
</div> | ||
); | ||
}, []); | ||
|
||
return cloneElement(Element, { |
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.
const Trigger = ({ children }: { children: ReactElement }) => {
const context = useContext(DropdownContext);
if (!context) return <></>;
const { isOpen, setIsOpen, label, setTriggerSize } = context;
useEffect(() => {
const $trigger = document.getElementById(`${label}-Trigger`);
if (!$trigger) return;
setTriggerSize({
width: $trigger.offsetWidth,
height: $trigger.offsetHeight,
});
}, []);
return cloneElement(children, {
이렇게 사용하면 가능할거에요! ReactNode는 아래처럼 생겼는데 Element 여러개를 받을 수도 있어서 타입 오류가 있었나봐요.
type ReactFragment = Iterable<ReactNode>;
type ReactNode = ReactElement | string | number | ReactFragment | ReactPortal | boolean | null | undefined;
// 이제 이건 되고
<Dropdown.Trigger>
<Button text="팀원 목록" icon={MdPeople} variant="light" />
</Dropdown.Trigger>
// 이건 안돼요
<Dropdown.Trigger>
<Button text="팀원 목록" icon={MdPeople} variant="light" />
<Button text="팀원 목록" icon={MdPeople} variant="light" />
</Dropdown.Trigger>
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.
어!
어!!
죄송해요 제가 회의하던 내용을 뒤죽박죽 섞어서 잘못 반영했나봐요 헐
너무 창피해요 금방 고칠게요
헐..!
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.
이거 다시 원래 결론대로 수정했어요!
src/components/Dropdown/index.tsx
Outdated
}, []); | ||
|
||
return cloneElement(Element, { | ||
onClick: (e: ReactMouseEvent) => { |
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.
const onClick: MouseEventHandler = (e) => {
e.stopPropagation();
setIsOpen((prev) => !prev);
};
const onKeyDown: KeyboardEventHandler = (e) => {
if (e.key === 'Enter') {
setIsOpen((prev) => !prev);
}
};
핸들러도 이렇게 key랑 같은 네이밍으로 정의하고 넣어주면 더 깔끔할 것 같아요.
그리고 tabIndex, onKeyDown도 이번 작업에 넣어주실 수 있을까요? (요소가 tabbable이 아닌 경우도 있을 수 있어요)
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.
tabIndex는 반영할게요!
onKeyDown은 tabbable element인 경우 기본 동작 + 추가 핸들러가 같이 동작해서 열 수 없게 되지는 않나요?
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.
헉 열 수 없을 것 같네요 !! if 블럭 안에 e.preventDefault() 추가하면 되나요?
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.
스페이스바랑 엔터에 대해 작동하도록 반영했어요 😁
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.
id가 쏘아올린 작은공 ... 마감합니다 ...
너무너무너무너무 고생하셨어요 🎉🎉🎉 그래도 밥은 드세요 ....
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.
cloneElement 로 Trigger 의 children에 속성을 할당하는 건가요?
멋있는 방법같아요~
🤠 개요
display:none;
으로 숨겨요.label
로 변경하고, 이를 기반으로 접근성 태그와 id를 붙이도록 했어요.💫 설명
display:none
은 접근성 트리에서도 제거되기 때문에 따로 aria-hidden은 붙이지 않았어요.Context만을 반환하는 Dropdown에 id props가 있는게 혼동을 일으킨다고 생각해서 label로 이름을 변경했어요.
이는 키보드 접근성에서도 실제 Trigger div, 껍데기 element 두 개의
tabIndex
가 생기는 부작용이 있어요.또 focus를 Trigger div에만 한정시켜야 하는 문제가 있어 css 스타일링에도 어려움이 있어요.
그 외에 aria 태그가 껍데기 element가 아닌 그 위의 Trigger div에 붙어 있는 구조가 어색하다고 생각했어요.
📷 스크린샷 (Optional)