fix(ui): use expand button in sidebar and tables [EE-6844] (#11608)

pull/11833/head
Chaim Lev-Ari 2024-05-15 08:26:23 +03:00 committed by GitHub
parent 413b9c3b04
commit a808f83e7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 155 additions and 61 deletions

View File

@ -0,0 +1,68 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { CollapseExpandButton } from './CollapseExpandButton';
it('should render the button with the correct icon and title', () => {
renderCollapseExpandButton();
const button = screen.getByRole('button');
expect(button).toBeInTheDocument();
expect(button).toHaveAttribute('title', 'Expand');
expect(button).toHaveAttribute('aria-label', 'Expand');
expect(button).toHaveAttribute('aria-expanded', 'false');
expect(button.querySelector('svg')).toBeInTheDocument();
});
it('should call the onClick handler when the button is clicked', async () => {
const onClick = vi.fn();
const { user } = renderCollapseExpandButton({ onClick });
const button = screen.getByRole('button');
await user.click(button);
expect(onClick).toHaveBeenCalledTimes(1);
});
it('should prevent default and stop propagation when the button is clicked', async () => {
const user = userEvent.setup();
const onClick = vi.fn();
const onOuterClick = vi.fn();
render(
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
<div onClick={onOuterClick}>
<CollapseExpandButton
onClick={onClick}
isExpanded={false}
data-cy="nothing"
/>
</div>
);
const button = screen.getByLabelText('Expand');
await user.click(button);
expect(onOuterClick).not.toHaveBeenCalled();
expect(onClick).toHaveBeenCalled();
});
function renderCollapseExpandButton({
isExpanded = false,
onClick = vi.fn(),
}: {
isExpanded?: boolean;
onClick?(): void;
} = {}) {
const user = userEvent.setup();
render(
<CollapseExpandButton
isExpanded={isExpanded}
data-cy="random"
onClick={onClick}
/>
);
return { user };
}

View File

@ -0,0 +1,41 @@
import { ChevronDown } from 'lucide-react';
import { ComponentProps } from 'react';
import clsx from 'clsx';
import { Icon } from './Icon';
export function CollapseExpandButton({
onClick,
isExpanded,
...props
}: { isExpanded: boolean } & ComponentProps<'button'>) {
return (
<button
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
onClick?.(e);
}}
color="none"
title={isExpanded ? 'Collapse' : 'Expand'}
aria-label={isExpanded ? 'Collapse' : 'Expand'}
aria-expanded={isExpanded}
type="button"
className="flex-none border-none bg-transparent flex items-center p-0 px-3 group"
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
>
<div className="flex items-center group-hover:bg-blue-5 be:group-hover:bg-gray-5 group-hover:th-dark:bg-gray-true-7 group-hover:bg-opacity-10 be:group-hover:bg-opacity-10 rounded-full p-[3px] transition ease-in-out">
<Icon
icon={ChevronDown}
size="md"
className={clsx('transition ease-in-out', {
'rotate-180': isExpanded,
'rotate-0': !isExpanded,
})}
/>
</div>
</button>
);
}

View File

@ -1,7 +1,6 @@
import { ChevronDown, ChevronUp } from 'lucide-react';
import { ColumnDef } from '@tanstack/react-table'; import { ColumnDef } from '@tanstack/react-table';
import { Button } from '@@/buttons'; import { CollapseExpandButton } from '../CollapseExpandButton';
import { DefaultType } from './types'; import { DefaultType } from './types';
@ -13,32 +12,25 @@ export function buildExpandColumn<T extends DefaultType>(): ColumnDef<T> {
return ( return (
hasExpandableItems && ( hasExpandableItems && (
<Button <CollapseExpandButton
isExpanded={table.getIsAllRowsExpanded()}
onClick={table.getToggleAllRowsExpandedHandler()} onClick={table.getToggleAllRowsExpandedHandler()}
color="none"
icon={table.getIsAllRowsExpanded() ? ChevronDown : ChevronUp}
title="Expand all"
data-cy="expand-all-rows-button" data-cy="expand-all-rows-button"
aria-label="Expand all rows" aria-label={
table.getIsAllRowsExpanded()
? 'Collapse all rows'
: 'Expand all rows'
}
/> />
) )
); );
}, },
cell: ({ row }) => cell: ({ row }) =>
row.getCanExpand() && ( row.getCanExpand() && (
<Button <CollapseExpandButton
onClick={(e) => { isExpanded={row.getIsExpanded()}
e.preventDefault(); onClick={row.getToggleExpandedHandler()}
e.stopPropagation();
row.toggleExpanded();
}}
color="none"
icon={row.getIsExpanded() ? ChevronDown : ChevronUp}
title={row.getIsExpanded() ? 'Collapse' : 'Expand'}
data-cy={`expand-row-button_${row.index}`} data-cy={`expand-row-button_${row.index}`}
aria-label={row.getIsExpanded() ? 'Collapse row' : 'Expand row'}
aria-expanded={row.getIsExpanded()}
/> />
), ),
enableColumnFilter: false, enableColumnFilter: false,

View File

@ -1,7 +1,6 @@
import { PropsWithChildren, ReactNode, useState } from 'react'; import { PropsWithChildren, ReactNode, useState } from 'react';
import { ChevronUp, ChevronRight } from 'lucide-react';
import { Icon } from '@@/Icon'; import { CollapseExpandButton } from '@@/CollapseExpandButton';
import { FormSectionTitle } from '../FormSectionTitle'; import { FormSectionTitle } from '../FormSectionTitle';
@ -26,30 +25,22 @@ export function FormSection({
htmlFor = '', htmlFor = '',
}: PropsWithChildren<Props>) { }: PropsWithChildren<Props>) {
const [isExpanded, setIsExpanded] = useState(!defaultFolded); const [isExpanded, setIsExpanded] = useState(!defaultFolded);
const id = `foldingButton${title}`;
return ( return (
<div className={className}> <div className={className}>
<FormSectionTitle <FormSectionTitle
htmlFor={isFoldable ? `foldingButton${title}` : htmlFor} htmlFor={isFoldable ? id : htmlFor}
titleSize={titleSize} titleSize={titleSize}
className={titleClassName} className={titleClassName}
> >
{isFoldable && ( {isFoldable && (
<button <CollapseExpandButton
id={`foldingButton${title}`} isExpanded={isExpanded}
type="button" data-cy={id}
onClick={(e) => { id={id}
setIsExpanded(!isExpanded); onClick={() => setIsExpanded((isExpanded) => !isExpanded)}
e.stopPropagation(); />
e.preventDefault();
}}
className="mx-2 !ml-0 inline-flex w-2 items-center justify-center border-0 bg-transparent"
>
<Icon
icon={isExpanded ? ChevronUp : ChevronRight}
className="shrink-0"
/>
</button>
)} )}
{title} {title}

View File

@ -1,11 +1,11 @@
import clsx from 'clsx'; import clsx from 'clsx';
import { ChevronDown } from 'lucide-react';
import { PropsWithChildren, useState } from 'react'; import { PropsWithChildren, useState } from 'react';
import { AutomationTestingProps } from '@/types'; import { AutomationTestingProps } from '@/types';
import { Icon } from '@@/Icon'; import { Icon } from '@@/Icon';
import { Link } from '@@/Link'; import { Link } from '@@/Link';
import { CollapseExpandButton } from '@@/CollapseExpandButton';
import { useSidebarState } from '../useSidebarState'; import { useSidebarState } from '../useSidebarState';
@ -79,29 +79,11 @@ export function SidebarParent({
</Link> </Link>
</button> </button>
{isSidebarOpen && ( {isSidebarOpen && (
<button <SidebarExpandButton
type="button" onClick={() => setIsExpanded((isExpanded) => !isExpanded)}
className="flex-none border-none bg-transparent flex items-center group p-0 px-3 h-8" isExpanded={isExpanded}
onClick={(e) => { listId={listId}
e.preventDefault(); />
e.stopPropagation();
setIsExpanded((isExpanded) => !isExpanded);
}}
title={isExpanded ? 'Collapse' : 'Expand'}
aria-expanded={isExpanded}
aria-controls={listId}
>
<div className="flex items-center group-hover:bg-blue-5 be:group-hover:bg-gray-5 group-hover:th-dark:bg-gray-true-7 group-hover:bg-opacity-10 be:group-hover:bg-opacity-10 rounded-full p-[3px] transition ease-in-out">
<Icon
icon={ChevronDown}
size="md"
className={clsx('transition ease-in-out', {
'rotate-180': isExpanded,
'rotate-0': !isExpanded,
})}
/>
</div>
</button>
)} )}
</div> </div>
</Wrapper> </Wrapper>
@ -145,3 +127,23 @@ export function SidebarParent({
</SidebarTooltip> </SidebarTooltip>
); );
} }
function SidebarExpandButton({
isExpanded,
listId,
onClick,
}: {
onClick(): void;
isExpanded: boolean;
listId: string;
}) {
return (
<CollapseExpandButton
isExpanded={isExpanded}
onClick={onClick}
aria-controls={listId}
data-cy="expand-button"
className="flex-none border-none bg-transparent flex items-center group p-0 px-3 h-8"
/>
);
}