Skip to content
Merged
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
26 changes: 20 additions & 6 deletions php/includes/custom_gauges_handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,13 @@ function _compute_custom_gauges_filters(array $rows): array
$huc8_present = [];
$has_no_huc = false;
foreach ($rows as $r) {
$abbrev = $r['state_abbrev'] ?? '';
if ($abbrev !== '' && isset(CUSTOM_GAUGES_STATE_ABBREVS[$abbrev])) {
$states_present[CUSTOM_GAUGES_STATE_ABBREVS[$abbrev]] = true;
// gauge.state may be a comma list ('OR,WA') for a border gauge; split
// it so each state contributes a pill, mirroring the static build.
foreach (explode(',', $r['state_abbrev'] ?? '') as $abbrev) {
$abbrev = trim($abbrev);
if ($abbrev !== '' && isset(CUSTOM_GAUGES_STATE_ABBREVS[$abbrev])) {
$states_present[CUSTOM_GAUGES_STATE_ABBREVS[$abbrev]] = true;
}
}
$huc = $r['huc'] ?? '';
if (strlen($huc) >= 8) {
Expand Down Expand Up @@ -323,7 +327,7 @@ function _render_custom_gauges_header(
<?php if (count($states_present) > 1): ?>
<details class="filter-group">
<summary>State <span class="fg-count"><?= count($states_present) ?></span></summary>
<div class="filter-pills" data-group="state">
<div class="filter-pills" data-group="state" data-split="csv">
<?= $fg_toggle ?>
<?php foreach (array_keys($states_present) as $st): ?>
<label><input type="checkbox" value="<?= htmlspecialchars($st) ?>" checked><?= htmlspecialchars($st) ?></label>
Expand Down Expand Up @@ -391,8 +395,18 @@ function _render_custom_gauges_table(array $rows, array $status_by_gauge): void
<tbody>
<?php foreach ($rows as $r):
$gid = $r['id'];
$abbrev = $r['state_abbrev'] ?? '';
$state = CUSTOM_GAUGES_STATE_ABBREVS[$abbrev] ?? '';
// A border gauge's state_abbrev is a comma list ('OR,WA'); map each abbrev
// to its full name and re-join ('Oregon,Washington'). The State filter
// group is rendered data-split="csv", so filters.js splits this data-state
// to match each pill.
$state_names = [];
foreach (explode(',', $r['state_abbrev'] ?? '') as $abbrev) {
$abbrev = trim($abbrev);
if (isset(CUSTOM_GAUGES_STATE_ABBREVS[$abbrev])) {
$state_names[] = CUSTOM_GAUGES_STATE_ABBREVS[$abbrev];
}
}
$state = implode(',', $state_names);
$huc_str = $r['huc'] ?? '';
$huc8 = strlen($huc_str) >= 8 ? substr($huc_str, 0, 8) : '';

Expand Down
50 changes: 48 additions & 2 deletions tests/php/CustomGaugesIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
* - Gauge without readings: cells blank, no crash
* - Status rollup: gauge with okay-status reach gets the okay label
*
* Seed: 2 gauges (id=7001 with reach + flow obs + class threshold so
* status rollup hits, 7002 minimal/no-readings).
* Seed: 3 gauges (id=7001 with reach + flow obs + class threshold so
* status rollup hits, 7002 minimal/no-readings, 7003 an OR,WA border gauge
* that must surface under both states in the pills + as a filterable row).
*/
final class CustomGaugesIntegrationTest extends IntegrationTestCase
{
private const GAUGE_WITH_READINGS = 7001;
private const GAUGE_NO_READINGS = 7002;
private const GAUGE_BORDER = 7003;
private const REACH_ID = 7501;

protected static function seedDatabase(PDO $db): void
Expand All @@ -50,6 +52,20 @@ protected static function seedDatabase(PDO $db): void
'CUSTGAUGE_E',
'Custom Gauges Test (empty)',
]);
// Gauge 3: an OR,WA border gauge (the Columbia mainstem shape) with an
// 8-digit HUC so it qualifies as a filterable row.
$db->prepare(
"INSERT INTO gauge (id, name, display_name, river, location, state, huc)
VALUES (?, ?, ?, ?, ?, ?, ?)"
)->execute([
self::GAUGE_BORDER,
'CUSTGAUGE_ORWA',
'Custom Gauges Test (border)',
'Columbia',
'Vancouver',
'OR,WA',
'17080003',
]);

foreach ([['flow', 750.0], ['gauge', 3.5], ['temperature', 48.0]] as [$dt, $v]) {
$db->prepare(
Expand Down Expand Up @@ -167,4 +183,34 @@ public function testGaugeNoReadingsRendersBlankCells(): void
$this->assertStringNotContainsString('level-low', $resp['body']);
$this->assertStringNotContainsString('level-high', $resp['body']);
}

public function testBorderGaugeSurfacesBothStatesAndStaysFilterable(): void
{
// A single OR,WA border gauge must contribute BOTH state pills and
// render as a filterable row carrying a comma data-state. Before the
// fix, 'OR,WA' mapped to no pill and the row dropped its data-state/
// data-huc8 (escaping the filters entirely).
$resp = $this->request('/custom_gauges.php', ['ids' => (string)self::GAUGE_BORDER]);

$this->assertSame(200, $resp['status']);

// Both pills present from the one border gauge (State group renders
// because count(states) > 1).
$this->assertStringContainsString('value="Oregon"', $resp['body']);
$this->assertStringContainsString('value="Washington"', $resp['body']);

// The State group must split its rows' data-state on the comma.
$this->assertStringContainsString(
'data-group="state" data-split="csv"',
$resp['body'],
'state filter group must be data-split="csv" to match a comma data-state'
);

// The row is filterable: comma-joined full-name data-state + data-huc8.
$this->assertMatchesRegularExpression(
'/data-state="Oregon,Washington"\s+data-huc8="17080003"/',
$resp['body'],
'border-gauge row should emit a comma-joined data-state plus data-huc8'
);
}
}
Loading