Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions frontend/src/components/LeftoverMapPlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,8 @@ function leftoverMapPlotAxisText(
* comparison-graphic and rank labels rather than adding SPA translation debt.
* ADR 0319 captions leftover-map distance on that comparison graphic using
* the same localized composition boundary rather than adding another static
* comparison-only translation key.
* comparison-only translation key. ADR 0320 names comparison coordinate ticks
* through the same composition boundary, preserving localized generic tick copy.
* Never invent a leftover score.
*/
export function LeftoverMapPlot({
Expand Down Expand Up @@ -300,9 +301,16 @@ export function LeftoverMapPlot({
</text>
{layout.ticks.map((tick) => (
<g
key={`tick:${tick.axis}:${tick.label}`}
key={`tick:${tick.axis}:${tick.value}`}
className="leftover-map-plot-tick"
aria-label={tf(LEFTOVER_MAP_PLOT_TICK, { axis: tick.axis, value: tick.label })}
aria-label={
variant === "comparison"
? `${t(LEFTOVER_MAP_COMPARE_PLOT_LABEL)}: ${tf(LEFTOVER_MAP_PLOT_TICK, {
axis: tick.axis,
value: tick.label,
})}`
: tf(LEFTOVER_MAP_PLOT_TICK, { axis: tick.axis, value: tick.label })
}
>
<line x1={tick.x} y1={tick.y} x2={tick.tickX2} y2={tick.tickY2} />
<text
Expand Down
17 changes: 17 additions & 0 deletions frontend/src/leftoverMapPlotLayout.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,23 @@ describe("layoutLeftoverMapPlot", () => {
expect(originAxis1).toMatchObject({ x: layout?.originX, y: layout?.originY, label: "0.00" });
});

it("keeps distinct persisted coordinate ticks when their displayed labels match", () => {
const layout = layoutLeftoverMapPlot(
[
pair({
leftover_map_person_axis_1: 0.501,
leftover_map_item_axis_1: 0.504,
}),
],
criterionLabel,
);
const matchingTicks = layout?.ticks.filter(
(tick) => tick.axis === 1 && tick.label === "+0.50",
);
expect(matchingTicks?.map((tick) => tick.value)).toEqual([0.501, 0.504]);
expect(matchingTicks?.[0]?.x).not.toBe(matchingTicks?.[1]?.x);
});

it("does not invent drawing-scale leftover-map ticks on a rank-0 origin cell", () => {
const layout = layoutLeftoverMapPlot(
[
Expand Down
10 changes: 6 additions & 4 deletions frontend/src/leftoverMapPlotLayout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,17 +227,19 @@ function boundedCaptionY(y: number, height: number): number {
}

function uniqueCoordinateTicks(values: number[]): { value: number; label: string }[] {
const byLabel = new Map<string, number>();
const uniqueValues = new Set<number>();
const ticks: { value: number; label: string }[] = [];
for (const value of values) {
const label = formatSignedLeftoverValue(value);
if (label === null) {
continue;
}
if (!byLabel.has(label)) {
byLabel.set(label, value);
if (!uniqueValues.has(value)) {
uniqueValues.add(value);
ticks.push({ value, label });
}
}
return [...byLabel.entries()].map(([label, value]) => ({ value, label }));
return ticks;
}

function leftoverMapCoordinateTicks(
Expand Down
42 changes: 42 additions & 0 deletions tests/test_grouping_comparison_graphic_tick_contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""Executable contract for coordinate ticks on the grouping-comparison graphic."""

from pathlib import Path
import re


ROOT = Path(__file__).resolve().parents[1]
PLOT_SOURCE = ROOT / "frontend" / "src" / "components" / "LeftoverMapPlot.tsx"
LAYOUT_SOURCE = ROOT / "frontend" / "src" / "leftoverMapPlotLayout.ts"


def test_comparison_graphic_names_ticks_with_distinct_accessible_copy() -> None:
"""Comparison ticks compose existing localized comparison and tick copy."""
plot_source = PLOT_SOURCE.read_text(encoding="utf-8")

assert "LEFTOVER_MAP_COMPARE_PLOT_LABEL" in plot_source
assert "LEFTOVER_MAP_PLOT_TICK" in plot_source
assert re.search(
r'variant\s*===\s*"comparison"\s*\?\s*`\$\{t\(LEFTOVER_MAP_COMPARE_PLOT_LABEL\)\}:\s*\$\{tf\(LEFTOVER_MAP_PLOT_TICK,\s*\{\s*axis:\s*tick\.axis,\s*value:\s*tick\.label,?\s*\}\)\}`\s*:\s*tf\(LEFTOVER_MAP_PLOT_TICK,\s*\{\s*axis:\s*tick\.axis,\s*value:\s*tick\.label,?\s*\}\)',
plot_source,
re.DOTALL,
)
assert "LEFTOVER_MAP_COMPARE_PLOT_TICK" not in plot_source


def test_tick_positions_come_from_persisted_coordinates_not_distance() -> None:
"""Tick candidates remain the origin plus finite persisted ξ/ζ projections."""
layout_source = LAYOUT_SOURCE.read_text(encoding="utf-8")

assert "const axis1Values = [0];" in layout_source
assert "const axis2Values = [0];" in layout_source
assert "pair.leftover_map_person_axis_1 as number" in layout_source
assert "pair.leftover_map_item_axis_1 as number" in layout_source
assert "pair.leftover_map_person_axis_2 as number" in layout_source
assert "pair.leftover_map_item_axis_2 as number" in layout_source
tick_builder = re.search(
r"function uniqueCoordinateTicks\(.*?\n}\n",
layout_source,
re.DOTALL,
)
assert tick_builder is not None
assert "leftover_distance" not in tick_builder.group(0)
Loading