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

Create TooltipOnTruncate Component #100

Merged
merged 1 commit into from
Oct 8, 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
21 changes: 12 additions & 9 deletions ui/src/components/table/groupings-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import GroupingPathCell from '@/components/table/table-element/grouping-path-cel
import Link from 'next/link';
import { useLocalStorage } from 'usehooks-ts';
import { GroupingPath } from '@/lib/types';
import TooltipOnTruncate from '@/components/table/table-element/tooltip-on-truncate';

const pageSize = parseInt(process.env.NEXT_PUBLIC_PAGE_SIZE as string);

Expand Down Expand Up @@ -109,15 +110,17 @@ const GroupingsTable = ({ groupingPaths }: { groupingPaths: GroupingPath[] }) =>
)}
</div>
{cell.column.id === 'description' && (
<div
className={`${
columnCount === 3
? 'truncate sm:max-w-[calc(6ch+1em)] md:max-w-[calc(40ch+1em)]'
: 'truncate'
}`}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</div>
<TooltipOnTruncate value={String(cell.getValue() as string)}>
<div
className={`${
columnCount === 3
? 'truncate sm:max-w-[calc(6ch+1em)] md:max-w-[calc(40ch+1em)]'
: 'truncate'
}`}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</div>
</TooltipOnTruncate>
)}
{cell.column.id === 'path' && (
<GroupingPathCell path={cell.row.getValue('path')} />
Expand Down
22 changes: 14 additions & 8 deletions ui/src/components/table/table-element/grouping-path-cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ClipboardIcon } from 'lucide-react';
import { Input } from '@/components/ui/input';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import { useState } from 'react';
import TooltipOnTruncate from '@/components/table/table-element/tooltip-on-truncate';

const GroupingPathCell = ({ path }: { path: string }) => {
const [tooltipContent, setTooltipContent] = useState('copy');
Expand All @@ -22,13 +23,15 @@ const GroupingPathCell = ({ path }: { path: string }) => {

return (
<div className="flex items-center w-full outline outline-1 rounded h-6 m-1">
<Input
id="dataInput"
value={path}
readOnly
className="flex-1 h-6 text-input-text-grey text-[0.875rem]
border-none rounded-none w-[161px] truncate"
/>
<TooltipOnTruncate value={path}>
<Input
id="dataInput"
value={path}
readOnly
className="flex-1 h-6 text-input-text-grey text-[0.875rem]
border-none rounded-none w-full truncate"
/>
</TooltipOnTruncate>

<TooltipProvider>
<Tooltip open={tooltipVisible} onOpenChange={setTooltipVisible}>
Expand All @@ -39,7 +42,10 @@ const GroupingPathCell = ({ path }: { path: string }) => {
justify-center hover:bg-green-blue h-6 p-2 transition ease-in-out duration-150"
data-testid="clipboard-button"
>
<ClipboardIcon className="group-hover:text-white h-4 w-4 text-gray-600" data-testid="clipboard-icon" />
<ClipboardIcon
className="group-hover:text-white h-4 w-4 text-gray-600"
data-testid="clipboard-icon"
/>
</button>
</TooltipTrigger>
<TooltipContent>
Expand Down
36 changes: 36 additions & 0 deletions ui/src/components/table/table-element/tooltip-on-truncate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use client';
import React, { useEffect, useState, useRef } from 'react';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';

const TooltipOnTruncate = ({ children, value }: { children: React.ReactNode; value: string }) => {
const [isTruncated, setIsTruncated] = useState(false);
const ref = useRef<HTMLButtonElement>(null);

useEffect(() => {
const checkIsTruncated = () => {
if (ref.current) {
setIsTruncated(ref.current.scrollWidth > ref.current.clientWidth);
}
};
checkIsTruncated();
window.addEventListener('resize', checkIsTruncated);
return () => window.removeEventListener('resize', checkIsTruncated);
}, []);

return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild ref={ref}>
{children}
</TooltipTrigger>
{isTruncated && (
<TooltipContent className="max-w-[190px] max-h-[180px] text-center whitespace-normal break-words bg-black text-white">
<p data-testid="tooltip-on-truncate">{value}</p>
</TooltipContent>
)}
</Tooltip>
</TooltipProvider>
);
};

export default TooltipOnTruncate;
20 changes: 20 additions & 0 deletions ui/tests/components/table/groupings-table.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,24 @@ describe('GroupingsTable', () => {
await checkPageContent('Last', mockData.length - pageSize, mockData.length - 1);
await checkPageContent('Previous', mockData.length - pageSize * 2, mockData.length - pageSize - 1);
});
it('should show tooltip if description content is truncated', async () => {
Object.defineProperties(HTMLElement.prototype, {
scrollWidth: { get: () => 500, configurable: true },
clientWidth: { get: () => 30, configurable: true }
});
render(<GroupingsTable groupingPaths={mockData} />);
const firstButton = screen.getByText('First');

fireEvent.click(firstButton);

const description = screen.getByText('Test Description 0');
await waitFor(async () => {
await userEvent.hover(description);
});

// Wait for the tooltip to appear
await waitFor(() => {
expect(screen.getAllByTestId('tooltip-on-truncate')[0]).toBeInTheDocument();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ describe('GroupingPathCell', () => {
() => {
expect(screen.queryByText('copied!')).not.toBeInTheDocument();
},
{ timeout: 2000 }
{ timeout: 3000 }
);

// Check the 'copy' text appears again
Expand All @@ -65,4 +65,24 @@ describe('GroupingPathCell', () => {
{ timeout: 2000 }
);
});

it('should show tooltip if content is truncated', async () => {
Object.defineProperties(HTMLElement.prototype, {
scrollWidth: { get: () => 500, configurable: true },
clientWidth: { get: () => 30, configurable: true }
});
render(<GroupingPathCell path={path} />);

const inputElement = screen.getByRole('textbox');
expect(inputElement).toHaveValue(path);

await waitFor(async () => {
await userEvent.hover(inputElement);
});

// Wait for the tooltip to appear
await waitFor(() => {
expect(screen.getAllByTestId('tooltip-on-truncate')[0]).toBeInTheDocument();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import TooltipOnTruncate from '@/components/table/table-element/tooltip-on-truncate';

describe('TooltipOnTruncate component', () => {
it('should show tooltip if content is truncated', async () => {
Object.defineProperties(HTMLElement.prototype, {
scrollWidth: { get: () => 500, configurable: true },
clientWidth: { get: () => 30, configurable: true }
});
render(
<TooltipOnTruncate value="Truncated Text">
<button>Truncated Text</button>
</TooltipOnTruncate>
);
const button = screen.getByRole('button');
await waitFor(async () => {
await userEvent.hover(button);
});

// Wait for the tooltip to appear
await waitFor(() => {
expect(screen.getAllByTestId('tooltip-on-truncate')[0]).toBeInTheDocument();
});
});

it('should not show tooltip if content is not truncated', async () => {
Object.defineProperties(HTMLElement.prototype, {
scrollWidth: { get: () => 30, configurable: true },
clientWidth: { get: () => 500, configurable: true }
});
render(
<TooltipOnTruncate value="Truncated Text">
<button>Truncated Text</button>
</TooltipOnTruncate>
);
const button = screen.getByRole('button');

await waitFor(async () => {
await userEvent.hover(button);
});

await waitFor(() => {
expect(screen.queryByTestId('tooltip-on-truncate')).not.toBeInTheDocument();
});
});
});
Loading