mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-18 09:02:11 +03:00
Add ButtonGroup concept + Soon pill on button + implement in timeline (#551)
* Add ButtonGroup concept * Add soon pill * Fix incorrect wrapping behavior * Implement button group in timeline
This commit is contained in:
parent
c529c49ea6
commit
a2da3a5f09
@ -0,0 +1,40 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
|
||||
import { Button } from '@/ui/components/buttons/Button';
|
||||
import { ButtonGroup } from '@/ui/components/buttons/ButtonGroup';
|
||||
import { IconCheckbox, IconNotes, IconTimelineEvent } from '@/ui/icons/index';
|
||||
|
||||
type CommentThreadCreateButtonProps = {
|
||||
onNoteClick?: () => void;
|
||||
onTaskClick?: () => void;
|
||||
onActivityClick?: () => void;
|
||||
};
|
||||
|
||||
export function CommentThreadCreateButton({
|
||||
onNoteClick,
|
||||
onTaskClick,
|
||||
onActivityClick,
|
||||
}: CommentThreadCreateButtonProps) {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<ButtonGroup variant="secondary">
|
||||
<Button
|
||||
icon={<IconNotes size={theme.icon.size.sm} />}
|
||||
title="Note"
|
||||
onClick={onNoteClick}
|
||||
/>
|
||||
<Button
|
||||
icon={<IconCheckbox size={theme.icon.size.sm} />}
|
||||
title="Task"
|
||||
soon={true}
|
||||
onClick={onTaskClick}
|
||||
/>
|
||||
<Button
|
||||
icon={<IconTimelineEvent size={theme.icon.size.sm} />}
|
||||
title="Activity"
|
||||
soon={true}
|
||||
onClick={onActivityClick}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
);
|
||||
}
|
@ -6,7 +6,6 @@ import { useOpenCommentThreadRightDrawer } from '@/comments/hooks/useOpenComment
|
||||
import { useOpenCreateCommentThreadDrawer } from '@/comments/hooks/useOpenCreateCommentThreadDrawer';
|
||||
import { CommentableEntity } from '@/comments/types/CommentableEntity';
|
||||
import { CommentThreadForDrawer } from '@/comments/types/CommentThreadForDrawer';
|
||||
import { TableActionBarButtonToggleComments } from '@/ui/components/table/action-bar/TableActionBarButtonOpenComments';
|
||||
import { IconCirclePlus, IconNotes } from '@/ui/icons/index';
|
||||
import {
|
||||
beautifyExactDate,
|
||||
@ -17,6 +16,8 @@ import {
|
||||
useGetCommentThreadsByTargetsQuery,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
import { CommentThreadCreateButton } from '../comment-thread/CommentThreadCreateButton';
|
||||
|
||||
const StyledMainContainer = styled.div`
|
||||
align-items: flex-start;
|
||||
align-self: stretch;
|
||||
@ -208,8 +209,8 @@ export function Timeline({ entity }: { entity: CommentableEntity }) {
|
||||
<StyledTimelineEmptyContainer>
|
||||
<StyledEmptyTimelineTitle>No activity yet</StyledEmptyTimelineTitle>
|
||||
<StyledEmptyTimelineSubTitle>Create one:</StyledEmptyTimelineSubTitle>
|
||||
<TableActionBarButtonToggleComments
|
||||
onClick={() => openCreateCommandThread(entity)}
|
||||
<CommentThreadCreateButton
|
||||
onNoteClick={() => openCreateCommandThread(entity)}
|
||||
/>
|
||||
</StyledTimelineEmptyContainer>
|
||||
);
|
||||
@ -223,8 +224,8 @@ export function Timeline({ entity }: { entity: CommentableEntity }) {
|
||||
<IconCirclePlus />
|
||||
</StyledIconContainer>
|
||||
|
||||
<TableActionBarButtonToggleComments
|
||||
onClick={() => openCreateCommandThread(entity)}
|
||||
<CommentThreadCreateButton
|
||||
onNoteClick={() => openCreateCommandThread(entity)}
|
||||
/>
|
||||
</StyledTimelineItemContainer>
|
||||
</StyledTopActionBar>
|
||||
|
22
front/src/modules/ui/components/accessories/SoonPill.tsx
Normal file
22
front/src/modules/ui/components/accessories/SoonPill.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const StyledSoonPill = styled.span`
|
||||
align-items: center;
|
||||
background: ${({ theme }) => theme.background.transparent.light};
|
||||
border-radius: 50px;
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
display: flex;
|
||||
font-size: ${({ theme }) => theme.font.size.xs};
|
||||
font-style: normal;
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
height: ${({ theme }) => theme.spacing(4)};
|
||||
justify-content: flex-end;
|
||||
line-height: ${({ theme }) => theme.text.lineHeight.lg};
|
||||
margin-left: auto;
|
||||
padding: ${({ theme }) => theme.spacing(1)} ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
export function SoonPill() {
|
||||
return <StyledSoonPill>Soon</StyledSoonPill>;
|
||||
}
|
@ -3,7 +3,9 @@ import styled from '@emotion/styled';
|
||||
|
||||
import { rgba } from '@/ui/themes/colors';
|
||||
|
||||
type Variant =
|
||||
import { SoonPill } from '../accessories/SoonPill';
|
||||
|
||||
export type ButtonVariant =
|
||||
| 'primary'
|
||||
| 'secondary'
|
||||
| 'tertiary'
|
||||
@ -11,18 +13,22 @@ type Variant =
|
||||
| 'tertiaryLight'
|
||||
| 'danger';
|
||||
|
||||
type Size = 'medium' | 'small';
|
||||
export type ButtonSize = 'medium' | 'small';
|
||||
|
||||
type Props = {
|
||||
export type ButtonPosition = 'left' | 'middle' | 'right' | undefined;
|
||||
|
||||
export type ButtonProps = {
|
||||
icon?: React.ReactNode;
|
||||
title?: string;
|
||||
fullWidth?: boolean;
|
||||
variant?: Variant;
|
||||
size?: Size;
|
||||
variant?: ButtonVariant;
|
||||
size?: ButtonSize;
|
||||
position?: ButtonPosition;
|
||||
soon?: boolean;
|
||||
} & React.ComponentProps<'button'>;
|
||||
|
||||
const StyledButton = styled.button<
|
||||
Pick<Props, 'fullWidth' | 'variant' | 'size' | 'title'>
|
||||
Pick<ButtonProps, 'fullWidth' | 'variant' | 'size' | 'position' | 'title'>
|
||||
>`
|
||||
align-items: center;
|
||||
background: ${({ theme, variant, disabled }) => {
|
||||
@ -49,7 +55,18 @@ const StyledButton = styled.button<
|
||||
return 'none';
|
||||
}
|
||||
}};
|
||||
border-radius: 4px;
|
||||
border-radius: ${({ position }) => {
|
||||
switch (position) {
|
||||
case 'left':
|
||||
return '4px 0px 0px 4px';
|
||||
case 'right':
|
||||
return '0px 4px 4px 0px';
|
||||
case 'middle':
|
||||
return '0px';
|
||||
default:
|
||||
return '4px';
|
||||
}
|
||||
}};
|
||||
box-shadow: ${({ theme, variant }) => {
|
||||
switch (variant) {
|
||||
case 'primary':
|
||||
@ -59,6 +76,7 @@ const StyledButton = styled.button<
|
||||
return 'none';
|
||||
}
|
||||
}};
|
||||
|
||||
color: ${({ theme, variant, disabled }) => {
|
||||
if (disabled) {
|
||||
if (variant === 'primary') {
|
||||
@ -105,6 +123,8 @@ const StyledButton = styled.button<
|
||||
|
||||
transition: background 0.1s ease;
|
||||
|
||||
white-space: nowrap;
|
||||
|
||||
width: ${({ fullWidth }) => (fullWidth ? '100%' : 'auto')};
|
||||
|
||||
&:hover,
|
||||
@ -144,18 +164,24 @@ export function Button({
|
||||
fullWidth = false,
|
||||
variant = 'primary',
|
||||
size = 'medium',
|
||||
position,
|
||||
soon = false,
|
||||
disabled = false,
|
||||
...props
|
||||
}: Props) {
|
||||
}: ButtonProps) {
|
||||
return (
|
||||
<StyledButton
|
||||
fullWidth={fullWidth}
|
||||
variant={variant}
|
||||
size={size}
|
||||
position={position}
|
||||
disabled={soon || disabled}
|
||||
title={title}
|
||||
{...props}
|
||||
>
|
||||
{icon}
|
||||
{title}
|
||||
{soon && <SoonPill />}
|
||||
</StyledButton>
|
||||
);
|
||||
}
|
||||
|
43
front/src/modules/ui/components/buttons/ButtonGroup.tsx
Normal file
43
front/src/modules/ui/components/buttons/ButtonGroup.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { ButtonPosition, ButtonProps } from './Button';
|
||||
|
||||
const StyledButtonGroupContainer = styled.div`
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
type ButtonGroupProps = Pick<ButtonProps, 'variant' | 'size'> & {
|
||||
children: React.ReactElement[];
|
||||
};
|
||||
|
||||
export function ButtonGroup({ children, variant, size }: ButtonGroupProps) {
|
||||
return (
|
||||
<StyledButtonGroupContainer>
|
||||
{React.Children.map(children, (child, index) => {
|
||||
let position: ButtonPosition;
|
||||
|
||||
if (index === 0) {
|
||||
position = 'left';
|
||||
} else if (index === children.length - 1) {
|
||||
position = 'right';
|
||||
} else {
|
||||
position = 'middle';
|
||||
}
|
||||
|
||||
const additionalProps: any = { position };
|
||||
|
||||
if (variant) {
|
||||
additionalProps.variant = variant;
|
||||
}
|
||||
|
||||
if (size) {
|
||||
additionalProps.size = size;
|
||||
}
|
||||
|
||||
return React.cloneElement(child, additionalProps);
|
||||
})}
|
||||
</StyledButtonGroupContainer>
|
||||
);
|
||||
}
|
@ -8,6 +8,7 @@ type Props = {
|
||||
title: string;
|
||||
fullWidth?: boolean;
|
||||
variant?: Variant;
|
||||
soon?: boolean;
|
||||
} & React.ComponentProps<'button'>;
|
||||
|
||||
const StyledButton = styled.button<Pick<Props, 'fullWidth' | 'variant'>>`
|
||||
|
@ -1,3 +1,4 @@
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { text, withKnobs } from '@storybook/addon-knobs';
|
||||
import { expect, jest } from '@storybook/jest';
|
||||
@ -8,6 +9,7 @@ import { IconSearch } from '@/ui/icons';
|
||||
import { getRenderWrapperForComponent } from '~/testing/renderWrappers';
|
||||
|
||||
import { Button } from '../Button';
|
||||
import { ButtonGroup } from '../ButtonGroup';
|
||||
|
||||
type ButtonProps = React.ComponentProps<typeof Button>;
|
||||
|
||||
@ -62,8 +64,6 @@ const meta: Meta<typeof Button> = {
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Button>;
|
||||
|
||||
const clickJestFn = jest.fn();
|
||||
|
||||
const variants: ButtonProps['variant'][] = [
|
||||
'primary',
|
||||
'secondary',
|
||||
@ -73,148 +73,157 @@ const variants: ButtonProps['variant'][] = [
|
||||
'danger',
|
||||
];
|
||||
|
||||
const ButtonLine = (props: ButtonProps) => (
|
||||
const clickJestFn = jest.fn();
|
||||
|
||||
const states = {
|
||||
'with-icon': {
|
||||
description: 'With icon',
|
||||
extraProps: (variant: string) => ({
|
||||
'data-testid': `${variant}-button-with-icon`,
|
||||
icon: <IconSearch size={14} />,
|
||||
}),
|
||||
},
|
||||
default: {
|
||||
description: 'Default',
|
||||
extraProps: (variant: string) => ({
|
||||
'data-testid': `${variant}-button-default`,
|
||||
onClick: clickJestFn,
|
||||
}),
|
||||
},
|
||||
hover: {
|
||||
description: 'Hover',
|
||||
extraProps: (variant: string) => ({
|
||||
id: `${variant}-button-hover`,
|
||||
'data-testid': `${variant}-button-hover`,
|
||||
}),
|
||||
},
|
||||
pressed: {
|
||||
description: 'Pressed',
|
||||
extraProps: (variant: string) => ({
|
||||
id: `${variant}-button-pressed`,
|
||||
'data-testid': `${variant}-button-pressed`,
|
||||
}),
|
||||
},
|
||||
disabled: {
|
||||
description: 'Disabled',
|
||||
extraProps: (variant: string) => ({
|
||||
'data-testid': `${variant}-button-disabled`,
|
||||
disabled: true,
|
||||
}),
|
||||
},
|
||||
soon: {
|
||||
description: 'Soon',
|
||||
extraProps: (variant: string) => ({
|
||||
'data-testid': `${variant}-button-soon`,
|
||||
soon: true,
|
||||
}),
|
||||
},
|
||||
focus: {
|
||||
description: 'Focus',
|
||||
extraProps: (variant: string) => ({
|
||||
id: `${variant}-button-focus`,
|
||||
'data-testid': `${variant}-button-focus`,
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
const ButtonLine: React.FC<ButtonProps> = ({ variant, ...props }) => (
|
||||
<>
|
||||
<StyledButtonContainer>
|
||||
<StyledDescription>With icon</StyledDescription>
|
||||
<Button
|
||||
data-testid={`${props.variant}-button-with-icon`}
|
||||
{...props}
|
||||
icon={<IconSearch size={14} />}
|
||||
/>
|
||||
</StyledButtonContainer>
|
||||
<StyledButtonContainer>
|
||||
<StyledDescription>Default</StyledDescription>
|
||||
<Button
|
||||
data-testid={`${props.variant}-button-default`}
|
||||
onClick={clickJestFn}
|
||||
{...props}
|
||||
/>
|
||||
</StyledButtonContainer>
|
||||
<StyledButtonContainer>
|
||||
<StyledDescription>Hover</StyledDescription>
|
||||
<Button
|
||||
id={`${props.variant}-button-hover`}
|
||||
data-testid={`${props.variant}-button-hover`}
|
||||
{...props}
|
||||
/>
|
||||
</StyledButtonContainer>
|
||||
<StyledButtonContainer>
|
||||
<StyledDescription>Pressed</StyledDescription>
|
||||
<Button
|
||||
id={`${props.variant}-button-pressed`}
|
||||
data-testid={`${props.variant}-button-pressed`}
|
||||
{...props}
|
||||
/>
|
||||
</StyledButtonContainer>
|
||||
<StyledButtonContainer>
|
||||
<StyledDescription>Disabled</StyledDescription>
|
||||
<Button
|
||||
data-testid={`${props.variant}-button-disabled`}
|
||||
{...props}
|
||||
disabled
|
||||
/>
|
||||
</StyledButtonContainer>
|
||||
<StyledButtonContainer>
|
||||
<StyledDescription>Focus</StyledDescription>
|
||||
<Button
|
||||
id={`${props.variant}-button-focus`}
|
||||
data-testid={`${props.variant}-button-focus`}
|
||||
{...props}
|
||||
/>
|
||||
</StyledButtonContainer>
|
||||
{Object.entries(states).map(([state, { description, extraProps }]) => (
|
||||
<StyledButtonContainer key={`${variant}-container-${state}`}>
|
||||
<StyledDescription>{description}</StyledDescription>
|
||||
<Button {...props} {...extraProps(variant ?? '')} variant={variant} />
|
||||
</StyledButtonContainer>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
const ButtonContainer = (props: Partial<ButtonProps>) => {
|
||||
const title = text('Text', 'A button title');
|
||||
const ButtonGroupLine: React.FC<ButtonProps> = ({ variant, ...props }) => (
|
||||
<>
|
||||
{Object.entries(states).map(([state, { description, extraProps }]) => (
|
||||
<StyledButtonContainer key={`${variant}-group-container-${state}`}>
|
||||
<StyledDescription>{description}</StyledDescription>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
{...props}
|
||||
{...extraProps(`${variant}-left`)}
|
||||
variant={variant}
|
||||
title="Left"
|
||||
/>
|
||||
<Button
|
||||
{...props}
|
||||
{...extraProps(`${variant}-center`)}
|
||||
variant={variant}
|
||||
title="Center"
|
||||
/>
|
||||
<Button
|
||||
{...props}
|
||||
{...extraProps(`${variant}-right`)}
|
||||
variant={variant}
|
||||
title="Right"
|
||||
/>
|
||||
</ButtonGroup>
|
||||
</StyledButtonContainer>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
const generateStory = (
|
||||
size: ButtonProps['size'],
|
||||
type: 'button' | 'group',
|
||||
LineComponent: React.ComponentType<ButtonProps>,
|
||||
): Story => ({
|
||||
render: getRenderWrapperForComponent(
|
||||
<StyledContainer>
|
||||
{variants.map((variant) => (
|
||||
<div key={variant}>
|
||||
<StyledTitle>{variant}</StyledTitle>
|
||||
<StyledLine>
|
||||
<ButtonLine {...props} title={title} variant={variant} />
|
||||
<LineComponent
|
||||
size={size}
|
||||
variant={variant}
|
||||
title={text('Text', 'A button title')}
|
||||
/>
|
||||
</StyledLine>
|
||||
</div>
|
||||
))}
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
// Medium size
|
||||
export const MediumSize: Story = {
|
||||
render: getRenderWrapperForComponent(<ButtonContainer />),
|
||||
</StyledContainer>,
|
||||
),
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
let button;
|
||||
if (type === 'group') {
|
||||
button = canvas.getByTestId(`primary-left-button-default`);
|
||||
} else {
|
||||
button = canvas.getByTestId(`primary-button-default`);
|
||||
}
|
||||
|
||||
expect(clickJestFn).toHaveBeenCalledTimes(0);
|
||||
|
||||
const button = canvas.getByTestId('primary-button-default');
|
||||
const numberOfClicks = clickJestFn.mock.calls.length;
|
||||
await userEvent.click(button);
|
||||
expect(clickJestFn).toHaveBeenCalledTimes(numberOfClicks + 1);
|
||||
},
|
||||
parameters: {
|
||||
pseudo: Object.keys(states).reduce(
|
||||
(acc, state) => ({
|
||||
...acc,
|
||||
[state]: variants.map(
|
||||
(variant) =>
|
||||
variant &&
|
||||
['#left', '#center', '#right'].map(
|
||||
(pos) => `${pos}-${variant}-${type}-${state}`,
|
||||
),
|
||||
),
|
||||
}),
|
||||
{},
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
expect(clickJestFn).toHaveBeenCalledTimes(1);
|
||||
},
|
||||
};
|
||||
MediumSize.parameters = {
|
||||
pseudo: {
|
||||
hover: [
|
||||
'#primary-button-hover',
|
||||
'#secondary-button-hover',
|
||||
'#tertiary-button-hover',
|
||||
'#tertiaryBold-button-hover',
|
||||
'#tertiaryLight-button-hover',
|
||||
'#danger-button-hover',
|
||||
],
|
||||
active: [
|
||||
'#primary-button-pressed',
|
||||
'#secondary-button-pressed',
|
||||
'#tertiary-button-pressed',
|
||||
'#tertiaryBold-button-pressed',
|
||||
'#tertiaryLight-button-pressed',
|
||||
'#danger-button-pressed',
|
||||
],
|
||||
focus: [
|
||||
'#primary-button-focus',
|
||||
'#secondary-button-focus',
|
||||
'#tertiary-button-focus',
|
||||
'#tertiaryBold-button-focus',
|
||||
'#tertiaryLight-button-focus',
|
||||
'#danger-button-focus',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
// Small size
|
||||
export const SmallSize: Story = {
|
||||
render: getRenderWrapperForComponent(<ButtonContainer size="small" />),
|
||||
};
|
||||
SmallSize.parameters = {
|
||||
pseudo: {
|
||||
hover: [
|
||||
'#primary-button-hover',
|
||||
'#secondary-button-hover',
|
||||
'#tertiary-button-hover',
|
||||
'#tertiaryBold-button-hover',
|
||||
'#tertiaryLight-button-hover',
|
||||
'#danger-button-hover',
|
||||
],
|
||||
active: [
|
||||
'#primary-button-pressed',
|
||||
'#secondary-button-pressed',
|
||||
'#tertiary-button-pressed',
|
||||
'#tertiaryBold-button-pressed',
|
||||
'#tertiaryLight-button-pressed',
|
||||
'#danger-button-pressed',
|
||||
],
|
||||
focus: [
|
||||
'#primary-button-focus',
|
||||
'#secondary-button-focus',
|
||||
'#tertiary-button-focus',
|
||||
'#tertiaryBold-button-focus',
|
||||
'#tertiaryLight-button-focus',
|
||||
'#danger-button-focus',
|
||||
],
|
||||
},
|
||||
};
|
||||
export const MediumSize = generateStory('medium', 'button', ButtonLine);
|
||||
export const SmallSize = generateStory('small', 'button', ButtonLine);
|
||||
export const MediumSizeGroup = generateStory(
|
||||
'medium',
|
||||
'group',
|
||||
ButtonGroupLine,
|
||||
);
|
||||
export const SmallSizeGroup = generateStory('small', 'group', ButtonGroupLine);
|
||||
|
@ -9,7 +9,7 @@ type OwnProps = {
|
||||
export function TableActionBarButtonToggleComments({ onClick }: OwnProps) {
|
||||
return (
|
||||
<EntityTableActionBarButton
|
||||
label="Notes"
|
||||
label="Note"
|
||||
icon={<IconNotes size={16} />}
|
||||
onClick={onClick}
|
||||
/>
|
||||
|
@ -33,3 +33,5 @@ export { IconFileUpload } from '@tabler/icons-react';
|
||||
export { IconChevronsRight } from '@tabler/icons-react';
|
||||
export { IconNotes } from '@tabler/icons-react';
|
||||
export { IconCirclePlus } from '@tabler/icons-react';
|
||||
export { IconCheckbox } from '@tabler/icons-react';
|
||||
export { IconTimelineEvent } from '@tabler/icons-react';
|
||||
|
@ -65,16 +65,16 @@ const StyledItemLabel = styled.div`
|
||||
`;
|
||||
|
||||
const StyledSoonPill = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 50px;
|
||||
background-color: ${({ theme }) => theme.background.transparent.light};
|
||||
border-radius: 50px;
|
||||
display: flex;
|
||||
font-size: ${({ theme }) => theme.font.size.xs};
|
||||
height: 16px;
|
||||
justify-content: center;
|
||||
margin-left: auto;
|
||||
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||
padding-right: ${({ theme }) => theme.spacing(2)};
|
||||
margin-left: auto; // this aligns the pill to the right
|
||||
`;
|
||||
|
||||
function NavItem({ label, icon, to, onClick, active, danger, soon }: OwnProps) {
|
||||
|
@ -32,7 +32,7 @@ export const Default: Story = {
|
||||
),
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
const notesButton = await canvas.findByText('Notes');
|
||||
const notesButton = await canvas.findByText('Note');
|
||||
await notesButton.click();
|
||||
},
|
||||
parameters: {
|
||||
|
Loading…
Reference in New Issue
Block a user