refactor(selection): update component descriptions and improve layout for stock detail panel
This commit is contained in:
@@ -85,7 +85,7 @@ export function ComponentsPreviewPage() {
|
|||||||
</PreviewCard>
|
</PreviewCard>
|
||||||
|
|
||||||
<PreviewCard
|
<PreviewCard
|
||||||
description="Select 使用 Base UI 的弹层和键盘语义,字段值由页面状态控制。"
|
description="Select 默认在触发器下方展开菜单;字段值由页面状态控制。"
|
||||||
title="Select & Input"
|
title="Select & Input"
|
||||||
>
|
>
|
||||||
<div className="grid gap-4 sm:grid-cols-2">
|
<div className="grid gap-4 sm:grid-cols-2">
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ function FilterSelect({
|
|||||||
<SelectTrigger aria-label={label} className="bg-background">
|
<SelectTrigger aria-label={label} className="bg-background">
|
||||||
<SelectValue placeholder={label} />
|
<SelectValue placeholder={label} />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent align="start" alignItemWithTrigger={false}>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
{options.map((option) => (
|
{options.map((option) => (
|
||||||
<SelectItem key={option.value} value={option.value}>
|
<SelectItem key={option.value} value={option.value}>
|
||||||
|
|||||||
@@ -6,13 +6,6 @@ interface PatternScoreProps {
|
|||||||
score: SelectionPatternScore | null
|
score: SelectionPatternScore | null
|
||||||
}
|
}
|
||||||
|
|
||||||
const breakdownLabels = {
|
|
||||||
trend_structure: "趋势",
|
|
||||||
kdj_state: "KDJ",
|
|
||||||
volume_pattern: "量能",
|
|
||||||
price_shape: "价格形态",
|
|
||||||
} as const
|
|
||||||
|
|
||||||
export function PatternScoreSummary({ score }: PatternScoreProps) {
|
export function PatternScoreSummary({ score }: PatternScoreProps) {
|
||||||
if (!score) return <span className="text-muted-foreground">未评分</span>
|
if (!score) return <span className="text-muted-foreground">未评分</span>
|
||||||
if (score.status === "failed") {
|
if (score.status === "failed") {
|
||||||
@@ -47,34 +40,13 @@ export function PatternScoreDetails({ score }: PatternScoreProps) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="space-y-3">
|
<div className="space-y-2">
|
||||||
<div className="flex items-start justify-between gap-3">
|
<p className="text-xl font-semibold tabular-nums">
|
||||||
<div>
|
{formatScore(score.value)} 分
|
||||||
<p className="text-2xl font-semibold tabular-nums">
|
</p>
|
||||||
{formatScore(score.value)} 分
|
<Badge variant={score.status === "matched" ? "default" : "outline"}>
|
||||||
</p>
|
{score.status === "matched" ? "已匹配" : "低于匹配阈值"}
|
||||||
<p className="text-xs text-muted-foreground">
|
</Badge>
|
||||||
最佳案例:{score.case?.name ?? "未知"}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Badge variant={score.status === "matched" ? "default" : "outline"}>
|
|
||||||
{score.status === "matched" ? "已匹配" : "低于匹配阈值"}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
{score.breakdown ? (
|
|
||||||
<dl className="grid grid-cols-2 gap-2 text-xs">
|
|
||||||
{Object.entries(breakdownLabels).map(([key, label]) => (
|
|
||||||
<div key={key} className="rounded-md border border-border/60 p-2">
|
|
||||||
<dt className="text-muted-foreground">{label}</dt>
|
|
||||||
<dd className="mt-1 font-semibold tabular-nums">
|
|
||||||
{formatScore(
|
|
||||||
score.breakdown?.[key as keyof typeof breakdownLabels],
|
|
||||||
)}
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</dl>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import {
|
|||||||
import * as echarts from "echarts/core"
|
import * as echarts from "echarts/core"
|
||||||
import { CanvasRenderer } from "echarts/renderers"
|
import { CanvasRenderer } from "echarts/renderers"
|
||||||
|
|
||||||
|
import { cn } from "@/shared/ui/utils"
|
||||||
|
|
||||||
import type { SelectionChart as SelectionChartData } from "../api/selection.types"
|
import type { SelectionChart as SelectionChartData } from "../api/selection.types"
|
||||||
import { buildSelectionChartOption } from "./selection-chart-option"
|
import { buildSelectionChartOption } from "./selection-chart-option"
|
||||||
|
|
||||||
@@ -29,9 +31,10 @@ echarts.use([
|
|||||||
|
|
||||||
interface SelectionChartProps {
|
interface SelectionChartProps {
|
||||||
chart: SelectionChartData
|
chart: SelectionChartData
|
||||||
|
className?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SelectionChart({ chart }: SelectionChartProps) {
|
export function SelectionChart({ chart, className }: SelectionChartProps) {
|
||||||
const containerRef = useRef<HTMLDivElement>(null)
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
const instanceRef = useRef<echarts.EChartsType | null>(null)
|
const instanceRef = useRef<echarts.EChartsType | null>(null)
|
||||||
|
|
||||||
@@ -62,7 +65,7 @@ export function SelectionChart({ chart }: SelectionChartProps) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
aria-label={`${chart.name} 日线、知行黄白线、成交量和 KDJ 图表`}
|
aria-label={`${chart.name} 日线、知行黄白线、成交量和 KDJ 图表`}
|
||||||
className="h-[620px] min-h-[520px] w-full"
|
className={cn("h-full min-h-70 w-full", className)}
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
role="img"
|
role="img"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -86,16 +86,44 @@ export function getSignalDetailItems(details: SelectionSignal["details"]) {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const stockBasicMetricKeys = [
|
||||||
|
"j",
|
||||||
|
"rsi",
|
||||||
|
"daily_change",
|
||||||
|
"daily_amplitude",
|
||||||
|
] as const
|
||||||
|
|
||||||
export function getSignalJValue(details: SelectionSignal["details"]) {
|
export function getSignalJValue(details: SelectionSignal["details"]) {
|
||||||
return formatSignalDetailValue("j", details.j)
|
return formatSignalDetailValue("j", details.j)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getStockJValue(stock: SelectionStockResult) {
|
export function getStockJValue(stock: SelectionStockResult) {
|
||||||
const signal = stock.signals.find(
|
return getStockDetailValue(stock, "j")
|
||||||
({ details }) =>
|
}
|
||||||
details.j !== null && details.j !== undefined && details.j !== "",
|
|
||||||
|
/**
|
||||||
|
* Read one stock-level metric from the first signal that carries it.
|
||||||
|
*
|
||||||
|
* Zhixing B1 writes the same target-row metrics onto every matched category,
|
||||||
|
* so the detail pane must not group these values by signal.
|
||||||
|
*/
|
||||||
|
export function getStockDetailValue(stock: SelectionStockResult, key: string) {
|
||||||
|
const signal = stock.signals.find(({ details }) =>
|
||||||
|
hasDetailValue(details[key]),
|
||||||
)
|
)
|
||||||
return getSignalJValue(signal?.details ?? {})
|
return formatSignalDetailValue(key, signal?.details[key])
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStockBasicMetrics(stock: SelectionStockResult) {
|
||||||
|
return stockBasicMetricKeys.map((key) => ({
|
||||||
|
key,
|
||||||
|
label: detailLabels[key],
|
||||||
|
value: getStockDetailValue(stock, key),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasDetailValue(value: number | string | null | undefined) {
|
||||||
|
return value !== null && value !== undefined && value !== ""
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatSignalDetailValue(
|
function formatSignalDetailValue(
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ export function SelectionResultsWorkbench({
|
|||||||
>
|
>
|
||||||
<SelectValue placeholder="筛选信号分类" />
|
<SelectValue placeholder="筛选信号分类" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent align="start" alignItemWithTrigger={false}>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
{signalCategoryOptions.map((option) => (
|
{signalCategoryOptions.map((option) => (
|
||||||
<SelectItem key={option.value} value={option.value}>
|
<SelectItem key={option.value} value={option.value}>
|
||||||
@@ -156,7 +156,7 @@ export function SelectionResultsWorkbench({
|
|||||||
>
|
>
|
||||||
<SelectValue placeholder="排序命中股票" />
|
<SelectValue placeholder="排序命中股票" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent align="start" alignItemWithTrigger={false}>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
{SCORE_SORT_OPTIONS.map((option) => (
|
{SCORE_SORT_OPTIONS.map((option) => (
|
||||||
<SelectItem key={option.value} value={option.value}>
|
<SelectItem key={option.value} value={option.value}>
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
import { lazy, Suspense } from "react"
|
import { lazy, Suspense } from "react"
|
||||||
import { Database, Search } from "lucide-react"
|
import { Search } from "lucide-react"
|
||||||
import { useSelectionChart } from "../api/selection.query"
|
import { useSelectionChart } from "../api/selection.query"
|
||||||
|
|
||||||
import { Badge } from "@/shared/ui/badge"
|
|
||||||
import { Card } from "@/shared/ui/card"
|
import { Card } from "@/shared/ui/card"
|
||||||
|
|
||||||
import type { SelectionStockResult } from "../api/selection.types"
|
import type { SelectionStockResult } from "../api/selection.types"
|
||||||
import { PatternScoreDetails } from "./pattern-score"
|
import { PatternScoreDetails } from "./pattern-score"
|
||||||
import { categoryToneClass, getCategoryLabel } from "./selection-presentation"
|
|
||||||
import { SignalDetails } from "./signal-details"
|
|
||||||
import { PatternCaseImage } from "./pattern-case-image"
|
import { PatternCaseImage } from "./pattern-case-image"
|
||||||
|
import { getStockBasicMetrics } from "./selection-presentation"
|
||||||
|
|
||||||
const SelectionChart = lazy(() =>
|
const SelectionChart = lazy(() =>
|
||||||
import("./selection-chart").then((module) => ({
|
import("./selection-chart").then((module) => ({
|
||||||
@@ -44,111 +42,95 @@ export function SignalDetailPanel({ stock }: SignalDetailPanelProps) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const metricRows = [
|
||||||
|
...getStockBasicMetrics(stock),
|
||||||
|
{
|
||||||
|
key: "target_trade_date",
|
||||||
|
label: "目标交易日",
|
||||||
|
value: stock.target_trade_date,
|
||||||
|
},
|
||||||
|
{ key: "strategy", label: "策略", value: "知行 B1" },
|
||||||
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside
|
<aside
|
||||||
aria-label="当前股票详情"
|
aria-label="当前股票详情"
|
||||||
className="hidden min-h-0 overflow-auto rounded-md border border-border/80 bg-card p-3 md:block"
|
className="hidden min-h-0 overflow-hidden rounded-md border border-border/80 bg-card md:flex"
|
||||||
>
|
>
|
||||||
<p className="text-[10px] font-bold tracking-[0.08em] text-muted-foreground uppercase">
|
<section
|
||||||
Selected stock
|
aria-labelledby="technical-chart"
|
||||||
</p>
|
className="flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden p-3"
|
||||||
<div className="mt-2 flex items-start justify-between gap-3">
|
>
|
||||||
<div className="min-w-0">
|
|
||||||
<h2 className="truncate text-xl font-semibold tracking-tight">
|
|
||||||
{stock.name}
|
|
||||||
</h2>
|
|
||||||
<p className="font-mono text-xs text-muted-foreground">
|
|
||||||
{stock.ts_code}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Database
|
|
||||||
className="mt-1 size-4 shrink-0 text-muted-foreground"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="mt-4 text-3xl font-semibold tabular-nums tracking-tight">
|
|
||||||
¥ {stock.close.toFixed(2)}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="mt-3 flex flex-wrap gap-1">
|
|
||||||
{stock.signals.map((signal) => (
|
|
||||||
<Badge
|
|
||||||
key={signal.category}
|
|
||||||
className={categoryToneClass(signal.category)}
|
|
||||||
variant="outline"
|
|
||||||
>
|
|
||||||
{getCategoryLabel(signal.category)}
|
|
||||||
</Badge>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<dl className="mt-4 divide-y divide-border/60 border-y border-border/60 text-xs">
|
|
||||||
<div className="flex items-center justify-between gap-3 py-2.5">
|
|
||||||
<dt className="text-muted-foreground">目标交易日</dt>
|
|
||||||
<dd className="font-medium tabular-nums">
|
|
||||||
{stock.target_trade_date}
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between gap-3 py-2.5">
|
|
||||||
<dt className="text-muted-foreground">策略</dt>
|
|
||||||
<dd className="font-medium">知行 B1</dd>
|
|
||||||
</div>
|
|
||||||
</dl>
|
|
||||||
|
|
||||||
<section className="mt-4" aria-labelledby="pattern-score-details">
|
|
||||||
<h3
|
<h3
|
||||||
className="mb-2 text-xs font-medium text-muted-foreground"
|
className="mb-2 shrink-0 text-xs font-medium text-muted-foreground"
|
||||||
id="pattern-score-details"
|
|
||||||
>
|
|
||||||
图形相似度评分
|
|
||||||
</h3>
|
|
||||||
<PatternScoreDetails score={stock.score} />
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mt-4" aria-labelledby="technical-chart">
|
|
||||||
<h3
|
|
||||||
className="mb-2 text-xs font-medium text-muted-foreground"
|
|
||||||
id="technical-chart"
|
id="technical-chart"
|
||||||
>
|
>
|
||||||
技术图表
|
技术图表
|
||||||
</h3>
|
</h3>
|
||||||
{chartQuery.isPending ? (
|
<div className="min-h-0 flex-1">
|
||||||
<p className="text-xs text-muted-foreground">正在加载行情图表…</p>
|
{chartQuery.isPending ? (
|
||||||
) : chartQuery.isError ? (
|
<p className="text-xs text-muted-foreground">正在加载行情图表…</p>
|
||||||
<p className="text-xs text-destructive">行情图表暂时不可用。</p>
|
) : chartQuery.isError ? (
|
||||||
) : chartQuery.data?.points.length ? (
|
<p className="text-xs text-destructive">行情图表暂时不可用。</p>
|
||||||
<Suspense
|
) : chartQuery.data?.points.length ? (
|
||||||
fallback={
|
<Suspense
|
||||||
<p className="text-xs text-muted-foreground">正在加载图表组件…</p>
|
fallback={
|
||||||
}
|
<p className="text-xs text-muted-foreground">
|
||||||
>
|
正在加载图表组件…
|
||||||
<SelectionChart chart={chartQuery.data} />
|
</p>
|
||||||
</Suspense>
|
}
|
||||||
) : (
|
>
|
||||||
<p className="text-xs text-muted-foreground">暂无可用行情数据。</p>
|
<SelectionChart chart={chartQuery.data} />
|
||||||
)}
|
</Suspense>
|
||||||
|
) : (
|
||||||
|
<p className="text-xs text-muted-foreground">暂无可用行情数据。</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="shrink-0">
|
||||||
|
<PatternCaseImage score={stock.score} />
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<PatternCaseImage score={stock.score} />
|
<section
|
||||||
|
aria-labelledby="selected-stock-heading"
|
||||||
<section className="mt-4" aria-labelledby="signal-detail-metrics">
|
className="flex w-50 shrink-0 flex-col overflow-auto border-l border-border/60 p-3"
|
||||||
<h3
|
>
|
||||||
className="mb-2 text-xs font-medium text-muted-foreground"
|
<h2
|
||||||
id="signal-detail-metrics"
|
className="truncate text-base font-semibold tracking-tight"
|
||||||
|
id="selected-stock-heading"
|
||||||
>
|
>
|
||||||
关键指标
|
{stock.name}
|
||||||
</h3>
|
</h2>
|
||||||
<div className="space-y-4">
|
<p className="font-mono text-xs text-muted-foreground">
|
||||||
{stock.signals.map((signal) => (
|
{stock.ts_code}
|
||||||
<section key={signal.category} className="space-y-2">
|
</p>
|
||||||
<h4 className="text-xs font-medium text-foreground">
|
<p className="mt-3 text-2xl font-semibold tabular-nums tracking-tight">
|
||||||
{getCategoryLabel(signal.category)}
|
¥ {stock.close.toFixed(2)}
|
||||||
</h4>
|
</p>
|
||||||
<SignalDetails details={signal.details} />
|
|
||||||
</section>
|
<dl className="mt-3 divide-y divide-border/60 border-y border-border/60 text-xs">
|
||||||
|
{metricRows.map((row) => (
|
||||||
|
<div
|
||||||
|
key={row.key}
|
||||||
|
className="flex items-center justify-between gap-2 py-2"
|
||||||
|
>
|
||||||
|
<dt className="shrink-0 text-muted-foreground">{row.label}</dt>
|
||||||
|
<dd className="min-w-0 truncate text-right font-medium tabular-nums">
|
||||||
|
{row.value}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</dl>
|
||||||
|
|
||||||
|
<section className="mt-3" aria-labelledby="pattern-score-details">
|
||||||
|
<h3
|
||||||
|
className="mb-2 text-xs font-medium text-muted-foreground"
|
||||||
|
id="pattern-score-details"
|
||||||
|
>
|
||||||
|
图形相似度评分
|
||||||
|
</h3>
|
||||||
|
<PatternScoreDetails score={stock.score} />
|
||||||
|
</section>
|
||||||
</section>
|
</section>
|
||||||
</aside>
|
</aside>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -90,7 +90,12 @@ const selectedResult: SelectionResults = {
|
|||||||
{
|
{
|
||||||
category: "zhixing_b1_original_b1",
|
category: "zhixing_b1_original_b1",
|
||||||
close: 10.5,
|
close: 10.5,
|
||||||
details: { j: 12, rsi: 20 },
|
details: {
|
||||||
|
daily_amplitude: 3.4,
|
||||||
|
daily_change: 1.2,
|
||||||
|
j: 12,
|
||||||
|
rsi: 20,
|
||||||
|
},
|
||||||
name: "平安银行",
|
name: "平安银行",
|
||||||
strategy: "zhixing_b1",
|
strategy: "zhixing_b1",
|
||||||
target_trade_date: "2026-08-08",
|
target_trade_date: "2026-08-08",
|
||||||
@@ -199,17 +204,22 @@ describe("SelectionResultsPage", () => {
|
|||||||
const detailPanel = screen.getByRole("complementary", {
|
const detailPanel = screen.getByRole("complementary", {
|
||||||
name: "当前股票详情",
|
name: "当前股票详情",
|
||||||
})
|
})
|
||||||
expect(within(detailPanel).getByText("关键指标")).toBeInTheDocument()
|
expect(within(detailPanel).queryByText("关键指标")).not.toBeInTheDocument()
|
||||||
expect(within(detailPanel).getByText("图形相似度评分")).toBeInTheDocument()
|
expect(within(detailPanel).getByText("图形相似度评分")).toBeInTheDocument()
|
||||||
expect(
|
expect(
|
||||||
within(detailPanel).getByText("暂无可用行情数据。"),
|
within(detailPanel).getByText("暂无可用行情数据。"),
|
||||||
).toBeInTheDocument()
|
).toBeInTheDocument()
|
||||||
expect(
|
expect(
|
||||||
within(detailPanel).getByText("最佳案例:华纳药厂"),
|
within(detailPanel).getByText("最接近案例:华纳药厂"),
|
||||||
).toBeInTheDocument()
|
).toBeInTheDocument()
|
||||||
expect(within(detailPanel).getByText("价格形态")).toBeInTheDocument()
|
expect(within(detailPanel).queryByText("价格形态")).not.toBeInTheDocument()
|
||||||
expect(within(detailPanel).getAllByText("J 值")).toHaveLength(2)
|
expect(within(detailPanel).getByText("J 值")).toBeInTheDocument()
|
||||||
expect(within(detailPanel).getByText("RSI")).toBeInTheDocument()
|
expect(within(detailPanel).getByText("RSI")).toBeInTheDocument()
|
||||||
|
expect(within(detailPanel).getByText("日涨跌幅")).toBeInTheDocument()
|
||||||
|
expect(within(detailPanel).getByText("1.2%")).toBeInTheDocument()
|
||||||
|
expect(within(detailPanel).getByText("日振幅")).toBeInTheDocument()
|
||||||
|
expect(within(detailPanel).getByText("3.4%")).toBeInTheDocument()
|
||||||
|
expect(within(detailPanel).getByText("20")).toBeInTheDocument()
|
||||||
const caseImages = within(detailPanel).getAllByRole("img", {
|
const caseImages = within(detailPanel).getAllByRole("img", {
|
||||||
name: "华纳药厂 B1 参考案例图",
|
name: "华纳药厂 B1 参考案例图",
|
||||||
})
|
})
|
||||||
@@ -590,8 +600,10 @@ describe("SelectionResultsPage", () => {
|
|||||||
name: "当前股票详情",
|
name: "当前股票详情",
|
||||||
})
|
})
|
||||||
expect(within(detailPanel).getByText("招商银行")).toBeInTheDocument()
|
expect(within(detailPanel).getByText("招商银行")).toBeInTheDocument()
|
||||||
expect(within(detailPanel).getByText("MACD")).toBeInTheDocument()
|
expect(within(detailPanel).queryByText("MACD")).not.toBeInTheDocument()
|
||||||
expect(within(detailPanel).getByText("3.2")).toBeInTheDocument()
|
expect(within(detailPanel).queryByText("3.2")).not.toBeInTheDocument()
|
||||||
|
expect(within(detailPanel).getByText("J 值")).toBeInTheDocument()
|
||||||
|
expect(within(detailPanel).getByText("RSI")).toBeInTheDocument()
|
||||||
expect(
|
expect(
|
||||||
screen.queryByRole("button", { name: "查看详情" }),
|
screen.queryByRole("button", { name: "查看详情" }),
|
||||||
).not.toBeInTheDocument()
|
).not.toBeInTheDocument()
|
||||||
|
|||||||
@@ -75,8 +75,10 @@ function SelectValue({ className, ...props }: SelectPrimitive.Value.Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function SelectContent({
|
function SelectContent({
|
||||||
align = "center",
|
// Project default: conventional dropdown below the trigger. Call sites should
|
||||||
alignItemWithTrigger = true,
|
// not re-declare align / alignItemWithTrigger for ordinary form and filter use.
|
||||||
|
align = "start",
|
||||||
|
alignItemWithTrigger = false,
|
||||||
alignOffset = 0,
|
alignOffset = 0,
|
||||||
className,
|
className,
|
||||||
children,
|
children,
|
||||||
|
|||||||
Reference in New Issue
Block a user