From 53f180a582c452e7324916bb71dc8c01b806f87d Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Tue, 1 Oct 2024 20:45:00 +0900 Subject: [PATCH 01/49] =?UTF-8?q?refactor(Header):=20=ED=97=A4=EB=8D=94?= =?UTF-8?q?=EC=97=90=20=EC=82=AC=EC=9A=A9=EB=90=9C=20IconButton=EB=93=A4?= =?UTF-8?q?=EC=97=90=20aria-label=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/Header/DefaultHeader/DefaultHeader.tsx | 1 + frontend/src/components/common/Header/Header.tsx | 3 ++- .../common/Header/HomePageHeader/HomePageHeader.tsx | 1 + .../common/Header/SearchHeader/SearchHeader.tsx | 9 ++++++++- .../SearchResultPageHeader.tsx | 1 + 5 files changed, 13 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/common/Header/DefaultHeader/DefaultHeader.tsx b/frontend/src/components/common/Header/DefaultHeader/DefaultHeader.tsx index a1f389ad..3797d96c 100644 --- a/frontend/src/components/common/Header/DefaultHeader/DefaultHeader.tsx +++ b/frontend/src/components/common/Header/DefaultHeader/DefaultHeader.tsx @@ -15,6 +15,7 @@ const DefaultHeader = () => { iconType="home-icon" size="20" onClick={() => navigation(ROUTE_PATHS_MAP.root)} + aria-label="홈 이동" /> } isHamburgerUsed diff --git a/frontend/src/components/common/Header/Header.tsx b/frontend/src/components/common/Header/Header.tsx index 42c979c2..01fc09d6 100644 --- a/frontend/src/components/common/Header/Header.tsx +++ b/frontend/src/components/common/Header/Header.tsx @@ -52,6 +52,7 @@ const Header = ({ navigate(ROUTE_PATHS_MAP.root) : () => goBack()} @@ -62,7 +63,7 @@ const Header = ({ {rightContent} {isHamburgerUsed && ( - + )} diff --git a/frontend/src/components/common/Header/HomePageHeader/HomePageHeader.tsx b/frontend/src/components/common/Header/HomePageHeader/HomePageHeader.tsx index a9aa846b..a71c699d 100644 --- a/frontend/src/components/common/Header/HomePageHeader/HomePageHeader.tsx +++ b/frontend/src/components/common/Header/HomePageHeader/HomePageHeader.tsx @@ -17,6 +17,7 @@ const HomePageHeader = () => { iconType="search-icon" size="18" onClick={() => navigation(ROUTE_PATHS_MAP.searchMain)} + aria-label="여행기 검색" /> } isHamburgerUsed diff --git a/frontend/src/components/common/Header/SearchHeader/SearchHeader.tsx b/frontend/src/components/common/Header/SearchHeader/SearchHeader.tsx index b133dd84..26557dd7 100644 --- a/frontend/src/components/common/Header/SearchHeader/SearchHeader.tsx +++ b/frontend/src/components/common/Header/SearchHeader/SearchHeader.tsx @@ -87,13 +87,20 @@ const SearchHeader = () => { > - + navigate(ROUTE_PATHS_MAP.root)} + aria-label="홈 이동" /> } diff --git a/frontend/src/components/common/Header/SearchResultPageHeaderHeader/SearchResultPageHeader.tsx b/frontend/src/components/common/Header/SearchResultPageHeaderHeader/SearchResultPageHeader.tsx index d9c5f28c..e9a99dd2 100644 --- a/frontend/src/components/common/Header/SearchResultPageHeaderHeader/SearchResultPageHeader.tsx +++ b/frontend/src/components/common/Header/SearchResultPageHeaderHeader/SearchResultPageHeader.tsx @@ -14,6 +14,7 @@ const SearchResultPageHeader = () => { rightContent={ navigation(ROUTE_PATHS_MAP.searchMain)} /> From e46a62a278d3609b3e8f7003ec4ffd32ad5ff0eb Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Tue, 1 Oct 2024 20:47:53 +0900 Subject: [PATCH 02/49] =?UTF-8?q?refactor(Drawer):=20button=20=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=20=EC=A4=91=EC=B2=A9=20=EC=82=AC=EC=9A=A9=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/common/Drawer/Drawer.styled.ts | 3 ++- frontend/src/components/common/Drawer/Drawer.tsx | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/common/Drawer/Drawer.styled.ts b/frontend/src/components/common/Drawer/Drawer.styled.ts index 78513c39..89f606a8 100644 --- a/frontend/src/components/common/Drawer/Drawer.styled.ts +++ b/frontend/src/components/common/Drawer/Drawer.styled.ts @@ -1,3 +1,4 @@ +import { css } from "@emotion/react"; import styled from "@emotion/styled"; export const DrawerContainer = styled.div<{ isOpen: boolean }>` @@ -45,7 +46,7 @@ export const DrawerContent = styled.div` padding: 1rem; `; -export const TriggerButton = styled.button` +export const triggerStyle = css` background: none; border: none; diff --git a/frontend/src/components/common/Drawer/Drawer.tsx b/frontend/src/components/common/Drawer/Drawer.tsx index 45614af4..d86d9ffa 100644 --- a/frontend/src/components/common/Drawer/Drawer.tsx +++ b/frontend/src/components/common/Drawer/Drawer.tsx @@ -52,9 +52,9 @@ const Content = ({ children }: React.PropsWithChildren) => { const Trigger = ({ children }: React.PropsWithChildren) => { const { toggleDrawer } = useDrawerContext(); return ( - +
{children} - +
); }; From 0d0b8d400bc5c783f7c7104dc6f2c47bc23b1659 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Tue, 1 Oct 2024 20:55:50 +0900 Subject: [PATCH 03/49] =?UTF-8?q?chore(MainPage):=20=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=97=86=EB=8A=94=20=EA=B3=B5=EB=B0=B1,=ED=83=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/pages/main/MainPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/pages/main/MainPage.tsx b/frontend/src/components/pages/main/MainPage.tsx index 2ded8c84..e9deb3f7 100644 --- a/frontend/src/components/pages/main/MainPage.tsx +++ b/frontend/src/components/pages/main/MainPage.tsx @@ -66,8 +66,8 @@ const MainPage = () => { 지금 뜨고 있는 여행기 - 다른 이들의 여행을 구경해보세요.{" "} - (태그는 최대 {FORM_VALIDATIONS_MAP.tags.maxCount}개까지 가능해요!) + 다른 이들의 여행을 구경해보세요. ( 태그는 최대 {FORM_VALIDATIONS_MAP.tags.maxCount} + 개까지 가능해요! ) From af6ffd4738b6ce6c0eb7dd0648d843b83fec2a1c Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Mon, 7 Oct 2024 16:42:01 +0900 Subject: [PATCH 04/49] =?UTF-8?q?refactor(AvatarCircle):=20props=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/common/AvatarCircle/AvatarCircle.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/common/AvatarCircle/AvatarCircle.tsx b/frontend/src/components/common/AvatarCircle/AvatarCircle.tsx index 653341cb..ffeedd5b 100644 --- a/frontend/src/components/common/AvatarCircle/AvatarCircle.tsx +++ b/frontend/src/components/common/AvatarCircle/AvatarCircle.tsx @@ -3,19 +3,18 @@ import useImageError from "@hooks/useImageError"; import * as S from "./AvatarCircle.styled"; import type { AvatarCircleSize } from "./AvatarCircle.type"; -interface AvatarCircleProps { +interface AvatarCircleProps extends React.ImgHTMLAttributes { $size?: AvatarCircleSize; profileImageUrl?: string; - imageAlt?: string; } -const AvatarCircle = ({ $size = "small", profileImageUrl, imageAlt }: AvatarCircleProps) => { +const AvatarCircle = ({ $size = "small", profileImageUrl, ...props }: AvatarCircleProps) => { const { imageError, handleImageError } = useImageError({ imageUrl: profileImageUrl }); return ( {!imageError ? ( - {imageAlt} + ) : ( Date: Mon, 7 Oct 2024 17:02:29 +0900 Subject: [PATCH 05/49] =?UTF-8?q?refactor(TravelogueCard):=20=EC=9B=B9=20?= =?UTF-8?q?=EC=A0=91=EA=B7=BC=EC=84=B1=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 장식 이미지에 alt 빈 값 추가 - 카드 컴포넌트 태그를 button 태그로 수정 - aria-label 추가 --- .../TravelogueCard/TravelogueCard.styled.ts | 5 +-- .../main/TravelogueCard/TravelogueCard.tsx | 38 ++++++++++++------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.styled.ts b/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.styled.ts index b70507ce..a2891f66 100644 --- a/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.styled.ts +++ b/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.styled.ts @@ -1,8 +1,9 @@ import styled from "@emotion/styled"; -export const TravelogueCardLayout = styled.li` +export const TravelogueCardButton = styled.button` display: flex; flex-direction: column; + width: 100%; max-width: calc(48rem - 3.2rem); padding: 1.6rem; border: 1px solid ${({ theme }) => theme.colors.border}; @@ -16,8 +17,6 @@ export const TravelogueCardLayout = styled.li` &:hover { transform: translateY(-5px); - - border-radius: 8px; } `; diff --git a/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx b/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx index 2f003c44..81272ef7 100644 --- a/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx +++ b/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx @@ -2,7 +2,7 @@ import { useNavigate } from "react-router-dom"; import { TravelogueResponse } from "@type/domain/travelogue"; -import { AvatarCircle, Chip, FallbackImage, IconButton, Text } from "@components/common"; +import { AvatarCircle, Chip, FallbackImage, Icon, Text } from "@components/common"; import useImageError from "@hooks/useImageError"; @@ -18,6 +18,22 @@ interface TravelogueCardProps { >; } +const removeEmoji = (str: string) => + str.replace(/(\p{Emoji_Presentation}|\p{Emoji}\uFE0F)/gu, "").trim(); + +const getCardAriaLabel = ({ + title, + authorNickname, + likeCount, + tags, +}: Pick) => { + const tagNames = tags.map((tag) => removeEmoji(tag.tag)).join(", "); + + const tagPart = tagNames ? `태그: ${tagNames}.` : ""; + + return `${title} 여행기. ${authorNickname} 작성. 좋아요 수: ${likeCount}개. ${tagPart}`; +}; + const TravelogueCard = ({ travelogueOverview: { id, @@ -37,14 +53,12 @@ const TravelogueCard = ({ navigate(ROUTE_PATHS_MAP.travelogue(id)); }; - const handleLikeClick = (e: React.MouseEvent) => { - e.stopPropagation(); - }; - return ( - {title} @@ -52,11 +66,7 @@ const TravelogueCard = ({ {!imageError ? ( - + ) : ( )} @@ -64,12 +74,12 @@ const TravelogueCard = ({ - + {authorNickname} - + {likeCount} @@ -79,7 +89,7 @@ const TravelogueCard = ({ ))} - + ); }; From 1e70067c69d1c02f238df747c1341ff515cb6e83 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Tue, 8 Oct 2024 21:42:08 +0900 Subject: [PATCH 06/49] =?UTF-8?q?refactor(Chip):=20as=20props=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/common/Chip/Chip.tsx | 22 ++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/common/Chip/Chip.tsx b/frontend/src/components/common/Chip/Chip.tsx index ad7a2af4..529129de 100644 --- a/frontend/src/components/common/Chip/Chip.tsx +++ b/frontend/src/components/common/Chip/Chip.tsx @@ -1,19 +1,33 @@ -import { ComponentPropsWithoutRef } from "react"; +import React from "react"; import { CYPRESS_DATA_MAP } from "@constants/cypress"; import Text from "../Text/Text"; import * as S from "./Chip.styled"; -interface ChipProps extends ComponentPropsWithoutRef<"li"> { +interface ChipOwnProps { + as?: Element; label: string; isSelected?: boolean; index?: number; } -const Chip = ({ label, isSelected = false, index, children, ...props }: ChipProps) => { +type ChipProps = ChipOwnProps & + Omit, keyof ChipOwnProps>; + +function Chip({ + as, + label, + isSelected = false, + index, + children, + ...props +}: ChipProps) { + const Component = as || "li"; + return ( ); -}; +} export default Chip; From a82b9a9198fd0bb2091364f91aea15f427c4bf7e Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Wed, 9 Oct 2024 18:02:58 +0900 Subject: [PATCH 07/49] =?UTF-8?q?style(FallbackImage):=20color=20contrast?= =?UTF-8?q?=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/common/FallbackImage/FallbackImage.styled.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/common/FallbackImage/FallbackImage.styled.ts b/frontend/src/components/common/FallbackImage/FallbackImage.styled.ts index e5b0706e..fe7bd415 100644 --- a/frontend/src/components/common/FallbackImage/FallbackImage.styled.ts +++ b/frontend/src/components/common/FallbackImage/FallbackImage.styled.ts @@ -12,5 +12,5 @@ export const Fallback = styled.div` background-color: #eee; ${(props) => props.theme.typography.mobile.detailBold}; - color: #9e9e9e; + color: ${({ theme }) => theme.colors.text.secondary}; `; From 6e320235be5d14ea3b92b1560d37edc91ada5bfd Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Wed, 9 Oct 2024 18:08:42 +0900 Subject: [PATCH 08/49] =?UTF-8?q?refactor(MainPage):=20Chip=EC=9D=84=20but?= =?UTF-8?q?ton=EC=9C=BC=EB=A1=9C=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/pages/main/MainPage.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/src/components/pages/main/MainPage.tsx b/frontend/src/components/pages/main/MainPage.tsx index e9deb3f7..b332bdee 100644 --- a/frontend/src/components/pages/main/MainPage.tsx +++ b/frontend/src/components/pages/main/MainPage.tsx @@ -74,6 +74,8 @@ const MainPage = () => { { Date: Thu, 10 Oct 2024 10:14:49 +0900 Subject: [PATCH 09/49] =?UTF-8?q?refactor(FloatingButton):=20=ED=94=8C?= =?UTF-8?q?=EB=A1=9C=ED=8C=85=20=EB=B2=84=ED=8A=BC=20title=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/common/FloatingButton/FloatingButton.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/common/FloatingButton/FloatingButton.tsx b/frontend/src/components/common/FloatingButton/FloatingButton.tsx index 62e47e48..2de2748c 100644 --- a/frontend/src/components/common/FloatingButton/FloatingButton.tsx +++ b/frontend/src/components/common/FloatingButton/FloatingButton.tsx @@ -46,7 +46,12 @@ const FloatingButton = () => { - + ); From e490f5ef3dba3ca610635a44fb554214233e3d07 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Thu, 10 Oct 2024 10:21:30 +0900 Subject: [PATCH 10/49] =?UTF-8?q?feat(FocusTrap):=20=ED=82=A4=EB=B3=B4?= =?UTF-8?q?=EB=93=9C=20=ED=8A=B8=EB=9E=A9=20hook=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: jinyoung <87177577+jinyoung234@users.noreply.github.com> --- frontend/src/components/common/FocusTrap.tsx | 106 +++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 frontend/src/components/common/FocusTrap.tsx diff --git a/frontend/src/components/common/FocusTrap.tsx b/frontend/src/components/common/FocusTrap.tsx new file mode 100644 index 00000000..f7371dce --- /dev/null +++ b/frontend/src/components/common/FocusTrap.tsx @@ -0,0 +1,106 @@ +import React, { useEffect, useRef } from "react"; + +const getFocusableElements = (element: HTMLElement | null): HTMLElement[] => { + if (!element) return []; + + const focusableSelectors = [ + "a[href]", + "button", + "input", + "textarea", + "select", + '[tabindex]:not([tabindex="-1"])', + "li", + ].join(","); + + return Array.from(element.querySelectorAll(focusableSelectors)).filter( + (el) => !el.hasAttribute("disabled") && !el.getAttribute("aria-hidden"), + ); +}; + +interface Props extends React.ComponentPropsWithoutRef<"div"> { + children: React.ReactElement; + onEscapeFocusTrap: () => void; +} + +const FocusTrap = ({ children, onEscapeFocusTrap, ...props }: Props) => { + const focusTrapRef = useRef(null); + const child = React.Children.only(children); + const focusableElements = useRef<(HTMLElement | null)[]>([]); + const currentFocusIndex = useRef(-1); + + console.log(focusTrapRef.current); + + useEffect(() => { + if (focusTrapRef.current) { + focusableElements.current = getFocusableElements(focusTrapRef.current); + } + + focusableElements.current[0]?.focus(); + + return () => { + focusableElements.current = []; + }; + }, []); + + const firstElement = focusableElements.current[0]; + const lastElement = focusableElements.current[focusableElements.current.length - 1]; + + useEffect(() => { + const focusNextElement = () => { + currentFocusIndex.current = + (currentFocusIndex.current + 1) % focusableElements.current.length; + focusableElements.current[currentFocusIndex.current]?.focus(); + }; + + const focusPrevElement = () => { + currentFocusIndex.current = + (currentFocusIndex.current - 1 + focusableElements.current.length) % + focusableElements.current.length; + focusableElements.current[currentFocusIndex.current]?.focus(); + }; + + const handleTabKeyDown = (event: KeyboardEvent) => { + const isTabKeyDown = !event.shiftKey && event.key === "Tab"; + if (!isTabKeyDown) return; + + event.preventDefault(); + focusNextElement(); + }; + + const handleShiftTabKeyDown = (event: KeyboardEvent) => { + const isShiftTabKeyDown = event.shiftKey && event.key === "Tab"; + if (!isShiftTabKeyDown) return; + + event.preventDefault(); + focusPrevElement(); + }; + + const handleEscapeKeyDown = (event: KeyboardEvent) => { + if (event.key === "Escape") { + onEscapeFocusTrap(); + } + }; + + const handleKeyPress = (event: KeyboardEvent) => { + handleTabKeyDown(event); + handleShiftTabKeyDown(event); + handleEscapeKeyDown(event); + }; + + document.addEventListener("keydown", handleKeyPress); + + return () => document.removeEventListener("keydown", handleKeyPress); + }, [firstElement, lastElement, onEscapeFocusTrap]); + + const Component = React.cloneElement(child, { + ...{ ...props, ...child?.props }, + ref: focusTrapRef, + }); + + console.log(Component); + + return <>{Component}; +}; + +export default FocusTrap; From 903206eced55a013dbd63241d58e6bc586892398 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Thu, 10 Oct 2024 10:25:03 +0900 Subject: [PATCH 11/49] =?UTF-8?q?refactor(Modal):=20createPortal=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=EB=A5=BC=20#root=EC=97=90=EC=84=9C=20body?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/common/Modal/Modal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/common/Modal/Modal.tsx b/frontend/src/components/common/Modal/Modal.tsx index d29d4c8a..f71869d0 100644 --- a/frontend/src/components/common/Modal/Modal.tsx +++ b/frontend/src/components/common/Modal/Modal.tsx @@ -46,7 +46,7 @@ const Modal = ({ )} , - document.querySelector("#root") as HTMLElement, + document.body, ); }; From d507bfc04399dc3f9f0f12fdf325bc68d37e071b Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Thu, 10 Oct 2024 10:41:01 +0900 Subject: [PATCH 12/49] =?UTF-8?q?refactor(FocusTrap):=20onEscapeFocusTrap?= =?UTF-8?q?=EC=9D=84=20=EC=98=B5=EC=85=94=EB=84=90=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/common/FocusTrap.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/common/FocusTrap.tsx b/frontend/src/components/common/FocusTrap.tsx index f7371dce..d8fb5c04 100644 --- a/frontend/src/components/common/FocusTrap.tsx +++ b/frontend/src/components/common/FocusTrap.tsx @@ -20,7 +20,7 @@ const getFocusableElements = (element: HTMLElement | null): HTMLElement[] => { interface Props extends React.ComponentPropsWithoutRef<"div"> { children: React.ReactElement; - onEscapeFocusTrap: () => void; + onEscapeFocusTrap?: () => void; } const FocusTrap = ({ children, onEscapeFocusTrap, ...props }: Props) => { @@ -29,8 +29,6 @@ const FocusTrap = ({ children, onEscapeFocusTrap, ...prop const focusableElements = useRef<(HTMLElement | null)[]>([]); const currentFocusIndex = useRef(-1); - console.log(focusTrapRef.current); - useEffect(() => { if (focusTrapRef.current) { focusableElements.current = getFocusableElements(focusTrapRef.current); @@ -77,7 +75,7 @@ const FocusTrap = ({ children, onEscapeFocusTrap, ...prop }; const handleEscapeKeyDown = (event: KeyboardEvent) => { - if (event.key === "Escape") { + if (event.key === "Escape" && onEscapeFocusTrap) { onEscapeFocusTrap(); } }; @@ -85,7 +83,7 @@ const FocusTrap = ({ children, onEscapeFocusTrap, ...prop const handleKeyPress = (event: KeyboardEvent) => { handleTabKeyDown(event); handleShiftTabKeyDown(event); - handleEscapeKeyDown(event); + onEscapeFocusTrap && handleEscapeKeyDown(event); }; document.addEventListener("keydown", handleKeyPress); From e37935c3afcef9a2a2b9c29f9eb59f123d0662b6 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Thu, 10 Oct 2024 10:41:38 +0900 Subject: [PATCH 13/49] =?UTF-8?q?feat(Header):=20=ED=82=A4=EB=B3=B4?= =?UTF-8?q?=EB=93=9C=20=ED=8A=B8=EB=9E=A9=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/common/Header/Header.tsx | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/frontend/src/components/common/Header/Header.tsx b/frontend/src/components/common/Header/Header.tsx index 01fc09d6..f68a4133 100644 --- a/frontend/src/components/common/Header/Header.tsx +++ b/frontend/src/components/common/Header/Header.tsx @@ -11,6 +11,7 @@ import { PRIMITIVE_COLORS } from "@styles/tokens"; import { DoubleRightArrow } from "@assets/svg"; import Drawer from "../Drawer/Drawer"; +import FocusTrap from "../FocusTrap"; import IconButton from "../IconButton/IconButton"; import * as S from "./Header.styled"; @@ -77,24 +78,26 @@ const Header = ({ - - - 마이페이지 - - - {user?.accessToken ? ( - 로그아웃 - ) : ( - { - navigate(ROUTE_PATHS_MAP.login); - }} - > - 로그인 - - )} - - + + + + 마이페이지 + + + {user?.accessToken ? ( + 로그아웃 + ) : ( + { + navigate(ROUTE_PATHS_MAP.login); + }} + > + 로그인 + + )} + + + ); From 1b6e35bf88a358599d1ee9d32fe6b37ce4e226cb Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Thu, 10 Oct 2024 10:44:03 +0900 Subject: [PATCH 14/49] =?UTF-8?q?refactor(Header):=20=EC=8B=9C=EB=A7=A8?= =?UTF-8?q?=ED=8B=B1=20=ED=83=9C=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/common/Header/Header.styled.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/components/common/Header/Header.styled.ts b/frontend/src/components/common/Header/Header.styled.ts index 5868ff73..929ac12a 100644 --- a/frontend/src/components/common/Header/Header.styled.ts +++ b/frontend/src/components/common/Header/Header.styled.ts @@ -23,10 +23,9 @@ export const DrawHeaderContainer = styled.div` display: flex; `; -export const MenuItem = styled.li` +export const MenuItem = styled.button` ${(props) => props.theme.typography.mobile.bodyBold}; padding: ${({ theme }) => theme.spacing.s}; - cursor: pointer; `; export const MenuList = styled.ul` From 5a1edf1925568e20c9e9b66134eda2b1262cb7cb Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Thu, 10 Oct 2024 10:45:50 +0900 Subject: [PATCH 15/49] =?UTF-8?q?feat(Drawer):=20esc=EB=A1=9C=20=EB=8B=AB?= =?UTF-8?q?=EA=B8=B0=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=97=B4=EB=A0=A4?= =?UTF-8?q?=EC=9E=88=EC=9D=84=20=EA=B2=BD=EC=9A=B0=EC=97=90=EB=A7=8C=20Dra?= =?UTF-8?q?werContainer=EB=A0=8C=EB=8D=94=EB=A7=81=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DrawerContainer를 처음부터 렌더링되어있으면 이때부터 이미 focusTrap도 같이 렌더링되어 Drawer가 열려있지 않아도 focus trap이 Drawer content 내부에 갇히는 이슈가 있었기 때문입니다. --- .../src/components/common/Drawer/Drawer.tsx | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/frontend/src/components/common/Drawer/Drawer.tsx b/frontend/src/components/common/Drawer/Drawer.tsx index d86d9ffa..7e4d789a 100644 --- a/frontend/src/components/common/Drawer/Drawer.tsx +++ b/frontend/src/components/common/Drawer/Drawer.tsx @@ -1,4 +1,5 @@ -import React, { useState } from "react"; +import React, { useCallback, useEffect, useState } from "react"; +import ReactDOM from "react-dom"; import DrawerProvider, { useDrawerContext } from "@contexts/DrawerProvider"; @@ -9,7 +10,7 @@ import * as S from "./Drawer.styled"; const Drawer = ({ children }: React.PropsWithChildren) => { const [isOpen, setIsOpen] = useState(false); - const toggleDrawer = () => setIsOpen((prev) => !prev); + const toggleDrawer = useCallback(() => setIsOpen((prev) => !prev), []); useModalControl(isOpen, toggleDrawer); @@ -29,14 +30,32 @@ const Drawer = ({ children }: React.PropsWithChildren) => { } }); + useEffect(() => { + const handleEscapeKey = (e: KeyboardEvent) => { + if (e.key === "Escape" && isOpen) { + toggleDrawer(); + } + }; + + document.addEventListener("keydown", handleEscapeKey); + + return () => { + document.removeEventListener("keydown", handleEscapeKey); + }; + }, [isOpen, toggleDrawer]); + return ( {otherContent} - - {headerContent} - {drawerContent} - + {isOpen && + ReactDOM.createPortal( + + {headerContent} + {drawerContent} + , + document.body, + )} ); }; @@ -52,9 +71,9 @@ const Content = ({ children }: React.PropsWithChildren) => { const Trigger = ({ children }: React.PropsWithChildren) => { const { toggleDrawer } = useDrawerContext(); return ( -
+
  • {children} -
  • + ); }; From a04e8dd40533a1d48be9da202b66be02616c7bd8 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Thu, 10 Oct 2024 11:19:21 +0900 Subject: [PATCH 16/49] =?UTF-8?q?feat(FocusTrap):=20=EC=B2=AB=EB=B2=88?= =?UTF-8?q?=EC=A7=B8=20=EC=9A=94=EC=86=8C=EC=97=90=20=EC=9E=90=EB=8F=99=20?= =?UTF-8?q?=ED=8F=AC=EC=BB=A4=EC=8A=A4=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/common/FocusTrap.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/src/components/common/FocusTrap.tsx b/frontend/src/components/common/FocusTrap.tsx index d8fb5c04..c148ffa7 100644 --- a/frontend/src/components/common/FocusTrap.tsx +++ b/frontend/src/components/common/FocusTrap.tsx @@ -34,8 +34,6 @@ const FocusTrap = ({ children, onEscapeFocusTrap, ...prop focusableElements.current = getFocusableElements(focusTrapRef.current); } - focusableElements.current[0]?.focus(); - return () => { focusableElements.current = []; }; From 72ff0365d74f4724d9af1bc4ca241ade2a931948 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Thu, 10 Oct 2024 11:20:39 +0900 Subject: [PATCH 17/49] =?UTF-8?q?feat(Modal):=20=EB=AA=A8=EB=8B=AC?= =?UTF-8?q?=EC=97=90=20FocusTrap=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/common/Modal/Modal.tsx | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/frontend/src/components/common/Modal/Modal.tsx b/frontend/src/components/common/Modal/Modal.tsx index f71869d0..9e16ae7e 100644 --- a/frontend/src/components/common/Modal/Modal.tsx +++ b/frontend/src/components/common/Modal/Modal.tsx @@ -8,6 +8,7 @@ import ModalHeader from "@components/common/Modal/ModalHeader/ModalHeader"; import useBottomSheet from "@hooks/useBottomSheet"; import useModalControl from "@hooks/useModalControl"; +import FocusTrap from "../FocusTrap"; import * as S from "./Modal.style"; import { GapSize } from "./Modal.type"; @@ -31,20 +32,22 @@ const Modal = ({ return ReactDOM.createPortal( - {position === "center" ? ( - - {children} - - ) : ( - - {children} - - )} + + {position === "center" ? ( + + {children} + + ) : ( + + {children} + + )} + , document.body, ); From 1810ce1ee3539e485420d9241e3c2e0a45617d09 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Thu, 10 Oct 2024 11:24:09 +0900 Subject: [PATCH 18/49] =?UTF-8?q?feat(MainPage):=20=EC=8B=9C=EB=A7=A8?= =?UTF-8?q?=ED=8B=B1=20=ED=83=9C=EA=B7=B8=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/pages/main/MainPage.styled.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/pages/main/MainPage.styled.ts b/frontend/src/components/pages/main/MainPage.styled.ts index bc6fa761..5ce14e92 100644 --- a/frontend/src/components/pages/main/MainPage.styled.ts +++ b/frontend/src/components/pages/main/MainPage.styled.ts @@ -42,7 +42,7 @@ export const TagsContainer = styled.div` gap: ${({ theme }) => theme.spacing.s}; `; -export const SingleSelectionTagsContainer = styled.ul` +export const SingleSelectionTagsContainer = styled.div` display: flex; gap: ${({ theme }) => theme.spacing.s}; @@ -85,7 +85,7 @@ export const MainPageTraveloguesList = styled.ul` gap: ${({ theme }) => theme.spacing.m}; `; -export const OptionContainer = styled.div` +export const OptionContainer = styled.button` display: flex; justify-content: space-between; From dcd103a808cbc541024065158596c7193dfb31f8 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Thu, 10 Oct 2024 11:26:33 +0900 Subject: [PATCH 19/49] =?UTF-8?q?feat(MainPage):=20=EB=AA=A8=EB=8B=AC=20?= =?UTF-8?q?=EC=97=B4=EB=A6=BC=20=EB=8B=AB=ED=9E=98=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/pages/main/MainPage.tsx | 85 ++++++++++++------- 1 file changed, 55 insertions(+), 30 deletions(-) diff --git a/frontend/src/components/pages/main/MainPage.tsx b/frontend/src/components/pages/main/MainPage.tsx index b332bdee..803b3d48 100644 --- a/frontend/src/components/pages/main/MainPage.tsx +++ b/frontend/src/components/pages/main/MainPage.tsx @@ -169,41 +169,66 @@ const MainPage = () => { {option === sorting.selectedOption ? ( <> + {!isFocused && } +
    + {sorting.isModalOpen + ? "여행기 정렬 모달이 열렸습니다." + : "여행기 정렬 모달이 닫혔습니다."} +
    + {sorting.isModalOpen && ( + + {SORTING_OPTIONS.map((option, index) => ( + sorting.handleClickOption(option)}> + {option === sorting.selectedOption ? ( + <> + + {SORTING_OPTIONS_MAP[option]} + + + + ) : ( + {SORTING_OPTIONS_MAP[option]} - - - ) : ( - - {SORTING_OPTIONS_MAP[option]} - - )} - - ))} - + )} + + ))} + + )} - - {TRAVEL_PERIOD_OPTIONS.map((option, index) => ( - travelPeriod.handleClickOption(option)}> - {option === travelPeriod.selectedOption ? ( - <> - +
    + {travelPeriod.isModalOpen + ? "여행기 필터 모달이 열렸습니다." + : "여행기 필터 모달이 닫혔습니다."} +
    + {travelPeriod.isModalOpen && ( + + {TRAVEL_PERIOD_OPTIONS.map((option, index) => ( + travelPeriod.handleClickOption(option)}> + {option === travelPeriod.selectedOption ? ( + <> + + {TRAVEL_PERIOD_OPTIONS_MAP[option]} + + + + ) : ( + {TRAVEL_PERIOD_OPTIONS_MAP[option]} - - - ) : ( - - {TRAVEL_PERIOD_OPTIONS_MAP[option]} - - )} - - ))} - + )} +
    + ))} +
    + )} ); From d091c408a57292a7e5ceceaefd2a7a4b37cd7bd9 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Thu, 10 Oct 2024 13:08:15 +0900 Subject: [PATCH 20/49] =?UTF-8?q?feat(removeEmojis):=20=EC=9D=B4=EB=AA=A8?= =?UTF-8?q?=EC=A7=80=EB=A5=BC=20=EC=A7=80=EC=9A=B0=EA=B3=A0=20string?= =?UTF-8?q?=EB=A7=8C=20=EB=B0=98=ED=99=98=ED=95=98=EB=8A=94=20=EC=9C=A0?= =?UTF-8?q?=ED=8B=B8=20=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/utils/removeEmojis.ts | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 frontend/src/utils/removeEmojis.ts diff --git a/frontend/src/utils/removeEmojis.ts b/frontend/src/utils/removeEmojis.ts new file mode 100644 index 00000000..a85b8349 --- /dev/null +++ b/frontend/src/utils/removeEmojis.ts @@ -0,0 +1,30 @@ +import type { Tag } from "@type/domain/travelogue"; + +const removeEmoji = (str: string) => + str.replace(/(\p{Emoji_Presentation}|\p{Emoji}\uFE0F)/gu, "").trim(); + +const isTagArray = (tags: unknown[]): tags is Tag[] => + tags.length > 0 && + tags[0] !== null && + typeof tags[0] === "object" && + "tag" in tags[0] && + typeof tags[0].tag === "string"; + +const processTagArray = (tags: Tag[]): string => tags.map((tag) => removeEmoji(tag.tag)).join(", "); + +const processStringArray = (tags: string[]): string => tags.map(removeEmoji).join(", "); + +const removeEmojis = (tags: Tag[] | string[] | string): string => { + if (typeof tags === "string") { + return removeEmoji(tags); + } + + if (!Array.isArray(tags)) { + return ""; + } + + const tagNames = isTagArray(tags) ? processTagArray(tags) : processStringArray(tags); + return tagNames ? tagNames : ""; +}; + +export default removeEmojis; From c24d50cf9d66dff46b5a197f773bea217e4f8a7d Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Thu, 10 Oct 2024 13:09:19 +0900 Subject: [PATCH 21/49] =?UTF-8?q?refactor(TravelogueCard):=20removeEmojis?= =?UTF-8?q?=20=EC=9C=A0=ED=8B=B8=20=ED=95=A8=EC=88=98=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/main/TravelogueCard/TravelogueCard.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx b/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx index 81272ef7..1de204ba 100644 --- a/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx +++ b/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx @@ -9,6 +9,8 @@ import useImageError from "@hooks/useImageError"; import { CYPRESS_DATA_MAP } from "@constants/cypress"; import { ROUTE_PATHS_MAP } from "@constants/route"; +import removeEmojis from "@utils/removeEmojis"; + import * as S from "./TravelogueCard.styled"; interface TravelogueCardProps { @@ -18,18 +20,14 @@ interface TravelogueCardProps { >; } -const removeEmoji = (str: string) => - str.replace(/(\p{Emoji_Presentation}|\p{Emoji}\uFE0F)/gu, "").trim(); - const getCardAriaLabel = ({ title, authorNickname, likeCount, tags, }: Pick) => { - const tagNames = tags.map((tag) => removeEmoji(tag.tag)).join(", "); - - const tagPart = tagNames ? `태그: ${tagNames}.` : ""; + const tagText = removeEmojis(tags); + const tagPart = tagText ? `태그: ${tagText}` : ""; return `${title} 여행기. ${authorNickname} 작성. 좋아요 수: ${likeCount}개. ${tagPart}`; }; From 59312972b2d5407337fd94a2f3a455f501563994 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Thu, 10 Oct 2024 13:12:25 +0900 Subject: [PATCH 22/49] =?UTF-8?q?refactor(FloatingButton):=20=ED=94=8C?= =?UTF-8?q?=EB=A1=9C=ED=8C=85=20=EB=B2=84=ED=8A=BC=EC=97=90=20focus=20trap?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/FloatingButton/FloatingButton.tsx | 60 +++++++++++++------ 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/frontend/src/components/common/FloatingButton/FloatingButton.tsx b/frontend/src/components/common/FloatingButton/FloatingButton.tsx index 2de2748c..0108b73c 100644 --- a/frontend/src/components/common/FloatingButton/FloatingButton.tsx +++ b/frontend/src/components/common/FloatingButton/FloatingButton.tsx @@ -5,12 +5,26 @@ import useModalControl from "@hooks/useModalControl"; import { ROUTE_PATHS_MAP } from "@constants/route"; +import removeEmojis from "@utils/removeEmojis"; + import { PRIMITIVE_COLORS } from "@styles/tokens"; +import FocusTrap from "../FocusTrap"; import IconButton from "../IconButton/IconButton"; import Text from "../Text/Text"; import * as S from "./FloatingButton.styled"; +const SUB_BUTTONS = [ + { + text: "✈️ 여행 계획 작성", + route: ROUTE_PATHS_MAP.travelPlanRegister, + }, + { + text: "📝 여행기 작성", + route: ROUTE_PATHS_MAP.travelogueRegister, + }, +]; + const FloatingButton = () => { const [isOpen, setIsOpen] = useState(false); const navigate = useNavigate(); @@ -19,31 +33,39 @@ const FloatingButton = () => { setIsOpen((prev) => !prev); }; - const handleClickTravelogueRegister = () => { - navigate(ROUTE_PATHS_MAP.travelogueRegister); - }; - - const handleClickTravelPlanRegister = () => { - navigate(ROUTE_PATHS_MAP.travelPlanRegister); + const handleClickSubButton = (route: string) => () => { + navigate(route); }; useModalControl(isOpen, handleToggleButton); return ( - {isOpen && } - - - - ✈️ 여행 계획 작성 - - - - - 📝 여행기 작성 - - - +
    + {isOpen + ? "여행기 및 여행 계획 작성 모달이 열렸습니다. 닫으려면 esc버튼을 눌러주세요." + : "여행기 및 여행 계획 작성 모달이 닫혔습니다."} +
    + {isOpen && ( + <> + + + + {SUB_BUTTONS.map(({ text, route }) => ( + + + {text} + + + ))} + + + + )} Date: Thu, 10 Oct 2024 13:21:33 +0900 Subject: [PATCH 23/49] =?UTF-8?q?style(FloatingButton):=20visual=20hidden?= =?UTF-8?q?=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/FloatingButton/FloatingButton.styled.ts | 13 +++++++++++++ .../common/FloatingButton/FloatingButton.tsx | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/common/FloatingButton/FloatingButton.styled.ts b/frontend/src/components/common/FloatingButton/FloatingButton.styled.ts index a5dda544..f1c205d5 100644 --- a/frontend/src/components/common/FloatingButton/FloatingButton.styled.ts +++ b/frontend/src/components/common/FloatingButton/FloatingButton.styled.ts @@ -74,3 +74,16 @@ export const MainButtonWrapper = styled.div<{ $isOpen: boolean }>` export const subButtonTextStyle = css` color: ${PRIMITIVE_COLORS.white}; `; + +export const visualHiddenStyle = css` + overflow: hidden; + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + border: 0; + + white-space: nowrap; + clip: rect(0, 0, 0, 0); +`; diff --git a/frontend/src/components/common/FloatingButton/FloatingButton.tsx b/frontend/src/components/common/FloatingButton/FloatingButton.tsx index 0108b73c..1cd966a6 100644 --- a/frontend/src/components/common/FloatingButton/FloatingButton.tsx +++ b/frontend/src/components/common/FloatingButton/FloatingButton.tsx @@ -41,7 +41,7 @@ const FloatingButton = () => { return ( -
    +
    {isOpen ? "여행기 및 여행 계획 작성 모달이 열렸습니다. 닫으려면 esc버튼을 눌러주세요." : "여행기 및 여행 계획 작성 모달이 닫혔습니다."} From 735d281c08499d4d1eaa58375be50a402840ebc9 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Sat, 12 Oct 2024 18:21:09 +0900 Subject: [PATCH 24/49] =?UTF-8?q?feat(VisuallyHidden):=20Visually=20hidden?= =?UTF-8?q?=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 웹 접근성 개선에서 사용할 수 있는 보이지 않는 컴포넌트 추가했습니다. --- .../common/VisuallyHidden/VisuallyHidden.styled.ts | 14 ++++++++++++++ .../common/VisuallyHidden/VisuallyHidden.tsx | 7 +++++++ 2 files changed, 21 insertions(+) create mode 100644 frontend/src/components/common/VisuallyHidden/VisuallyHidden.styled.ts create mode 100644 frontend/src/components/common/VisuallyHidden/VisuallyHidden.tsx diff --git a/frontend/src/components/common/VisuallyHidden/VisuallyHidden.styled.ts b/frontend/src/components/common/VisuallyHidden/VisuallyHidden.styled.ts new file mode 100644 index 00000000..bb1e97ee --- /dev/null +++ b/frontend/src/components/common/VisuallyHidden/VisuallyHidden.styled.ts @@ -0,0 +1,14 @@ +import styled from "@emotion/styled"; + +export const Layout = styled.div` + overflow: hidden; + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + border: 0; + + white-space: nowrap; + clip: rect(0, 0, 0, 0); +`; diff --git a/frontend/src/components/common/VisuallyHidden/VisuallyHidden.tsx b/frontend/src/components/common/VisuallyHidden/VisuallyHidden.tsx new file mode 100644 index 00000000..f0f0aff2 --- /dev/null +++ b/frontend/src/components/common/VisuallyHidden/VisuallyHidden.tsx @@ -0,0 +1,7 @@ +import * as S from "./VisuallyHidden.styled"; + +const VisuallyHidden = ({ children }: React.PropsWithChildren) => { + return {children}; +}; + +export default VisuallyHidden; From 5760fddc9e25bd1e2882e19de846d0c07c0f5f1b Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Sat, 12 Oct 2024 18:33:19 +0900 Subject: [PATCH 25/49] =?UTF-8?q?feat(MainPage):=20=ED=83=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D,=ED=95=B4=EC=A0=9C=EC=8B=9C=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/pages/main/MainPage.tsx | 42 +++++++++++++++---- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/pages/main/MainPage.tsx b/frontend/src/components/pages/main/MainPage.tsx index 803b3d48..eaafd466 100644 --- a/frontend/src/components/pages/main/MainPage.tsx +++ b/frontend/src/components/pages/main/MainPage.tsx @@ -1,3 +1,5 @@ +import { useState } from "react"; + import useInfiniteTravelogues from "@queries/useInfiniteTravelogues"; import { @@ -8,6 +10,7 @@ import { SingleSelectionTagModalBottomSheet, Text, } from "@components/common"; +import VisuallyHidden from "@components/common/VisuallyHidden/VisuallyHidden"; import TravelogueCard from "@components/pages/main/TravelogueCard/TravelogueCard"; import { useDragScroll } from "@hooks/useDragScroll"; @@ -19,6 +22,8 @@ import { ERROR_MESSAGE_MAP } from "@constants/errorMessage"; import { FORM_VALIDATIONS_MAP } from "@constants/formValidation"; import { STORAGE_KEYS_MAP } from "@constants/storage"; +import removeEmojis from "@utils/removeEmojis"; + import theme from "@styles/theme"; import { @@ -60,6 +65,8 @@ const MainPage = () => { alert(error?.message); } + const [tagSelectionAnnouncement, setTagSelectionAnnouncement] = useState(""); + return ( <> @@ -109,15 +116,32 @@ const MainPage = () => { onMouseUp={onMouseUp} onMouseMove={onMouseMove} > - {sortedTags.map((tag, index) => ( - handleClickTag(tag.id)} - /> - ))} + {sortedTags.map((tag, index) => { + const isSelected = selectedTagIDs.includes(tag.id); + const tagName = removeEmojis(tag.tag); + + return ( +
  • + { + handleClickTag(tag.id); + setTagSelectionAnnouncement( + isSelected + ? `${tagName} 태그가 선택 해제되었습니다.` + : `${tagName} 태그가 선택되었습니다.`, + ); + }} + aria-label={`${tagName} 태그`} + /> +
  • + ); + })} + {tagSelectionAnnouncement}
    From 67dad2468cd65c69d66341d0c1228178d4e9bb76 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Sat, 12 Oct 2024 18:36:35 +0900 Subject: [PATCH 26/49] =?UTF-8?q?feat(MainPage):=20TravelogueCard=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=8B=9C=EB=A7=A8=ED=8B=B1=20?= =?UTF-8?q?=ED=83=9C=EA=B7=B8=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/pages/main/MainPage.styled.ts | 5 ++++ .../src/components/pages/main/MainPage.tsx | 25 ++++++++++--------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/pages/main/MainPage.styled.ts b/frontend/src/components/pages/main/MainPage.styled.ts index 5ce14e92..d04acae2 100644 --- a/frontend/src/components/pages/main/MainPage.styled.ts +++ b/frontend/src/components/pages/main/MainPage.styled.ts @@ -119,3 +119,8 @@ export const selectedOptionStyle = css` export const unselectedOptionStyle = css` color: ${theme.colors.text.secondary}; `; + +export const MainPageList = styled.li` + width: 100%; +`; + diff --git a/frontend/src/components/pages/main/MainPage.tsx b/frontend/src/components/pages/main/MainPage.tsx index eaafd466..8bfd8424 100644 --- a/frontend/src/components/pages/main/MainPage.tsx +++ b/frontend/src/components/pages/main/MainPage.tsx @@ -159,18 +159,19 @@ const MainPage = () => { {hasTravelogue ? ( travelogues.map( ({ authorProfileUrl, authorNickname, id, title, thumbnail, likeCount, tags }) => ( - + + + ), ) ) : ( From 3a923ef83175c2422c4949f456861c51c7b2fe96 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Sat, 12 Oct 2024 18:37:56 +0900 Subject: [PATCH 27/49] =?UTF-8?q?fix(TravelogueCard):=20aria-live=20?= =?UTF-8?q?=EC=86=8D=EC=84=B1=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 필터,정렬딜 때 마다 렌더링되어있는 모든 카드 aria-label이 읽히는 이슈가 있어서 삭제했습니다. --- .../src/components/pages/main/TravelogueCard/TravelogueCard.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx b/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx index 1de204ba..48c9e535 100644 --- a/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx +++ b/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx @@ -55,7 +55,6 @@ const TravelogueCard = ({ From 1b5e434228f2bb0409550f566a14f5c32f11e37a Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Sat, 12 Oct 2024 18:45:23 +0900 Subject: [PATCH 28/49] =?UTF-8?q?feat(MainPage):=20=EB=AA=A8=EB=8B=AC=20?= =?UTF-8?q?=EC=97=B4/=EB=8B=AB=ED=9E=98=20=EC=95=8C=EB=A6=BC=EC=97=90=20Vi?= =?UTF-8?q?suallyHidden=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/common/VisuallyHidden/VisuallyHidden.tsx | 4 ++-- frontend/src/components/pages/main/MainPage.tsx | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/common/VisuallyHidden/VisuallyHidden.tsx b/frontend/src/components/common/VisuallyHidden/VisuallyHidden.tsx index f0f0aff2..ece8dc66 100644 --- a/frontend/src/components/common/VisuallyHidden/VisuallyHidden.tsx +++ b/frontend/src/components/common/VisuallyHidden/VisuallyHidden.tsx @@ -1,7 +1,7 @@ import * as S from "./VisuallyHidden.styled"; -const VisuallyHidden = ({ children }: React.PropsWithChildren) => { - return {children}; +const VisuallyHidden = ({ children, ...props }: React.PropsWithChildren) => { + return {children}; }; export default VisuallyHidden; diff --git a/frontend/src/components/pages/main/MainPage.tsx b/frontend/src/components/pages/main/MainPage.tsx index 8bfd8424..97433567 100644 --- a/frontend/src/components/pages/main/MainPage.tsx +++ b/frontend/src/components/pages/main/MainPage.tsx @@ -195,11 +195,11 @@ const MainPage = () => { <> {!isFocused && } -
    + {sorting.isModalOpen ? "여행기 정렬 모달이 열렸습니다." : "여행기 정렬 모달이 닫혔습니다."} -
    + {sorting.isModalOpen && ( { )} -
    + {travelPeriod.isModalOpen ? "여행기 필터 모달이 열렸습니다." : "여행기 필터 모달이 닫혔습니다."} -
    + {travelPeriod.isModalOpen && ( Date: Sun, 13 Oct 2024 14:47:31 +0900 Subject: [PATCH 29/49] =?UTF-8?q?refactor(Drawer):=20Trigger=20=EC=8B=9C?= =?UTF-8?q?=EB=A7=A8=ED=8B=B1=20=ED=83=9C=EA=B7=B8=20=EB=B0=8F=20props=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - button 태그를 사용하고 onClick props를 받아서 button 내부에 button을 받지 않도록 하기 위함입니다. --- .../src/components/common/Drawer/Drawer.tsx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/common/Drawer/Drawer.tsx b/frontend/src/components/common/Drawer/Drawer.tsx index 7e4d789a..c5fa9b95 100644 --- a/frontend/src/components/common/Drawer/Drawer.tsx +++ b/frontend/src/components/common/Drawer/Drawer.tsx @@ -68,12 +68,23 @@ const Content = ({ children }: React.PropsWithChildren) => { return {children}; }; -const Trigger = ({ children }: React.PropsWithChildren) => { +interface TriggerProps extends React.ButtonHTMLAttributes { + children: React.ReactNode; + onClick?: (event: React.MouseEvent) => void; +} + +const Trigger = ({ children, onClick }: TriggerProps) => { const { toggleDrawer } = useDrawerContext(); + + const handleClick = (event: React.MouseEvent) => { + toggleDrawer(); + onClick?.(event); + }; + return ( -
  • +
  • + ); }; From 81ba587b110bbab061d133f4d7ae97c618e30305 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Sun, 13 Oct 2024 14:48:35 +0900 Subject: [PATCH 30/49] =?UTF-8?q?refactor(Header):=20=EC=8B=9C=EB=A7=A8?= =?UTF-8?q?=ED=8B=B1=20=ED=83=9C=EA=B7=B8=20=EC=88=98=EC=A0=95=20=EB=B0=8F?= =?UTF-8?q?=20button=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 수정된 Drawer.Trigger에 맞춰 수정했습니다. --- .../components/common/Header/Header.styled.ts | 2 +- .../src/components/common/Header/Header.tsx | 44 +++++++++---------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/frontend/src/components/common/Header/Header.styled.ts b/frontend/src/components/common/Header/Header.styled.ts index 929ac12a..5542ce63 100644 --- a/frontend/src/components/common/Header/Header.styled.ts +++ b/frontend/src/components/common/Header/Header.styled.ts @@ -28,7 +28,7 @@ export const MenuItem = styled.button` padding: ${({ theme }) => theme.spacing.s}; `; -export const MenuList = styled.ul` +export const MenuList = styled.div` display: flex; flex-direction: column; justify-content: space-between; diff --git a/frontend/src/components/common/Header/Header.tsx b/frontend/src/components/common/Header/Header.tsx index f68a4133..b50a5cb0 100644 --- a/frontend/src/components/common/Header/Header.tsx +++ b/frontend/src/components/common/Header/Header.tsx @@ -8,11 +8,11 @@ import { ROUTE_PATHS_MAP } from "@constants/route"; import theme from "@styles/theme"; import { PRIMITIVE_COLORS } from "@styles/tokens"; -import { DoubleRightArrow } from "@assets/svg"; - import Drawer from "../Drawer/Drawer"; import FocusTrap from "../FocusTrap"; +import Icon from "../Icon/Icon"; import IconButton from "../IconButton/IconButton"; +import Text from "../Text/Text"; import * as S from "./Header.styled"; interface HeaderProps { @@ -64,38 +64,36 @@ const Header = ({ {rightContent} {isHamburgerUsed && ( - + )} - - - - - + + + - - 마이페이지 - - - {user?.accessToken ? ( - 로그아웃 - ) : ( - { - navigate(ROUTE_PATHS_MAP.login); - }} - > - 로그인 - - )} + + 마이페이지 + {user?.accessToken ? ( + + 로그아웃 + + ) : ( + { + navigate(ROUTE_PATHS_MAP.login); + }} + > + 로그인 + + )} From e8f4a4c110151c71436f77c3362d5ba4ecb8053b Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Sun, 13 Oct 2024 15:22:11 +0900 Subject: [PATCH 31/49] =?UTF-8?q?feat(MainPage):=20=EC=97=AC=ED=96=89?= =?UTF-8?q?=EA=B8=B0=20=EB=A1=9C=EB=93=9C=EC=8B=9C=20=EC=83=88=EB=A1=9C=20?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=EB=90=9C=20=EC=97=AC=ED=96=89=EA=B8=B0?= =?UTF-8?q?=EC=97=90=20focus=EB=90=98=EA=B8=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/pages/main/MainPage.tsx | 68 +++++----- .../main/TravelogueCard/TravelogueCard.tsx | 118 +++++++++--------- frontend/src/hooks/useTravelogueCardFocus.ts | 22 ++++ 3 files changed, 121 insertions(+), 87 deletions(-) create mode 100644 frontend/src/hooks/useTravelogueCardFocus.ts diff --git a/frontend/src/components/pages/main/MainPage.tsx b/frontend/src/components/pages/main/MainPage.tsx index 97433567..f52a58fc 100644 --- a/frontend/src/components/pages/main/MainPage.tsx +++ b/frontend/src/components/pages/main/MainPage.tsx @@ -17,6 +17,7 @@ import { useDragScroll } from "@hooks/useDragScroll"; import useIntersectionObserver from "@hooks/useIntersectionObserver"; import useMultiSelectionTag from "@hooks/useMultiSelectionTag"; import useSingleSelectionTag from "@hooks/useSingleSelectionTag"; +import useTravelogueCardFocus from "@hooks/useTravelogueCardFocus"; import { ERROR_MESSAGE_MAP } from "@constants/errorMessage"; import { FORM_VALIDATIONS_MAP } from "@constants/formValidation"; @@ -45,11 +46,12 @@ const MainPage = () => { STORAGE_KEYS_MAP.mainPageTravelPeriod, ); - const { travelogues, status, fetchNextPage, isPaused, error } = useInfiniteTravelogues({ - selectedTagIDs, - selectedSortingOption: sorting.selectedOption, - selectedTravelPeriodOption: travelPeriod.selectedOption, - }); + const { travelogues, status, fetchNextPage, isPaused, error, isFetchingNextPage } = + useInfiniteTravelogues({ + selectedTagIDs, + selectedSortingOption: sorting.selectedOption, + selectedTravelPeriodOption: travelPeriod.selectedOption, + }); const { scrollRef, onMouseDown, onMouseMove, onMouseUp } = useDragScroll(); @@ -57,6 +59,7 @@ const MainPage = () => { const hasTravelogue = travelogues.length > 0; + const cardRefs = useTravelogueCardFocus(isFetchingNextPage); if (isPaused) { alert(ERROR_MESSAGE_MAP.network); } @@ -155,31 +158,36 @@ const MainPage = () => { )} {status === "success" && ( - - {hasTravelogue ? ( - travelogues.map( - ({ authorProfileUrl, authorNickname, id, title, thumbnail, likeCount, tags }) => ( - - - - ), - ) - ) : ( - - - - )} - + + {hasTravelogue ? ( + travelogues.map( + ( + { authorProfileUrl, authorNickname, id, title, thumbnail, likeCount, tags }, + index, + ) => ( + + (cardRefs.current[index] = el)} + key={index} + travelogueOverview={{ + authorProfileUrl, + id, + title, + thumbnail, + likeCount, + authorNickname, + tags, + }} + /> + + ), + ) + ) : ( + + + + )} + )} diff --git a/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx b/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx index 48c9e535..b13cbd68 100644 --- a/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx +++ b/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx @@ -1,3 +1,4 @@ +import React from "react"; import { useNavigate } from "react-router-dom"; import { TravelogueResponse } from "@type/domain/travelogue"; @@ -13,7 +14,7 @@ import removeEmojis from "@utils/removeEmojis"; import * as S from "./TravelogueCard.styled"; -interface TravelogueCardProps { +interface TravelogueCardProps extends React.ComponentPropsWithoutRef<"button"> { travelogueOverview: Pick< TravelogueResponse, "id" | "authorProfileUrl" | "title" | "thumbnail" | "authorNickname" | "likeCount" | "tags" @@ -32,62 +33,65 @@ const getCardAriaLabel = ({ return `${title} 여행기. ${authorNickname} 작성. 좋아요 수: ${likeCount}개. ${tagPart}`; }; -const TravelogueCard = ({ - travelogueOverview: { - id, - authorProfileUrl, - title, - thumbnail, - authorNickname, - likeCount = 0, - tags, +const TravelogueCard = React.forwardRef( + ({ travelogueOverview, ...props }, ref) => { + const { + id, + authorProfileUrl, + title, + thumbnail, + authorNickname, + likeCount = 0, + tags, + } = travelogueOverview; + + const navigate = useNavigate(); + const { imageError, handleImageError } = useImageError({ imageUrl: thumbnail }); + + const handleCardClick = () => { + navigate(ROUTE_PATHS_MAP.travelogue(id)); + }; + + return ( + + + {title} + + + + {!imageError ? ( + + ) : ( + + )} + + + + + + {authorNickname} + + + + + {likeCount} + + + + + {tags.map((tag) => ( + + ))} + + + ); }, -}: TravelogueCardProps) => { - const navigate = useNavigate(); - - const { imageError, handleImageError } = useImageError({ imageUrl: thumbnail }); - - const handleCardClick = () => { - navigate(ROUTE_PATHS_MAP.travelogue(id)); - }; - - return ( - - - {title} - - - - {!imageError ? ( - - ) : ( - - )} - - - - - - {authorNickname} - - - - - {likeCount} - - - - - {tags.map((tag) => ( - - ))} - - - ); -}; +); export default TravelogueCard; diff --git a/frontend/src/hooks/useTravelogueCardFocus.ts b/frontend/src/hooks/useTravelogueCardFocus.ts new file mode 100644 index 00000000..0ee3bc04 --- /dev/null +++ b/frontend/src/hooks/useTravelogueCardFocus.ts @@ -0,0 +1,22 @@ +import { useEffect, useRef } from "react"; + +const DATA_LOAD_COUNT = 5; + +const useTravelogueCardFocus = (isFetchingNextPage: boolean) => { + const cardRefs = useRef<(HTMLButtonElement | null)[]>([]); + + useEffect(() => { + if (!isFetchingNextPage && cardRefs.current.length > 0) { + const focusIndex = cardRefs.current.findLastIndex( + (_, index) => index > 0 && index % DATA_LOAD_COUNT === 0, + ); + if (focusIndex !== -1 && cardRefs.current[focusIndex]) { + cardRefs.current[focusIndex]?.focus(); + } + } + }, [isFetchingNextPage]); + + return cardRefs; +}; + +export default useTravelogueCardFocus; From c92ada7d348d4ad8dfece545a15237ad8e910e36 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Sun, 13 Oct 2024 15:32:09 +0900 Subject: [PATCH 32/49] =?UTF-8?q?feat(MainPage):=20fetchButton=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=A1=9C=EB=93=9C=EC=8B=9C=20=EC=95=8C=EB=A6=BC=20?= =?UTF-8?q?=EB=B0=8F=20=ED=83=9C=EA=B7=B8=20=EC=84=A0=ED=83=9D=EC=8B=9C=20?= =?UTF-8?q?=EC=95=8C=EB=9E=8C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - fetchButton으로 여행기 로드시 알림 울리도록 구현 - 태그 선택 알림 구현 코드 일부분이 다른 부분에 딸려 들어간것같습니다.. --- .../components/pages/main/MainPage.styled.ts | 16 ++++++++ .../src/components/pages/main/MainPage.tsx | 39 ++++++++++++------- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/frontend/src/components/pages/main/MainPage.styled.ts b/frontend/src/components/pages/main/MainPage.styled.ts index d04acae2..3eacfeee 100644 --- a/frontend/src/components/pages/main/MainPage.styled.ts +++ b/frontend/src/components/pages/main/MainPage.styled.ts @@ -124,3 +124,19 @@ export const MainPageList = styled.li` width: 100%; `; +export const FetchButton = styled.button` + position: fixed; + bottom: 0; + left: 0; + z-index: 100; + padding: 8px; + + color: white; + background: #000; + transform: translateY(100%); + transition: transform 0.3s; + + &:focus { + transform: translateY(0); + } +`; diff --git a/frontend/src/components/pages/main/MainPage.tsx b/frontend/src/components/pages/main/MainPage.tsx index f52a58fc..7803b38e 100644 --- a/frontend/src/components/pages/main/MainPage.tsx +++ b/frontend/src/components/pages/main/MainPage.tsx @@ -54,12 +54,17 @@ const MainPage = () => { }); const { scrollRef, onMouseDown, onMouseMove, onMouseUp } = useDragScroll(); - const { lastElementRef } = useIntersectionObserver(fetchNextPage); + const [isFocused, setIsFocused] = useState(false); + const hasTravelogue = travelogues.length > 0; + const [tagSelectionAnnouncement, setTagSelectionAnnouncement] = useState(""); + const [announcement, setAnnouncement] = useState(""); + const cardRefs = useTravelogueCardFocus(isFetchingNextPage); + if (isPaused) { alert(ERROR_MESSAGE_MAP.network); } @@ -68,8 +73,6 @@ const MainPage = () => { alert(error?.message); } - const [tagSelectionAnnouncement, setTagSelectionAnnouncement] = useState(""); - return ( <> @@ -144,8 +147,8 @@ const MainPage = () => { ); })} - {tagSelectionAnnouncement} + {tagSelectionAnnouncement} @@ -158,6 +161,8 @@ const MainPage = () => { )} {status === "success" && ( + <> + {announcement} {hasTravelogue ? ( travelogues.map( @@ -188,21 +193,24 @@ const MainPage = () => { )} + )} - - - setIsFocused(true)} + onBlur={() => setIsFocused(false)} + onClick={async () => { + await fetchNextPage(); + setAnnouncement("새로운 여행기가 로드되었습니다."); + }} + aria-label="더 많은 여행기 불러오기" > - {SORTING_OPTIONS.map((option, index) => ( - sorting.handleClickOption(option)}> - {option === sorting.selectedOption ? ( - <> - + 더 불러오기 + + {!isFocused && } + + {sorting.isModalOpen ? "여행기 정렬 모달이 열렸습니다." @@ -232,6 +240,7 @@ const MainPage = () => { ))} )} + {travelPeriod.isModalOpen From 7ac5c26832ea702e629ca942f930d0a4a28d7843 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Sun, 13 Oct 2024 15:59:46 +0900 Subject: [PATCH 33/49] =?UTF-8?q?feat(Drawer):=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=20=EB=A9=94=EB=89=B4=20=EB=AA=A8=EB=8B=AC=20=EC=97=B4?= =?UTF-8?q?/=EB=8B=AB=ED=9E=98=20=EC=95=88=EB=82=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/common/Drawer/Drawer.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/src/components/common/Drawer/Drawer.tsx b/frontend/src/components/common/Drawer/Drawer.tsx index c5fa9b95..c3cdd316 100644 --- a/frontend/src/components/common/Drawer/Drawer.tsx +++ b/frontend/src/components/common/Drawer/Drawer.tsx @@ -5,6 +5,7 @@ import DrawerProvider, { useDrawerContext } from "@contexts/DrawerProvider"; import useModalControl from "@hooks/useModalControl"; +import VisuallyHidden from "../VisuallyHidden/VisuallyHidden"; import * as S from "./Drawer.styled"; const Drawer = ({ children }: React.PropsWithChildren) => { @@ -46,6 +47,9 @@ const Drawer = ({ children }: React.PropsWithChildren) => { return ( + + {isOpen ? "사용자 메뉴 모달이 열렸습니다." : "사용자 메뉴 모달이 닫혔습니다."} + {otherContent} {isOpen && From 623cadc74c5f8e50170bb2c4bab50417d9e241d1 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Sun, 13 Oct 2024 20:33:17 +0900 Subject: [PATCH 34/49] =?UTF-8?q?fix(TravelogueCard):=20=EC=97=AC=ED=96=89?= =?UTF-8?q?=EA=B8=B0=20=EC=A0=9C=EB=AA=A9=20=EC=9D=BD=EC=9D=84=20=EC=8B=9C?= =?UTF-8?q?=20=EC=9D=B4=EB=AA=A8=EC=A7=80=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/pages/main/TravelogueCard/TravelogueCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx b/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx index b13cbd68..bfecfe23 100644 --- a/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx +++ b/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx @@ -30,7 +30,7 @@ const getCardAriaLabel = ({ const tagText = removeEmojis(tags); const tagPart = tagText ? `태그: ${tagText}` : ""; - return `${title} 여행기. ${authorNickname} 작성. 좋아요 수: ${likeCount}개. ${tagPart}`; + return `${removeEmojis(title)} 여행기. ${authorNickname} 작성. 좋아요 수: ${likeCount}개. ${tagPart}`; }; const TravelogueCard = React.forwardRef( From 3e519ca70191718b8253c4dd59f4d5563bfbc260 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Mon, 14 Oct 2024 13:45:19 +0900 Subject: [PATCH 35/49] =?UTF-8?q?refactor(FloatingButton):=20VisuallyHidde?= =?UTF-8?q?n=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/FloatingButton/FloatingButton.styled.ts | 13 ------------- .../common/FloatingButton/FloatingButton.tsx | 5 +++-- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/frontend/src/components/common/FloatingButton/FloatingButton.styled.ts b/frontend/src/components/common/FloatingButton/FloatingButton.styled.ts index f1c205d5..a5dda544 100644 --- a/frontend/src/components/common/FloatingButton/FloatingButton.styled.ts +++ b/frontend/src/components/common/FloatingButton/FloatingButton.styled.ts @@ -74,16 +74,3 @@ export const MainButtonWrapper = styled.div<{ $isOpen: boolean }>` export const subButtonTextStyle = css` color: ${PRIMITIVE_COLORS.white}; `; - -export const visualHiddenStyle = css` - overflow: hidden; - position: absolute; - width: 1px; - height: 1px; - margin: -1px; - padding: 0; - border: 0; - - white-space: nowrap; - clip: rect(0, 0, 0, 0); -`; diff --git a/frontend/src/components/common/FloatingButton/FloatingButton.tsx b/frontend/src/components/common/FloatingButton/FloatingButton.tsx index 1cd966a6..ff47d525 100644 --- a/frontend/src/components/common/FloatingButton/FloatingButton.tsx +++ b/frontend/src/components/common/FloatingButton/FloatingButton.tsx @@ -12,6 +12,7 @@ import { PRIMITIVE_COLORS } from "@styles/tokens"; import FocusTrap from "../FocusTrap"; import IconButton from "../IconButton/IconButton"; import Text from "../Text/Text"; +import VisuallyHidden from "../VisuallyHidden/VisuallyHidden"; import * as S from "./FloatingButton.styled"; const SUB_BUTTONS = [ @@ -41,11 +42,11 @@ const FloatingButton = () => { return ( -
    + {isOpen ? "여행기 및 여행 계획 작성 모달이 열렸습니다. 닫으려면 esc버튼을 눌러주세요." : "여행기 및 여행 계획 작성 모달이 닫혔습니다."} -
    +
    {isOpen && ( <> From d3b46548b9dfc1b36cd25d16c4e4a752227ac607 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Mon, 14 Oct 2024 13:50:04 +0900 Subject: [PATCH 36/49] =?UTF-8?q?refactor(FloatingButton):=20=EC=83=81?= =?UTF-8?q?=EC=88=98=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/FloatingButton/FloatingButton.tsx | 14 +------------- .../components/common/FloatingButton/constants.ts | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 13 deletions(-) create mode 100644 frontend/src/components/common/FloatingButton/constants.ts diff --git a/frontend/src/components/common/FloatingButton/FloatingButton.tsx b/frontend/src/components/common/FloatingButton/FloatingButton.tsx index ff47d525..2a9af52d 100644 --- a/frontend/src/components/common/FloatingButton/FloatingButton.tsx +++ b/frontend/src/components/common/FloatingButton/FloatingButton.tsx @@ -3,8 +3,6 @@ import { useNavigate } from "react-router-dom"; import useModalControl from "@hooks/useModalControl"; -import { ROUTE_PATHS_MAP } from "@constants/route"; - import removeEmojis from "@utils/removeEmojis"; import { PRIMITIVE_COLORS } from "@styles/tokens"; @@ -14,17 +12,7 @@ import IconButton from "../IconButton/IconButton"; import Text from "../Text/Text"; import VisuallyHidden from "../VisuallyHidden/VisuallyHidden"; import * as S from "./FloatingButton.styled"; - -const SUB_BUTTONS = [ - { - text: "✈️ 여행 계획 작성", - route: ROUTE_PATHS_MAP.travelPlanRegister, - }, - { - text: "📝 여행기 작성", - route: ROUTE_PATHS_MAP.travelogueRegister, - }, -]; +import SUB_BUTTONS from "./constants"; const FloatingButton = () => { const [isOpen, setIsOpen] = useState(false); diff --git a/frontend/src/components/common/FloatingButton/constants.ts b/frontend/src/components/common/FloatingButton/constants.ts new file mode 100644 index 00000000..403d7f9a --- /dev/null +++ b/frontend/src/components/common/FloatingButton/constants.ts @@ -0,0 +1,14 @@ +import { ROUTE_PATHS_MAP } from "@constants/route"; + +const SUB_BUTTONS = [ + { + text: "✈️ 여행 계획 작성", + route: ROUTE_PATHS_MAP.travelPlanRegister, + }, + { + text: "📝 여행기 작성", + route: ROUTE_PATHS_MAP.travelogueRegister, + }, +]; + +export default SUB_BUTTONS; From d998713bb7f5fe3e9ef1a80f2493d860b2ce5e2b Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:02:29 +0900 Subject: [PATCH 37/49] =?UTF-8?q?refactor(Chip):=20=ED=99=94=EC=82=B4?= =?UTF-8?q?=ED=91=9C=20=ED=95=A8=EC=88=98=EB=A1=9C=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20=EB=85=BC=EB=A6=AC=20=EC=97=B0=EC=82=B0=EC=9E=90=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/common/Chip/Chip.tsx | 12 ++++++------ frontend/src/components/common/Chip/constants.ts | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 frontend/src/components/common/Chip/constants.ts diff --git a/frontend/src/components/common/Chip/Chip.tsx b/frontend/src/components/common/Chip/Chip.tsx index 529129de..baf768cb 100644 --- a/frontend/src/components/common/Chip/Chip.tsx +++ b/frontend/src/components/common/Chip/Chip.tsx @@ -4,8 +4,9 @@ import { CYPRESS_DATA_MAP } from "@constants/cypress"; import Text from "../Text/Text"; import * as S from "./Chip.styled"; +import { DEFAULT_ELEMENT } from "./constants"; -interface ChipOwnProps { +interface ChipOwnProps { as?: Element; label: string; isSelected?: boolean; @@ -15,15 +16,15 @@ interface ChipOwnProps { type ChipProps = ChipOwnProps & Omit, keyof ChipOwnProps>; -function Chip({ +const Chip = ({ as, label, isSelected = false, index, children, ...props -}: ChipProps) { - const Component = as || "li"; +}: ChipProps) => { + const Component = as ?? DEFAULT_ELEMENT; return ( ({ {children} ); -} - +}; export default Chip; diff --git a/frontend/src/components/common/Chip/constants.ts b/frontend/src/components/common/Chip/constants.ts new file mode 100644 index 00000000..ca829f13 --- /dev/null +++ b/frontend/src/components/common/Chip/constants.ts @@ -0,0 +1 @@ +export const DEFAULT_ELEMENT = "li" as const; From b524e4b5c3aec4821a9f0a127537ee45b4a2de35 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:34:29 +0900 Subject: [PATCH 38/49] =?UTF-8?q?refactor(removeEmojis):=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=B1=85=EC=9E=84=20=EA=B0=84=EC=86=8C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/FloatingButton/FloatingButton.tsx | 4 +-- .../src/components/pages/main/MainPage.tsx | 4 +-- .../main/TravelogueCard/TravelogueCard.tsx | 6 ++-- frontend/src/utils/removeEmojis.ts | 31 ++++--------------- 4 files changed, 13 insertions(+), 32 deletions(-) diff --git a/frontend/src/components/common/FloatingButton/FloatingButton.tsx b/frontend/src/components/common/FloatingButton/FloatingButton.tsx index 2a9af52d..669c6a43 100644 --- a/frontend/src/components/common/FloatingButton/FloatingButton.tsx +++ b/frontend/src/components/common/FloatingButton/FloatingButton.tsx @@ -3,7 +3,7 @@ import { useNavigate } from "react-router-dom"; import useModalControl from "@hooks/useModalControl"; -import removeEmojis from "@utils/removeEmojis"; +import { removeEmoji } from "@utils/removeEmojis"; import { PRIMITIVE_COLORS } from "@styles/tokens"; @@ -44,7 +44,7 @@ const FloatingButton = () => { {text} diff --git a/frontend/src/components/pages/main/MainPage.tsx b/frontend/src/components/pages/main/MainPage.tsx index 7803b38e..85803604 100644 --- a/frontend/src/components/pages/main/MainPage.tsx +++ b/frontend/src/components/pages/main/MainPage.tsx @@ -23,7 +23,7 @@ import { ERROR_MESSAGE_MAP } from "@constants/errorMessage"; import { FORM_VALIDATIONS_MAP } from "@constants/formValidation"; import { STORAGE_KEYS_MAP } from "@constants/storage"; -import removeEmojis from "@utils/removeEmojis"; +import { removeEmoji } from "@utils/removeEmojis"; import theme from "@styles/theme"; @@ -124,7 +124,7 @@ const MainPage = () => { > {sortedTags.map((tag, index) => { const isSelected = selectedTagIDs.includes(tag.id); - const tagName = removeEmojis(tag.tag); + const tagName = removeEmoji(tag.tag); return (
  • diff --git a/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx b/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx index bfecfe23..332b7f2b 100644 --- a/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx +++ b/frontend/src/components/pages/main/TravelogueCard/TravelogueCard.tsx @@ -10,7 +10,7 @@ import useImageError from "@hooks/useImageError"; import { CYPRESS_DATA_MAP } from "@constants/cypress"; import { ROUTE_PATHS_MAP } from "@constants/route"; -import removeEmojis from "@utils/removeEmojis"; +import { removeEmoji } from "@utils/removeEmojis"; import * as S from "./TravelogueCard.styled"; @@ -27,10 +27,10 @@ const getCardAriaLabel = ({ likeCount, tags, }: Pick) => { - const tagText = removeEmojis(tags); + const tagText = tags.map((tag) => removeEmoji(tag.tag)); const tagPart = tagText ? `태그: ${tagText}` : ""; - return `${removeEmojis(title)} 여행기. ${authorNickname} 작성. 좋아요 수: ${likeCount}개. ${tagPart}`; + return `${removeEmoji(title)} 여행기. ${authorNickname} 작성. 좋아요 수: ${likeCount}개. ${tagPart}`; }; const TravelogueCard = React.forwardRef( diff --git a/frontend/src/utils/removeEmojis.ts b/frontend/src/utils/removeEmojis.ts index a85b8349..a8e5ae6c 100644 --- a/frontend/src/utils/removeEmojis.ts +++ b/frontend/src/utils/removeEmojis.ts @@ -1,30 +1,11 @@ -import type { Tag } from "@type/domain/travelogue"; +export const removeEmoji = (string: string) => + string.replace(/(\p{Emoji_Presentation}|\p{Emoji}\uFE0F)/gu, "").trim(); -const removeEmoji = (str: string) => - str.replace(/(\p{Emoji_Presentation}|\p{Emoji}\uFE0F)/gu, "").trim(); +const joinStringsWithoutEmojis = (strings: string[]) => + strings.map((string) => removeEmoji(string)).join(", "); -const isTagArray = (tags: unknown[]): tags is Tag[] => - tags.length > 0 && - tags[0] !== null && - typeof tags[0] === "object" && - "tag" in tags[0] && - typeof tags[0].tag === "string"; - -const processTagArray = (tags: Tag[]): string => tags.map((tag) => removeEmoji(tag.tag)).join(", "); - -const processStringArray = (tags: string[]): string => tags.map(removeEmoji).join(", "); - -const removeEmojis = (tags: Tag[] | string[] | string): string => { - if (typeof tags === "string") { - return removeEmoji(tags); - } - - if (!Array.isArray(tags)) { - return ""; - } - - const tagNames = isTagArray(tags) ? processTagArray(tags) : processStringArray(tags); - return tagNames ? tagNames : ""; +const removeEmojis = (stringArray: string[]) => { + return joinStringsWithoutEmojis(stringArray); }; export default removeEmojis; From 517a96975e176b677e957848fa7243a6d9cfba83 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:36:12 +0900 Subject: [PATCH 39/49] =?UTF-8?q?chore(FocusTrap):=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=A3=BC=EC=84=9D=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/common/FocusTrap.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/src/components/common/FocusTrap.tsx b/frontend/src/components/common/FocusTrap.tsx index c148ffa7..239c957b 100644 --- a/frontend/src/components/common/FocusTrap.tsx +++ b/frontend/src/components/common/FocusTrap.tsx @@ -94,8 +94,6 @@ const FocusTrap = ({ children, onEscapeFocusTrap, ...prop ref: focusTrapRef, }); - console.log(Component); - return <>{Component}; }; From c5b073325641e1d5b741ddffefb6916ce9a06e4e Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:37:30 +0900 Subject: [PATCH 40/49] =?UTF-8?q?refactor(FocusTrap):=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EA=B0=92=EB=93=A4=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/common/FocusTrap.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/frontend/src/components/common/FocusTrap.tsx b/frontend/src/components/common/FocusTrap.tsx index 239c957b..ce638208 100644 --- a/frontend/src/components/common/FocusTrap.tsx +++ b/frontend/src/components/common/FocusTrap.tsx @@ -39,9 +39,6 @@ const FocusTrap = ({ children, onEscapeFocusTrap, ...prop }; }, []); - const firstElement = focusableElements.current[0]; - const lastElement = focusableElements.current[focusableElements.current.length - 1]; - useEffect(() => { const focusNextElement = () => { currentFocusIndex.current = @@ -87,7 +84,7 @@ const FocusTrap = ({ children, onEscapeFocusTrap, ...prop document.addEventListener("keydown", handleKeyPress); return () => document.removeEventListener("keydown", handleKeyPress); - }, [firstElement, lastElement, onEscapeFocusTrap]); + }, [onEscapeFocusTrap]); const Component = React.cloneElement(child, { ...{ ...props, ...child?.props }, From 2db1cb2493afdcc9b7960af67e9ba7054688df8a Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:42:26 +0900 Subject: [PATCH 41/49] =?UTF-8?q?refactor(SearchHeader):=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20option=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/Header/SearchHeader/SearchHeader.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/frontend/src/components/common/Header/SearchHeader/SearchHeader.tsx b/frontend/src/components/common/Header/SearchHeader/SearchHeader.tsx index 26557dd7..a44d53d8 100644 --- a/frontend/src/components/common/Header/SearchHeader/SearchHeader.tsx +++ b/frontend/src/components/common/Header/SearchHeader/SearchHeader.tsx @@ -87,13 +87,7 @@ const SearchHeader = () => { > - + Date: Mon, 14 Oct 2024 14:56:36 +0900 Subject: [PATCH 42/49] =?UTF-8?q?refactor(Drawer):=20usePressESC=20?= =?UTF-8?q?=ED=9B=85=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/common/Drawer/Drawer.tsx | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/frontend/src/components/common/Drawer/Drawer.tsx b/frontend/src/components/common/Drawer/Drawer.tsx index c3cdd316..48477432 100644 --- a/frontend/src/components/common/Drawer/Drawer.tsx +++ b/frontend/src/components/common/Drawer/Drawer.tsx @@ -1,17 +1,18 @@ -import React, { useCallback, useEffect, useState } from "react"; +import React, { useCallback, useState } from "react"; import ReactDOM from "react-dom"; import DrawerProvider, { useDrawerContext } from "@contexts/DrawerProvider"; import useModalControl from "@hooks/useModalControl"; +import usePressESC from "@hooks/usePressESC"; import VisuallyHidden from "../VisuallyHidden/VisuallyHidden"; import * as S from "./Drawer.styled"; const Drawer = ({ children }: React.PropsWithChildren) => { const [isOpen, setIsOpen] = useState(false); - const toggleDrawer = useCallback(() => setIsOpen((prev) => !prev), []); + usePressESC(isOpen, toggleDrawer); useModalControl(isOpen, toggleDrawer); @@ -31,20 +32,6 @@ const Drawer = ({ children }: React.PropsWithChildren) => { } }); - useEffect(() => { - const handleEscapeKey = (e: KeyboardEvent) => { - if (e.key === "Escape" && isOpen) { - toggleDrawer(); - } - }; - - document.addEventListener("keydown", handleEscapeKey); - - return () => { - document.removeEventListener("keydown", handleEscapeKey); - }; - }, [isOpen, toggleDrawer]); - return ( From 85e2886b2ca1767ac4e7ddd4880badc513fd15ec Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:02:26 +0900 Subject: [PATCH 43/49] =?UTF-8?q?refactor(Drawer):=20styled=20component?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/common/Drawer/Drawer.styled.ts | 3 +-- frontend/src/components/common/Drawer/Drawer.tsx | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/common/Drawer/Drawer.styled.ts b/frontend/src/components/common/Drawer/Drawer.styled.ts index 89f606a8..78513c39 100644 --- a/frontend/src/components/common/Drawer/Drawer.styled.ts +++ b/frontend/src/components/common/Drawer/Drawer.styled.ts @@ -1,4 +1,3 @@ -import { css } from "@emotion/react"; import styled from "@emotion/styled"; export const DrawerContainer = styled.div<{ isOpen: boolean }>` @@ -46,7 +45,7 @@ export const DrawerContent = styled.div` padding: 1rem; `; -export const triggerStyle = css` +export const TriggerButton = styled.button` background: none; border: none; diff --git a/frontend/src/components/common/Drawer/Drawer.tsx b/frontend/src/components/common/Drawer/Drawer.tsx index 48477432..4a2a6f04 100644 --- a/frontend/src/components/common/Drawer/Drawer.tsx +++ b/frontend/src/components/common/Drawer/Drawer.tsx @@ -73,9 +73,9 @@ const Trigger = ({ children, onClick }: TriggerProps) => { }; return ( - + ); }; From ee46e07a274da4b4a19210d67d791b7ffdc00685 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:04:15 +0900 Subject: [PATCH 44/49] =?UTF-8?q?refactor(FloatingButton):=20=ED=81=B4?= =?UTF-8?q?=EB=A1=9C=EC=A0=80=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/common/FloatingButton/FloatingButton.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/common/FloatingButton/FloatingButton.tsx b/frontend/src/components/common/FloatingButton/FloatingButton.tsx index 669c6a43..3220eeca 100644 --- a/frontend/src/components/common/FloatingButton/FloatingButton.tsx +++ b/frontend/src/components/common/FloatingButton/FloatingButton.tsx @@ -22,7 +22,7 @@ const FloatingButton = () => { setIsOpen((prev) => !prev); }; - const handleClickSubButton = (route: string) => () => { + const handleClickSubButton = (route: string) => { navigate(route); }; @@ -43,7 +43,7 @@ const FloatingButton = () => { {SUB_BUTTONS.map(({ text, route }) => ( handleClickSubButton(route)} aria-label={removeEmoji(text)} > From 10a5943ef41648bb3134a559c37b516ecf422acd Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:09:22 +0900 Subject: [PATCH 45/49] =?UTF-8?q?chore(Header):=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20styled=20component=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/common/Header/Header.styled.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/frontend/src/components/common/Header/Header.styled.ts b/frontend/src/components/common/Header/Header.styled.ts index 5542ce63..78832d78 100644 --- a/frontend/src/components/common/Header/Header.styled.ts +++ b/frontend/src/components/common/Header/Header.styled.ts @@ -23,11 +23,6 @@ export const DrawHeaderContainer = styled.div` display: flex; `; -export const MenuItem = styled.button` - ${(props) => props.theme.typography.mobile.bodyBold}; - padding: ${({ theme }) => theme.spacing.s}; -`; - export const MenuList = styled.div` display: flex; flex-direction: column; From f79737b1614df9cfd8e0d053c2c47d4544ac5909 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:19:39 +0900 Subject: [PATCH 46/49] =?UTF-8?q?styled(MainPage):=20theme=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/pages/main/MainPage.styled.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/pages/main/MainPage.styled.ts b/frontend/src/components/pages/main/MainPage.styled.ts index 3eacfeee..68eb62ca 100644 --- a/frontend/src/components/pages/main/MainPage.styled.ts +++ b/frontend/src/components/pages/main/MainPage.styled.ts @@ -128,11 +128,13 @@ export const FetchButton = styled.button` position: fixed; bottom: 0; left: 0; - z-index: 100; - padding: 8px; + z-index: ${({ theme }) => theme.zIndex.floating}; + padding: ${({ theme }) => theme.spacing.xs}; + + background-color: ${PRIMITIVE_COLORS.black}; + + color: ${PRIMITIVE_COLORS.white}; - color: white; - background: #000; transform: translateY(100%); transition: transform 0.3s; From 8d32894fb9f7a592733f2c061d3864667bec53bf Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Mon, 14 Oct 2024 16:05:49 +0900 Subject: [PATCH 47/49] =?UTF-8?q?refactor(FloatingButton):=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/common/FloatingButton/FloatingButton.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/common/FloatingButton/FloatingButton.tsx b/frontend/src/components/common/FloatingButton/FloatingButton.tsx index 3220eeca..c036b823 100644 --- a/frontend/src/components/common/FloatingButton/FloatingButton.tsx +++ b/frontend/src/components/common/FloatingButton/FloatingButton.tsx @@ -32,8 +32,8 @@ const FloatingButton = () => { {isOpen - ? "여행기 및 여행 계획 작성 모달이 열렸습니다. 닫으려면 esc버튼을 눌러주세요." - : "여행기 및 여행 계획 작성 모달이 닫혔습니다."} + ? "여행기 및 여행 계획 작성 플로팅 버튼이 열렸습니다. 닫으려면 esc버튼을 눌러주세요." + : "여행기 및 여행 계획 작성 플로팅 버튼이 닫혔습니다."} {isOpen && ( <> From 8c5d737685446a050037d98667f692aff7b99944 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Tue, 15 Oct 2024 19:39:32 +0900 Subject: [PATCH 48/49] =?UTF-8?q?refactor(LoginPage):=20=EC=83=81=EC=88=98?= =?UTF-8?q?=20=ED=8C=8C=EC=9D=BC=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/pages/login/LoginPage.tsx | 5 +- .../src/components/pages/login/contants.ts | 4 + frontend/src/hooks/useKeyDown.ts | 101 ++++++++++++++++++ frontend/src/mocks/handlers/index.ts | 16 +-- .../src/types/domain/keyboardNavigation.ts | 10 ++ 5 files changed, 124 insertions(+), 12 deletions(-) create mode 100644 frontend/src/components/pages/login/contants.ts create mode 100644 frontend/src/hooks/useKeyDown.ts create mode 100644 frontend/src/types/domain/keyboardNavigation.ts diff --git a/frontend/src/components/pages/login/LoginPage.tsx b/frontend/src/components/pages/login/LoginPage.tsx index c2685151..287f3b35 100644 --- a/frontend/src/components/pages/login/LoginPage.tsx +++ b/frontend/src/components/pages/login/LoginPage.tsx @@ -6,6 +6,7 @@ import { ExcitedTturi } from "@assets/gif"; import { KakaoSymbol } from "@assets/svg"; import * as S from "./LoginPage.styled"; +import { GREETING_MAIN_TEXT, GREETING_SUB_TEXT, KAKAO_LABEL, TTURI } from "./contants"; declare global { interface Window { @@ -17,10 +18,6 @@ declare global { const kakao = window.Kakao; const LoginPage = () => { - const TTURI = "뚜리"; - const GREETING_MAIN_TEXT = "투룻에 온 걸 환영해요!"; - const GREETING_SUB_TEXT = "To your route, touroot!"; - const KAKAO_LABEL = "카카오 로그인"; useEffect(() => { if (!kakao?.isInitialized()) { diff --git a/frontend/src/components/pages/login/contants.ts b/frontend/src/components/pages/login/contants.ts new file mode 100644 index 00000000..dab8fe5d --- /dev/null +++ b/frontend/src/components/pages/login/contants.ts @@ -0,0 +1,4 @@ +export const TTURI = "뚜리"; +export const GREETING_MAIN_TEXT = "투룻에 온 걸 환영해요!"; +export const GREETING_SUB_TEXT = "To your route, touroot!"; +export const KAKAO_LABEL = "카카오 로그인"; diff --git a/frontend/src/hooks/useKeyDown.ts b/frontend/src/hooks/useKeyDown.ts new file mode 100644 index 00000000..77669aa4 --- /dev/null +++ b/frontend/src/hooks/useKeyDown.ts @@ -0,0 +1,101 @@ +import { useEffect, useRef, useState } from "react"; + +import type { + ArrowKey, + Direction, + HorizontalKey, + IncrementValue, + KeyActions, + VerticalKey, +} from "@type/domain/keyboardNavigation"; + +interface UseKeyDownProps { + isOpen: boolean; + direction?: Direction; +} +const isVerticalKey = (key: unknown): key is VerticalKey => { + return key === "ArrowDown" || key === "ArrowUp"; +}; + +const isHorizontalKey = (key: unknown): key is HorizontalKey => { + return key === "ArrowRight" || key === "ArrowLeft"; +}; + +const isArrowKey = (key: unknown): key is ArrowKey => { + return isVerticalKey(key) || isHorizontalKey(key); +}; + +const keyActions: KeyActions = { + vertical: { + ArrowDown: 1, + ArrowUp: -1, + }, + horizontal: { + ArrowRight: 1, + ArrowLeft: -1, + }, +}; + +const useKeyDown = ({ isOpen, direction = "vertical" }: UseKeyDownProps) => { + const modalRef = useRef(null); + const focusableElements = useRef([]); + const [currentFocusIndex, setCurrentFocusIndex] = useState(-1); + + // useEffect(() => { + // if (isOpen && modalRef.current) { + // focusableElements.current = Array.from( + // modalRef.current.querySelectorAll( + // 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])', + // ), + // ); + // setCurrentFocusIndex(0); + // focusableElements.current[0]?.focus(); + // } + // }, [isOpen]); + + useEffect(() => { + if (isOpen && modalRef.current) { + const observer = new MutationObserver(() => { + focusableElements.current = Array.from( + modalRef.current!.querySelectorAll( + 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])', + ), + ); + }); + + observer.observe(modalRef.current, { childList: true, subtree: true }); + + return () => observer.disconnect(); + } + }, [isOpen]); + + const handleKeyDown = (event: React.KeyboardEvent) => { + if (!isOpen) return; + + const { key } = event; + + if (!isArrowKey(key)) return; + + event.preventDefault(); + + let increment: IncrementValue = 1; + + if (direction === "vertical" && isVerticalKey(key)) { + increment = keyActions.vertical[key]; + } else if (direction === "horizontal" && isHorizontalKey(key)) { + increment = keyActions.horizontal[key]; + } + + const length = focusableElements.current.length; + const nextIndex = (currentFocusIndex + increment + length) % length; + + if (nextIndex !== currentFocusIndex) { + setCurrentFocusIndex(nextIndex); + focusableElements.current[nextIndex]?.focus(); + } + }; + + return { modalRef, handleKeyDown }; +}; + +export default useKeyDown; diff --git a/frontend/src/mocks/handlers/index.ts b/frontend/src/mocks/handlers/index.ts index efd11fdf..6e70d154 100644 --- a/frontend/src/mocks/handlers/index.ts +++ b/frontend/src/mocks/handlers/index.ts @@ -1,11 +1,11 @@ -import { modifyMemberNicknameHandler } from "@mocks/handlers/modifyMemberNicknameHandler"; -import { travelPlanHandler } from "@mocks/handlers/travelPlanHandler"; -import { travelPlanRegisterHandler } from "@mocks/handlers/travelPlanRegisterHandler"; -import { travelogueInfiniteHandler } from "@mocks/handlers/travelogueInfiniteHandler"; +// import { modifyMemberNicknameHandler } from "@mocks/handlers/modifyMemberNicknameHandler"; +// import { travelPlanHandler } from "@mocks/handlers/travelPlanHandler"; +// import { travelPlanRegisterHandler } from "@mocks/handlers/travelPlanRegisterHandler"; +// import { travelogueInfiniteHandler } from "@mocks/handlers/travelogueInfiniteHandler"; export const handlers = [ - travelogueInfiniteHandler, - travelPlanHandler, - travelPlanRegisterHandler, - modifyMemberNicknameHandler, + // travelogueInfiniteHandler, + // travelPlanHandler, + // travelPlanRegisterHandler, + // modifyMemberNicknameHandler, ]; diff --git a/frontend/src/types/domain/keyboardNavigation.ts b/frontend/src/types/domain/keyboardNavigation.ts new file mode 100644 index 00000000..fed08711 --- /dev/null +++ b/frontend/src/types/domain/keyboardNavigation.ts @@ -0,0 +1,10 @@ +export type Direction = "vertical" | "horizontal"; +export type VerticalKey = "ArrowDown" | "ArrowUp"; +export type HorizontalKey = "ArrowRight" | "ArrowLeft"; +export type ArrowKey = VerticalKey | HorizontalKey; +export type IncrementValue = 1 | -1; + +export interface KeyActions { + vertical: Record; + horizontal: Record; +} From 3974935bf8bc0cda893657685d4fc852d1502848 Mon Sep 17 00:00:00 2001 From: river <130737187+0jenn0@users.noreply.github.com> Date: Tue, 15 Oct 2024 19:40:13 +0900 Subject: [PATCH 49/49] =?UTF-8?q?feat(LoginPage):=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=A0=91=EC=86=8D?= =?UTF-8?q?=EC=8B=9C=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=B2=84=ED=8A=BC?= =?UTF-8?q?=EC=97=90=20focus=EB=90=98=EC=96=B4=EC=9E=88=EA=B8=B0=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/pages/login/LoginPage.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/pages/login/LoginPage.tsx b/frontend/src/components/pages/login/LoginPage.tsx index 287f3b35..78a57e92 100644 --- a/frontend/src/components/pages/login/LoginPage.tsx +++ b/frontend/src/components/pages/login/LoginPage.tsx @@ -1,4 +1,4 @@ -import { useEffect } from "react"; +import { useEffect, useRef } from "react"; import { Text } from "@components/common"; @@ -18,11 +18,14 @@ declare global { const kakao = window.Kakao; const LoginPage = () => { + const loginButtonRef = useRef(null); useEffect(() => { if (!kakao?.isInitialized()) { kakao?.init(process.env.JAVASCRIPT_KEY); } + + loginButtonRef.current && loginButtonRef.current.focus(); }, []); const handleKakaoLogin = () => { @@ -43,7 +46,7 @@ const LoginPage = () => { - + {KAKAO_LABEL}