Skip to content

fix(dashboard): prevent native filter Select dropdown from obscuring input on small screens#40297

Open
BansonVuong wants to merge 1 commit into
apache:masterfrom
BansonVuong:fix/dashboard-filter-popup-container-34135
Open

fix(dashboard): prevent native filter Select dropdown from obscuring input on small screens#40297
BansonVuong wants to merge 1 commit into
apache:masterfrom
BansonVuong:fix/dashboard-filter-popup-container-34135

Conversation

@BansonVuong
Copy link
Copy Markdown

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 getSelectPopupContainer helper that mounts to document.body when the filter is not overflowed. That lets DROPDOWN_ALIGN_BOTTOM's overflow: { 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

  1. Open the example Sales Dashboard.
  2. Reduce the viewport height (or increase browser zoom) until there isn't enough room below the Value filter for a normal dropdown.
  3. Click a Value filter.
  4. Confirm the dropdown flips above the input instead of obscuring it.
  5. Verify these still work as before:
    • Overflowed native filters (filter bar with many filters in horizontal mode).
    • Filter-config modal value selects.
    • Non-dashboard Select usage of this plugin.

Unit tests: npm test -- SelectFilterPlugin.test (36/36 pass, including 3 new tests for the helper).

ADDITIONAL INFORMATION

…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
@dosubot dosubot Bot added change:frontend Requires changing the frontend dashboard:native-filters Related to the native filters of the Dashboard design:accessibility Related to accessibility standards labels May 20, 2026
Comment on lines +112 to +117
if (
appSection === AppSection.Dashboard ||
appSection === AppSection.FilterBar
) {
return () => document.body;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 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

@bito-code-review
Copy link
Copy Markdown
Contributor

The PR diff shows that the getSelectPopupContainer function was added to handle popup container logic based on appSection and showOverflow. The function ensures that for dashboard and filter bar sections, the popup is mounted to document.body when not overflowing, and to the parent node otherwise. The test cases added validate this behavior. To resolve the issue, the appSection should be propagated from the native filter bar into SuperChart with appSection={AppSection.Dashboard} or AppSection.FilterBar in FilterValue.tsx. This will ensure the correct container is used for the dropdowns.

superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx

export const getSelectPopupContainer = (
  appSection: AppSection,
  showOverflow: boolean,
  parentRef?: RefObject<any>,
) => {
  if (showOverflow) {
    return () => (parentRef?.current as HTMLElement) || document.body;
  }

  if (
    appSection === AppSection.Dashboard ||
    appSection === AppSection.FilterBar
  ) {
    return () => document.body;
  }

  return (trigger: HTMLElement) =>
    (trigger?.parentNode as HTMLElement) || document.body;
};

superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx

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);
});

Comment on lines +625 to +629
getPopupContainer={getSelectPopupContainer(
appSection,
showOverflow,
parentRef,
)}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 fix
👍 | 👎

Copy link
Copy Markdown
Contributor

@bito-code-review bito-code-review Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #06c067

Actionable Suggestions - 1
  • superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx - 1
Additional Suggestions - 1
  • superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx - 1
    • Misleading test name · Line 221-221
      Test 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

AI Code Review powered by Bito Logo

Comment on lines +208 to +230
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);
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing test for FilterBar

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

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

Labels

change:frontend Requires changing the frontend dashboard:native-filters Related to the native filters of the Dashboard design:accessibility Related to accessibility standards size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant