Skip to content

Replace native datalist with custom dropdown for inspect field - #87

Merged
marota merged 3 commits into
mainfrom
claude/fix-asset-search-tied-zoom-gF8Ug
Apr 14, 2026
Merged

marota merged 3 commits into
mainfrom
claude/fix-asset-search-tied-zoom-gF8Ug

Conversation

@marota

@marota marota commented Apr 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

Replaces the native HTML <input list> + <datalist> pattern with a custom-rendered suggestions dropdown component in the inspect search field. This fixes a reliability issue where the datalist dropdown would appear in the wrong window or not at all when a detached tab shares inspect state with the main window.

Key Changes

  • New InspectSearchField component: A custom React component that renders an input field with a manually-managed suggestions dropdown, replacing the unreliable native datalist approach

    • Implements proper focus/blur handling with a debounced close timer to allow option selection before the dropdown unmounts
    • Hides the dropdown when an exact match is found (user has selected an item)
    • Uses onMouseDown on suggestion items to ensure selection registers before the input's onBlur event
    • Positions the dropdown absolutely within a relative container, guaranteeing it stays in the same DOM subtree regardless of window relocation
  • Updated VisualizationPanel: Replaced the inline <input list> + <datalist> markup with the new InspectSearchField component

  • Added regression tests: Two new test cases verify:

    • No <datalist> elements exist in the rendered output
    • The custom dropdown appears and displays filtered suggestions when the field is focused with a partial query

Implementation Details

The custom dropdown solves the core issue by ensuring the suggestion list lives in the same DOM subtree as the input field. When DetachableTabHost physically relocates the overlay to a popup window, the dropdown moves with it, avoiding the Chromium bug where native datalists can render in the wrong window context.

https://claude.ai/code/session_01YKKksRnZEYAehHndPxAXkA

claude added 3 commits April 14, 2026 07:26
… detached

The asset-focus inspect field used `<input list=...>` paired with a
`<datalist>` sibling. When a visualization tab is detached into a
popup window via DetachableTabHost, its overlay subtree is physically
relocated across documents via appendChild. With tied zoom mirroring
state changes between windows, the native datalist dropdown stopped
appearing in the main window while still showing in the detached
popup — a browser-level quirk of cross-document datalist lookups.

Replace the native datalist with a small custom suggestions dropdown
rendered as a plain absolutely-positioned sibling of the input. The
dropdown now lives in whatever DOM subtree its owning overlay lives
in, so it renders reliably in either window regardless of tied state.

Add two regression tests: one locks the inspect field off the native
<datalist>+list pattern, and the other exercises the custom dropdown
rendering path.
The standalone interface exposes a "💡 Make a first guess" button
when the Selected Actions panel is empty — it opens the Manual
Selection search so the user can start exploring actions without
first having to run a full analysis. The React frontend was missing
this entry point, leaving users on the empty state with only the
italic "Select an action manually…" hint.

Mirror the standalone behavior in ActionFeed: when selectedEntries
is empty, render the dashed-border "Make a first guess" button
alongside the hint and wire it to the existing handleOpenSearch
callback.

Add three regression tests: the button appears in the empty state,
clicking it opens the manual search dropdown, and the button is
hidden once any action is selected.
Before this fix, a user's manually added ("first guess") action would
disappear from the Selected Actions section once the analysis was run
and displayed — especially the overlap case, where the same action
was ALSO returned by the recommender. Two issues were at play:

1. `handleRunAnalysis` called the full `clearContingencyState`, which
   wiped `result`, `selectedActionIds` and `manuallyAddedIds` before
   the stream even started, so the manual action was gone by the time
   the results came back.
2. `handleDisplayPrioritizedActions` preserved manual actions by
   looking them up via `selectedActionIds.has(id)` — but with that
   set already cleared, nothing was preserved, and for the overlap
   case the plain recommender entry (missing `is_manual: true`)
   replaced the manual one.

Mirror the standalone interface's behavior:

- Add a narrower `resetForAnalysisRun` in App.tsx and wire
  `wrappedRunAnalysis` to it. It filters `result.actions` down to the
  `is_manual=true` subset (clearing pdf / lines_overloaded so the UI
  still shows the in-progress state), trims `selectedActionIds` down
  to the manually-added ids, and leaves `manuallyAddedIds` intact.
- Make `handleDisplayPrioritizedActions` preserve actions flagged
  `is_manual` directly (not only those in `selectedActionIds`) and
  re-apply `is_manual: true` on the preserved entries so the overlap
  case keeps the user's manual flag and lands back in the Selected
  bucket. Manual entries now win over their analysis-suggested twins
  in the merged result.

Regression tests:

- useAnalysis: the overlap case — manual action survives
  `handleDisplayPrioritizedActions` with an empty selected set, keeps
  `is_manual=true`, and coexists with the other recommender entries.
- ActionFeed: when a manually added action is also present in
  `actionScores`, it stays rendered in Selected and the yellow
  "also recommended" warning fires with the overlapping id.
@marota
marota merged commit bb49655 into main Apr 14, 2026
2 checks passed
marota pushed a commit that referenced this pull request Apr 30, 2026
Reconciliation of section 2 (0.5.0):
- Drop misattributed PRs that were actually pre-rebrand: save/reload
  (#49/#52), MW Start (#62), interaction-logging (#64), SLD highlights
  (#63), load shedding initial integration (#61). All now properly
  cited in section 1.5–1.6.
- Disambiguate App.tsx refactor history: PR #56 (hooks, 2100 → 800,
  pre-rebrand) vs PR #74 (components, 1000 → 650, 0.5.0) vs PR #75
  (memoization Phase 2, same LoC).
- Add accurate 0.5.0 PRs: #66 (vectorization w/ benchmark table),
  #69/#70/#71 (UI polish), #72 (curtailment), #73 (loads_p/gens_p
  format + configurable MW), #74/#75 (App.tsx decomposition),
  #78 (PST tap re-simulation), #84/#86/#87/#90 (detachable tabs).
- Add a recap table summarizing what's truly new in 0.5.0.

Diagrams added (Mermaid, GitHub-rendered):
- Gantt timeline of all 4 phases (top of doc).
- High-level architecture (frontend / backend / data).
- Two-step analysis sequence diagram (section 1.6).
- App.tsx LoC evolution flow (section 2.4).
- Backend mixin decomposition before/after PR #104/#106 (section 3).
- PyPSA-EUR pipeline flowchart (section 4).

https://claude.ai/code/session_01Pg7fuCUG2edfm5PyHS6SbN
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants