fix(plugin-dashboard): optionsFrom filters commit the raw value, not the display label (#4465) - #4504
Merged
Merged
Conversation
…the display label (#4465) A dashboard global filter sourced from `optionsFrom` built its options from a server GROUP BY, whose response carries both forms of every grouped value: `rows` holds the server-resolved display LABELS (`{status: 'In Review'}`) and the index-aligned `drillRawRows` holds the RAW stored values (`{status: 'in_review'}`). `DashboardFilterBar` read the value off `rows`, so picking an option broadcast `runtimeFilter {status: 'In Review'}` — a value no record carries — into every bound widget, and each repainted to "No rows". Options are now paired index-wise: value from `drillRawRows[i]`, label from the displayed row. The reading discipline is mirrored from the drill path, which consumes this exact response correctly (`DatasetWidget`'s `openDrill` reads `drillRawRows?.[index]`; `buildDatasetDrillFilter` documents that the visible row carries the label and would mis-filter). No helper is shared with it: that one builds an ObjectQL filter keyed by object field and has no overlap with pairing an option list. Three abstentions all land on the previous read rather than on a guess: no `drillRawRows`, a length disagreement with `rows` (index pairing would commit another bucket's raw value — silently wrong in a new way), and raw rows that carry no such field (a date-bucketed dimension the server excludes by design). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
Collaborator
Author
|
ACCEPT — step-7 复核 by PM session
Flipping ready + arming auto-merge. Generated by Claude Code Generated by Claude Code |
yinlianghui
marked this pull request as ready for review
August 12, 2026 22:16
This was referenced Aug 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #4465
Defect
A dashboard global filter sourced from
optionsFromcommitted the rendered LABEL as its value, so every widget it drives queried for a value no record carries and repainted to "No rows".The option source is a server GROUP BY, and that response carries both forms of every grouped value:
rows— server-resolved display labels:[{status: 'Backlog'}, …, {status: 'In Review'}, …]drillRawRows— the index-aligned raw values:[{status: 'backlog'}, …, {status: 'in_review'}, …]DashboardFilterBar.tsxreadr?.[from.valueField]offrowsand never consulteddrillRawRows, so picking "In Review" broadcastruntimeFilter {status: 'In Review'}. Statically declaredoptionswere never affected (Revenue Pulse'sregionshowsEMEA, commitsemea).Fix
Options are paired index-wise — value from
drillRawRows[i], label from the displayed row — in one new module-localpairOptionRowshelper both option paths call.The reading discipline is mirrored from the drill path, which consumes this exact response correctly and always has:
DatasetWidget'sopenDrillreadsdrillRawRows?.[index]at the same index the display row was resolved at, andbuildDatasetDrillFilterstates why — "the dimension's RAW grouped value … NOT the visible row which carries the display LABEL — a select/lookup label would mis-filter". No helper is shared with it: that one builds an ObjectQL filter keyed by object FIELD, ANDingruntimeFilterand date ranges, which has no overlap with pairing an option list; reshaping it would have meant refactoring the drill path to serve this call site.Three abstentions, all landing on the previous read rather than on a guess:
drillRawRowsin the responserows, exactly as beforerowsanddrillRawRowsdisagree in LENGTHrows— pairing anyway commits another bucket's raw value, silently wrong in a new way and indistinguishable from a correct filter that legitimately matched nothing; labels-as-values is at least visibly wrongvalueFieldrows— adateGranularitydimension is excluded fromdrillRawRowsby the server (it sendsdrillRangesinstead), so this is the server saying "no raw form of this dimension"The client-side
findfallback is unchanged in behavior: real records already carry the stored value, so it pairs with no raw sidecar.Red-first — the COMMITTED value
The pre-existing
DashboardFilterBar.options.test.tsxasserts only that the option list renders, which is true on both sides of the defect; that gap was its cover. The newDashboardFilterBar.optionsFromRawValue.test.tsxasserts what is committed. Againstorigin/main'sDashboardFilterBar.tsxwith the new tests in place: 4 failed | 6 passed (10), verbatim —The first case goes through
DashboardRendererend-to-end, so the assertion is the bound widget's real re-query — the card'sruntimeFilter {"status":"In Review"}reproduced as a test. Post-fix: 10 passed (10).The 6 that pass on BOTH sides are the must-not-change pins: the trigger still displays the label and never the raw token (its own test, so it actually runs pre-fix); static metadata-declared
optionscommitemea/ displayEMEAand never consult the data source; a response with nodrillRawRowskeeps today's read; the length-mismatch abstention; the no-such-field abstention; and the client-sidefindpath.Verification
pnpm exec vitest run packages/plugin-dashboard/— 46 files, 381 tests passed (includes the flaky: DatasetWidget.dottedDimensionTable's "issues the ONE read" case asserts a fetch AFTER waiting only on the rendered cell #4487 flake file, green).pnpm --filter @object-ui/plugin-dashboard type-check— clean.TEST_DEBT), so they were checked separately under a temp project lifting the test exclusion: the new file contributes 0 errors (14 pre-existing, unchanged).dist/index.d.tsbuilt from this branch and fromorigin/mainis byte-identical — runtime-only, hencepatch.check-control-bytes,check-changeset-presence,check-changeset-no-major,check-changeset-fixed,check-type-check-coverage— all green. ESLint on the changed files: 0 errors.Generated by Claude Code