feat: v0.15.0 — ISS-002 hovering a plane or package in the side panels highlights it on the map
CI / build + unit + e2e (pull_request) Failing after 6m8s

This commit is contained in:
2026-09-19 09:37:42 +00:00
parent 7db8fcc549
commit 3b6506832f
8 changed files with 386 additions and 11 deletions
+23 -3
View File
@@ -5,6 +5,7 @@ import type { PackageState, PlaneState, StateSnapshot } from '@/lib/game/state';
import { PLANE_TYPES } from '@/lib/game/catalog';
import { abandonPenalty } from '@/lib/game/penalty';
import { formatCash, formatDeadlineRelative } from '@/lib/format';
import type { HoverTarget } from './hoverTypes';
interface RightPaneProps {
state: StateSnapshot | null;
@@ -12,6 +13,9 @@ interface RightPaneProps {
// Push the full snapshot returned by an action endpoint into the shared shell state (v0.10.0),
// so the pane updates on click without waiting for the next poll tick.
applySnapshot: (snapshot: StateSnapshot) => void;
// ISS-002 (v0.15.0): report row hovers to the shell so the map can highlight the plane or
// the package's endpoint airports.
onHover: (target: HoverTarget | null) => void;
}
const planeName = (p: PlaneState): string => PLANE_TYPES[p.model]?.name ?? p.model;
@@ -100,7 +104,7 @@ function LoadControls({
);
}
export default function RightPane({ state, loading, applySnapshot }: RightPaneProps) {
export default function RightPane({ state, loading, applySnapshot, onHover }: RightPaneProps) {
const [busy, setBusy] = useState(false);
// Inline error from a failed action — auto-clears on the next successful poll (the shell's
// snapshot prop changes every tick) or the next successful action.
@@ -192,7 +196,14 @@ export default function RightPane({ state, loading, applySnapshot }: RightPanePr
{open.map((pkg) => {
const eligible = loadEligiblePlanes(state, pkg);
return (
<li key={pkg.id} data-testid={`open-package-${pkg.id}`} className="contract-row">
// ISS-002: hovering an open package highlights its two endpoint airports on the map.
<li
key={pkg.id}
data-testid={`open-package-${pkg.id}`}
className="contract-row"
onMouseEnter={() => onHover({ kind: 'package', originIcao: pkg.originIcao, destIcao: pkg.destIcao })}
onMouseLeave={() => onHover(null)}
>
<div className="contract-main">
<span className="contract-route">{pkg.originName} → {pkg.destName}</span>
{eligible.length > 0 ? (
@@ -232,7 +243,16 @@ export default function RightPane({ state, loading, applySnapshot }: RightPanePr
(pkg) => pkg.planeId === plane.id && (pkg.status === 'loaded' || pkg.status === 'expired'),
);
return (
<div key={plane.id} data-testid={`onboard-${plane.id}`} className="onboard-block">
// ISS-002: hovering the on-board block (including its cargo rows) highlights the plane.
// Handlers live only on the outer div — React's enter/leave don't fire when moving to a
// child, so this covers the whole block without flicker between sub-elements.
<div
key={plane.id}
data-testid={`onboard-${plane.id}`}
className="onboard-block"
onMouseEnter={() => onHover({ kind: 'plane', id: plane.id })}
onMouseLeave={() => onHover(null)}
>
<div className="contract-main">
<span className="plane-model">{planeName(plane)}</span>
{plane.status === 'flying' ? (