Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add tooltip e2e #1988

Merged
merged 3 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions packages/blade/src/components/Tooltip/Tooltip.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,8 @@ const Tooltip = ({
open: isOpen,
strategy: 'fixed',
onOpenChange: (open) => {
if (open) {
setIsOpen(true);
onOpenChange?.({ isOpen: open });
} else {
setIsOpen(false);
onOpenChange?.({ isOpen: open });
}
setIsOpen(open);
onOpenChange?.({ isOpen: open });
},
middleware: [
shift({ crossAxis: false, padding: GAP }),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import type { StoryFn } from '@storybook/react';
import { within, userEvent } from '@storybook/testing-library';
import { expect, jest } from '@storybook/jest';
import React from 'react';
import { TooltipInteractiveWrapper, Tooltip as TooltipComponent } from '..';
import { Button } from '~components/Button';
import { Text } from '~components/Typography';
import { Badge } from '~components/Badge';
import BaseBox from '~components/Box/BaseBox';
import type { BladeCommonEvents } from '~components/types';

const sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));

const onOpenChange = jest.fn();

export const TestTooltipOpenClose: StoryFn<typeof TooltipComponent> = (
props,
): React.ReactElement => {
return (
<TooltipComponent {...props} onOpenChange={onOpenChange}>
<Button>Hover me</Button>
</TooltipComponent>
);
};

TestTooltipOpenClose.args = {
content: 'Some text',
};
TestTooltipOpenClose.play = async () => {
const { getByRole, queryByText } = within(document.body);
const tooltipContent = 'Some text';
await expect(queryByText(tooltipContent)).not.toBeInTheDocument();
const showButton = getByRole('button', { name: 'Hover me' });
// open
await userEvent.hover(showButton);
await sleep(600);
await expect(onOpenChange).toBeCalledWith({ isOpen: true });
await expect(queryByText(tooltipContent)).toBeVisible();
// close
await userEvent.unhover(showButton);
await sleep(600);
await expect(onOpenChange).toBeCalledWith({ isOpen: false });
await expect(queryByText(tooltipContent)).not.toBeInTheDocument();
};

// TooltipInteractiveWrapper
export const TestTooltipInteractiveWrapper: StoryFn<typeof TooltipComponent> = (
props,
): React.ReactElement => {
onOpenChange.mockReset();
return (
<TooltipComponent {...props} onOpenChange={onOpenChange}>
<TooltipInteractiveWrapper>
<Badge>NEW</Badge>
</TooltipInteractiveWrapper>
</TooltipComponent>
);
};
TestTooltipInteractiveWrapper.args = {
content: 'Hello World',
};
TestTooltipInteractiveWrapper.play = async () => {
const { getByText, queryByText } = within(document.body);
const tooltipContent = 'Hello World';
await expect(queryByText(tooltipContent)).not.toBeInTheDocument();
const badge = getByText('NEW');
// open
await userEvent.hover(badge);
await sleep(600);
await expect(onOpenChange).toBeCalledWith({ isOpen: true });
await expect(queryByText(tooltipContent)).toBeVisible();
// close
await userEvent.unhover(badge);
await sleep(600);
await expect(onOpenChange).toBeCalledWith({ isOpen: false });
await expect(queryByText(tooltipContent)).not.toBeInTheDocument();
};

const CustomTrigger = React.forwardRef<
HTMLDivElement,
{ children: React.ReactNode } & BladeCommonEvents
>(
(
{
children,
onBlur,
onFocus,
onMouseLeave,
onMouseMove,
onPointerDown,
onPointerEnter,
onTouchEnd,
onTouchStart,
},
ref,
) => {
return (
<BaseBox
width="80px"
height="80px"
Comment on lines +99 to +100
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why pass manually? this would cause issue when consumer use it the way you were using before right? also why use basebox? we can use Box right?

Copy link
Member Author

@anuraghazra anuraghazra Jan 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just an example showing how to pass proper props like onMouseOver onMouseHover ref tabIndex to properly create a trigger for tooltip. The styling & usage of box etc hardly matters in this example.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plus Box doesn't even support onBlur onFocus etc props.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure but what i'm trying to ask is if consumer will create custom tooltip they'll have to pass these things as well right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. events are required for users to create custom triggers. This is already well documented in the main story example

https://blade.razorpay.com/?path=/story/components-tooltip--with-custom-trigger

backgroundColor="surface.background.level2.lowContrast"
textAlign="center"
ref={ref}
tabIndex={0}
onBlur={onBlur}
onFocus={onFocus}
onMouseLeave={onMouseLeave}
onMouseMove={onMouseMove}
onPointerDown={onPointerDown}
onPointerEnter={onPointerEnter}
onTouchEnd={onTouchEnd}
onTouchStart={onTouchStart}
>
<Text contrast="low">{children}</Text>
</BaseBox>
);
},
);

// custom trigger
export const TestCustomTrigger: StoryFn<typeof TooltipComponent> = (props): React.ReactElement => {
onOpenChange.mockReset();
return (
<TooltipComponent {...props} onOpenChange={onOpenChange}>
<CustomTrigger>Custom Trigger</CustomTrigger>
</TooltipComponent>
);
};
TestCustomTrigger.args = {
content: 'Hello World',
};
TestCustomTrigger.play = async () => {
const { getByText, queryByText } = within(document.body);
const tooltipContent = 'Hello World';
await expect(queryByText(tooltipContent)).not.toBeInTheDocument();
const trigger = getByText('Custom Trigger');
// open
await userEvent.hover(trigger);
await sleep(600);
await expect(onOpenChange).toBeCalledWith({ isOpen: true });
await expect(queryByText(tooltipContent)).toBeVisible();
// close
await userEvent.unhover(trigger);
await sleep(600);
await expect(onOpenChange).toBeCalledWith({ isOpen: false });
await expect(queryByText(tooltipContent)).not.toBeInTheDocument();
};

// should open/close immediately on focus/blur without the default delay of 300ms
export const TestTooltipOpenCloseFocus: StoryFn<typeof TooltipComponent> = (
props,
): React.ReactElement => {
return (
<TooltipComponent {...props} onOpenChange={onOpenChange}>
<Button>Hover me</Button>
</TooltipComponent>
);
};

TestTooltipOpenCloseFocus.args = {
content: 'Some text',
};
TestTooltipOpenCloseFocus.play = async () => {
const { queryByText } = within(document.body);
const tooltipContent = 'Some text';
await expect(queryByText(tooltipContent)).not.toBeInTheDocument();
// open
await userEvent.keyboard('{Tab}', {});
await sleep(600);
await expect(onOpenChange).toBeCalledWith({ isOpen: true });
await expect(queryByText(tooltipContent)).toBeVisible();
// close
await userEvent.keyboard('{Tab}', {});
await sleep(600);
await expect(onOpenChange).toBeCalledWith({ isOpen: false });
await expect(queryByText(tooltipContent)).not.toBeInTheDocument();
};

export default {
title: 'Components/Interaction Tests/Tooltip',
component: TooltipComponent,
parameters: {
controls: {
disable: true,
},
a11y: { disable: true },
essentials: { disable: true },
actions: { disable: true },
},
};
Loading