Skip to content

ui: route partial-response localStorage through a shared accessor - #8992

Open
donaldraph wants to merge 1 commit into
thanos-io:mainfrom
donaldraph:fix-localstorage-bypass
Open

donaldraph wants to merge 1 commit into
thanos-io:mainfrom
donaldraph:fix-localstorage-bypass

Conversation

@donaldraph

@donaldraph donaldraph commented Aug 26, 2026

Copy link
Copy Markdown
  • I added CHANGELOG entry for this change.
  • Change is not relevant to the end user.

Changes

Every localStorage read/write in the React app goes through the useLocalStorage hook, except the usePartialResponse key in Panel.tsx and PanelList.tsx, which touch localStorage directly. Panel is a class component and can't call the hook, which is why it bypassed it.

I pulled the raw operations out into three plain functions in useLocalStorage.tsxgetStorageItem, setStorageItem, hasStorageItem — so both hook and non-hook callers go through the same accessor. useLocalStorage becomes a thin wrapper over them, and I routed the three bypass sites through them instead of touching localStorage directly. After this, no raw localStorage. access remains in the app outside that one module.

I intended this as a read/write refactor with no behavior change, so I checked the full git history of the key: it has only ever been written as JSON.stringify(<boolean>) since it was introduced, so under normal use the stored value is always "true", "false", or absent.

One thing I want to flag: routing reads through JSON.parse (which the old raw-string comparison didn't do) would throw on a non-JSON value. I confirmed that can't happen through app code, only via manual localStorage tampering or corruption — but to keep the old "never crashes on a bad value" behavior, I added a try/catch in getStorageItem that falls back to the initial value on a parse error, the same approach jaeger-ui uses for this. I used hasStorageItem (no parsing) for the componentDidMount existence check to keep exact parity there.

One residual I'll disclose: handleChangePartialResponse now parses via the typed accessor, so a whitespace-padded value like " true " would parse to true where the old === 'true' didn't match. No app code has ever produced such a value, and closing it fully would mean abandoning the typed accessor for that one read, which didn't seem worth it — happy to change that if you'd prefer.

Verification

I verified behavior parity empirically across every value the key could hold before settling on this shape. make react-app-lint and make react-app-test both pass (260 tests).

Signed-off-by: DonaldRaph <62525712+donaldraph@users.noreply.github.com>
@codecov

codecov Bot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 64.52%. Comparing base (51afa7e) to head (6a8c1e4).
⚠️ Report is 21 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8992      +/-   ##
==========================================
- Coverage   64.53%   64.52%   -0.02%     
==========================================
  Files         289      289              
  Lines       37366    37366              
==========================================
- Hits        24113    24109       -4     
- Misses      11156    11161       +5     
+ Partials     2097     2096       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant