From 1bfb43fd0e5ffc1ee9b879e9ee7aa4a2a197d9c7 Mon Sep 17 00:00:00 2001 From: FeimeiChen <54688836+FeimeiChen@users.noreply.github.com> Date: Fri, 4 Oct 2024 12:55:31 -1000 Subject: [PATCH] B Create TooltipOnTruncate Component --- ui/src/components/table/groupings-table.tsx | 21 +++++---- .../table-element/grouping-path-cell.tsx | 22 +++++---- .../table-element/tooltip-on-truncate.tsx | 36 ++++++++++++++ .../components/table/groupings-table.test.tsx | 20 ++++++++ .../table-element/grouping-path-cell.test.tsx | 20 ++++++++ .../tooltip-on-truncate.test.tsx | 47 +++++++++++++++++++ 6 files changed, 149 insertions(+), 17 deletions(-) create mode 100644 ui/src/components/table/table-element/tooltip-on-truncate.tsx create mode 100644 ui/tests/components/table/table-element/tooltip-on-truncate.test.tsx diff --git a/ui/src/components/table/groupings-table.tsx b/ui/src/components/table/groupings-table.tsx index 82e561cc..cb3df24b 100644 --- a/ui/src/components/table/groupings-table.tsx +++ b/ui/src/components/table/groupings-table.tsx @@ -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); @@ -109,15 +110,17 @@ const GroupingsTable = ({ groupingPaths }: { groupingPaths: GroupingPath[] }) => )} {cell.column.id === 'description' && ( -
- {flexRender(cell.column.columnDef.cell, cell.getContext())} -
+ +
+ {flexRender(cell.column.columnDef.cell, cell.getContext())} +
+
)} {cell.column.id === 'path' && ( diff --git a/ui/src/components/table/table-element/grouping-path-cell.tsx b/ui/src/components/table/table-element/grouping-path-cell.tsx index c08b218c..b3ce74ef 100644 --- a/ui/src/components/table/table-element/grouping-path-cell.tsx +++ b/ui/src/components/table/table-element/grouping-path-cell.tsx @@ -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'); @@ -22,13 +23,15 @@ const GroupingPathCell = ({ path }: { path: string }) => { return (
- + + + @@ -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" > - + diff --git a/ui/src/components/table/table-element/tooltip-on-truncate.tsx b/ui/src/components/table/table-element/tooltip-on-truncate.tsx new file mode 100644 index 00000000..a4104546 --- /dev/null +++ b/ui/src/components/table/table-element/tooltip-on-truncate.tsx @@ -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(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 ( + + + + {children} + + {isTruncated && ( + +

{value}

+
+ )} +
+
+ ); +}; + +export default TooltipOnTruncate; diff --git a/ui/tests/components/table/groupings-table.test.tsx b/ui/tests/components/table/groupings-table.test.tsx index 164630ba..02ac6b03 100644 --- a/ui/tests/components/table/groupings-table.test.tsx +++ b/ui/tests/components/table/groupings-table.test.tsx @@ -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(); + 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(); + }); + }); }); diff --git a/ui/tests/components/table/table-element/grouping-path-cell.test.tsx b/ui/tests/components/table/table-element/grouping-path-cell.test.tsx index 61177ab9..1a44ee22 100644 --- a/ui/tests/components/table/table-element/grouping-path-cell.test.tsx +++ b/ui/tests/components/table/table-element/grouping-path-cell.test.tsx @@ -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(); + + 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(); + }); + }); }); diff --git a/ui/tests/components/table/table-element/tooltip-on-truncate.test.tsx b/ui/tests/components/table/table-element/tooltip-on-truncate.test.tsx new file mode 100644 index 00000000..61d00449 --- /dev/null +++ b/ui/tests/components/table/table-element/tooltip-on-truncate.test.tsx @@ -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( + + + + ); + 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( + + + + ); + const button = screen.getByRole('button'); + + await waitFor(async () => { + await userEvent.hover(button); + }); + + await waitFor(() => { + expect(screen.queryByTestId('tooltip-on-truncate')).not.toBeInTheDocument(); + }); + }); +});