Skip to content

Commit

Permalink
merge: 라운드 결과 페이지 스크린 리더 개선 #325
Browse files Browse the repository at this point in the history
[REFACTOR] 라운드 결과 페이지 스크린 리더 개선
  • Loading branch information
useon authored Oct 16, 2024
2 parents 04f306c + 46ab039 commit 4e13fea
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 39 deletions.
2 changes: 1 addition & 1 deletion frontend/src/components/NicknameItem/NicknameItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const NicknameItem = ({ nickName }: NicknameItemProp) => {

return (
<li css={nicknameItemLayout}>
<img src={SillyDdangkong} alt="사용자 프로필" css={profileImage} />
<img src={SillyDdangkong} alt="" css={profileImage} />
<span css={nicknameText(isMyNickname)}>{nickName}</span>
</li>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
optionParticipantsLayout,
participantsListWrapper,
} from './OptionParticipants.styled';
import A11yOnly from '../common/a11yOnly/A11yOnly';
import NicknameItem from '../NicknameItem/NicknameItem';

export interface OptionParticipantsProps {
Expand All @@ -14,7 +15,10 @@ export interface OptionParticipantsProps {
const OptionParticipants = ({ optionName, memberCount, members }: OptionParticipantsProps) => {
return (
<div css={optionParticipantsLayout}>
<p css={optionInfo}>
<A11yOnly>
{optionName}.{memberCount}
</A11yOnly>
<p css={optionInfo} aria-hidden>
{optionName}: {memberCount}
</p>
<ul css={participantsListWrapper}>
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/components/RoundResultTab/RoundResultTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ const TAB_TITLE = {

const RoundResultTab = ({ tab, activeTab, handleClickTab }: RoundResultTabProps) => {
return (
<button css={tabButtonStyle(activeTab === tab)} onClick={() => handleClickTab(tab)}>
<button
css={tabButtonStyle(activeTab === tab)}
onClick={() => handleClickTab(tab)}
role="tab"
aria-current={activeTab === tab}
>
{TAB_TITLE[tab]}
</button>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,36 @@ import { userEvent } from '@testing-library/user-event';

import RoundVoteContainer from './RoundVoteContainer';

import ROUND_VOTE_RESULT from '@/mocks/data/roundVoteResult.json';

import { customRender } from '@/test-utils';

describe('RoundVoteContainer 컴포넌트 테스트', () => {
it('기본 탭인 투표 통계 탭에서, 투표 현황 탭을 클릭하면 투표 현황을 보여준다.', async () => {
const user = userEvent.setup();
customRender(<RoundVoteContainer />);

const button = await screen.findByRole('button', { name: '투표 현황' });
const button = await screen.findByRole('tab', { name: '투표 현황' });
await user.click(button);

await waitFor(() => {
// 첫 번째 선택지와 투표자 확인
expect(screen.getByText((content) => content.includes('100억 빚 송강'))).toBeInTheDocument();
expect(screen.getByText((content) => content.includes('d'))).toBeInTheDocument();
// 두 번째 선택지와 투표자 확인
expect(
screen.getByText((content) => content.includes('100억 부자 송강호')),
).toBeInTheDocument();
expect(
screen.getByText((content) => content.includes('일이삼사오육칠팔구십일이')),
).toBeInTheDocument();
// 기권자 확인
expect(
screen.getByText((content) => content.includes('투표에 참여하지 않으셨어요')),
).toBeInTheDocument();
expect(screen.getByText((content) => content.includes('ㅁ'))).toBeInTheDocument();
// 첫 번째 선택지의 투표 멤버 확인
const firstOptionMembers = ROUND_VOTE_RESULT.group.firstOption.members;
firstOptionMembers.forEach((member) => {
expect(screen.getByText(member)).toBeInTheDocument();
});

// 두 번째 선택지의 투표 멤버 확인
const secondOptionMembers = ROUND_VOTE_RESULT.group.secondOption.members;
secondOptionMembers.forEach((member) => {
expect(screen.getByText(member)).toBeInTheDocument();
});

// 투표를 하지 않은 멤버 확인
const giveUpMembers = ROUND_VOTE_RESULT.group.giveUp.members;
giveUpMembers.forEach((member) => {
expect(screen.getByText(member)).toBeInTheDocument();
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
totalResultInfoText,
} from './TabContentContainer.styled';
import getDominantVote from './TabContentContainer.util';
import A11yOnly from '../common/a11yOnly/A11yOnly';
import OptionParticipantsContainer from '../OptionParticipantsContainer/OptionParticipantsContainer';
import useTotalCountAnimation from '../RoundVoteContainer/RoundVoteContainer.hook';
import TopicContainer from '../TopicContainer/TopicContainer';
Expand Down Expand Up @@ -50,12 +51,20 @@ const TabContentContainer = ({ isVoteStatisticsTabActive }: TabContentContainerP

const dominantVoteData = totalResult ? getDominantVote(totalResult) : null;

const screenReaderFirstOption = `${groupRoundResult.firstOption.name} ${groupRoundResult.firstOption.percent}%. ${groupRoundResult.firstOption.memberCount}명 선택.`;
const screenReaderSecondOption = `${groupRoundResult.secondOption.name} ${groupRoundResult.secondOption.percent}%. ${groupRoundResult.secondOption.memberCount}명 선택`;
const screenReaderDominantVote = `📢 전체 유저 중 ${dominantVoteData?.dominantPercent}%는. ${dominantVoteData?.dominantName}를 선택했어요`;

return (
<div css={contentWrapperStyle}>
<TopicContainer />
{isVote && isVoteStatisticsTabActive && (
<>
<div css={roundVoteResultContainer}>
<A11yOnly>
{screenReaderFirstOption}
{screenReaderSecondOption}
</A11yOnly>
<div css={roundVoteResultContainer} aria-hidden>
<div css={categoryContainer}>
<span>{groupRoundResult.firstOption.name}</span>
<span>{groupRoundResult.secondOption.name}</span>
Expand All @@ -76,16 +85,15 @@ const TabContentContainer = ({ isVoteStatisticsTabActive }: TabContentContainerP
{totalResult && dominantVoteData && (
<div css={totalResultInfoContainer}>
{dominantVoteData.isEqual ? (
<span css={totalResultInfoText}>
🥜 땅콩 유저들 사이에서 선택이 팽팽하게 갈렸어요! 😲
</span>
<span css={totalResultInfoText}>📢 전체 유저 사이에서는 의견이 반반이에요 😲</span>
) : (
<>
<span css={totalResultInfoText}>
🥜 땅콩 유저 중{' '}
<A11yOnly>{screenReaderDominantVote}</A11yOnly>
<span css={totalResultInfoText} aria-hidden>
📢 전체 유저 중{' '}
<span css={emphasizeText}>{dominantVoteData.dominantPercent}%</span>
</span>
<span css={totalResultInfoText}>
<span css={totalResultInfoText} aria-hidden>
<span css={emphasizeText}>{dominantVoteData.dominantName}</span>를 선택했어요 !
</span>
</>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/layout/Header/Header.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { StoryObj, Meta } from '@storybook/react';

import Header, { BackHeader, RoomSettingHeader, RoundHeader, TitleHeader } from './Header';
import Header, { BackHeader, RoomSettingHeader, RoundResultHeader, TitleHeader } from './Header';

const meta = {
title: 'Header',
Expand All @@ -20,7 +20,7 @@ export const 방_설정_헤더: Story = {
};

export const 라운드_헤더: Story = {
render: () => <RoundHeader />,
render: () => <RoundResultHeader />,
};

export const 투표_현황_헤더: Story = {
Expand Down
19 changes: 10 additions & 9 deletions frontend/src/components/layout/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import useRoutePath from './hooks/useRoutePath';
import ArrowLeft from '@/assets/images/arrowLeft.svg';
import ExitIcon from '@/assets/images/exitIcon.webp';
import SettingIcon from '@/assets/images/settingsIcon.webp';
import A11yOnly from '@/components/common/a11yOnly/A11yOnly';
import RoomSettingModal from '@/components/common/RoomSettingModal/RoomSettingModal';
import { ROUTES } from '@/constants/routes';
import useBalanceContentQuery from '@/hooks/useBalanceContentQuery';
Expand All @@ -35,7 +36,7 @@ const Header = () => {

if (isNicknamePage) return <TitleHeader title="닉네임 설정" />;
if (isReadyPage) return <RoomSettingHeader title="밸런스 게임" />;
if (isRoundResultPage) return <RoundHeader />;
if (isRoundResultPage) return <RoundResultHeader />;
if (isMatchingResultPage) return <MatchingResultHeader title="매칭 결과" />;
};

Expand Down Expand Up @@ -84,21 +85,21 @@ export const RoomSettingHeader = ({ title }: HeaderProps) => {
};

// 4. 좌측 상단 라운드, 가운데 제목 차지하는 헤더 (API 호출 O) : 게임 화면, 라운드 통계 화면
export const RoundHeader = () => {
export const RoundResultHeader = () => {
const { roomId } = useParams();
const isRoundResultPage = location.pathname === ROUTES.roundResult(Number(roomId));

const { balanceContent } = useBalanceContentQuery(Number(roomId));

const title = isRoundResultPage ? '투표 결과' : '밸런스 게임';
const screenReaderRoundResult = `${balanceContent.totalRound}라운드 중. ${balanceContent.currentRound}라운드. 투표 결과 페이지`;

return (
<header css={headerLayout()}>
<span css={roundText}>
<A11yOnly>{screenReaderRoundResult}</A11yOnly>
<span css={roundText} aria-hidden>
{balanceContent.currentRound}/{balanceContent.totalRound}
</span>
<h1 css={gameTitle}>{title}</h1>
<span css={roundText}></span>
<h1 css={gameTitle} aria-hidden>
투표 결과
</h1>
<span css={roundText} aria-hidden></span>
</header>
);
};
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/pages/GamePage/GamePage.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import Content from '@/components/layout/Content/Content';
import { RoundHeader } from '@/components/layout/Header/Header';
import { RoundResultHeader } from '@/components/layout/Header/Header';
import SelectContainer from '@/components/SelectContainer/SelectContainer';
import TopicContainer from '@/components/TopicContainer/TopicContainer';

const GamePage = () => {
return (
<>
<RoundHeader />
<RoundResultHeader />
<Content>
<TopicContainer />
<SelectContainer />
Expand Down

0 comments on commit 4e13fea

Please sign in to comment.