fix(#1486): collapse chevron no longer reopens closed detail panel (#1492)

## Summary

Fixes #1486 — clicking the collapse chevron on a grouped packet row in
the packets table no longer reopens the detail panel that the operator
just closed.

## Root cause

In the `#pktBody` row click handler the `toggle-select` action ran
**both** `pktToggleGroup(value)` and `pktSelectHash(value)` on every
chevron click. `pktToggleGroup()` already opens the detail panel itself
(via `selectPacket()`) when it expands a row, so the trailing
`pktSelectHash()` was:

  - redundant on **expand** (the panel was already opening), and
  - harmful on **collapse** — after the operator closed the detail panel
    via the ✕ in `#pktRight`, clicking the same chevron a second time
    to collapse the tree re-fetched `/packets/<hash>` and re-populated
    the panel with the same packet, exactly the behavior the issue
    describes.

## Fix

Drop the unconditional `pktSelectHash(value)` call inside the
`toggle-select` branch. `pktToggleGroup()` already handles the
expand-side panel open; the collapse branch does no panel work, so a
closed panel stays closed.

```js
else if (action === 'toggle-select') {
  // #1486: pktToggleGroup() already opens the detail panel on EXPAND
  // (via selectPacket()), and must NOT open it on COLLAPSE.
  pktToggleGroup(value);
}
```

## Tests

- New Playwright E2E `test-issue-1486-collapse-reopens-detail-e2e.js`
  walks the operator-visible repro: expand → assert panel open →
  click ✕ → assert panel empty → click chevron again → assert row
  collapsed AND panel STILL empty.
- Committed red-first: the test was added in its own commit and FAILS
  on the unpatched code (3 passed / 1 failed), then GREEN on the fix
  commit (4 passed / 0 failed).
- CI workflow seeds two extra observations onto the newest fixture
  transmission so a grouped (`toggle-select`) row exists; without this
  the fixture renders only flat rows and the chevron can't be
  exercised.

## Reproduction (manual, against staging or local)

1. Open `/#/packets` on desktop.
2. Click a grouped row's `▶` chevron — the tree expands and the detail
   panel opens on the right.
3. Click the `✕` in the top-right of the detail panel — panel goes back
   to "Select a packet to view details".
4. Click the same chevron (now `▼`) again — **before:** detail panel
   reopens with the same packet. **After:** the row collapses and the
   panel stays empty.

---------

Co-authored-by: mc-bot <bot@meshcore.local>
This commit is contained in:
Kpa-clawbot
2026-05-29 15:17:16 +00:00
committed by GitHub
co-authored by mc-bot
parent 268751ff56
commit 7fcb226cd8
3 changed files with 187 additions and 1 deletions
+49
View File
@@ -241,6 +241,54 @@ jobs:
- name: Freshen fixture timestamps
run: bash tools/freshen-fixture.sh test-fixtures/e2e-fixture.db
- name: Seed grouped-packet row for #1486 collapse test
# The committed fixture has 499 packets, each with exactly ONE
# observation, so the packets-page renders only flat
# (select-hash) rows. The #1486 repro needs at least one grouped
# (toggle-select) row. Insert a NEW transmission with 3
# observations.
#
# The server's async hash-migrate (cmd/server/hash_migrate.go)
# recomputes `transmissions.hash` from `raw_hex` via
# ComputeContentHash(), so the inserted hash MUST equal that
# function's output for the chosen raw_hex — otherwise the row
# gets relabelled and the E2E can't find it.
#
# raw_hex 15000102030405060708090a0b0c0d0e0f
# → header=0x15 (route_type=1, payload_type=5)
# → ComputeContentHash(...) = fae0c9e6d357a814
#
# The first_seen / observation timestamps are pinned to a date
# within retentionHours but outside the default 15-min UI
# window so the row is hidden in the default view (keeping
# test-e2e-playwright's first-10-rows hex-pane test
# unaffected) and reachable via the explicit ?timeWindow=0
# deep-link the #1486 test uses.
run: |
sqlite3 test-fixtures/e2e-fixture.db <<'SQL'
-- Sort the seeded row LAST in BOTH default packets views:
-- • flat view sorts by transmissions.id DESC → id=0 puts it last
-- • grouped view (#default for the packets page) sorts by
-- MAX(observations.timestamp) DESC → we must keep our obs
-- timestamps OLDER than every other fixture observation.
-- Fixture (after freshen) has obs timestamps spanning
-- 2026-05-17 16:01:39Z .. 2026-05-28 00:00:00Z (max).
-- Note: freshen only shifts transmissions.first_seen forward
-- to ~now; observation.timestamp is left alone except for
-- the timestamp=0 case.
-- Use 2026-05-15 (~2 days older than the oldest fixture obs)
-- so our row sorts LAST in the grouped view too, keeping
-- test-e2e-playwright's first-10-rows hex-pane test
-- unaffected. The #1486 test still reaches the row via the
-- explicit hash + ?timeWindow=0 deep-link.
INSERT INTO transmissions(id,raw_hex,hash,first_seen,route_type,payload_type,payload_version,decoded_json,channel_hash,from_pubkey)
VALUES (0,'15000102030405060708090a0b0c0d0e0f','fae0c9e6d357a814','2026-05-15T00:00:00Z',1,5,0,'{"type":"CHAN","channel":"#test","text":"#1486 fixture"}',NULL,NULL);
INSERT INTO observations(transmission_id,observer_idx,direction,snr,rssi,score,path_json,timestamp,resolved_path) VALUES
(0,1,'rx',5.0,-95,0,'["AA"]',CAST(strftime('%s','2026-05-15T00:00:00Z') AS INTEGER),'["aa00000000000000000000000000000000000000000000000000000000000000"]'),
(0,2,'rx',5.5,-92,0,'["BB"]',CAST(strftime('%s','2026-05-15T00:00:00Z') AS INTEGER),'["bb00000000000000000000000000000000000000000000000000000000000000"]'),
(0,3,'rx',6.0,-90,0,'["CC"]',CAST(strftime('%s','2026-05-15T00:00:00Z') AS INTEGER),'["cc00000000000000000000000000000000000000000000000000000000000000"]');
SQL
- name: Migrate fixture DB to current schema (#1287)
# Server now ASSERTs schema is migrated and refuses to start
# otherwise (cmd/server/main.go: dbschema.AssertReady). In prod
@@ -302,6 +350,7 @@ jobs:
BASE_URL=http://localhost:13581 node test-issue-1146-path-link-contrast-e2e.js 2>&1 | tee -a e2e-output.txt
BASE_URL=http://localhost:13581 node test-issue-1147-section-order-e2e.js 2>&1 | tee -a e2e-output.txt
BASE_URL=http://localhost:13581 node test-issue-1151-orphan-separators-e2e.js 2>&1 | tee -a e2e-output.txt
BASE_URL=http://localhost:13581 node test-issue-1486-collapse-reopens-detail-e2e.js 2>&1 | tee -a e2e-output.txt
CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-logo-rebrand-e2e.js 2>&1 | tee -a e2e-output.txt
CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-logo-theme-e2e.js 2>&1 | tee -a e2e-output.txt
CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-logo-default-sage-teal-e2e.js 2>&1 | tee -a e2e-output.txt