refactor: share responsive batch actions across admin pages

This commit is contained in:
Cita
2026-03-23 13:12:06 +08:00
parent b82d7c31d9
commit f7a7084278
7 changed files with 124 additions and 96 deletions
@@ -0,0 +1,16 @@
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { describe, expect, it } from 'vitest';
function readPageSource(relativePath: string): string {
return readFileSync(resolve(process.cwd(), relativePath), 'utf8').replace(/\r\n/g, '\n');
}
describe('ResponsiveBatchActionBar page adoption', () => {
it('is used by the repeated admin list pages instead of open-coded mobile and desktop batch wrappers', () => {
expect(readPageSource('src/web/pages/Accounts.tsx')).toContain('ResponsiveBatchActionBar');
expect(readPageSource('src/web/pages/Sites.tsx')).toContain('ResponsiveBatchActionBar');
expect(readPageSource('src/web/pages/Tokens.tsx')).toContain('ResponsiveBatchActionBar');
expect(readPageSource('src/web/pages/DownstreamKeys.tsx')).toContain('ResponsiveBatchActionBar');
});
});
@@ -0,0 +1,30 @@
import React from 'react';
import { describe, expect, it } from 'vitest';
import { create } from 'react-test-renderer';
import ResponsiveBatchActionBar from './ResponsiveBatchActionBar.js';
describe('ResponsiveBatchActionBar', () => {
it('renders the shared mobile batch bar on mobile', () => {
const root = create(
<ResponsiveBatchActionBar isMobile info="已选 2 项">
<button type="button">批量启用</button>
</ResponsiveBatchActionBar>,
);
const bar = root.root.find((node) => node.props.className === 'mobile-actions-bar mobile-batch-bar');
expect(bar).toBeTruthy();
expect(root.root.findByType('button').children).toContain('批量启用');
});
it('renders the desktop card wrapper on desktop', () => {
const root = create(
<ResponsiveBatchActionBar isMobile={false} info="已选 3 项">
<button type="button">批量删除</button>
</ResponsiveBatchActionBar>,
);
const card = root.root.find((node) => node.props.className === 'card');
expect(card).toBeTruthy();
expect(root.root.findAllByType('span').some((node) => node.children.includes('已选 3 项'))).toBe(true);
});
});
@@ -0,0 +1,42 @@
import React from 'react';
import MobileBatchBar from './MobileBatchBar.js';
type ResponsiveBatchActionBarProps = {
isMobile: boolean;
info: React.ReactNode;
children: React.ReactNode;
desktopStyle?: React.CSSProperties;
infoStyle?: React.CSSProperties;
};
const DEFAULT_DESKTOP_STYLE: React.CSSProperties = {
padding: 12,
display: 'flex',
gap: 8,
flexWrap: 'wrap',
alignItems: 'center',
};
const DEFAULT_INFO_STYLE: React.CSSProperties = {
fontSize: 13,
fontWeight: 600,
};
export default function ResponsiveBatchActionBar({
isMobile,
info,
children,
desktopStyle,
infoStyle,
}: ResponsiveBatchActionBarProps) {
if (isMobile) {
return <MobileBatchBar info={info}>{children}</MobileBatchBar>;
}
return (
<div className="card" style={{ ...DEFAULT_DESKTOP_STYLE, ...desktopStyle }}>
<span style={{ ...DEFAULT_INFO_STYLE, ...infoStyle }}>{info}</span>
{children}
</div>
);
}