继续扩展共享异步状态壳
将 PlatformAsyncStatePanel 扩展到公共素材选择网格 将 PlatformAsyncStatePanel 扩展到视觉小说存档面板与账号安全子区块 将兑换码弹窗提交动作改为使用标准 profile modal footer 补充对应测试并更新 PlatformUiKit 收口计划与共享决策记录
This commit is contained in:
@@ -187,6 +187,26 @@ test('renders selectable asset grid cards with shared error chrome', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('asset grid keeps error banner while loading state remains mutually exclusive with empty state', () => {
|
||||
render(
|
||||
<PlatformAssetPickerGrid
|
||||
items={[]}
|
||||
isLoading
|
||||
error="历史素材读取失败。"
|
||||
loadingLabel="读取中..."
|
||||
emptyLabel="暂无历史素材"
|
||||
getKey={(item: { id: string }) => item.id}
|
||||
getImageSrc={(item) => item.id}
|
||||
getImageAlt={() => ''}
|
||||
onSelect={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText('历史素材读取失败。')).toBeTruthy();
|
||||
expect(screen.getByText('读取中...')).toBeTruthy();
|
||||
expect(screen.queryByText('暂无历史素材')).toBeNull();
|
||||
});
|
||||
|
||||
test('supports dark editor surface with an in-card select affordance', () => {
|
||||
const onSelect = vi.fn();
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { ButtonHTMLAttributes, Key, ReactNode } from 'react';
|
||||
|
||||
import { ResolvedAssetImage } from '../ResolvedAssetImage';
|
||||
import { PlatformAsyncStatePanel } from './PlatformAsyncStatePanel';
|
||||
import { PlatformEmptyState } from './PlatformEmptyState';
|
||||
import { PlatformStatusMessage } from './PlatformStatusMessage';
|
||||
|
||||
@@ -250,6 +251,13 @@ export function PlatformAssetPickerGrid<TItem>({
|
||||
imageClassName,
|
||||
bodyClassName,
|
||||
}: PlatformAssetPickerGridProps<TItem>) {
|
||||
const sharedEmptyStateClassName = [
|
||||
PLATFORM_ASSET_PICKER_GRID_EMPTY_CLASS[surface],
|
||||
emptyClassName,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ');
|
||||
|
||||
return (
|
||||
<>
|
||||
{error ? (
|
||||
@@ -262,61 +270,53 @@ export function PlatformAssetPickerGrid<TItem>({
|
||||
{error}
|
||||
</PlatformStatusMessage>
|
||||
) : null}
|
||||
|
||||
{isLoading ? (
|
||||
<PlatformEmptyState
|
||||
surface="dashed"
|
||||
size="panel"
|
||||
className={[
|
||||
PLATFORM_ASSET_PICKER_GRID_EMPTY_CLASS[surface],
|
||||
emptyClassName,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
{loadingLabel}
|
||||
</PlatformEmptyState>
|
||||
) : null}
|
||||
|
||||
{!isLoading && !error && items.length <= 0 ? (
|
||||
<PlatformEmptyState
|
||||
surface="dashed"
|
||||
size="panel"
|
||||
className={[
|
||||
PLATFORM_ASSET_PICKER_GRID_EMPTY_CLASS[surface],
|
||||
emptyClassName,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
{emptyLabel}
|
||||
</PlatformEmptyState>
|
||||
) : null}
|
||||
|
||||
{!isLoading && items.length > 0 ? (
|
||||
<div className={gridClassName}>
|
||||
{items.map((item) => (
|
||||
<PlatformAssetPickerCard
|
||||
key={getKey(item)}
|
||||
disabled={disabled}
|
||||
aria-label={getAriaLabel?.(item)}
|
||||
onClick={() => onSelect(item)}
|
||||
imageSrc={getImageSrc(item)}
|
||||
imageAlt={getImageAlt(item)}
|
||||
assetTitle={getTitle?.(item)}
|
||||
subtitle={getSubtitle?.(item)}
|
||||
surface={surface}
|
||||
selectLabel={selectLabel}
|
||||
selected={isSelected?.(item) ?? false}
|
||||
className={cardClassName}
|
||||
cardRadiusClassName={cardRadiusClassName}
|
||||
imageShellClassName={imageShellClassName}
|
||||
imageClassName={imageClassName}
|
||||
bodyClassName={bodyClassName}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
<PlatformAsyncStatePanel
|
||||
isLoading={isLoading}
|
||||
loadingState={
|
||||
<PlatformEmptyState
|
||||
surface="dashed"
|
||||
size="panel"
|
||||
className={sharedEmptyStateClassName}
|
||||
>
|
||||
{loadingLabel}
|
||||
</PlatformEmptyState>
|
||||
}
|
||||
isEmpty={items.length <= 0}
|
||||
emptyState={
|
||||
<PlatformEmptyState
|
||||
surface="dashed"
|
||||
size="panel"
|
||||
className={sharedEmptyStateClassName}
|
||||
>
|
||||
{emptyLabel}
|
||||
</PlatformEmptyState>
|
||||
}
|
||||
>
|
||||
{items.length > 0 ? (
|
||||
<div className={gridClassName}>
|
||||
{items.map((item) => (
|
||||
<PlatformAssetPickerCard
|
||||
key={getKey(item)}
|
||||
disabled={disabled}
|
||||
aria-label={getAriaLabel?.(item)}
|
||||
onClick={() => onSelect(item)}
|
||||
imageSrc={getImageSrc(item)}
|
||||
imageAlt={getImageAlt(item)}
|
||||
assetTitle={getTitle?.(item)}
|
||||
subtitle={getSubtitle?.(item)}
|
||||
surface={surface}
|
||||
selectLabel={selectLabel}
|
||||
selected={isSelected?.(item) ?? false}
|
||||
className={cardClassName}
|
||||
cardRadiusClassName={cardRadiusClassName}
|
||||
imageShellClassName={imageShellClassName}
|
||||
imageClassName={imageClassName}
|
||||
bodyClassName={bodyClassName}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</PlatformAsyncStatePanel>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user