feat(ui): highlight search term for schema table in table details page (#19110)

* feat: highlight search term

* refactor: use stringToHTML instead of parse

* test: highlightSearchText method

* fix: test for schema table

* fix: mock implemetation of stringToHTML

* test: highlightSearchText function with null or falsy parameters
This commit is contained in:
Pranita Fulsundar 2024-12-18 14:40:10 +05:30 committed by GitHub
parent fec328bb98
commit 31b1132389
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 95 additions and 4 deletions

View File

@ -55,9 +55,11 @@ import {
getColumnSorter,
getEntityName,
getFrequentlyJoinedColumns,
highlightSearchText,
searchInColumns,
} from '../../../utils/EntityUtils';
import { getEntityColumnFQN } from '../../../utils/FeedUtils';
import { stringToHTML } from '../../../utils/StringsUtils';
import {
getAllTags,
searchTagInData,
@ -249,7 +251,7 @@ const SchemaTable = ({
<Typography.Paragraph
className="cursor-pointer"
ellipsis={{ tooltip: displayValue, rows: 3 }}>
{displayValue}
{stringToHTML(highlightSearchText(displayValue, searchText))}
</Typography.Paragraph>
);
};
@ -264,7 +266,7 @@ const SchemaTable = ({
<TableDescription
columnData={{
fqn: record.fullyQualifiedName ?? '',
field: record.description,
field: highlightSearchText(record.description, searchText),
record,
}}
entityFqn={tableFqn}
@ -374,7 +376,7 @@ const SchemaTable = ({
<Typography.Text
className="m-b-0 d-block text-grey-muted break-word"
data-testid="column-name">
{name}
{stringToHTML(highlightSearchText(name, searchText))}
</Typography.Text>
</div>
{!isEmpty(displayName) ? (
@ -382,7 +384,9 @@ const SchemaTable = ({
<Typography.Text
className="m-b-0 d-block break-word"
data-testid="column-display-name">
{getEntityName(record)}
{stringToHTML(
highlightSearchText(getEntityName(record), searchText)
)}
</Typography.Text>
) : null}

View File

@ -167,6 +167,16 @@ jest.mock('../../../constants/Table.constants', () => ({
},
}));
jest.mock('../../../utils/StringsUtils', () => ({
...jest.requireActual('../../../utils/StringsUtils'),
stringToHTML: jest.fn((text) => text),
}));
jest.mock('../../../utils/EntityUtils', () => ({
...jest.requireActual('../../../utils/EntityUtils'),
highlightSearchText: jest.fn((text) => text),
}));
describe('Test EntityTable Component', () => {
it('Initially, Table should load', async () => {
render(<EntityTableV1 {...mockEntityTableProp} />, {

View File

@ -23,12 +23,16 @@ import {
getEntityLinkFromType,
getEntityOverview,
highlightEntityNameAndDescription,
highlightSearchText,
} from './EntityUtils';
import {
entityWithoutNameAndDescHighlight,
highlightedEntityDescription,
highlightedEntityDisplayName,
mockHighlightedResult,
mockHighlights,
mockSearchText,
mockText,
} from './mocks/EntityUtils.mock';
jest.mock('../constants/constants', () => ({
@ -199,4 +203,56 @@ describe('EntityUtils unit tests', () => {
expect(sorter(item2, item1)).toBe(1);
});
});
describe('highlightSearchText method', () => {
it('should return the text with highlighted search text', () => {
const result = highlightSearchText(mockText, mockSearchText);
expect(result).toBe(mockHighlightedResult);
});
it('should return the original text if searchText is not found', () => {
const result = highlightSearchText(mockText, 'nonexistent');
expect(result).toBe(mockText);
});
it('should return an empty string if no text is provided', () => {
const result = highlightSearchText('', 'test');
expect(result).toBe('');
});
it('should return an empty string if no searchText is provided', () => {
const result = highlightSearchText(mockText, '');
expect(result).toBe(mockText);
});
it('should return empty string if both text and searchText are missing', () => {
const result = highlightSearchText('', '');
expect(result).toBe('');
});
const falsyTestCases = [
{ text: null, searchText: 'test', expected: '' },
{ text: 'mockText', searchText: null, expected: 'mockText' },
{ text: null, searchText: null, expected: '' },
{ text: 0 as any, searchText: '', expected: 0 },
{ text: false as any, searchText: '', expected: false },
];
it.each(falsyTestCases)(
'should return expected when text or searchText is null or falsy',
({ text, searchText, expected }) => {
const result = highlightSearchText(
text ?? undefined,
searchText ?? undefined
);
expect(result).toBe(expected);
}
);
});
});

View File

@ -2393,3 +2393,16 @@ export const getColumnSorter = <T, K extends keyof T>(field: K) => {
return 0;
};
};
export const highlightSearchText = (
text?: string,
searchText?: string
): string => {
if (!searchText || !text) {
return text ?? '';
}
const regex = new RegExp(`(${searchText})`, 'gi');
return text.replace(regex, `<mark class="text-highlighter">$1</mark>`);
};

View File

@ -136,3 +136,11 @@ export const mockHighlights = {
export const highlightedEntityDescription = `This dimension table contains the billing and shipping <span class="text-highlighter">addresses</span> of customers.`;
export const highlightedEntityDisplayName = `dim_<span class="text-highlighter">address</span>`;
export const mockText =
'This is a test description to verify highlightText method.';
export const mockSearchText = 'test';
export const mockHighlightedResult =
'This is a <mark class="text-highlighter">test</mark> description to verify highlightText method.';