From 791b7135785f914e428936f58186de4f81ddee2f Mon Sep 17 00:00:00 2001 From: Ashish Gupta Date: Thu, 19 Dec 2024 18:08:41 +0530 Subject: [PATCH] fix table page breaking due to highligther text --- .../playwright/support/entity/TableClass.ts | 9 +- .../SchemaTable/SchemaTable.component.tsx | 3 +- .../ui/src/utils/EntityUtils.test.tsx | 100 ++++++++++++++++++ .../resources/ui/src/utils/EntityUtils.tsx | 29 ++++- .../ui/src/utils/mocks/EntityUtils.mock.ts | 2 +- 5 files changed, 136 insertions(+), 7 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/support/entity/TableClass.ts b/openmetadata-ui/src/main/resources/ui/playwright/support/entity/TableClass.ts index 29ac8d6490cb..bd40908dceb3 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/support/entity/TableClass.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/support/entity/TableClass.ts @@ -77,16 +77,17 @@ export class TableClass extends EntityClass { children: [ { name: 'first_name', - dataType: 'VARCHAR', + dataType: 'STRUCT', dataLength: 100, - dataTypeDisplay: 'varchar', + dataTypeDisplay: + 'struct', description: 'First name of the staff member.', }, { name: 'last_name', - dataType: 'VARCHAR', + dataType: 'ARRAY', dataLength: 100, - dataTypeDisplay: 'varchar', + dataTypeDisplay: 'array>>', }, ], }, diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.component.tsx index 1736e47f18da..c34b5f822a3b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.component.tsx @@ -55,6 +55,7 @@ import { getColumnSorter, getEntityName, getFrequentlyJoinedColumns, + highlightSearchArrayElement, highlightSearchText, searchInColumns, } from '../../../utils/EntityUtils'; @@ -251,7 +252,7 @@ const SchemaTable = ({ - {stringToHTML(highlightSearchText(displayValue, searchText))} + {highlightSearchArrayElement(dataTypeDisplay, searchText)} ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.test.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.test.tsx index 757e69de9f71..b0e77f33a64c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.test.tsx @@ -10,6 +10,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { render } from '@testing-library/react'; +import React from 'react'; import { getEntityDetailsPath } from '../constants/constants'; import { EntityTabs, EntityType } from '../enums/entity.enum'; import { ExplorePageTabs } from '../enums/Explore.enum'; @@ -23,6 +25,7 @@ import { getEntityLinkFromType, getEntityOverview, highlightEntityNameAndDescription, + highlightSearchArrayElement, highlightSearchText, } from './EntityUtils'; import { @@ -255,4 +258,101 @@ describe('EntityUtils unit tests', () => { } ); }); + + describe('highlightSearchArrayElement method', () => { + it('should highlight the searchText in the text', () => { + const result = highlightSearchArrayElement(mockText, 'highlightText'); + const { container } = render(<>{result}); // Render the result to check JSX output + + // Check if the correct part of the text is wrapped in a with the correct class + const highlighted = container.querySelector('.text-highlighter'); + + expect(highlighted).toBeInTheDocument(); + expect(highlighted?.textContent).toBe('highlightText'); + }); + + it('should highlight multiple occurrences of the searchText', () => { + const result = highlightSearchArrayElement( + 'Data testing environment, Manually test data', + 'data' + ); + const { container } = render(<>{result}); + + // Check that there are two highlighted parts (one for each 'hello') + const highlightedElements = + container.querySelectorAll('.text-highlighter'); + + expect(highlightedElements).toHaveLength(2); + expect(highlightedElements[0].textContent).toBe('Data'); + expect(highlightedElements[1].textContent).toBe('data'); + }); + + it('should not modify parts of the text that do not match searchText', () => { + const result = highlightSearchArrayElement(mockText, 'highlightText'); + const { container } = render(<>{result}); + + // Ensure the non-matching part is plain text + const nonHighlighted = container.textContent; + + expect(nonHighlighted).toContain('description'); + }); + + it('should not wrap searchText in the result if it does not appear in text', () => { + const result = highlightSearchArrayElement(mockText, 'foo'); + const { container } = render(<>{result}); + + // Ensure that no parts of the text are highlighted + const highlighted = container.querySelector('.text-highlighter'); + + expect(highlighted).toBeNull(); + }); + + it('should handle case-insensitive search', () => { + const result = highlightSearchArrayElement(mockText, 'HighlightText'); + const { container } = render(<>{result}); + + const highlighted = container.querySelector('.text-highlighter'); + + expect(highlighted).toBeInTheDocument(); + expect(highlighted?.textContent).toBe('highlightText'); + }); + + it('should return an empty string if no text is provided', () => { + const result = highlightSearchArrayElement('', 'test'); + + expect(result).toBe(''); + }); + + it('should return an empty string if no searchText is provided', () => { + const result = highlightSearchArrayElement(mockText, ''); + + expect(result).toBe(mockText); + }); + + it('should return empty string if both text and searchText are missing', () => { + const result = highlightSearchArrayElement('', ''); + + 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 = highlightSearchArrayElement( + text ?? undefined, + searchText ?? undefined + ); + + expect(result).toBe(expected); + } + ); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx index 1802654d2e4a..f8d7a36b833f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx @@ -2404,5 +2404,32 @@ export const highlightSearchText = ( const regex = new RegExp(`(${searchText})`, 'gi'); - return text.replace(regex, `$1`); + return text.replace(regex, `$1`); +}; + +/** + * It searches for a given text in a given string and returns an array that contains the string parts that have + * highlighted element if match found. + * @param text - The text to search in. + * @param searchText - The text to search for. + * @returns An Array of string or JSX.Element which contains highlighted element. + */ +export const highlightSearchArrayElement = ( + text?: string, + searchText?: string +): string | (string | JSX.Element)[] => { + if (!searchText || !text) { + return text ?? ''; + } + const stringParts = text.split(new RegExp(`(${searchText})`, 'gi')); + + return stringParts.map((part, index) => + part.toLowerCase() === (searchText ?? '').toLowerCase() ? ( + + {part} + + ) : ( + part + ) + ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/mocks/EntityUtils.mock.ts b/openmetadata-ui/src/main/resources/ui/src/utils/mocks/EntityUtils.mock.ts index 295ef7a91681..7d6a4a520e7d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/mocks/EntityUtils.mock.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/mocks/EntityUtils.mock.ts @@ -143,4 +143,4 @@ export const mockText = export const mockSearchText = 'test'; export const mockHighlightedResult = - 'This is a test description to verify highlightText method.'; + 'This is a test description to verify highlightText method.';