fix(ui): F8-10 owner-feedback — persistent filters, camera, disabled visual, dropdown narrowing (#53)
Polish pass after the first F8-10 walkthrough:
- table-planets: moved the `foreign` chip to the end of the row and
hid the race dropdown until `foreign` is on (it never made sense
to pick a race while the bucket itself was off).
- persistent per-table filter / sort state — extracted to
`table-{planets,ship-groups,fleets}-state.svelte.ts` singletons so
a row click → map → back to the table restores the prior chip /
dropdown / sort state. Held in memory only; an F5 still resets.
- table-ship-groups: the planet and class dropdowns now narrow to
the slice surviving the owner checkboxes, so toggling `foreign`
off removes planets / classes touched only by foreign rows.
- map.svelte: camera (centre + zoom) is captured on every dispose
path into a new `GameStateStore.lastCamera` and consumed on the
next mount, so leaving the map for any other active view and
coming back restores the prior pan / zoom. A pending focus from
the tables still wins for the centre point.
- table-ship-classes: `:disabled` now reads as disabled (muted
colour, no hover ring, not-allowed cursor) — the click was already
a no-op, only the visual was lying.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
SelectionStore,
|
||||
} from "../src/lib/selection.svelte";
|
||||
import { activeView } from "../src/lib/app-nav.svelte";
|
||||
import { resetFleetsTableState } from "../src/lib/active-view/table-fleets-state.svelte";
|
||||
import { EMPTY_SHIP_GROUPS } from "./helpers/empty-ship-groups";
|
||||
|
||||
const pageMock = vi.hoisted(() => ({
|
||||
@@ -49,6 +50,7 @@ beforeEach(() => {
|
||||
pageMock.params = { id: "g1" };
|
||||
gotoMock.mockClear();
|
||||
activeView.reset();
|
||||
resetFleetsTableState();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
SelectionStore,
|
||||
} from "../src/lib/selection.svelte";
|
||||
import { activeView } from "../src/lib/app-nav.svelte";
|
||||
import { resetPlanetsTableState } from "../src/lib/active-view/table-planets-state.svelte";
|
||||
import { EMPTY_SHIP_GROUPS } from "./helpers/empty-ship-groups";
|
||||
|
||||
const pageMock = vi.hoisted(() => ({
|
||||
@@ -45,6 +46,7 @@ beforeEach(() => {
|
||||
pageMock.params = { id: "g1" };
|
||||
gotoMock.mockClear();
|
||||
activeView.reset();
|
||||
resetPlanetsTableState();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -189,6 +191,25 @@ describe("planets table", () => {
|
||||
expect(activeView.view).toBe("map");
|
||||
});
|
||||
|
||||
test("filter checkboxes survive an unmount/remount cycle", async () => {
|
||||
const report = makeReport([
|
||||
planet({ number: 1, kind: "local" }),
|
||||
planet({ number: 2, kind: "other", owner: "Klingon" }),
|
||||
]);
|
||||
const first = mount(report);
|
||||
await fireEvent.click(first.getByTestId("planets-filter-own"));
|
||||
first.unmount();
|
||||
const second = mount(report);
|
||||
const ownCheckbox = second.getByTestId(
|
||||
"planets-filter-own",
|
||||
) as HTMLInputElement;
|
||||
expect(ownCheckbox.checked).toBe(false);
|
||||
const kinds = second
|
||||
.getAllByTestId("planets-row")
|
||||
.map((r) => r.getAttribute("data-kind"));
|
||||
expect(kinds).toEqual(["other"]);
|
||||
});
|
||||
|
||||
test("number column sorts ascending then descending", async () => {
|
||||
const ui = mount(
|
||||
makeReport([
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
SelectionStore,
|
||||
} from "../src/lib/selection.svelte";
|
||||
import { activeView } from "../src/lib/app-nav.svelte";
|
||||
import { resetShipGroupsTableState } from "../src/lib/active-view/table-ship-groups-state.svelte";
|
||||
import { EMPTY_SHIP_GROUPS } from "./helpers/empty-ship-groups";
|
||||
|
||||
const pageMock = vi.hoisted(() => ({
|
||||
@@ -50,6 +51,7 @@ beforeEach(() => {
|
||||
pageMock.params = { id: "g1" };
|
||||
gotoMock.mockClear();
|
||||
activeView.reset();
|
||||
resetShipGroupsTableState();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -287,6 +289,37 @@ describe("ship-groups table", () => {
|
||||
expect(activeView.view).toBe("map");
|
||||
});
|
||||
|
||||
test("planet/class dropdowns narrow to the owner-checkbox cut", async () => {
|
||||
const ui = mount(
|
||||
makeReport({
|
||||
planets: [planet(1), planet(2), planet(3)],
|
||||
local: [localGroup({ id: "L1", class: "Cruiser", destination: 1 })],
|
||||
other: [
|
||||
otherGroup({ class: "Hunter", destination: 3, race: "Klingon" }),
|
||||
],
|
||||
}),
|
||||
);
|
||||
// All four planet options available when both owner kinds are on
|
||||
const planetSelect = ui.getByTestId(
|
||||
"ship-groups-filter-planet",
|
||||
) as HTMLSelectElement;
|
||||
const valuesFull = Array.from(planetSelect.options).map((o) => o.value);
|
||||
expect(valuesFull).toContain("1");
|
||||
expect(valuesFull).toContain("3");
|
||||
// Hide foreign rows; planet 3 (only touched by Klingon) disappears
|
||||
await fireEvent.click(ui.getByTestId("ship-groups-filter-foreign"));
|
||||
const valuesNarrowed = Array.from(planetSelect.options).map((o) => o.value);
|
||||
expect(valuesNarrowed).toContain("1");
|
||||
expect(valuesNarrowed).not.toContain("3");
|
||||
// Class dropdown narrows too: Hunter disappears
|
||||
const classSelect = ui.getByTestId(
|
||||
"ship-groups-filter-class",
|
||||
) as HTMLSelectElement;
|
||||
const classValues = Array.from(classSelect.options).map((o) => o.value);
|
||||
expect(classValues).not.toContain("Hunter");
|
||||
expect(classValues).toContain("Cruiser");
|
||||
});
|
||||
|
||||
test("click on in-space foreign group focuses other variant by index", async () => {
|
||||
const ui = mount(
|
||||
makeReport({
|
||||
|
||||
Reference in New Issue
Block a user