Skip to content

Commit 3d0031a

Browse files
committed
fix: preserve chuni ranking scroll position
1 parent 454fdb8 commit 3d0031a

3 files changed

Lines changed: 46 additions & 32 deletions

File tree

src/features/chuni/ChuniV2SongScoreRanking.css

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ body:has(.chuni-v2-song-score-ranking-panel[data-state='open']) > [data-slot='sh
8080
z-index: 1;
8181
display: block;
8282
width: min(60%, 12em);
83-
margin: 0 auto;
8483
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
8584
}
8685

src/features/chuni/ChuniV2SongScoreRanking.tsx

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useEffect, useRef, useState } from 'react';
22
import { StopFill } from 'react-bootstrap-icons';
33
import { useNavigate } from 'react-router-dom';
4-
import { Sheet, SheetClose, SheetContent, SheetTitle } from '@/components/ui/sheet';
4+
import { Sheet, SheetContent, SheetTitle } from '@/components/ui/sheet';
55
import { api } from '@/lib/api/client';
66
import { notice } from '@/lib/message';
77
import { getCurrentUser } from '@/lib/user';
@@ -153,7 +153,6 @@ export function ChuniV2SongScoreRanking({
153153

154154
function selectDifficulty(level: number) {
155155
setCurrentLevel(level);
156-
setRanking([]);
157156
void api
158157
.get('api/game/chuni/v2/musicScoreRanking', { musicId: music!.musicId, level })
159158
.then((response) => setRanking((response ?? []) as ChuniV2SongRankingRow[]))
@@ -189,34 +188,37 @@ export function ChuniV2SongScoreRanking({
189188
className="chuni-v2-song-score-ranking-panel chuni-v2-song-score-ranking-sheet w-[400px] max-w-full p-0 text-sm outline-none sm:max-w-[400px]"
190189
onOpenAutoFocus={(event) => event.preventDefault()}
191190
>
192-
<div className="offcanvas-header position-absolute end-0 z-3">
193-
<SheetClose asChild>
194-
<button type="button" className="btn-close" aria-label="Close" />
195-
</SheetClose>
196-
</div>
197191
<SheetTitle className="visually-hidden">{music.name}</SheetTitle>
198-
<div className="offcanvas-body pt-0 px-0">
192+
<div
193+
className="offcanvas-body"
194+
style={{ maxHeight: '100vh', overflowY: 'scroll', boxSizing: 'border-box', margin: 0, padding: 0 }}
195+
>
196+
<button
197+
type="button"
198+
className="btn-close"
199+
aria-label="Close"
200+
style={{ position: 'fixed', top: '1rem', right: '1rem', zIndex: 1000 }}
201+
onClick={requestClose}
202+
/>
199203
<div
200-
className="music-info-container row pb-3 pt-3 gap-3 px-3 m-0"
201-
style={
202-
{
203-
'--jacket-img': `url(${assetsHost}assets/chuni/jacket/CHU_UI_Jacket_${padDigits(music.musicId, 4)}.webp)`,
204-
} as React.CSSProperties
205-
}
204+
className="music-info-container position-relative"
205+
style={{
206+
'--jacket-img': `url(${assetsHost}assets/chuni/jacket/CHU_UI_Jacket_${padDigits(music.musicId, 4)}.webp)`,
207+
} as React.CSSProperties}
206208
>
207-
<div className="col-12 p-0">
208-
{enableImages && (
209-
<img
210-
className="music-img"
211-
src={`${assetsHost}assets/chuni/jacket/CHU_UI_Jacket_${padDigits(music.musicId, 4)}.webp`}
212-
alt=""
213-
/>
214-
)}
215-
</div>
216-
<div className="col-12 music-info">
217-
<h4 className="music-title">{music.name}</h4>
218-
<div className="mb-1">{music.artistName}</div>
219-
<div className="text-secondary">{music.genre}</div>
209+
{enableImages && (
210+
<img
211+
className="card-img mb-3 music-img sm ms-4 mt-4 rounded"
212+
src={`${assetsHost}assets/chuni/jacket/CHU_UI_Jacket_${padDigits(music.musicId, 4)}.webp`}
213+
alt=""
214+
/>
215+
)}
216+
<div className="position-relative">
217+
<h5 className="card-title mb-1 fw-bold ms-4">{music.name}</h5>
218+
<span className="card-subtitle music-artistName ms-4" style={{ fontSize: 12 }}>
219+
{music.artistName}
220+
</span>
221+
<hr className="mb-0" />
220222
</div>
221223
</div>
222224

tests/ui-parity/chuni-song-pages.spec.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,10 @@ const songRecords = [0, 1, 2, 3, 4].map((difficulty) => ({
115115
}));
116116

117117
const ranking = [
118-
{ username: 'RANK ONE', score: 1_010_000 },
119-
{ username: 'RANK TWO', score: 1_009_500 },
120-
{ username: 'RANK THREE', score: 1_009_000 },
121-
{ username: 'RANK FOUR', score: 1_008_000 },
118+
...Array.from({ length: 20 }, (_, index) => ({
119+
username: `RANK ${String(index + 1).padStart(2, '0')}`,
120+
score: 1_010_000 - index * 500,
121+
})),
122122
];
123123

124124
async function installFixtureApi(context: BrowserContext) {
@@ -344,6 +344,8 @@ test.describe('Chunithm v2 song pages visual parity', () => {
344344
const newRoot = newPage.locator('.chuni-v2-song-score-ranking-panel[data-state="open"]');
345345
const musicInfo = newRoot.locator('.music-info-container');
346346
await expect(musicInfo).toHaveCount(1);
347+
await expect(newRoot.locator('.music-img')).toHaveClass(/\bms-4\b/);
348+
await expect(newRoot.locator('.music-img')).toHaveClass(/\bmt-4\b/);
347349
const backgroundStyle = await musicInfo.evaluate((element) => {
348350
const style = getComputedStyle(element, '::before');
349351
return { backgroundImage: style.backgroundImage, filter: style.filter };
@@ -359,6 +361,17 @@ test.describe('Chunithm v2 song pages visual parity', () => {
359361
expect(oldRoot.getByRole('tab', { name: 'BA', exact: true })).toHaveClass(/active/),
360362
expect(newRoot.getByRole('tab', { name: 'BA', exact: true })).toHaveClass(/active/),
361363
]);
364+
const scrollState = await newRoot.locator('.offcanvas-body').evaluate((element) => {
365+
const body = element as HTMLElement;
366+
body.scrollTop = body.scrollHeight;
367+
return { before: body.scrollTop, max: body.scrollHeight - body.clientHeight };
368+
});
369+
await newRoot.getByRole('tab', { name: 'MA', exact: true }).evaluate((element) => element.click());
370+
await expect(newRoot.getByRole('tab', { name: 'MA', exact: true })).toHaveClass(/active/);
371+
await newPage.waitForTimeout(50);
372+
await expect.poll(async () => newRoot.locator('.offcanvas-body').evaluate((element) => (element as HTMLElement).scrollTop)).toBe(
373+
scrollState.before,
374+
);
362375
expect(blockedBusinessWrites, 'Difficulty-tab interaction must remain read-only').toEqual([]);
363376

364377
const newSheet = newPage.locator('.chuni-v2-song-score-ranking-panel');

0 commit comments

Comments
 (0)