fix(import): readable dropdowns in dark mode, select all and none

This commit is contained in:
lobo committed 2026-09-29 15:10:41 +02:00
1 parent 24a98062ee
commit 111ab1e200
5 files changed
+49 -1

No files matched your search

+9 -1
View File
@@ -371,6 +371,12 @@ function Review({data, favorite, onSaved, onDone, expireSession}) {
return {...choice, candidates, igdbId: candidate.igdbId,
platform: preselectPlatform(data.rows[index].platform, candidate.platforms, favorite)};
})), [data.rows, favorite]);
// A line without a match cannot be checked, one in the library only by hand; a hand check stays.
const checkAll = (checked) => setChoices((current) => current.map((choice) => {
const candidate = choice.candidates.find((known) => known.igdbId === choice.igdbId);
const next = checked && (choice.checked || (!!candidate && !candidate.inLibrary));
return choice.checked === next ? choice : {...choice, checked: next};
}));
const chosen = choices.map((choice, index) => ({choice, row: data.rows[index]}))
.filter(({choice}) => choice.checked && choice.igdbId && choice.platform.trim());
@@ -402,7 +408,9 @@ function Review({data, favorite, onSaved, onDone, expireSession}) {
<Card className="gap-0 overflow-hidden p-0">
<div className="flex flex-wrap items-center justify-between gap-3 border-b px-5 py-4">
<p className="text-sm text-muted-foreground">{t('IMPORT.REVIEW_HINT')}</p>
<div className="flex gap-2">
<div className="flex flex-wrap gap-2">
<Button variant="outline" onClick={() => checkAll(true)} disabled={busy}>{t('IMPORT.SELECT_ALL')}</Button>
<Button variant="outline" onClick={() => checkAll(false)} disabled={busy}>{t('IMPORT.SELECT_NONE')}</Button>
<Button variant="ghost" onClick={onDone} disabled={busy}>{t('IMPORT.NEW')}</Button>
<Button onClick={save} disabled={busy || chosen.length === 0}>
{busy && <Loader2 className="animate-spin"/>}{t('IMPORT.SAVE', {count: chosen.length})}
+26
View File
@@ -171,6 +171,32 @@ describe('ImportPage', () => {
]});
});
it('checks every line with a new match at once, and none', async () => {
renderPage({'GET ': json(200, {imports: []}), 'GET /5': json(200, ready)}, '/import?id=5');
const checked = () => ['Hades', 'Celeste', 'Hade'].map((title) => screen.getByLabelText(`IMPORT.LINE: ${title}`).checked);
await screen.findByLabelText('IMPORT.LINE: Hades');
fireEvent.click(screen.getByRole('button', {name: 'IMPORT.SELECT_ALL'}));
// Celeste is in the library already.
expect(checked()).toEqual([true, false, true]);
expect(screen.getByRole('button', {name: 'IMPORT.SAVE:2'})).toBeTruthy();
fireEvent.click(screen.getByRole('button', {name: 'IMPORT.SELECT_NONE'}));
expect(checked()).toEqual([false, false, false]);
expect(screen.getByRole('button', {name: 'IMPORT.SAVE:0'}).disabled).toBe(true);
});
it('keeps a line in the library that was checked by hand on select all', async () => {
renderPage({'GET ': json(200, {imports: []}), 'GET /5': json(200, ready)}, '/import?id=5');
const celesteBox = await screen.findByLabelText('IMPORT.LINE: Celeste');
fireEvent.click(celesteBox);
expect(celesteBox.checked).toBe(true);
fireEvent.click(screen.getByRole('button', {name: 'IMPORT.SELECT_ALL'}));
expect(screen.getByLabelText('IMPORT.LINE: Celeste').checked).toBe(true);
});
it('undoes a saved import after a confirm with the changed count', async () => {
renderPage({
'GET ': json(200, {imports: [{id: 4, source: 'CSV', status: 'SAVED', total: 3, matched: 3, createdAt: '2026-09-29T10:00:00Z'}]}),
+10
View File
@@ -222,6 +222,16 @@
body {
@apply bg-background text-foreground antialiased;
}
/* The open list of a native select is drawn by the browser: without these it stays light in dark mode, with the
page's light text on it. */
.dark {
color-scheme: dark;
}
option {
@apply bg-popover text-popover-foreground;
}
}
/* Radix menus and selects lock the page scroll and add a right margin as wide as the scrollbar, measured as