refactor: converge semi metric rail

This commit is contained in:
lingniu
2026-07-19 12:32:01 +08:00
parent 1e57fdfff1
commit f825c9f417
3 changed files with 45 additions and 111 deletions

View File

@@ -346,6 +346,10 @@ describe('V2 production entry', () => {
expect(corePageSources.OperationsPage).toContain('<Card className="v2-ops-overview"');
expect(corePageSources.OperationsPage).toContain("from '../shared/WorkspaceMetricRail'");
expect(corePageSources.OperationsPage).toContain('<WorkspaceMetricRail');
expect(workspaceMetricRailSource).toContain("variant: 'queue'");
expect(workspaceMetricRailSource).not.toContain('CardGroup');
expect(workspaceMetricRailSource).not.toContain('primaryValueClassName');
expect(workspaceMetricRailSource).not.toContain('secondaryClassName');
expect(corePageSources.OperationsPage).not.toContain('<OpsMetric');
expect(corePageSources.OperationsPage).toContain('<Card className="v2-ops-panel v2-ops-links"');
expect(corePageSources.OperationsPage).toContain('<article className={`v2-ops-link-card');
@@ -484,9 +488,9 @@ describe('V2 production entry', () => {
expect(corePageSources.StatisticsPage).toContain('<PanelEmpty className="v2-mileage-empty"');
expect(corePageSources.StatisticsPage).not.toContain('<table className="v2-mileage-table"');
expect(corePageSources.StatisticsPage).not.toContain('<input');
expect(workspaceMetricRailSource).toContain("import { Button, Card, CardGroup } from '@douyinfe/semi-ui'");
expect(workspaceMetricRailSource).toContain('<Card className={className}');
expect(workspaceMetricRailSource).toContain('<CardGroup className={secondaryClassName}');
expect(workspaceMetricRailSource).toContain("import { Button, Card } from '@douyinfe/semi-ui'");
expect(workspaceMetricRailSource).not.toContain('CardGroup');
expect(workspaceMetricRailSource).not.toContain('secondaryClassName');
expect(workspaceMetricRailSource).toContain("variant: 'queue'");
expect(workspaceMetricRailSource).toContain('className={`v2-workspace-metric-rail is-queue');
expect(workspaceMetricRailSource).toContain('className="v2-workspace-metric-action"');

View File

@@ -3,28 +3,7 @@ import { describe, expect, it, vi } from 'vitest';
import { WorkspaceMetricRail } from './WorkspaceMetricRail';
describe('WorkspaceMetricRail', () => {
it('renders one primary outcome and supporting facts as accessible Semi cards', () => {
const view = render(<WorkspaceMetricRail
ariaLabel="查询统计"
className="test-rail"
cardClassName="test-card"
secondaryClassName="test-secondary"
primaryValueClassName="test-primary-value"
primary={{ label: '结果行数', value: '1,024', note: '来自 20 辆车辆' }}
secondary={[
{ label: '车辆数', value: '20', note: '当前授权范围', className: 'is-vehicles' },
{ label: '数据来源', value: 'GB32960', note: '全部协议来源', className: 'is-sources' }
]}
/>);
expect(view.container.querySelector('.test-rail.semi-card')).toHaveAttribute('aria-label', '查询统计');
expect(view.container.querySelector('.test-card.is-primary .test-primary-value')).toHaveTextContent('1,024');
expect(view.container.querySelector('.test-secondary.semi-card-group')).toBeInTheDocument();
expect(view.container.querySelectorAll('.test-card.semi-card')).toHaveLength(3);
expect(screen.getByLabelText('车辆数20当前授权范围')).toHaveClass('is-vehicles');
});
it('renders a list-first queue variant inside one Semi card', () => {
it('renders one accessible, list-first Semi metric rail', () => {
const view = render(<WorkspaceMetricRail
variant="queue"
ariaLabel="处置队列概览"

View File

@@ -1,13 +1,6 @@
import { Button, Card, CardGroup } from '@douyinfe/semi-ui';
import { Button, Card } from '@douyinfe/semi-ui';
import type { ReactNode } from 'react';
export type WorkspaceMetricRailItem = {
label: string;
value: ReactNode;
note: string;
className?: string;
};
export type WorkspaceQueueMetricRailItem = {
label: string;
value: ReactNode;
@@ -19,16 +12,6 @@ export type WorkspaceQueueMetricRailItem = {
onClick?: () => void;
};
type WorkspaceSummaryMetricRailProps = {
ariaLabel: string;
primary: WorkspaceMetricRailItem;
secondary: WorkspaceMetricRailItem[];
className: string;
cardClassName: string;
secondaryClassName: string;
primaryValueClassName?: string;
};
type WorkspaceQueueMetricRailProps = {
variant: 'queue';
ariaLabel: string;
@@ -37,8 +20,7 @@ type WorkspaceQueueMetricRailProps = {
context?: ReactNode;
};
export function WorkspaceMetricRail(props: WorkspaceSummaryMetricRailProps | WorkspaceQueueMetricRailProps) {
if ('items' in props) {
export function WorkspaceMetricRail(props: WorkspaceQueueMetricRailProps) {
const hasContext = props.context != null;
return <Card className={`v2-workspace-metric-rail is-queue${hasContext ? ' has-context' : ''} ${props.className}`} bodyStyle={{ padding: 0 }}>
<div className="v2-workspace-metric-list" role="list" aria-label={props.ariaLabel}>
@@ -75,35 +57,4 @@ export function WorkspaceMetricRail(props: WorkspaceSummaryMetricRailProps | Wor
</div>
{hasContext ? <div className="v2-workspace-metric-context">{props.context}</div> : null}
</Card>;
}
const {
ariaLabel,
primary,
secondary,
className,
cardClassName,
secondaryClassName,
primaryValueClassName
} = props;
const cardClass = (item: WorkspaceMetricRailItem, primaryCard = false) => [
cardClassName,
primaryCard ? 'is-primary' : '',
item.className ?? ''
].filter(Boolean).join(' ');
return <Card className={className} aria-label={ariaLabel} bodyStyle={{ padding: 0 }}>
<Card className={cardClass(primary, true)} bodyStyle={{ padding: 0 }} aria-label={`${primary.label}${primary.value}${primary.note}`}>
<small>{primary.label}</small>
<strong className={primaryValueClassName}>{primary.value}</strong>
<span title={primary.note}>{primary.note}</span>
</Card>
<CardGroup className={secondaryClassName} type="grid" spacing={0}>
{secondary.map((item) => <Card className={cardClass(item)} bodyStyle={{ padding: 0 }} aria-label={`${item.label}${item.value}${item.note}`} key={item.label}>
<small>{item.label}</small>
<strong>{item.value}</strong>
<span title={item.note}>{item.note}</span>
</Card>)}
</CardGroup>
</Card>;
}