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

fix(SILVA-449): truncating text based on parent #456

Merged
merged 9 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package ca.bc.gov.restapi.results.oracle.dto;

public record OpenCategoryCodeDto(
String code,
String description
) {

}
45 changes: 45 additions & 0 deletions frontend/src/__test__/components/TruncatedText.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { vi } from 'vitest';
import TruncatedText from '../../components/TruncatedText';

// Mock Tooltip component from Carbon to ensure tests run without extra dependencies
vi.mock('@carbon/react', () => {
const Tooltip = ({ label, children }) => <span data-tooltip={label}>{children}</span>;
return { Tooltip };
});

describe('TruncatedText Component', () => {

it('renders the component with default props', () => {
render(<TruncatedText text="This is a test text that may be truncated" />);
expect(screen.getByText(/This is a test text/)).toBeInTheDocument();
});

it('truncates text based on maxLength', () => {
render(<TruncatedText text="This is a test text that should be truncated" maxLength={10} />);
expect(screen.getByText("This is a ...")).toBeInTheDocument();
});

it('does not truncate text if length is within maxLength', () => {
render(<TruncatedText text="Short text" maxLength={20} />);
expect(screen.getByText("Short text")).toBeInTheDocument();
});

it('truncates text based on parentWidth less than 200px', () => {
render(<TruncatedText text="This is a test text that should be truncated" parentWidth={150} />);
expect(screen.getByText("This is a test ...")).toBeInTheDocument(); // 150/10 = 15 chars
});

it('truncates text based on parentWidth greater than 200px', () => {
const text = "This is a test text that should be truncated".repeat(10);
render(<TruncatedText text={text} parentWidth={300} />);
expect(screen.getByText("This is a test text that should be truncatedThis is a test t...")).toBeInTheDocument(); // 300/5 = 60 chars
});

it('renders tooltip with full text on hover', async () => {
const {container} = render(<TruncatedText text="This is a test text that should be truncated" maxLength={10} />);
expect(container.querySelector('span').getAttribute('data-tooltip')).toBe("This is a test text that should be truncated");
});

});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useContext, useEffect, useState } from "react";
import React, { useContext, useEffect, useState, useRef } from "react";
import {
TableToolbar,
TableToolbarAction,
Expand Down Expand Up @@ -42,6 +42,7 @@ import {
import { useNavigate } from "react-router-dom";
import { setOpeningFavorite } from '../../../../services/OpeningFavouriteService';
import { useNotification } from "../../../../contexts/NotificationProvider";
import TruncatedText from "../../../TruncatedText";

interface ISearchScreenDataTable {
rows: OpeningsSearch[];
Expand All @@ -53,6 +54,10 @@ interface ISearchScreenDataTable {
totalItems: number;
}

interface ICellRefs {
offsetWidth: number;
}

const SearchScreenDataTable: React.FC<ISearchScreenDataTable> = ({
rows,
headers,
Expand All @@ -75,6 +80,24 @@ const SearchScreenDataTable: React.FC<ISearchScreenDataTable> = ({
const [selectedRows, setSelectedRows] = useState<string[]>([]); // State to store selected rows
const navigate = useNavigate();

// This ref is used to calculate the width of the container for each cell
const cellRefs = useRef([]);
// Holds the with of each cell in the table
const [cellWidths, setCellWidths] = useState<number[]>([]);

useEffect(() => {
const widths = cellRefs.current.map((cell: ICellRefs) => cell.offsetWidth || 0);
setCellWidths(widths);

const handleResize = () => {
const newWidths = cellRefs.current.map((cell: ICellRefs) => cell.offsetWidth || 0);
setCellWidths(newWidths);
};

window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);

useEffect(() => {
setInitialItemsPerPage(itemsPerPage);
}, [rows, totalItems]);
Expand Down Expand Up @@ -286,9 +309,12 @@ const SearchScreenDataTable: React.FC<ISearchScreenDataTable> = ({
{headers.map((header) =>
header.selected ? (
<TableCell
ref={el => (cellRefs.current[i] = el)}
key={header.key}
className={
header.key === "actions" && showSpatial ? "p-0" : null
header.key === "actions" && showSpatial ? "p-0" :
header.elipsis ? "ellipsis" :
null
}
>
{header.key === "statusDescription" ? (
Expand Down Expand Up @@ -345,10 +371,10 @@ const SearchScreenDataTable: React.FC<ISearchScreenDataTable> = ({
</OverflowMenu>
</CheckboxGroup>
) : header.header === "Category" ? (
row["categoryCode"] +
" - " +
row["categoryDescription"]
) : (
<TruncatedText
text={row["categoryCode"] + " - " + row["categoryDescription"]}
parentWidth={cellWidths[i]} />
) : (
row[header.key]
)}
</TableCell>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
{
key: 'categoryDescription',
header: 'Category',
selected: true
selected: true,
elipsis: true
},
{
key: 'orgUnitName',
Expand Down Expand Up @@ -54,7 +55,7 @@
];


export const rows:any = [

Check failure on line 58 in frontend/src/components/SilvicultureSearch/Openings/SearchScreenDataTable/testData.ts

View workflow job for this annotation

GitHub Actions / Lint (Frontend)

Unexpected any. Specify a different type
{
id: '114207',
openingId: '114207',
Expand Down
20 changes: 20 additions & 0 deletions frontend/src/components/TruncatedText/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import { Tooltip } from '@carbon/react';

const TruncatedText: React.FC<{text: string, maxLength?: number, parentWidth?: number}> = ({ text, maxLength, parentWidth }) => {

// If parentWidth is provided, calculate the number of characters that can fit in the parent container
// Otherwise, use maxLength if provided, otherwise default to 50
// The number of characters that can fit in the parent container is calculated based on the parentWidth
// If the parentWidth is less than 200px, we divide the parentWidth by 10, otherwise we divide by 5
const charCount = parentWidth ? Math.floor(parentWidth / (parentWidth < 200 ? 10 : 5)) : maxLength ? maxLength : 50;
const truncated = text.length > charCount ? text.slice(0, charCount) + "..." : text;
return <Tooltip
label={text}
align="bottom-left"
autoAlign
>
<span>{truncated}</span></Tooltip>;
}

export default TruncatedText;
1 change: 1 addition & 0 deletions frontend/src/types/TableHeader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export interface ITableHeader {
key: string;
header: string;
selected:boolean;
elipsis?:boolean;
}
Loading