-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
フォームデータの解析関数を追加し、投票ボタンコンポーネントをクライアントサイドに移動しました
- Loading branch information
Showing
12 changed files
with
442 additions
and
210 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
140 changes: 140 additions & 0 deletions
140
...ndle]/page/[slug]/components/segment-and-translation-section/vote-buttons/client.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import type { SegmentTranslationWithVote } from "@/app/[locale]/types"; | ||
import { render, screen } from "@testing-library/react"; | ||
// VoteButtons.test.tsx | ||
import React from "react"; | ||
import { vi } from "vitest"; | ||
import { VoteButtons } from "./client"; | ||
import type { VoteTarget } from "./constants"; | ||
const dummyVoteTarget = "example-target" as VoteTarget; | ||
|
||
const dummyTranslationUpvote = { | ||
segmentTranslation: { id: 1, point: 10 }, | ||
translationVote: { isUpvote: true }, | ||
} as SegmentTranslationWithVote; | ||
|
||
const dummyTranslationDownvote = { | ||
segmentTranslation: { id: 2, point: 5 }, | ||
translationVote: { isUpvote: false }, | ||
} as SegmentTranslationWithVote; | ||
|
||
vi.mock("next/form", () => ({ | ||
__esModule: true, | ||
default: function Form({ | ||
children, | ||
...props | ||
}: { children: React.ReactNode }) { | ||
return <form {...props}>{children}</form>; | ||
}, | ||
})); | ||
|
||
describe("VoteButtons コンポーネント", () => { | ||
afterEach(() => { | ||
vi.restoreAllMocks(); | ||
}); | ||
|
||
test("フォームと hidden input、アップ/ダウンボタンがレンダリングされる", () => { | ||
// useActionState の戻り値をモック | ||
vi.spyOn(React, "useActionState").mockReturnValue([ | ||
{ data: { isUpvote: true, point: 10 } }, | ||
vi.fn(), | ||
false, // isVoting: false | ||
]); | ||
|
||
render( | ||
<VoteButtons | ||
translationWithVote={dummyTranslationUpvote} | ||
voteTarget={dummyVoteTarget} | ||
/>, | ||
); | ||
|
||
// hidden input (voteTarget) の検証 | ||
const voteTargetInput = screen.getByDisplayValue(dummyVoteTarget); | ||
expect(voteTargetInput).toBeInTheDocument(); | ||
|
||
// hidden input (segmentTranslationId) の検証 | ||
const segmentTranslationIdInput = screen.getByDisplayValue( | ||
dummyTranslationUpvote.segmentTranslation.id, | ||
); | ||
expect(segmentTranslationIdInput).toBeInTheDocument(); | ||
|
||
// VoteButton の data-testid を用いた検証 | ||
expect(screen.getByTestId("vote-up-button")).toBeInTheDocument(); | ||
expect(screen.getByTestId("vote-down-button")).toBeInTheDocument(); | ||
}); | ||
|
||
test("アップボタンが正しい投票数とアクティブ状態のアイコンクラスを表示する", () => { | ||
vi.spyOn(React, "useActionState").mockReturnValue([ | ||
{ data: { isUpvote: true, point: 10 } }, | ||
vi.fn(), | ||
false, | ||
]); | ||
|
||
render( | ||
<VoteButtons | ||
translationWithVote={dummyTranslationUpvote} | ||
voteTarget={dummyVoteTarget} | ||
/>, | ||
); | ||
|
||
const upvoteButton = screen.getByTestId("vote-up-button"); | ||
// upvote ボタンは voteCount (10) を表示する | ||
expect(upvoteButton).toHaveTextContent("10"); | ||
|
||
// ThumbsUp アイコンがレンダリングされ、アクティブ状態のクラスが含まれている | ||
const thumbsUpIcon = upvoteButton.querySelector("svg"); | ||
expect(thumbsUpIcon).toBeInTheDocument(); | ||
// アクティブの場合、"[&>path]:fill-primary" が付与される | ||
expect(thumbsUpIcon?.getAttribute("class") || "").toContain( | ||
"[&>path]:fill-primary", | ||
); | ||
}); | ||
|
||
test("ダウンボタンがアクティブの場合、適切なアイコンクラスが付与され、voteCount は表示されない", () => { | ||
vi.spyOn(React, "useActionState").mockReturnValue([ | ||
{ data: { isUpvote: false, point: 5 } }, | ||
vi.fn(), | ||
false, | ||
]); | ||
|
||
render( | ||
<VoteButtons | ||
translationWithVote={dummyTranslationDownvote} | ||
voteTarget={dummyVoteTarget} | ||
/>, | ||
); | ||
|
||
const downvoteButton = screen.getByTestId("vote-down-button"); | ||
expect(downvoteButton).toBeInTheDocument(); | ||
|
||
// downvote ボタンは voteCount を表示しない(upvote のみ表示される) | ||
expect(downvoteButton).not.toHaveTextContent("5"); | ||
|
||
// ThumbsDown アイコンの active クラスの確認 | ||
const thumbsDownIcon = downvoteButton.querySelector("svg"); | ||
expect(thumbsDownIcon).toBeInTheDocument(); | ||
expect(thumbsDownIcon?.getAttribute("class") || "").toContain( | ||
"[&>path]:fill-primary", | ||
); | ||
}); | ||
|
||
test("isVoting が true の場合、全てのボタンが disabled になる", () => { | ||
vi.spyOn(React, "useActionState").mockReturnValue([ | ||
{ data: { isUpvote: true, point: 10 } }, | ||
vi.fn(), | ||
true, // isVoting: true | ||
]); | ||
|
||
render( | ||
<VoteButtons | ||
translationWithVote={dummyTranslationUpvote} | ||
voteTarget={dummyVoteTarget} | ||
/>, | ||
); | ||
|
||
const upvoteButton = screen.getByTestId("vote-up-button"); | ||
const downvoteButton = screen.getByTestId("vote-down-button"); | ||
|
||
expect(upvoteButton.className).toContain("disabled:pointer-events-none"); | ||
expect(downvoteButton.className).toContain("disabled:pointer-events-none"); | ||
}); | ||
}); |
62 changes: 62 additions & 0 deletions
62
...r/[handle]/page/[slug]/components/segment-and-translation-section/vote-buttons/client.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
"use client"; | ||
import type { SegmentTranslationWithVote } from "@/app/[locale]/types"; | ||
import { ThumbsDown, ThumbsUp } from "lucide-react"; | ||
import Form from "next/form"; | ||
import { memo } from "react"; | ||
import { useActionState } from "react"; | ||
import { | ||
type VoteTranslationActionResponse, | ||
voteTranslationAction, | ||
} from "./action"; | ||
import type { VoteTarget } from "./constants"; | ||
import { VoteButton } from "./vote-button"; | ||
|
||
interface VoteButtonsProps { | ||
translationWithVote: SegmentTranslationWithVote; | ||
voteTarget: VoteTarget; | ||
} | ||
|
||
export const VoteButtons = memo(function VoteButtons({ | ||
translationWithVote, | ||
voteTarget, | ||
}: VoteButtonsProps) { | ||
const [voteState, voteAction, isVoting] = useActionState< | ||
VoteTranslationActionResponse, | ||
FormData | ||
>(voteTranslationAction, { | ||
success: false, | ||
data: { | ||
isUpvote: translationWithVote.translationVote?.isUpvote, | ||
point: translationWithVote.segmentTranslation.point, | ||
}, | ||
}); | ||
return ( | ||
<span className="flex h-full justify-end items-center"> | ||
<Form action={voteAction}> | ||
<input type="hidden" name="voteTarget" value={voteTarget} /> | ||
<input | ||
type="hidden" | ||
name="segmentTranslationId" | ||
value={translationWithVote.segmentTranslation.id} | ||
/> | ||
<span className="flex h-8"> | ||
<VoteButton | ||
type="upvote" | ||
isActive={voteState.data?.isUpvote === true} | ||
isVoting={isVoting} | ||
voteCount={voteState.data?.point} | ||
> | ||
{({ iconClass }) => <ThumbsUp className={iconClass} />} | ||
</VoteButton> | ||
<VoteButton | ||
type="downvote" | ||
isActive={voteState.data?.isUpvote === false} | ||
isVoting={isVoting} | ||
> | ||
{({ iconClass }) => <ThumbsDown className={iconClass} />} | ||
</VoteButton> | ||
</span> | ||
</Form> | ||
</span> | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 0 additions & 77 deletions
77
...er/[handle]/page/[slug]/components/segment-and-translation-section/vote-buttons/index.tsx
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.