Compare commits

...

2 Commits

Author SHA1 Message Date
Claude
d59840db73 fix(dropdown-container): prevent empty popover from opening when alwaysShowDropdownButton is set
Gates popover open state and onOpenChange on popoverContent existence,
addressing review feedback that the button could trigger an empty popover
during layout recalculation. Adds a test covering this behavior.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 10:00:51 -07:00
Evan Rusackas
d0c4a0aff0 fix(dashboard): prevent filter dropdown button from disappearing during layout recalculations
Add `alwaysShowDropdownButton` prop to DropdownContainer to keep the dropdown
button visible even when no items are overflowing. This prevents the button from
flickering/disappearing when the filter bar recalculates layout, which caused
a confusing UX where users would lose access to overflowed filters momentarily.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 10:00:51 -07:00
4 changed files with 46 additions and 4 deletions

View File

@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { screen, render } from '@superset-ui/core/spec';
import { screen, render, fireEvent } from '@superset-ui/core/spec';
import { Button, DropdownContainer, Icons } from '..';
const generateItems = (n: number) =>
@@ -158,6 +158,36 @@ test('accepts custom style props', () => {
expect(container).toHaveStyle('padding: 10px');
});
test('shows dropdown button when alwaysShowDropdownButton is true even without overflow', () => {
render(
<DropdownContainer items={generateItems(3)} alwaysShowDropdownButton />,
);
expect(screen.getByTestId('dropdown-container-btn')).toBeInTheDocument();
});
test('does not open popover when alwaysShowDropdownButton is true but there is no popover content', () => {
render(
<DropdownContainer items={generateItems(3)} alwaysShowDropdownButton />,
);
const btn = screen.getByTestId('dropdown-container-btn');
expect(btn).toBeInTheDocument();
fireEvent.click(btn);
// No popover content exists, so the popover should not open
expect(screen.queryByRole('tooltip')).not.toBeInTheDocument();
});
test('does not show dropdown button when alwaysShowDropdownButton is false and not overflowing', () => {
render(
<DropdownContainer
items={generateItems(3)}
alwaysShowDropdownButton={false}
/>,
);
expect(
screen.queryByTestId('dropdown-container-btn'),
).not.toBeInTheDocument();
});
// Integration test that doesn't rely on specific overflow behavior
test('component renders and functions without throwing errors', () => {
const onOverflowingStateChange = jest.fn();

View File

@@ -53,6 +53,7 @@ export const DropdownContainer = forwardRef(
dropdownTriggerTooltip = null,
forceRender,
style,
alwaysShowDropdownButton,
}: DropdownContainerProps,
outerRef: RefObject<DropdownRef>,
) => {
@@ -314,7 +315,7 @@ export const DropdownContainer = forwardRef(
>
{notOverflowedItems.map(item => item.element)}
</div>
{popoverContent && (
{(popoverContent || alwaysShowDropdownButton) && (
<>
<Global
styles={css`
@@ -348,8 +349,13 @@ export const DropdownContainer = forwardRef(
}}
content={popoverContent}
trigger="click"
open={popoverVisible}
onOpenChange={visible => setPopoverVisible(visible)}
open={popoverVisible && !!popoverContent}
onOpenChange={visible => {
// When alwaysShowDropdownButton is set but there is no content
// yet (e.g. during layout recalculation), ignore open attempts
// so the button stays visible without opening an empty popover.
if (popoverContent) setPopoverVisible(visible);
}}
placement="bottom"
forceRender={forceRender}
>

View File

@@ -87,6 +87,11 @@ export interface DropdownContainerProps {
* Force render popover content before it's first opened
*/
forceRender?: boolean;
/**
* Always show the dropdown button, even when no items are overflowing.
* Useful to prevent button flickering during layout recalculations.
*/
alwaysShowDropdownButton?: boolean;
}
export type DropdownRef = HTMLDivElement & { open: () => void };

View File

@@ -630,6 +630,7 @@ const FilterControls: FC<FilterControlsProps> = ({
: undefined
}
forceRender={hasRequiredFirst}
alwaysShowDropdownButton={items.length > 0}
ref={popoverRef}
onOverflowingStateChange={({ overflowed: nextOverflowedIds }) => {
if (