Skip to content

Commit 9508198

Browse files
authored
RI-7226: fix RiTooltip when content is empty (#4742)
* RI-7226: fix RiTooltip when content is empty * add unit tests for RiTooltip
1 parent b993a06 commit 9508198

File tree

2 files changed

+190
-1
lines changed

2 files changed

+190
-1
lines changed

redisinsight/ui/src/components/base/tooltip/RITooltip.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const RiTooltip = ({
2323
<TooltipProvider>
2424
<Tooltip
2525
{...props}
26-
content={<HoverContent title={title} content={content} />}
26+
content={content && <HoverContent title={title} content={content} />}
2727
placement={position}
2828
openDelayDuration={delay}
2929
>
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import React from 'react'
2+
import { fireEvent, screen, act } from '@testing-library/react'
3+
import { render, waitForRiTooltipVisible } from 'uiSrc/utils/test-utils'
4+
import { RiTooltip, RiTooltipProps } from './RITooltip'
5+
6+
const TestButton = () => (
7+
<button type="button" data-testid="tooltip-trigger">
8+
Hover me
9+
</button>
10+
)
11+
12+
const defaultProps: RiTooltipProps = {
13+
children: <TestButton />,
14+
content: 'Test tooltip content',
15+
}
16+
17+
describe('RiTooltip', () => {
18+
it('should render', () => {
19+
expect(render(<RiTooltip {...defaultProps} />)).toBeTruthy()
20+
})
21+
22+
it('should render children', () => {
23+
render(<RiTooltip {...defaultProps} />)
24+
25+
expect(screen.getByTestId('tooltip-trigger')).toBeInTheDocument()
26+
})
27+
28+
it('should render tooltip content on focus', async () => {
29+
render(<RiTooltip {...defaultProps} />)
30+
31+
await act(async () => {
32+
fireEvent.focus(screen.getByTestId('tooltip-trigger'))
33+
})
34+
await waitForRiTooltipVisible()
35+
36+
expect(screen.getAllByText('Test tooltip content')[0]).toBeInTheDocument()
37+
})
38+
39+
it('should render tooltip with title and content', async () => {
40+
render(
41+
<RiTooltip {...defaultProps} title="Test Title" content="Test content" />,
42+
)
43+
44+
await act(async () => {
45+
fireEvent.focus(screen.getByTestId('tooltip-trigger'))
46+
})
47+
await waitForRiTooltipVisible()
48+
49+
expect(screen.getAllByText('Test Title')[0]).toBeInTheDocument()
50+
expect(screen.getAllByText('Test content')[0]).toBeInTheDocument()
51+
})
52+
53+
it('should render tooltip with only content when title is not provided', async () => {
54+
render(<RiTooltip {...defaultProps} content="Only content" />)
55+
56+
await act(async () => {
57+
fireEvent.focus(screen.getByTestId('tooltip-trigger'))
58+
})
59+
await waitForRiTooltipVisible()
60+
61+
expect(screen.getAllByText('Only content')[0]).toBeInTheDocument()
62+
expect(screen.queryByRole('heading')).not.toBeInTheDocument()
63+
})
64+
65+
it('should not render tooltip when content is not provided', async () => {
66+
render(
67+
<RiTooltip title="Test Title">
68+
<TestButton />
69+
</RiTooltip>,
70+
)
71+
72+
await act(async () => {
73+
fireEvent.focus(screen.getByTestId('tooltip-trigger'))
74+
})
75+
76+
// Wait a bit to ensure tooltip doesn't appear
77+
await new Promise((resolve) => setTimeout(resolve, 100))
78+
79+
expect(screen.queryByText('Test Title')).not.toBeInTheDocument()
80+
})
81+
82+
it('should apply anchorClassName to the wrapper span', () => {
83+
render(
84+
<RiTooltip {...defaultProps} anchorClassName="custom-anchor-class" />,
85+
)
86+
87+
const wrapper = screen.getAllByTestId('tooltip-trigger')[0].parentElement
88+
expect(wrapper).toHaveClass('custom-anchor-class')
89+
})
90+
91+
it('should render with React node as title', async () => {
92+
const titleNode = <span data-testid="custom-title">Custom Title Node</span>
93+
94+
render(
95+
<RiTooltip {...defaultProps} title={titleNode} content="Test content" />,
96+
)
97+
98+
await act(async () => {
99+
fireEvent.focus(screen.getByTestId('tooltip-trigger'))
100+
})
101+
await waitForRiTooltipVisible()
102+
103+
expect(screen.getAllByTestId('custom-title')[0]).toBeInTheDocument()
104+
expect(screen.getAllByText('Test content')[0]).toBeInTheDocument()
105+
})
106+
107+
it('should render with React node as content', async () => {
108+
const contentNode = (
109+
<div data-testid="tooltip-custom-content">
110+
<p>Custom content with HTML</p>
111+
<TestButton />
112+
</div>
113+
)
114+
115+
render(<RiTooltip {...defaultProps} content={contentNode} />)
116+
117+
await act(async () => {
118+
fireEvent.focus(screen.getByTestId('tooltip-trigger'))
119+
})
120+
await waitForRiTooltipVisible()
121+
122+
expect(screen.getAllByTestId('tooltip-custom-content')[0]).toBeInTheDocument()
123+
expect(screen.getAllByText('Custom content with HTML')[0]).toBeInTheDocument()
124+
expect(
125+
screen.getAllByRole('button', { name: 'Hover me' })[0],
126+
).toBeInTheDocument()
127+
})
128+
129+
it('should pass through additional props to underlying Tooltip component', async () => {
130+
render(
131+
<RiTooltip
132+
{...defaultProps}
133+
position="top"
134+
delay={100}
135+
data-testid="custom-tooltip"
136+
/>,
137+
)
138+
139+
await act(async () => {
140+
fireEvent.focus(screen.getByTestId('tooltip-trigger'))
141+
})
142+
await waitForRiTooltipVisible()
143+
144+
// The tooltip should be rendered (testing that props are passed through)
145+
expect(screen.getAllByText('Test tooltip content')[0]).toBeInTheDocument()
146+
})
147+
148+
it('should handle empty string content', async () => {
149+
render(<RiTooltip {...defaultProps} content="" />)
150+
151+
await act(async () => {
152+
fireEvent.focus(screen.getByTestId('tooltip-trigger'))
153+
})
154+
155+
// Wait a bit to ensure tooltip doesn't appear
156+
await new Promise((resolve) => setTimeout(resolve, 100))
157+
158+
// Should not render tooltip for empty content
159+
expect(screen.queryByRole('tooltip')).not.toBeInTheDocument()
160+
})
161+
162+
it('should handle null content', async () => {
163+
render(<RiTooltip {...defaultProps} content={null} />)
164+
165+
await act(async () => {
166+
fireEvent.focus(screen.getByTestId('tooltip-trigger'))
167+
})
168+
169+
// Wait a bit to ensure tooltip doesn't appear
170+
await new Promise((resolve) => setTimeout(resolve, 100))
171+
172+
// Should not render tooltip for null content
173+
expect(screen.queryByRole('tooltip')).not.toBeInTheDocument()
174+
})
175+
176+
it('should handle undefined content', async () => {
177+
render(<RiTooltip {...defaultProps} content={undefined} />)
178+
179+
await act(async () => {
180+
fireEvent.focus(screen.getByTestId('tooltip-trigger'))
181+
})
182+
183+
// Wait a bit to ensure tooltip doesn't appear
184+
await new Promise((resolve) => setTimeout(resolve, 100))
185+
186+
// Should not render tooltip for undefined content
187+
expect(screen.queryByRole('tooltip')).not.toBeInTheDocument()
188+
})
189+
})

0 commit comments

Comments
 (0)