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 table page breaking due to highlighter text #19146

Merged
merged 1 commit into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
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);
}
);
});
});
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.';
Loading