refactor: share responsive batch actions across admin pages
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user