Chore: Improve dropdown draggable list (#1738)

* draggable menu item component

* Menu item isDragged prop removed

* Droppable list component

* Draggablee item component

* Drag and drop use refactor

* lint fix

* isDragDisabled check on DraggableItem

* revert changes on non visibility items

* MenuItemDraggable stroybook

* DraggableItem storybook

* lint fix

* lint fix

* BoardColumnMenu css fix

* showGrip prop addition

* isDragged css fix
This commit is contained in:
Aditya Pimpalkar
2023-10-04 14:56:25 +01:00
committed by GitHub
parent 1e402aca5f
commit 93a01c7292
11 changed files with 368 additions and 91 deletions
@@ -1,11 +1,13 @@
import { MouseEvent } from 'react';
import styled from '@emotion/styled';
import { FloatingIconButtonGroup } from '@/ui/button/components/FloatingIconButtonGroup';
import { IconComponent } from '@/ui/icon/types/IconComponent';
import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase';
import {
StyledHoverableMenuItemBase,
StyledMenuItemLeftContent,
} from '../internals/components/StyledMenuItemBase';
import { MenuItemAccent } from '../types/MenuItemAccent';
export type MenuItemIconButton = {
@@ -14,7 +16,6 @@ export type MenuItemIconButton = {
};
export type MenuItemProps = {
isDraggable?: boolean;
LeftIcon?: IconComponent | null;
accent?: MenuItemAccent;
text: string;
@@ -24,25 +25,7 @@ export type MenuItemProps = {
onClick?: () => void;
};
const StyledHoverableMenuItemBase = styled(StyledMenuItemBase)`
& .hoverable-buttons {
opacity: 0;
pointer-events: none;
position: fixed;
right: ${({ theme }) => theme.spacing(2)};
transition: opacity ${({ theme }) => theme.animation.duration.instant}s ease;
}
&:hover {
& .hoverable-buttons {
opacity: 1;
pointer-events: auto;
}
}
`;
export const MenuItem = ({
isDraggable,
LeftIcon,
accent = 'default',
text,
@@ -60,11 +43,9 @@ export const MenuItem = ({
className={className}
accent={accent}
>
<MenuItemLeftContent
isDraggable={isDraggable ? true : false}
LeftIcon={LeftIcon ?? undefined}
text={text}
/>
<StyledMenuItemLeftContent>
<MenuItemLeftContent LeftIcon={LeftIcon ?? undefined} text={text} />
</StyledMenuItemLeftContent>
<div className="hoverable-buttons">
{showIconButtons && (
<FloatingIconButtonGroup iconButtons={iconButtons} />
@@ -0,0 +1,52 @@
import { FloatingIconButtonGroup } from '@/ui/button/components/FloatingIconButtonGroup';
import { IconComponent } from '@/ui/icon/types/IconComponent';
import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
import { StyledHoverableMenuItemBase } from '../internals/components/StyledMenuItemBase';
import { MenuItemAccent } from '../types/MenuItemAccent';
import { MenuItemIconButton } from './MenuItem';
export type MenuItemDraggableProps = {
key: string;
LeftIcon: IconComponent | undefined;
accent?: MenuItemAccent;
iconButtons?: MenuItemIconButton[];
onClick?: () => void;
text: string;
isDragDisabled?: boolean;
className?: string;
};
export const MenuItemDraggable = ({
key,
LeftIcon,
accent = 'default',
iconButtons,
onClick,
text,
isDragDisabled = false,
className,
}: MenuItemDraggableProps) => {
const showIconButtons = Array.isArray(iconButtons) && iconButtons.length > 0;
return (
<StyledHoverableMenuItemBase
data-testid={key ?? undefined}
onClick={onClick}
accent={accent}
className={className}
>
<MenuItemLeftContent
LeftIcon={LeftIcon}
text={text}
key={key}
showGrip={!isDragDisabled}
/>
<div className="hoverable-buttons">
{showIconButtons && (
<FloatingIconButtonGroup iconButtons={iconButtons} />
)}
</div>
</StyledHoverableMenuItemBase>
);
};
@@ -4,7 +4,10 @@ import { IconChevronRight } from '@/ui/icon';
import { IconComponent } from '@/ui/icon/types/IconComponent';
import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
import { StyledMenuItemBase } from '../internals/components/StyledMenuItemBase';
import {
StyledMenuItemBase,
StyledMenuItemLeftContent,
} from '../internals/components/StyledMenuItemBase';
export type MenuItemProps = {
LeftIcon?: IconComponent;
@@ -23,7 +26,9 @@ export const MenuItemNavigate = ({
return (
<StyledMenuItemBase onClick={onClick} className={className}>
<MenuItemLeftContent LeftIcon={LeftIcon} text={text} />
<StyledMenuItemLeftContent>
<MenuItemLeftContent LeftIcon={LeftIcon} text={text} />
</StyledMenuItemLeftContent>
<IconChevronRight size={theme.icon.size.sm} />
</StyledMenuItemBase>
);
@@ -0,0 +1,107 @@
import { Meta, StoryObj } from '@storybook/react';
import { IconBell, IconMinus } from '@/ui/icon';
import {
CatalogDecorator,
CatalogDimension,
CatalogOptions,
} from '~/testing/decorators/CatalogDecorator';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { MenuItemAccent } from '../../types/MenuItemAccent';
import { MenuItemDraggable } from '../MenuItemDraggable';
const meta: Meta<typeof MenuItemDraggable> = {
title: 'ui/MenuItem/MenuItemDraggable',
component: MenuItemDraggable,
};
export default meta;
type Story = StoryObj<typeof MenuItemDraggable>;
export const Default: Story = {
args: {
key: 'key-1',
LeftIcon: IconBell,
accent: 'default',
iconButtons: [{ Icon: IconMinus, onClick: () => console.log('Clicked') }],
onClick: () => console.log('Clicked'),
text: 'Menu item draggable',
isDragDisabled: false,
},
decorators: [ComponentDecorator],
};
export const Catalog: Story = {
args: { ...Default.args },
argTypes: {
accent: { control: false },
iconButtons: { control: false },
},
parameters: {
pseudo: { hover: ['.hover'] },
catalog: {
dimensions: [
{
name: 'isDragDisabled',
values: [true, false],
props: (isDragDisabled: boolean) => ({
isDragDisabled: isDragDisabled,
}),
labels: (isDragDisabled: boolean) =>
isDragDisabled ? 'Without drag icon' : 'With drag icon',
},
{
name: 'accents',
values: ['default', 'danger', 'placeholder'] as MenuItemAccent[],
props: (accent: MenuItemAccent) => ({ accent }),
},
{
name: 'states',
values: ['default', 'hover'],
props: (state: string) => {
switch (state) {
case 'default':
return {};
case 'hover':
return { className: state };
default:
return {};
}
},
},
{
name: 'iconButtons',
values: ['no icon button', 'minus icon buttons'],
props: (choice: string) => {
switch (choice) {
case 'no icon button': {
return {
iconButtons: [],
};
}
case 'minus icon buttons': {
return {
iconButtons: [
{
Icon: IconMinus,
onClick: () =>
console.log('Clicked on minus icon button'),
},
],
};
}
}
},
},
] as CatalogDimension[],
options: {
elementContainer: {
width: 200,
},
} as CatalogOptions,
},
},
decorators: [CatalogDecorator],
};