Skip to content
Open
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: 7 additions & 2 deletions modules/react/tooltip/lib/OverflowTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ export const findEllipsisElement = (element: Element): Element | null => {
}
};

const getInnerText = (element: Element): string => {
const ownerWindow = element.ownerDocument?.defaultView;
return ownerWindow && element instanceof ownerWindow.HTMLElement ? element.innerText : '';
};

const isOverflowed = (element: Element) => {
const overflowElement = findEllipsisElement(element) || findOverflowElement(element);

Expand Down Expand Up @@ -134,14 +139,14 @@ export const OverflowTooltip = ({

const onMouseEnter = (event: React.MouseEvent) => {
const target = event.currentTarget;
setTitleText(target instanceof HTMLElement ? target.innerText : '');
setTitleText(getInnerText(target));
if (isOverflowed(target)) {
targetProps.onMouseEnter(event as React.MouseEvent);
}
};
const onFocus = (event: React.FocusEvent) => {
const target = event.currentTarget;
setTitleText(target instanceof HTMLElement ? target.innerText : '');
setTitleText(getInnerText(target));
if (isOverflowed(target)) {
targetProps.onFocus(event as React.FocusEvent);
}
Expand Down
54 changes: 53 additions & 1 deletion modules/react/tooltip/spec/OverflowTooltip.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {fireEvent, render, screen} from '@testing-library/react';
import {fireEvent, render, screen, within} from '@testing-library/react';
import {act} from 'react-dom/test-utils';

import {OverflowTooltip, findEllipsisElement, findOverflowElement} from '..';
Expand Down Expand Up @@ -29,6 +29,7 @@ describe('OverflowTooltip', () => {
const markAsOverflowed = (target: HTMLElement) => {
Object.defineProperty(target, 'scrollWidth', {configurable: true, value: 200});
Object.defineProperty(target, 'clientWidth', {configurable: true, value: 100});
Object.defineProperty(target, 'innerText', {configurable: true, value: target.textContent});
};

it('should render the tooltip after the delay when "showDelay" is passed', () => {
Expand All @@ -54,6 +55,57 @@ describe('OverflowTooltip', () => {
expect(screen.getByRole('tooltip')).toBeInTheDocument();
});

it('should show the target text in the tooltip when the target is overflowed', () => {
render(
<OverflowTooltip>
<span style={{overflow: 'hidden'}}>Overflowed Text</span>
</OverflowTooltip>
);

const target = screen.getByText('Overflowed Text');
markAsOverflowed(target);

fireEvent.mouseEnter(target);

act(() => {
vi.advanceTimersByTime(500);
});
expect(screen.getByRole('tooltip')).toHaveTextContent('Overflowed Text');
});

it('should show the target text when the target is in a different window realm', () => {
const testWindow = document.createElement('iframe');
document.body.appendChild(testWindow);

const containerBody = testWindow.contentDocument?.body;
if (!containerBody) {
throw new Error('iframe contentDocument not available');
}

const {unmount} = render(
<OverflowTooltip>
<span style={{overflow: 'hidden'}}>Overflowed Text</span>
</OverflowTooltip>,
{container: containerBody}
);

try {
const target = within(containerBody).getByText('Overflowed Text');
markAsOverflowed(target);

fireEvent.mouseEnter(target);

act(() => {
vi.advanceTimersByTime(500);
});
// Use textContent directly: toHaveTextContent() uses instanceof Node and fails across window realms
expect(within(containerBody).getByRole('tooltip').textContent).toBe('Overflowed Text');
} finally {
unmount();
testWindow.remove();
}
});

it('should hide the tooltip after the delay when "hideDelay" is passed', () => {
render(
<OverflowTooltip hideDelay={300}>
Expand Down
Loading