fix(ui): use expand button in sidebar and tables [EE-6844] (#11608)
parent
413b9c3b04
commit
a808f83e7d
|
@ -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 };
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
import { ChevronDown, ChevronUp } from 'lucide-react';
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
|
||||
import { Button } from '@@/buttons';
|
||||
import { CollapseExpandButton } from '../CollapseExpandButton';
|
||||
|
||||
import { DefaultType } from './types';
|
||||
|
||||
|
@ -13,32 +12,25 @@ export function buildExpandColumn<T extends DefaultType>(): ColumnDef<T> {
|
|||
|
||||
return (
|
||||
hasExpandableItems && (
|
||||
<Button
|
||||
<CollapseExpandButton
|
||||
isExpanded={table.getIsAllRowsExpanded()}
|
||||
onClick={table.getToggleAllRowsExpandedHandler()}
|
||||
color="none"
|
||||
icon={table.getIsAllRowsExpanded() ? ChevronDown : ChevronUp}
|
||||
title="Expand all"
|
||||
data-cy="expand-all-rows-button"
|
||||
aria-label="Expand all rows"
|
||||
aria-label={
|
||||
table.getIsAllRowsExpanded()
|
||||
? 'Collapse all rows'
|
||||
: 'Expand all rows'
|
||||
}
|
||||
/>
|
||||
)
|
||||
);
|
||||
},
|
||||
cell: ({ row }) =>
|
||||
row.getCanExpand() && (
|
||||
<Button
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
row.toggleExpanded();
|
||||
}}
|
||||
color="none"
|
||||
icon={row.getIsExpanded() ? ChevronDown : ChevronUp}
|
||||
title={row.getIsExpanded() ? 'Collapse' : 'Expand'}
|
||||
<CollapseExpandButton
|
||||
isExpanded={row.getIsExpanded()}
|
||||
onClick={row.getToggleExpandedHandler()}
|
||||
data-cy={`expand-row-button_${row.index}`}
|
||||
aria-label={row.getIsExpanded() ? 'Collapse row' : 'Expand row'}
|
||||
aria-expanded={row.getIsExpanded()}
|
||||
/>
|
||||
),
|
||||
enableColumnFilter: false,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { PropsWithChildren, ReactNode, useState } from 'react';
|
||||
import { ChevronUp, ChevronRight } from 'lucide-react';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
import { CollapseExpandButton } from '@@/CollapseExpandButton';
|
||||
|
||||
import { FormSectionTitle } from '../FormSectionTitle';
|
||||
|
||||
|
@ -26,30 +25,22 @@ export function FormSection({
|
|||
htmlFor = '',
|
||||
}: PropsWithChildren<Props>) {
|
||||
const [isExpanded, setIsExpanded] = useState(!defaultFolded);
|
||||
const id = `foldingButton${title}`;
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<FormSectionTitle
|
||||
htmlFor={isFoldable ? `foldingButton${title}` : htmlFor}
|
||||
htmlFor={isFoldable ? id : htmlFor}
|
||||
titleSize={titleSize}
|
||||
className={titleClassName}
|
||||
>
|
||||
{isFoldable && (
|
||||
<button
|
||||
id={`foldingButton${title}`}
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
setIsExpanded(!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>
|
||||
<CollapseExpandButton
|
||||
isExpanded={isExpanded}
|
||||
data-cy={id}
|
||||
id={id}
|
||||
onClick={() => setIsExpanded((isExpanded) => !isExpanded)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{title}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import clsx from 'clsx';
|
||||
import { ChevronDown } from 'lucide-react';
|
||||
import { PropsWithChildren, useState } from 'react';
|
||||
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
import { Link } from '@@/Link';
|
||||
import { CollapseExpandButton } from '@@/CollapseExpandButton';
|
||||
|
||||
import { useSidebarState } from '../useSidebarState';
|
||||
|
||||
|
@ -79,29 +79,11 @@ export function SidebarParent({
|
|||
</Link>
|
||||
</button>
|
||||
{isSidebarOpen && (
|
||||
<button
|
||||
type="button"
|
||||
className="flex-none border-none bg-transparent flex items-center group p-0 px-3 h-8"
|
||||
onClick={(e) => {
|
||||
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>
|
||||
<SidebarExpandButton
|
||||
onClick={() => setIsExpanded((isExpanded) => !isExpanded)}
|
||||
isExpanded={isExpanded}
|
||||
listId={listId}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Wrapper>
|
||||
|
@ -145,3 +127,23 @@ export function SidebarParent({
|
|||
</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"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue