继续收口生成页顶部返回按钮
抽出生成页共用返回按钮壳并复用共享图标按钮能力 将自定义世界生成页接入共享返回按钮壳 将汪汪声浪生成页接入共享返回按钮壳并保留禁用态 补充两个生成页返回按钮的样式与交互回归测试
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
/* @vitest-environment jsdom */
|
||||
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { describe, expect, test, vi } from 'vitest';
|
||||
|
||||
import type { CustomWorldGenerationProgress } from '../../packages/shared/src/contracts/runtime';
|
||||
import { CustomWorldGenerationView } from './CustomWorldGenerationView';
|
||||
@@ -104,6 +105,12 @@ describe('CustomWorldGenerationView', () => {
|
||||
expect(
|
||||
screen.getByRole('button', { name: '返回创作中心' }).className,
|
||||
).toContain('text-xs');
|
||||
expect(
|
||||
screen.getByRole('button', { name: '返回创作中心' }).className,
|
||||
).toContain('bg-transparent');
|
||||
expect(
|
||||
screen.getByRole('button', { name: '返回创作中心' }).className,
|
||||
).toContain('gap-2');
|
||||
expect(screen.getByText('世界建设中')).toBeTruthy();
|
||||
expect(screen.getByText('世界建设中').className).toContain('text-xs');
|
||||
expect(screen.getByText('世界建设中').className).toContain(
|
||||
@@ -289,4 +296,29 @@ describe('CustomWorldGenerationView', () => {
|
||||
expect(screen.queryByText('大鱼吃小鱼题材')).toBeNull();
|
||||
expect(screen.getByTestId('generation-page-background-video')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('keeps the shared generation back button click behavior', async () => {
|
||||
const user = userEvent.setup();
|
||||
const onBack = vi.fn();
|
||||
|
||||
render(
|
||||
<CustomWorldGenerationView
|
||||
settingText="大鱼吃小鱼题材"
|
||||
progress={createProgress()}
|
||||
isGenerating
|
||||
error={null}
|
||||
onBack={onBack}
|
||||
onEditSetting={() => {}}
|
||||
onRetry={() => {}}
|
||||
backLabel="返回创作中心"
|
||||
settingDescription={null}
|
||||
settingActionLabel={null}
|
||||
progressTitle="大鱼吃小鱼草稿生成进度"
|
||||
/>,
|
||||
);
|
||||
|
||||
await user.click(screen.getByRole('button', { name: '返回创作中心' }));
|
||||
|
||||
expect(onBack).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { ArrowLeft } from 'lucide-react';
|
||||
|
||||
import type { CustomWorldGenerationProgress } from '../../packages/shared/src/contracts/runtime';
|
||||
import type { CustomWorldStructuredAnchorEntry } from '../services/customWorldAgentGenerationProgress';
|
||||
import { PlatformActionButton } from './common/PlatformActionButton';
|
||||
import { PlatformPillBadge } from './common/PlatformPillBadge';
|
||||
import {
|
||||
GenerationCurrentStepCard,
|
||||
GenerationHeaderBackButton,
|
||||
GenerationPageBackdrop,
|
||||
GenerationProgressHero,
|
||||
} from './GenerationProgressHero';
|
||||
@@ -137,14 +136,7 @@ export function CustomWorldGenerationView({
|
||||
<div className="relative isolate z-[1] -mx-3 -my-3 flex h-[calc(100%+1.5rem)] min-h-0 flex-col overflow-hidden bg-transparent px-4 pb-[max(1.25rem,env(safe-area-inset-bottom))] pt-4 text-[#3d1f10] sm:mx-0 sm:my-0 sm:h-full sm:rounded-[2rem] sm:px-5 sm:pt-5">
|
||||
<GenerationPageBackdrop />
|
||||
<div className="relative z-30 mb-4 flex shrink-0 items-center justify-between gap-3 py-2 sm:mb-5">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onBack}
|
||||
className="inline-flex items-center gap-2 rounded-full bg-transparent px-0 py-2 text-xs font-black text-[#171411] sm:text-sm"
|
||||
>
|
||||
<ArrowLeft className="h-5 w-5 shrink-0" strokeWidth={2.6} />
|
||||
<span className="break-keep">{backLabel}</span>
|
||||
</button>
|
||||
<GenerationHeaderBackButton label={backLabel} onClick={onBack} />
|
||||
<PlatformPillBadge
|
||||
tone="warning"
|
||||
size="xs"
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Clock3, Hourglass } from 'lucide-react';
|
||||
import { ArrowLeft, Clock3, Hourglass } from 'lucide-react';
|
||||
import { useEffect, useId, useRef } from 'react';
|
||||
|
||||
import generationHeroVideo from '../../media/create_bg_video.mp4';
|
||||
import { PlatformIconButton } from './common/PlatformIconButton';
|
||||
import { PlatformProgressBar } from './common/PlatformProgressBar';
|
||||
|
||||
const GENERATION_PROGRESS_RING_GAP_DEGREES = 90;
|
||||
@@ -35,6 +36,14 @@ type GenerationCurrentStepCardProps = {
|
||||
progressValue: number;
|
||||
};
|
||||
|
||||
type GenerationHeaderBackButtonProps = {
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
disabled?: boolean;
|
||||
disabledOpacity?: number;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function clampGenerationProgress(value: number) {
|
||||
return Math.max(0, Math.min(100, Math.round(value)));
|
||||
}
|
||||
@@ -51,6 +60,34 @@ function buildGenerationRingMetrics(progressValue: number) {
|
||||
};
|
||||
}
|
||||
|
||||
export function GenerationHeaderBackButton({
|
||||
label,
|
||||
onClick,
|
||||
disabled = false,
|
||||
disabledOpacity,
|
||||
className,
|
||||
}: GenerationHeaderBackButtonProps) {
|
||||
return (
|
||||
<PlatformIconButton
|
||||
label={label}
|
||||
title={label}
|
||||
variant="darkMini"
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
className={[
|
||||
'gap-2 rounded-full !border-transparent !bg-transparent px-0 py-2 text-xs font-black !text-[#171411] shadow-none hover:!bg-transparent hover:!text-[#171411] sm:text-sm',
|
||||
className,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
style={disabled && disabledOpacity != null ? { opacity: disabledOpacity } : undefined}
|
||||
icon={<ArrowLeft className="h-5 w-5 shrink-0" strokeWidth={2.6} />}
|
||||
>
|
||||
<span className="break-keep">{label}</span>
|
||||
</PlatformIconButton>
|
||||
);
|
||||
}
|
||||
|
||||
export function GenerationPageBackdrop() {
|
||||
const videoRef = useRef<HTMLVideoElement | null>(null);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* @vitest-environment jsdom */
|
||||
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
@@ -87,6 +88,12 @@ describe('BarkBattleGeneratingView', () => {
|
||||
expect(
|
||||
screen.getByRole('button', { name: '返回编辑' }).className,
|
||||
).toContain('text-xs');
|
||||
expect(
|
||||
screen.getByRole('button', { name: '返回编辑' }).className,
|
||||
).toContain('bg-transparent');
|
||||
expect(
|
||||
screen.getByRole('button', { name: '返回编辑' }).className,
|
||||
).toContain('gap-2');
|
||||
expect(screen.getByText('生成中').className).toContain('text-[11px]');
|
||||
expect(screen.getByText('生成中').className).toContain(
|
||||
'border-[var(--platform-warm-border)]',
|
||||
@@ -479,4 +486,42 @@ describe('BarkBattleGeneratingView', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps the shared generation back button disabled state and click behavior', async () => {
|
||||
const user = userEvent.setup();
|
||||
const onBack = vi.fn();
|
||||
vi.mocked(generateAllBarkBattleImageAssets).mockReturnValue(
|
||||
new Promise<BarkBattleImageGenerationBatchResult>(() => {}),
|
||||
);
|
||||
|
||||
const { rerender } = render(
|
||||
<BarkBattleGeneratingView
|
||||
draft={draft}
|
||||
isBusy
|
||||
onBack={onBack}
|
||||
onComplete={() => {}}
|
||||
onError={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
const busyBackButton = screen.getByRole('button', { name: '返回编辑' });
|
||||
expect(busyBackButton.getAttribute('disabled')).toBe('');
|
||||
expect(busyBackButton.style.opacity).toBe('0.45');
|
||||
|
||||
await user.click(busyBackButton);
|
||||
expect(onBack).not.toHaveBeenCalled();
|
||||
|
||||
rerender(
|
||||
<BarkBattleGeneratingView
|
||||
draft={draft}
|
||||
isBusy={false}
|
||||
onBack={onBack}
|
||||
onComplete={() => {}}
|
||||
onError={() => {}}
|
||||
/>,
|
||||
);
|
||||
|
||||
await user.click(screen.getByRole('button', { name: '返回编辑' }));
|
||||
expect(onBack).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ArrowLeft } from 'lucide-react';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import type { BarkBattleDraftConfig } from '../../../packages/shared/src/contracts/barkBattle';
|
||||
@@ -15,6 +14,7 @@ import {
|
||||
import { PlatformPillBadge } from '../common/PlatformPillBadge';
|
||||
import {
|
||||
GenerationCurrentStepCard,
|
||||
GenerationHeaderBackButton,
|
||||
GenerationPageBackdrop,
|
||||
GenerationProgressHero,
|
||||
} from '../GenerationProgressHero';
|
||||
@@ -367,15 +367,12 @@ export function BarkBattleGeneratingView({
|
||||
<div className="relative isolate z-[1] -mx-3 -my-3 flex h-[calc(100%+1.5rem)] min-h-0 flex-col overflow-hidden bg-transparent px-4 pb-[max(1.25rem,env(safe-area-inset-bottom))] pt-4 text-[#3d1f10] sm:mx-0 sm:my-0 sm:h-full sm:rounded-[2rem] sm:px-5 sm:pt-5 xl:px-6 xl:pb-5 xl:pt-5">
|
||||
<GenerationPageBackdrop />
|
||||
<div className="relative z-30 mx-auto mb-4 flex w-full max-w-[48rem] shrink-0 items-center justify-between gap-3 sm:mb-5">
|
||||
<button
|
||||
type="button"
|
||||
<GenerationHeaderBackButton
|
||||
label="返回编辑"
|
||||
onClick={onBack}
|
||||
disabled={isBusy}
|
||||
className={`inline-flex items-center gap-2 rounded-full bg-transparent px-0 py-2 text-xs font-black text-[#171411] sm:text-sm ${isBusy ? 'opacity-45' : ''}`}
|
||||
>
|
||||
<ArrowLeft className="h-5 w-5" strokeWidth={2.6} />
|
||||
<span className="break-keep">返回编辑</span>
|
||||
</button>
|
||||
disabledOpacity={0.45}
|
||||
/>
|
||||
<PlatformPillBadge
|
||||
tone="warning"
|
||||
size="xs"
|
||||
|
||||
Reference in New Issue
Block a user