Support swing radar metrics and sector detail dialogs

This commit is contained in:
yuxuanhui
2026-09-21 22:28:35 +08:00
parent 3f50ca3076
commit 2f1c4f36c2
2 changed files with 169 additions and 94 deletions
@@ -1,4 +1,4 @@
import { fireEvent, render, screen } from "@testing-library/react"
import { fireEvent, render, screen, within } from "@testing-library/react"
import { beforeEach, describe, expect, it, vi } from "vitest"
import type {
@@ -13,6 +13,7 @@ const navigate = vi.hoisted(() => vi.fn())
const useSectorRadarDates = vi.fn()
const useSectorRadarRankings = vi.fn()
const useSectorRadarHistory = vi.fn()
const useSectorRadarDetail = vi.fn()
const refetchDates = vi.fn()
const refetchRankings = vi.fn()
const fetchNextPage = vi.fn()
@@ -25,6 +26,7 @@ vi.mock("@tanstack/react-router", () => ({
}))
vi.mock("@/features/sector-radar/api/sector-radar.query", () => ({
useSectorRadarDetail: (...args: unknown[]) => useSectorRadarDetail(...args),
useSectorRadarHistory: (...args: unknown[]) => useSectorRadarHistory(...args),
useSectorRadarDates: () => useSectorRadarDates(),
useSectorRadarRankings: (...args: unknown[]) =>
@@ -180,6 +182,7 @@ describe("SectorRadarPage", () => {
isError: false,
data: { status: "no_data" },
})
useSectorRadarDetail.mockReturnValue({ isPending: true })
navigate.mockReset()
refetchDates.mockReset()
refetchRankings.mockReset()
@@ -230,65 +233,130 @@ describe("SectorRadarPage", () => {
).not.toBeInTheDocument()
})
it("mirrors ratio columns and expands only the selected sector history, resetting on date change", () => {
routeSearch = { ...routeSearch, view: "ratio" }
const ratioPage = {
...rankingsResponse,
view: "ratio",
rows: rankingsResponse.rows.map((row) => ({
...row,
unit: "ratio",
metric_value: -0.01,
})),
}
useSectorRadarRankings.mockReturnValue(
rankingQueryResult({ data: { pages: [ratioPage], pageParams: [1] } }),
)
const { rerender } = render(<SectorRadarPage />)
expect(screen.getAllByRole("columnheader", { name: "在榜" })).toHaveLength(
2,
)
expect(screen.getAllByText("-1%")).toHaveLength(4)
const trigger = screen.getAllByRole("button", {
name: "机器人展开在榜历史",
})[0]!
fireEvent.click(trigger)
expect(trigger).toHaveAttribute("aria-expanded", "true")
expect(useSectorRadarHistory).toHaveBeenLastCalledWith({
sectorType: "concept",
sectorCode: "BK0001.DC",
tradeDate: "2026-08-28",
})
expect(screen.getByText("暂无在榜历史数据。")).toBeInTheDocument()
fireEvent.click(trigger)
expect(screen.queryByText("暂无在榜历史数据。")).not.toBeInTheDocument()
fireEvent.click(trigger)
useSectorRadarRankings.mockReturnValue(
rankingQueryResult({
data: {
pages: [
{
...ratioPage,
publication: {
...successPublication,
publication_id: "next-day",
it.each(["ratio", "swing"] as const)(
"mirrors %s columns and expands only the selected sector history, resetting on date change",
(view) => {
routeSearch = { ...routeSearch, view }
const ratioPage = {
...rankingsResponse,
view,
rows: rankingsResponse.rows.map((row) => ({
...row,
unit: "ratio",
metric_value: -0.01,
})),
}
useSectorRadarRankings.mockReturnValue(
rankingQueryResult({ data: { pages: [ratioPage], pageParams: [1] } }),
)
const { rerender } = render(<SectorRadarPage />)
expect(
screen.getAllByRole("columnheader", { name: "在榜" }),
).toHaveLength(2)
if (view === "swing") {
expect(
within(screen.getAllByRole("row")[1]!)
.getAllByRole("columnheader")
.map((cell) => cell.textContent),
).toEqual([
"板块/排名",
"涨跌幅",
"净额",
"在榜",
"流入",
"流出",
"在榜",
"净额",
"涨跌幅",
"板块/排名",
])
expect(screen.getAllByText("-1.25%")).toHaveLength(2)
expect(screen.getAllByText("+12.5 亿元")).toHaveLength(2)
}
expect(screen.getAllByText("-1%")).toHaveLength(4)
const trigger = screen.getAllByRole("button", {
name: "机器人展开在榜历史",
})[0]!
fireEvent.click(trigger)
expect(trigger).toHaveAttribute("aria-expanded", "true")
expect(useSectorRadarHistory).toHaveBeenLastCalledWith({
sectorType: "concept",
sectorCode: "BK0001.DC",
tradeDate: "2026-08-28",
})
expect(screen.getByText("暂无在榜历史数据。")).toBeInTheDocument()
fireEvent.click(trigger)
expect(screen.queryByText("暂无在榜历史数据。")).not.toBeInTheDocument()
fireEvent.click(trigger)
useSectorRadarRankings.mockReturnValue(
rankingQueryResult({
data: {
pages: [
{
...ratioPage,
publication: {
...successPublication,
publication_id: "next-day",
},
rows: ratioPage.rows.map((row) => ({
...row,
trade_date: "2026-08-31",
})),
},
rows: ratioPage.rows.map((row) => ({
...row,
trade_date: "2026-08-31",
})),
},
],
pageParams: [1],
},
}),
)
rerender(<SectorRadarPage />)
expect(screen.queryByText("暂无在榜历史数据。")).not.toBeInTheDocument()
expect(
screen.getAllByRole("button", { name: "机器人展开在榜历史" })[0],
).toHaveAttribute("aria-expanded", "false")
})
],
pageParams: [1],
},
}),
)
rerender(<SectorRadarPage />)
expect(screen.queryByText("暂无在榜历史数据。")).not.toBeInTheDocument()
expect(
screen.getAllByRole("button", { name: "机器人展开在榜历史" })[0],
).toHaveAttribute("aria-expanded", "false")
},
)
it.each(["swing", "ratio", "amount", "rank_change"] as const)(
"opens sector details from both sides of the %s list",
async (view) => {
routeSearch = { ...routeSearch, view }
useSectorRadarRankings.mockImplementation((query: { side: string }) =>
rankingQueryResult({
data: {
pageParams: [1],
pages: [
{
...(query.side === "top"
? rankingsResponse
: bottomRankingsResponse),
view,
},
],
},
}),
)
render(<SectorRadarPage />)
expect(screen.queryByText("BK0001.DC")).not.toBeInTheDocument()
for (const [name, code] of [
["机器人", "BK0001.DC"],
["新能源", "BOTTOM-1"],
]) {
const trigger = screen.getByRole("button", { name })
expect(trigger.closest("td")).toHaveTextContent(`1${name}`)
fireEvent.click(trigger)
const dialog = await screen.findByRole("dialog")
expect(
within(dialog).getByRole("heading", { name }),
).toBeInTheDocument()
expect(useSectorRadarDetail).toHaveBeenLastCalledWith({
sectorType: "concept",
sectorCode: code,
tradeDate: "2026-08-28",
})
fireEvent.click(within(dialog).getByRole("button", { name: "Close" }))
}
},
)
it("resets filters to the first batch in router search state", () => {
routeSearch = { ...routeSearch, page: 3 }