fix(dashboard): prevent native filter Select dropdown from obscuring input on small screens#40297
Conversation
…input on small screens The Value filter dropdown in the dashboard native filter bar was mounting its popup to the trigger's parent node. On smaller viewports or with elevated browser zoom the constrained parent prevented antd's overflow logic from flipping the menu, so the dropdown rendered over the input field. Route Dashboard/FilterBar selects through a shared `getSelectPopupContainer` helper that mounts to `document.body` when the filter is not overflowed, allowing `DROPDOWN_ALIGN_BOTTOM`'s `adjustY` flip behavior to position the menu against the viewport. Overflowed filters and the filter-config modal keep their existing container behavior. Fixes apache#34135
| if ( | ||
| appSection === AppSection.Dashboard || | ||
| appSection === AppSection.FilterBar | ||
| ) { | ||
| return () => document.body; | ||
| } |
There was a problem hiding this comment.
🟠 Architect Review — HIGH
Dashboard native filter Selects never set ChartProps.appSection, so getSelectPopupContainer is called with appSection undefined in the FilterBar path; the Dashboard/FilterBar branch (lines 112–117) is never taken and non-overflowing dropdowns still mount to the trigger parent instead of document.body.
Suggestion: Propagate the correct AppSection from the native filter bar into SuperChart (e.g., appSection={AppSection.Dashboard} or AppSection.FilterBar in FilterValue.tsx) so transformProps can pass it through to PluginFilterSelect, and add an integration test that renders a filter via FilterValue/SuperChart and asserts getSelectPopupContainer returns document.body for non-overflowing dashboard filters.
Fix in Cursor | Fix in VSCode Claude
(Use Cmd/Ctrl + Click for best experience)
Prompt for AI Agent 🤖
This is an **Architect / Logical Review** comment left during a code review. These reviews are first-class, important findings — not optional suggestions. Do NOT dismiss this as a 'big architectural change' just because the title says architect review; most of these can be resolved with a small, localized fix once the intent is understood.
**Path:** superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx
**Line:** 112:117
**Comment:**
*HIGH: Dashboard native filter Selects never set ChartProps.appSection, so getSelectPopupContainer is called with appSection undefined in the FilterBar path; the Dashboard/FilterBar branch (lines 112–117) is never taken and non-overflowing dropdowns still mount to the trigger parent instead of document.body.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
If a suggested approach is provided above, use it as the authoritative instruction. If no explicit code suggestion is given, you MUST still draft and apply your own minimal, localized fix — do not punt back with 'no suggestion provided, review manually'. Keep the change as small as possible: add a guard clause, gate on a loading state, reorder an await, wrap in a conditional, etc. Do not refactor surrounding code or expand scope beyond the finding.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix|
The PR diff shows that the superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx |
| getPopupContainer={getSelectPopupContainer( | ||
| appSection, | ||
| showOverflow, | ||
| parentRef, | ||
| )} |
There was a problem hiding this comment.
Suggestion: The popup-container decision now depends on showOverflow, but this prop is not the overflow flag used by the filter-bar pipeline (FilterControls computes and passes overflow, not showOverflow). As a result, overflowed filters are treated as non-overflowed and this code routes their dropdowns to document.body, which can break positioning inside the "More filters" popover. Wire the actual overflow state into this call (or use the existing overflow signal passed through display settings) so overflowed filters keep the popover-scoped container. [api mismatch]
Severity Level: Major ⚠️
- ❌ Overflowed dashboard filters misplace dropdowns outside popover.
- ⚠️ "More filters" popover UI appears visually broken.
- ⚠️ Users may see dropdown overlapping unrelated dashboard content.Steps of Reproduction ✅
1. In
`superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx`
(around lines 158–176 and 184–195), note that `overflowedByIndex` is computed and the
boolean `overflow` is passed into `filterControlFactory(index, filterBarOrientation,
overflow)`, indicating which filters are actually overflowed into the "More filters"
popover.
2. In
`superset-frontend/src/dashboard/components/nativeFilters/FilterBar/useFilterControlFactory.tsx`
(around lines 53–80), see that `filterControlFactory` passes `overflow` into
`<FilterControl overflow={overflow} ... />` but does not pass any `showOverflow` prop, so
`showOverflow` in `FilterControlProps` is always `undefined` in real dashboard usage.
3. In
`superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControl.tsx`
(around lines 44–56 and 139–150), observe that `FilterControl` simply forwards
`showOverflow` to `<FilterValue showOverflow={showOverflow} ... overflow={overflow} />`,
meaning `FilterValue` receives the correct overflow flag via `overflow` and an
always-undefined `showOverflow`.
4. In
`superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx`
(around lines 315–321 and 354–371), see that `displaySettings` is built with
`isOverflowingFilterBar: overflow`, and then `<SuperChart showOverflow={showOverflow}
displaySettings={displaySettings} parentRef={parentRef} ... />` is rendered; thus the
actual overflow state flows as `displaySettings.isOverflowingFilterBar`, not via
`showOverflow`.
5. In `superset-frontend/src/filters/components/Select/transformProps.ts` (around lines
64–83), confirm that the Select filter plugin's props are built from `chartProps`: it
passes `displaySettings?.isOverflowingFilterBar` into
`PluginFilterSelectProps.isOverflowingFilterBar` but never maps `chartProps.showOverflow`
to `PluginFilterSelectProps.showOverflow`, so `showOverflow` inside `PluginFilterSelect`
stays `undefined` when used through `SuperChart`.
6. In `superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx` (lines
103–121 and 146–168, 625–629), see that `getSelectPopupContainer(appSection, showOverflow,
parentRef)` uses `showOverflow` to decide whether to mount the dropdown to `parentRef`
(overflowed case) or `document.body` (non-overflowed dashboard/filter-bar case). Because
`showOverflow` is never wired from the filter-bar pipeline, even overflowed filters inside
the "More filters" popover will have `showOverflow === false`, causing their dropdowns to
mount to `document.body` instead of the popover container and breaking the intended
in-popover positioning.Fix in Cursor | Fix in VSCode Claude
(Use Cmd/Ctrl + Click for best experience)
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx
**Line:** 625:629
**Comment:**
*Api Mismatch: The popup-container decision now depends on `showOverflow`, but this prop is not the overflow flag used by the filter-bar pipeline (`FilterControls` computes and passes `overflow`, not `showOverflow`). As a result, overflowed filters are treated as non-overflowed and this code routes their dropdowns to `document.body`, which can break positioning inside the "More filters" popover. Wire the actual overflow state into this call (or use the existing overflow signal passed through display settings) so overflowed filters keep the popover-scoped container.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fixThere was a problem hiding this comment.
Code Review Agent Run #06c067
Actionable Suggestions - 1
-
superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx - 1
- Missing test for FilterBar · Line 208-230
Additional Suggestions - 1
-
superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx - 1
-
Misleading test name · Line 221-221Test name 'keeps trigger parent popup container outside dashboard filter bar' is misleading. The implementation returns `trigger.parentNode` for any non-Dashboard/FilterBar appSection (SelectFilterPlugin.tsx:119-120), not specifically 'filter bar'. Consider renaming to reflect the actual behavior.
-
Review Details
-
Files reviewed - 2 · Commit Range:
78c8816..78c8816- superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx
- superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx
-
Files skipped - 0
-
Tools
- Whispers (Secret Scanner) - ✔︎ Successful
- Detect-secrets (Secret Scanner) - ✔︎ Successful
Bito Usage Guide
Commands
Type the following command in the pull request comment and save the comment.
-
/review- Manually triggers a full AI review. -
/pause- Pauses automatic reviews on this pull request. -
/resume- Resumes automatic reviews. -
/resolve- Marks all Bito-posted review comments as resolved. -
/abort- Cancels all in-progress reviews.
Refer to the documentation for additional commands.
Configuration
This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.
Documentation & Help
| test('uses document.body as popup container in dashboard filter bar', () => { | ||
| const container = getSelectPopupContainer(AppSection.Dashboard, false); | ||
| expect(container(document.createElement('div'))).toBe(document.body); | ||
| }); | ||
|
|
||
| test('uses parent ref as popup container for overflowed filters', () => { | ||
| const parentNode = document.createElement('div'); | ||
| const container = getSelectPopupContainer(AppSection.Dashboard, true, { | ||
| current: parentNode, | ||
| }); | ||
| expect(container()).toBe(parentNode); | ||
| }); | ||
|
|
||
| test('keeps trigger parent popup container outside dashboard filter bar', () => { | ||
| const triggerParent = document.createElement('div'); | ||
| const trigger = document.createElement('div'); | ||
| triggerParent.appendChild(trigger); | ||
| const container = getSelectPopupContainer( | ||
| AppSection.FilterConfigModal, | ||
| false, | ||
| ); | ||
| expect(container(trigger)).toBe(triggerParent); | ||
| }); |
There was a problem hiding this comment.
The implementation at lines 113-114 handles AppSection.FilterBar but no test covers this case. The AppSection enum includes FilterBar = 'FILTER_BAR' (Base.ts:54) and the implementation explicitly checks for it. Add a test: test('uses document.body as popup container in filter bar', () => { const container = getSelectPopupContainer(AppSection.FilterBar, false); expect(container(document.createElement('div'))).toBe(document.body); });
Code suggestion
Check the AI-generated fix before applying
--- superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx (lines 208-230) ---
208: test('uses document.body as popup container in dashboard filter bar', () => {
209: const container = getSelectPopupContainer(AppSection.Dashboard, false);
210: expect(container(document.createElement('div'))).toBe(document.body);
211: });
212: +
213: + test('uses document.body as popup container in filter bar', () => {
214: + const container = getSelectPopupContainer(AppSection.FilterBar, false);
215: + expect(container(document.createElement('div'))).toBe(document.body);
216: + });
217: +
218: test('uses parent ref as popup container for overflowed filters', () => {
219: const parentNode = document.createElement('div');
220: const container = getSelectPopupContainer(AppSection.Dashboard, true, {
221: current: parentNode,
222: });
223: expect(container()).toBe(parentNode);
224: });
225: +
226: test('keeps trigger parent popup container outside dashboard filter bar', () => {
227: const triggerParent = document.createElement('div');
228: const trigger = document.createElement('div');
229: triggerParent.appendChild(trigger);
230: const container = getSelectPopupContainer(
231: AppSection.FilterConfigModal,
232: false,
233: );
234: expect(container(trigger)).toBe(triggerParent);
235: });
Code Review Run #06c067
Should Bito avoid suggestions like this for future reviews? (Manage Rules)
- Yes, avoid them
SUMMARY
The Value filter dropdown in the dashboard native filter bar was mounting its popup to the trigger's parent node. On smaller viewports or with elevated browser zoom the constrained parent prevented antd's overflow logic from flipping the menu, so the dropdown rendered over the input field.
This PR routes Dashboard/FilterBar Select filters through a new shared
getSelectPopupContainerhelper that mounts todocument.bodywhen the filter is not overflowed. That letsDROPDOWN_ALIGN_BOTTOM'soverflow: { adjustY: 1 }position the menu against the viewport — so the popup flips above the input when there is no space below. Overflowed filters (which render inside the filter-bar popover) and the filter-config modal keep their existing container behavior to avoid regressing those positioning contexts.BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
TESTING INSTRUCTIONS
Unit tests:
npm test -- SelectFilterPlugin.test(36/36 pass, including 3 new tests for the helper).ADDITIONAL INFORMATION