Skip to content

Commit

Permalink
fix table page breaking due to highligther text (#19146)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ashish8689 authored Dec 19, 2024
1 parent fb6cbce commit 7d962d9
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,17 @@ export class TableClass extends EntityClass {
children: [
{
name: 'first_name',
dataType: 'VARCHAR',
dataType: 'STRUCT',
dataLength: 100,
dataTypeDisplay: 'varchar',
dataTypeDisplay:
'struct<username:varchar(32),name:varchar(32),sex:char(1),address:varchar(128),mail:varchar(64),birthdate:varchar(16)>',
description: 'First name of the staff member.',
},
{
name: 'last_name',
dataType: 'VARCHAR',
dataType: 'ARRAY',
dataLength: 100,
dataTypeDisplay: 'varchar',
dataTypeDisplay: 'array<struct<type:string,provider:array<int>>>',
},
],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import {
getColumnSorter,
getEntityName,
getFrequentlyJoinedColumns,
highlightSearchArrayElement,
highlightSearchText,
searchInColumns,
} from '../../../utils/EntityUtils';
Expand Down Expand Up @@ -251,7 +252,7 @@ const SchemaTable = ({
<Typography.Paragraph
className="cursor-pointer"
ellipsis={{ tooltip: displayValue, rows: 3 }}>
{stringToHTML(highlightSearchText(displayValue, searchText))}
{highlightSearchArrayElement(dataTypeDisplay, searchText)}
</Typography.Paragraph>
);
};
Expand Down
100 changes: 100 additions & 0 deletions openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -23,6 +25,7 @@ import {
getEntityLinkFromType,
getEntityOverview,
highlightEntityNameAndDescription,
highlightSearchArrayElement,
highlightSearchText,
} from './EntityUtils';
import {
Expand Down Expand Up @@ -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 <span> 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);
}
);
});
});
29 changes: 28 additions & 1 deletion openmetadata-ui/src/main/resources/ui/src/utils/EntityUtils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2404,5 +2404,32 @@ export const highlightSearchText = (

const regex = new RegExp(`(${searchText})`, 'gi');

return text.replace(regex, `<mark class="text-highlighter">$1</mark>`);
return text.replace(regex, `<span class="text-highlighter">$1</span>`);
};

/**
* 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() ? (
<span className="text-highlighter" key={`${part}-${index}`}>
{part}
</span>
) : (
part
)
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,4 @@ export const mockText =
export const mockSearchText = 'test';

export const mockHighlightedResult =
'This is a <mark class="text-highlighter">test</mark> description to verify highlightText method.';
'This is a <span class="text-highlighter">test</span> description to verify highlightText method.';

0 comments on commit 7d962d9

Please sign in to comment.