feat: Adds more customization properties to DropdownContainer (#22031)

This commit is contained in:
Michael S. Molina 2022-11-04 10:20:49 -04:00 committed by GitHub
parent 9b6322b640
commit b040211970
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 223 additions and 180 deletions

View File

@ -20,7 +20,8 @@ import React, { useEffect, useState } from 'react';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { css } from '@superset-ui/core'; import { css } from '@superset-ui/core';
import Select from '../Select/Select'; import Select from '../Select/Select';
import DropdownContainer, { DropdownContainerProps } from '.'; import Button from '../Button';
import DropdownContainer, { DropdownContainerProps, Ref } from '.';
export default { export default {
title: 'DropdownContainer', title: 'DropdownContainer',
@ -31,6 +32,7 @@ const ITEMS_COUNT = 6;
const ITEM_OPTIONS = 10; const ITEM_OPTIONS = 10;
const MIN_WIDTH = 700; const MIN_WIDTH = 700;
const MAX_WIDTH = 1500; const MAX_WIDTH = 1500;
const HEIGHT = 400;
const itemsOptions = Array.from({ length: ITEM_OPTIONS }).map((_, i) => ({ const itemsOptions = Array.from({ length: ITEM_OPTIONS }).map((_, i) => ({
label: `Option ${i}`, label: `Option ${i}`,
@ -60,23 +62,25 @@ const generateItems = (overflowingState?: OverflowingState) =>
export const Component = (props: DropdownContainerProps) => { export const Component = (props: DropdownContainerProps) => {
const [items, setItems] = useState<ItemsType>([]); const [items, setItems] = useState<ItemsType>([]);
const [overflowingState, setOverflowingState] = useState<OverflowingState>(); const [overflowingState, setOverflowingState] = useState<OverflowingState>();
const containerRef = React.useRef<Ref>(null);
useEffect(() => { useEffect(() => {
setItems(generateItems(overflowingState)); setItems(generateItems(overflowingState));
}, [overflowingState]); }, [overflowingState]);
return ( return (
<div>
<div <div
css={css` css={css`
position: relative;
overflow: auto; overflow: auto;
min-width: ${MIN_WIDTH}px; min-width: ${MIN_WIDTH}px;
width: ${MIN_WIDTH}px; width: ${MIN_WIDTH}px;
max-width: ${MAX_WIDTH}px; max-width: ${MAX_WIDTH}px;
height: 80vh; height: ${HEIGHT}px;
border: 1px solid lightgray; border: 1px solid lightgray;
resize: horizontal; resize: horizontal;
padding: 24px; padding: 24px;
margin-bottom: 12px;
`} `}
> >
<DropdownContainer <DropdownContainer
@ -87,12 +91,13 @@ export const Component = (props: DropdownContainerProps) => {
setOverflowingState(value); setOverflowingState(value);
} }
}} }}
ref={containerRef}
/> />
</div>
<Button onClick={() => containerRef.current?.open()}>Open</Button>
<span <span
css={css` css={css`
position: absolute; margin-left: ${MIN_WIDTH - 340}px;
right: 20px;
bottom: 8px;
color: gray; color: gray;
`} `}
> >

View File

@ -18,8 +18,11 @@
*/ */
import React, { import React, {
CSSProperties, CSSProperties,
forwardRef,
ReactElement, ReactElement,
RefObject,
useEffect, useEffect,
useImperativeHandle,
useLayoutEffect, useLayoutEffect,
useMemo, useMemo,
useState, useState,
@ -68,10 +71,18 @@ export interface DropdownContainerProps {
* Option to customize the content of the popover. * Option to customize the content of the popover.
*/ */
popoverContent?: (overflowedItems: Item[]) => ReactElement; popoverContent?: (overflowedItems: Item[]) => ReactElement;
/**
* Popover ref.
*/
popoverRef?: RefObject<HTMLDivElement>;
/** /**
* Popover additional style properties. * Popover additional style properties.
*/ */
popoverStyle?: CSSProperties; popoverStyle?: CSSProperties;
/**
* Displayed count in the popover trigger.
*/
popoverTriggerCount?: number;
/** /**
* Icon of the popover trigger. * Icon of the popover trigger.
*/ */
@ -86,21 +97,30 @@ export interface DropdownContainerProps {
style?: CSSProperties; style?: CSSProperties;
} }
const DropdownContainer = ({ export type Ref = HTMLDivElement & { open: () => void };
const DropdownContainer = forwardRef(
(
{
items, items,
onOverflowingStateChange, onOverflowingStateChange,
popoverContent, popoverContent,
popoverRef,
popoverStyle = {}, popoverStyle = {},
popoverTriggerCount,
popoverTriggerIcon, popoverTriggerIcon,
popoverTriggerText = t('More'), popoverTriggerText = t('More'),
style, style,
}: DropdownContainerProps) => { }: DropdownContainerProps,
outerRef: RefObject<Ref>,
) => {
const theme = useTheme(); const theme = useTheme();
const { ref, width = 0 } = useResizeDetector<HTMLDivElement>(); const { ref, width = 0 } = useResizeDetector<HTMLDivElement>();
const previousWidth = usePrevious(width) || 0; const previousWidth = usePrevious(width) || 0;
const { current } = ref; const { current } = ref;
const [overflowingIndex, setOverflowingIndex] = useState<number>(-1); const [overflowingIndex, setOverflowingIndex] = useState<number>(-1);
const [itemsWidth, setItemsWidth] = useState<number[]>([]); const [itemsWidth, setItemsWidth] = useState<number[]>([]);
const [popoverVisible, setPopoverVisible] = useState(false);
useLayoutEffect(() => { useLayoutEffect(() => {
const container = current?.children.item(0); const container = current?.children.item(0);
@ -194,16 +214,31 @@ const DropdownContainer = ({
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: ${theme.gridUnit * 3}px; gap: ${theme.gridUnit * 3}px;
width: 200px;
`} `}
style={popoverStyle} style={popoverStyle}
ref={popoverRef}
> >
{popoverContent {popoverContent
? popoverContent(overflowedItems) ? popoverContent(overflowedItems)
: overflowedItems.map(item => item.element)} : overflowedItems.map(item => item.element)}
</div> </div>
), ),
[overflowedItems, popoverContent, popoverStyle, theme.gridUnit], [
overflowedItems,
popoverContent,
popoverRef,
popoverStyle,
theme.gridUnit,
],
);
useImperativeHandle(
outerRef,
() => ({
...(ref.current as HTMLDivElement),
open: () => setPopoverVisible(true),
}),
[ref],
); );
const overflowingCount = const overflowingCount =
@ -233,15 +268,17 @@ const DropdownContainer = ({
<Popover <Popover
content={content} content={content}
trigger="click" trigger="click"
visible={popoverVisible}
onVisibleChange={visible => setPopoverVisible(visible)}
overlayInnerStyle={{ overlayInnerStyle={{
overflow: 'auto',
maxHeight: 500, maxHeight: 500,
overflowY: 'auto',
}} }}
> >
<Button buttonStyle="secondary"> <Button buttonStyle="secondary">
{popoverTriggerIcon} {popoverTriggerIcon}
{popoverTriggerText} {popoverTriggerText}
<Badge count={overflowingCount} /> <Badge count={popoverTriggerCount || overflowingCount} />
<Icons.DownOutlined <Icons.DownOutlined
iconSize="m" iconSize="m"
iconColor={theme.colors.grayscale.base} iconColor={theme.colors.grayscale.base}
@ -251,6 +288,7 @@ const DropdownContainer = ({
)} )}
</div> </div>
); );
}; },
);
export default DropdownContainer; export default DropdownContainer;