Skip to content

Commit c0622aa

Browse files
Merge pull request #170 from CSSLab/feature/overhaul-deep-analysis-modal
Change deep analysis from modal to notification + fix Stockfish mate case
2 parents 9f03521 + 012cea0 commit c0622aa

File tree

10 files changed

+300
-274
lines changed

10 files changed

+300
-274
lines changed

__tests__/components/Analysis/AnalyzeEntireGame.test.tsx

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react'
22
import { render, screen } from '@testing-library/react'
33
import { AnalysisConfigModal } from 'src/components/Analysis/AnalysisConfigModal'
4-
import { AnalysisProgressOverlay } from 'src/components/Analysis/AnalysisProgressOverlay'
4+
import { AnalysisNotification } from 'src/components/Analysis/AnalysisNotification'
55
import '@testing-library/jest-dom'
66

77
// Mock framer-motion to avoid animation issues in tests
@@ -54,7 +54,7 @@ describe('Analyze Entire Game Components', () => {
5454
})
5555
})
5656

57-
describe('AnalysisProgressOverlay', () => {
57+
describe('AnalysisNotification', () => {
5858
const mockProgress = {
5959
currentMoveIndex: 5,
6060
totalMoves: 20,
@@ -69,28 +69,26 @@ describe('Analyze Entire Game Components', () => {
6969
onCancel: jest.fn(),
7070
}
7171

72-
it('renders progress overlay when analyzing', () => {
73-
render(<AnalysisProgressOverlay {...defaultProps} />)
72+
it('renders notification when analyzing', () => {
73+
render(<AnalysisNotification {...defaultProps} />)
7474

7575
expect(screen.getByText('Analyzing Game')).toBeInTheDocument()
76-
expect(
77-
screen.getByText('Deep analysis in progress...'),
78-
).toBeInTheDocument()
7976
expect(screen.getByText('Position 5 of 20')).toBeInTheDocument()
8077
expect(screen.getByText('25%')).toBeInTheDocument()
8178
})
8279

8380
it('renders current move being analyzed', () => {
84-
render(<AnalysisProgressOverlay {...defaultProps} />)
81+
render(<AnalysisNotification {...defaultProps} />)
8582

86-
expect(screen.getByText('Currently analyzing:')).toBeInTheDocument()
83+
expect(screen.getByText('Current:')).toBeInTheDocument()
8784
expect(screen.getByText('e4')).toBeInTheDocument()
8885
})
8986

9087
it('renders cancel button', () => {
91-
render(<AnalysisProgressOverlay {...defaultProps} />)
88+
render(<AnalysisNotification {...defaultProps} />)
9289

93-
expect(screen.getByText('Cancel Analysis')).toBeInTheDocument()
90+
const cancelButton = screen.getByTitle('Cancel Analysis')
91+
expect(cancelButton).toBeInTheDocument()
9492
})
9593

9694
it('does not render when not analyzing', () => {
@@ -100,7 +98,7 @@ describe('Analyze Entire Game Components', () => {
10098
}
10199

102100
render(
103-
<AnalysisProgressOverlay
101+
<AnalysisNotification
104102
{...defaultProps}
105103
progress={notAnalyzingProgress}
106104
/>,

package-lock.json

Lines changed: 105 additions & 115 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import React from 'react'
2+
import { motion } from 'framer-motion'
3+
import { GameAnalysisProgress } from 'src/hooks/useAnalysisController/useAnalysisController'
4+
5+
interface Props {
6+
progress: GameAnalysisProgress
7+
onCancel: () => void
8+
}
9+
10+
export const AnalysisNotification: React.FC<Props> = ({
11+
progress,
12+
onCancel,
13+
}) => {
14+
const progressPercentage =
15+
progress.totalMoves > 0
16+
? Math.round((progress.currentMoveIndex / progress.totalMoves) * 100)
17+
: 0
18+
19+
if (!progress.isAnalyzing) return null
20+
21+
return (
22+
<motion.div
23+
className="fixed bottom-4 right-4 z-30 w-80 rounded-lg border border-white/10 bg-background-1 p-4 shadow-lg"
24+
initial={{ opacity: 0, y: 20, scale: 0.95 }}
25+
animate={{ opacity: 1, y: 0, scale: 1 }}
26+
exit={{ opacity: 0, y: 20, scale: 0.95 }}
27+
transition={{ duration: 0.2 }}
28+
data-testid="analysis-notification"
29+
>
30+
<div className="flex items-center justify-between">
31+
<div className="flex items-center gap-3">
32+
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-human-4/20">
33+
<span className="material-symbols-outlined animate-spin !text-lg text-human-4">
34+
network_intelligence
35+
</span>
36+
</div>
37+
<div className="flex flex-col">
38+
<h3 className="text-sm font-semibold">Analyzing Game</h3>
39+
<p className="text-xs text-secondary">
40+
Position {progress.currentMoveIndex} of {progress.totalMoves}
41+
</p>
42+
</div>
43+
</div>
44+
<div className="flex items-center gap-2">
45+
<span className="text-xs font-medium text-human-4">
46+
{progressPercentage}%
47+
</span>
48+
<button
49+
onClick={onCancel}
50+
className="flex h-6 w-6 items-center justify-center rounded bg-background-2 text-xs transition duration-200 hover:bg-background-3"
51+
title="Cancel Analysis"
52+
>
53+
<span className="material-symbols-outlined !text-sm">close</span>
54+
</button>
55+
</div>
56+
</div>
57+
58+
<div className="mt-3">
59+
<div className="relative h-1.5 w-full overflow-hidden rounded-full bg-background-2">
60+
<motion.div
61+
className="h-full rounded-full bg-human-4"
62+
initial={{ width: 0 }}
63+
animate={{ width: `${progressPercentage}%` }}
64+
transition={{ duration: 0.3, ease: 'easeOut' }}
65+
/>
66+
</div>
67+
</div>
68+
69+
{progress.currentMove && (
70+
<div className="mt-2">
71+
<p className="text-xs text-secondary">
72+
Current:{' '}
73+
<span className="font-mono text-human-3">
74+
{progress.currentMove}
75+
</span>
76+
</p>
77+
</div>
78+
)}
79+
</motion.div>
80+
)
81+
}

0 commit comments

Comments
 (0)