펀잇에서 dialog 위에 토스트가 가려 보이지 않는 문제가 있었다.
엄~청 삽질하다가 결국 div에 role = 'dialog'를 주는 방식으로 해결했는데...
(자세한 내용은 아래에 있다)
어찌어찌 해결은 했지만, 뭔가 만족스럽지는 않은 해결법이었다.
dialog 태그를 꼭 사용하고 싶었기 때문!
dialog 태그를 사용해야 웹 접근성 측면에서 효율적이기 때문이다.
그래서 사용할 수 있는 다른 방법을 찾으면 적용해보자 싶었는데
'보투게더' 팀에서 올린 포스트가 있어 이를 참고해 수정해 보았다.
간단하게 축약하자면,
토스트의 id를 동적으로 바꾸는 것이다.
dialog가 있으면 토스트가 dialog 내부에 붙게 하고,
없으면 원래 있던 위치에 붙게 하는 것이다.
const BottomSheet = (
{ maxWidth, maxHeight, isClosing, close, hasToast, children, ...props }: BottomSheetProps,
ref: ForwardedRef<HTMLDialogElement>
) => {
return createPortal(
<ModalDialog ref={ref} {...props}>
<BackDrop onClick={close} />
<ModalWrapper maxWidth={maxWidth} isClosing={isClosing}>
{children}
</ModalWrapper>
{hasToast && <div id="toast-in-dialog-container" aria-hidden />} //여기 추가
</ModalDialog>,
containerElement
);
};
export default forwardRef(BottomSheet);
좀 잘라먹긴 했는데 아래쪽에 보면
{hasToast && <div id="toast-in-dialog-container" aria-hidden />}
요 부분을 추가해 준다.
dialog랑 상관없는 경우에는 원래 위치의 portal id에 따라 토스트가 붙어야 하기 때문에
dialog에서 사용할 때만 hasToast라는 props를 따로 받도록 했다.
그래서
<BottomSheet hasToast isClosing={isClosing} close={handleCloseBottomSheet} ref={ref}>
요렇게 dialog에 hasToast props를 주면,
토스트가 toast-in-dialog-container에 붙게 되는 것이다.
그리고 토스트에서도 id를 받아서 변경해줘야 한다.
아래는 전체 토스트 context 코드이다.
export interface ToastState {
id: number;
message: string;
isError?: boolean;
}
export interface ToastValue {
toasts: ToastState[];
}
export interface ToastAction {
toast: {
success: (message: string) => void;
error: (message: string) => void;
};
deleteToast: (id: number) => void;
setToastId: (id: ToastId) => void;
}
export const ToastValueContext = createContext<ToastValue | null>(null);
export const ToastActionContext = createContext<ToastAction | null>(null);
export const ToastProvider = ({ children }: PropsWithChildren) => {
const [toasts, setToasts] = useState<ToastState[]>([]);
const [toastElementId, setToastElementId] = useState<ToastId>('toast-container');
const showToast = (id: number, message: string, isError?: boolean) => {
setToasts([...toasts, { id, message, isError }]);
};
const deleteToast = (id: number) => {
setToasts((prevToasts) => prevToasts.filter((toast) => toast.id !== id));
};
const setToastId = (id: ToastId) => {
setToastElementId(id);
};
const toast = {
success: (message: string) => showToast(Number(Date.now()), message),
error: (message: string) => showToast(Number(Date.now()), message, true),
};
const toastValue = {
toasts,
};
const toastAction = {
toast,
deleteToast,
setToastId,
};
return (
<ToastActionContext.Provider value={toastAction}>
<ToastValueContext.Provider value={toastValue}>
{children}
{createPortal(
<ToastContainer>
{toasts.map(({ id, message, isError }) => (
<Toast key={id} id={id} message={message} isError={isError} />
))}
</ToastContainer>,
document.getElementById(toastElementId) as HTMLElement
)}
</ToastValueContext.Provider>
</ToastActionContext.Provider>
);
};
export default ToastProvider;
이렇게 기존 토스트 id 값으로 상태를 만들어주고,
const [toastElementId, setToastElementId] = useState<ToastId>('toast-container');
토스트 아이디를 바꿔주는 함수를 만든다.
const setToastId = (id: ToastId) => {
setToastElementId(id);
};
그리고 마지막으로 기존에는 toast-container라는 주어진 값으로 받던걸
document.getElementById('toast-container') as HTMLElement
아래처럼 상태로 받게 하면 끗 -!
return (
<ToastActionContext.Provider value={toastAction}>
<ToastValueContext.Provider value={toastValue}>
{children}
{createPortal(
<ToastContainer>
{toasts.map(({ id, message, isError }) => (
<Toast key={id} id={id} message={message} isError={isError} />
))}
</ToastContainer>,
document.getElementById(toastElementId) as HTMLElement //여기
)}
</ToastValueContext.Provider>
</ToastActionContext.Provider>
);
};
마지막으로 dialog를 열고 닫을 때마다 id를 바꿔준다.
const { setToastId } = useToastActionContext();
const handleOpenBottomSheet = () => {
setToastId('toast-in-dialog-container');
ref.current?.showModal();
};
const handleCloseBottomSheet = () => {
setToastId('toast-container');
closeAnimated();
};
전체 코드는 아래와 같다.
div로 하던 걸 dialog 태그로 다시 바꾼 거라
열고 닫는 로직까지 다 변경되어 있어 좀 헷갈릴 수 있지만...!
'펀잇' 카테고리의 다른 글
compound component pattern을 활용해 컴포넌트 조립하기 (0) | 2024.08.20 |
---|---|
Meta OG image 교체가 안 된다면?! (1) | 2024.07.23 |
펀잇 리뷰 작성 폼에서 웹 접근성 개선하기 (2) | 2023.11.12 |
최종 데모데이 후기 (2) | 2023.11.05 |
4, 5차 데모데이 피드백과 반영 (0) | 2023.11.01 |
댓글