Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Adds overflow to the DropdownContainer popover #22250

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

/superset-frontend/src/components/Select/ @michael-s-molina @geido @ktmud
/superset-frontend/src/components/MetadataBar/ @michael-s-molina
/superset-frontend/src/components/DropdownContainer/ @michael-s-molina

# Notify Helm Chart maintainers about changes in it

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import Button from '../Button';
import DropdownContainer, { DropdownContainerProps, Ref } from '.';

export default {
title: 'DropdownContainer',
title: 'Design System/Components/DropdownContainer',
component: DropdownContainer,
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Meta, Source } from '@storybook/addon-docs';

<Meta title="DropdownContainer/Overview" />
<Meta title="Design System/Components/DropdownContainer/Overview" />

# Usage

Expand All @@ -14,4 +14,4 @@ the available width, they are displayed vertically in a dropdown. Some of the co

The component accepts any React element which ensures a high level of variability in Superset.

To check the component in detail and its interactions, check the [DropdownContainer](/story/dropdowncontainer--component) page.
To check the component in detail and its interactions, check the [DropdownContainer](/story/design-system-components-dropdowncontainer--component) page.
137 changes: 94 additions & 43 deletions superset-frontend/src/components/DropdownContainer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ import React, {
useLayoutEffect,
useMemo,
useState,
useRef,
} from 'react';
import { Global } from '@emotion/react';
import { css, t, useTheme } from '@superset-ui/core';
import { useResizeDetector } from 'react-resize-detector';
import { usePrevious } from 'src/hooks/usePrevious';
Expand All @@ -35,6 +37,8 @@ import Icons from '../Icons';
import Button from '../Button';
import Popover from '../Popover';

const MAX_HEIGHT = 500;

/**
* Container item.
*/
Expand Down Expand Up @@ -104,12 +108,12 @@ const DropdownContainer = forwardRef(
{
items,
onOverflowingStateChange,
dropdownContent: getPopoverContent,
dropdownRef: popoverRef,
dropdownStyle: popoverStyle = {},
dropdownTriggerCount: popoverTriggerCount,
dropdownTriggerIcon: popoverTriggerIcon,
dropdownTriggerText: popoverTriggerText = t('More'),
dropdownContent,
dropdownRef,
dropdownStyle = {},
dropdownTriggerCount,
dropdownTriggerIcon,
dropdownTriggerText = t('More'),
style,
}: DropdownContainerProps,
outerRef: RefObject<Ref>,
Expand All @@ -124,6 +128,13 @@ const DropdownContainer = forwardRef(
// We use React.useState to be able to mock the state in Jest
const [overflowingIndex, setOverflowingIndex] = React.useState<number>(-1);

let targetRef = useRef<HTMLDivElement>(null);
if (dropdownRef) {
targetRef = dropdownRef;
}

const [showOverflow, setShowOverflow] = useState(false);

useLayoutEffect(() => {
const container = current?.children.item(0);
if (container) {
Expand Down Expand Up @@ -214,32 +225,44 @@ const DropdownContainer = forwardRef(

const popoverContent = useMemo(
() =>
getPopoverContent || overflowingCount ? (
dropdownContent || overflowingCount ? (
<div
css={css`
display: flex;
flex-direction: column;
gap: ${theme.gridUnit * 4}px;
`}
data-test="dropdown-content"
style={popoverStyle}
ref={popoverRef}
style={dropdownStyle}
ref={targetRef}
>
{getPopoverContent
? getPopoverContent(overflowedItems)
{dropdownContent
? dropdownContent(overflowedItems)
: overflowedItems.map(item => item.element)}
</div>
) : null,
[
getPopoverContent,
overflowedItems,
dropdownContent,
overflowingCount,
popoverRef,
popoverStyle,
theme.gridUnit,
dropdownStyle,
overflowedItems,
],
);

useLayoutEffect(() => {
if (popoverVisible) {
// Measures scroll height after rendering the elements
setTimeout(() => {
if (targetRef.current) {
// We only set overflow when there's enough space to display
// Select's popovers because they are restrained by the overflow property.
setShowOverflow(targetRef.current.scrollHeight > MAX_HEIGHT);
}
}, 100);
}
}, [popoverVisible]);

useImperativeHandle(
outerRef,
() => ({
Expand Down Expand Up @@ -271,35 +294,63 @@ const DropdownContainer = forwardRef(
{notOverflowedItems.map(item => item.element)}
</div>
{popoverContent && (
<Popover
content={popoverContent}
trigger="click"
visible={popoverVisible}
onVisibleChange={visible => setPopoverVisible(visible)}
placement="bottom"
>
<Button buttonStyle="secondary">
{popoverTriggerIcon}
{popoverTriggerText}
<Badge
count={popoverTriggerCount ?? overflowingCount}
css={css`
margin-left: ${popoverTriggerCount ?? overflowingCount
? '8px'
: '0'};
`}
/>
<Icons.DownOutlined
iconSize="m"
iconColor={theme.colors.grayscale.light1}
css={css`
.anticon {
display: flex;
<>
<Global
styles={css`
.ant-popover-inner-content {
max-height: ${MAX_HEIGHT}px;
overflow: ${showOverflow ? 'auto' : 'visible'};
padding: ${theme.gridUnit * 3}px ${theme.gridUnit * 4}px;
// Some OS versions only show the scroll when hovering.
// These settings will make the scroll always visible.
::-webkit-scrollbar {
-webkit-appearance: none;
width: 14px;
}
::-webkit-scrollbar-thumb {
border-radius: 9px;
background-color: ${theme.colors.grayscale.light1};
border: 3px solid transparent;
background-clip: content-box;
}
::-webkit-scrollbar-track {
background-color: ${theme.colors.grayscale.light4};
border-left: 1px solid ${theme.colors.grayscale.light2};
}
`}
/>
</Button>
</Popover>
}
`}
/>
<Popover
content={popoverContent}
trigger="click"
visible={popoverVisible}
onVisibleChange={visible => setPopoverVisible(visible)}
placement="bottom"
>
<Button buttonStyle="secondary">
{dropdownTriggerIcon}
{dropdownTriggerText}
<Badge
count={dropdownTriggerCount ?? overflowingCount}
css={css`
margin-left: ${dropdownTriggerCount ?? overflowingCount
? `${theme.gridUnit * 2}px`
: '0'};
`}
/>
<Icons.DownOutlined
iconSize="m"
iconColor={theme.colors.grayscale.light1}
css={css`
.anticon {
display: flex;
}
`}
/>
</Button>
</Popover>
</>
)}
</div>
);
Expand Down