Skip to content

Commit 56b09ea

Browse files
pietro-maximoffsoulBitcreed-victor
authored
[Tooltip] - add component (#28)
* [Tooltip] - add component * [Tooltip] - update component with dynamic position + disabled & target events props were added * [Tooltip]: change dataActionId -> dataLayoutId * [Tooltip] - added export component * [Tooltip] - added click outside event + fix for hover * chore: move Tabs to molecules and update layout id (#31) * [Tooltip] - added fixes from comments * [Tooltip] - added changes from comment * chore: add changeset * [Tooltip] - fix positioning Co-authored-by: soulBit <its.soulBit@gmail.com> Co-authored-by: Victor Creed <69458664+creed-victor@users.noreply.github.com>
1 parent af3bacf commit 56b09ea

9 files changed

Lines changed: 677 additions & 0 deletions

File tree

.changeset/rotten-pugs-enjoy.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@sovryn/ui": patch
3+
---
4+
5+
SOV-146: add tooltip component
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
.tooltip {
2+
@apply bg-gray-10 rounded p-3 text-gray-90 absolute inline-block min-w-5 max-w-72 leading-5 opacity-0;
3+
4+
&.bottom,
5+
&.bottom-start,
6+
&.bottom-end,
7+
&.top,
8+
&.top-start,
9+
&.top-end,
10+
&.left,
11+
&.left-start,
12+
&.left-end,
13+
&.right,
14+
&.right-start,
15+
&.right-end {
16+
@apply opacity-100;
17+
&:after {
18+
@apply content-[''] absolute w-0 h-0 border-solid;
19+
}
20+
}
21+
22+
&.top,
23+
&.top-start,
24+
&.top-end {
25+
&:after {
26+
@apply border-t-[0.656rem] border-b-0 border-t-gray-10 border-b-transparent -bottom-[0.656rem] border-x-[0.516rem] border-x-transparent;
27+
}
28+
}
29+
30+
&.right,
31+
&.right-start,
32+
&.right-end {
33+
&:after {
34+
@apply border-r-[0.656rem] border-l-0 border-r-gray-10 border-l-transparent -left-[0.656rem] border-y-[0.516rem] border-y-transparent;
35+
}
36+
}
37+
38+
&.left,
39+
&.left-start,
40+
&.left-end {
41+
&:after {
42+
@apply border-l-[0.656rem] border-r-0 border-l-gray-10 border-r-transparent -right-[0.656rem] border-y-[0.516rem] border-y-transparent;
43+
}
44+
}
45+
46+
&.bottom,
47+
&.bottom-start,
48+
&.bottom-end {
49+
&:after {
50+
@apply border-b-[0.656rem] border-t-0 border-b-gray-10 border-t-transparent -top-[0.656rem] border-x-[0.516rem] border-x-transparent;
51+
}
52+
}
53+
54+
&.top,
55+
&.bottom {
56+
&:after {
57+
@apply left-0 right-0 m-auto;
58+
}
59+
&-start {
60+
&:after {
61+
@apply left-auto right-4;
62+
}
63+
}
64+
&-end {
65+
&:after {
66+
@apply left-4 right-auto;
67+
}
68+
}
69+
}
70+
71+
&.left,
72+
&.right {
73+
&:after {
74+
@apply top-0 bottom-0 m-auto;
75+
}
76+
&-start {
77+
&:after {
78+
@apply top-auto bottom-4;
79+
}
80+
}
81+
&-end {
82+
&:after {
83+
@apply top-4 bottom-auto;
84+
}
85+
}
86+
}
87+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { Story } from '@storybook/react';
2+
3+
import React, { ComponentProps } from 'react';
4+
5+
import { Button, Icon, Link } from '../../1_atoms';
6+
import { Tooltip } from './Tooltip';
7+
import { TooltipPlacement, TooltipTrigger } from './Tooltip.types';
8+
9+
export default {
10+
title: 'Molecule/Tooltip',
11+
component: Tooltip,
12+
};
13+
14+
const Template: Story<ComponentProps<typeof Tooltip>> = args => (
15+
<div className="flex justify-center items-center h-96 w-full">
16+
<Tooltip {...args} />
17+
</div>
18+
);
19+
20+
export const Basic = Template.bind({});
21+
Basic.args = {
22+
content: (
23+
<div className="max-w-52">
24+
Click here to fund your wallet and get started with Sovryn
25+
<Link
26+
className="mt-4 block text-blue-2 hover:no-underline"
27+
text="Read more"
28+
href="#"
29+
/>
30+
</div>
31+
),
32+
children: (
33+
<div>
34+
<Icon icon="info" />
35+
</div>
36+
),
37+
className: '',
38+
tooltipClassName: '',
39+
dataLayoutId: '',
40+
placement: TooltipPlacement.top,
41+
disabled: false,
42+
trigger: TooltipTrigger.hover,
43+
};
44+
45+
const InteractiveTemplate: Story<ComponentProps<typeof Tooltip>> = args => (
46+
<div className="flex justify-center mt-32">
47+
<Tooltip
48+
{...args}
49+
onHide={() => console.log('onHide event called')}
50+
onShow={() => console.log('onShow event called')}
51+
/>
52+
</div>
53+
);
54+
55+
export const Interactive = InteractiveTemplate.bind({});
56+
Interactive.args = {
57+
content: (
58+
<div className="max-w-52">
59+
Click here to fund your wallet and get started with Sovryn
60+
<Link
61+
className="mt-4 block text-blue-2 hover:no-underline"
62+
text="Read more"
63+
href="#"
64+
/>
65+
</div>
66+
),
67+
children: (
68+
<div>
69+
<Button text="Info" />
70+
</div>
71+
),
72+
className: '',
73+
tooltipClassName: '',
74+
dataLayoutId: '',
75+
placement: TooltipPlacement.top,
76+
disabled: false,
77+
trigger: TooltipTrigger.hover,
78+
};
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { act, render, waitFor } from '@testing-library/react';
2+
import userEvent from '@testing-library/user-event';
3+
4+
import React from 'react';
5+
6+
import { Tooltip } from './Tooltip';
7+
import { TooltipPlacement, TooltipTrigger } from './Tooltip.types';
8+
9+
describe('Tooltip', () => {
10+
beforeEach(() => {
11+
jest.useFakeTimers();
12+
});
13+
14+
afterEach(() => {
15+
jest.useRealTimers();
16+
});
17+
18+
test('should render a tooltip on hover in and hide on hover out', () => {
19+
const { getByRole, queryByText } = render(
20+
<Tooltip children={<button>Text</button>} content={<>Tooltip</>} />,
21+
);
22+
const button = getByRole('button');
23+
userEvent.hover(button);
24+
const tooltip = queryByText('Tooltip');
25+
expect(tooltip).toBeInTheDocument();
26+
userEvent.unhover(button);
27+
act(() => {
28+
jest.runAllTimers();
29+
});
30+
expect(tooltip).not.toBeInTheDocument();
31+
});
32+
33+
it('should render a tooltip on focus in and hide on focus out', async () => {
34+
const { getByRole, queryByText } = render(
35+
<Tooltip
36+
trigger={TooltipTrigger.focus}
37+
children={<button>Text</button>}
38+
content={<>Tooltip</>}
39+
/>,
40+
);
41+
const button = getByRole('button');
42+
await waitFor(() => button.focus());
43+
const tooltip = queryByText('Tooltip');
44+
expect(tooltip).toBeInTheDocument();
45+
await waitFor(() => button.blur());
46+
act(() => {
47+
jest.runAllTimers();
48+
});
49+
expect(tooltip).not.toBeInTheDocument();
50+
});
51+
52+
it('should render a tooltip on click and hide on a second click', () => {
53+
const { getByRole, queryByText } = render(
54+
<Tooltip
55+
trigger={TooltipTrigger.click}
56+
children={<button>Text</button>}
57+
content={<>Tooltip</>}
58+
/>,
59+
);
60+
const button = getByRole('button');
61+
userEvent.click(button);
62+
const tooltip = queryByText('Tooltip');
63+
expect(tooltip).toBeInTheDocument();
64+
userEvent.click(button);
65+
act(() => {
66+
jest.runAllTimers();
67+
});
68+
expect(tooltip).not.toBeInTheDocument();
69+
});
70+
71+
it('should not render a tooltip if disabled is true', () => {
72+
const { getByRole, queryByText } = render(
73+
<Tooltip
74+
disabled
75+
children={<button>Text</button>}
76+
content={<>Tooltip</>}
77+
/>,
78+
);
79+
userEvent.click(getByRole('button'));
80+
expect(queryByText('Tooltip')).not.toBeInTheDocument();
81+
});
82+
83+
it('should render a tooltip with a bottom placement', () => {
84+
const { getByRole, getByText } = render(
85+
<Tooltip
86+
children={<button>Text</button>}
87+
placement={TooltipPlacement.bottom}
88+
content={<>Tooltip</>}
89+
/>,
90+
);
91+
userEvent.hover(getByRole('button'));
92+
const tooltip = getByText('Tooltip');
93+
const classes = tooltip.getAttribute('class');
94+
expect(classes).toContain('bottom');
95+
});
96+
});

0 commit comments

Comments
 (0)