mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-27 01:45:32 +00:00
fix table page breaking due to highligther text (#19146)
(cherry picked from commit 7d962d91eb9e1ad48a1be89e4b9b54e33011ee24)
This commit is contained in:
parent
1f59cec66e
commit
aa5b6ca939
@ -77,16 +77,17 @@ export class TableClass extends EntityClass {
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
name: 'first_name',
|
name: 'first_name',
|
||||||
dataType: 'VARCHAR',
|
dataType: 'STRUCT',
|
||||||
dataLength: 100,
|
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.',
|
description: 'First name of the staff member.',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'last_name',
|
name: 'last_name',
|
||||||
dataType: 'VARCHAR',
|
dataType: 'ARRAY',
|
||||||
dataLength: 100,
|
dataLength: 100,
|
||||||
dataTypeDisplay: 'varchar',
|
dataTypeDisplay: 'array<struct<type:string,provider:array<int>>>',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -55,6 +55,7 @@ import {
|
|||||||
getColumnSorter,
|
getColumnSorter,
|
||||||
getEntityName,
|
getEntityName,
|
||||||
getFrequentlyJoinedColumns,
|
getFrequentlyJoinedColumns,
|
||||||
|
highlightSearchArrayElement,
|
||||||
highlightSearchText,
|
highlightSearchText,
|
||||||
searchInColumns,
|
searchInColumns,
|
||||||
} from '../../../utils/EntityUtils';
|
} from '../../../utils/EntityUtils';
|
||||||
@ -251,7 +252,7 @@ const SchemaTable = ({
|
|||||||
<Typography.Paragraph
|
<Typography.Paragraph
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
ellipsis={{ tooltip: displayValue, rows: 3 }}>
|
ellipsis={{ tooltip: displayValue, rows: 3 }}>
|
||||||
{stringToHTML(highlightSearchText(displayValue, searchText))}
|
{highlightSearchArrayElement(dataTypeDisplay, searchText)}
|
||||||
</Typography.Paragraph>
|
</Typography.Paragraph>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
import { render } from '@testing-library/react';
|
||||||
|
import React from 'react';
|
||||||
import { getEntityDetailsPath } from '../constants/constants';
|
import { getEntityDetailsPath } from '../constants/constants';
|
||||||
import { EntityTabs, EntityType } from '../enums/entity.enum';
|
import { EntityTabs, EntityType } from '../enums/entity.enum';
|
||||||
import { ExplorePageTabs } from '../enums/Explore.enum';
|
import { ExplorePageTabs } from '../enums/Explore.enum';
|
||||||
@ -23,6 +25,7 @@ import {
|
|||||||
getEntityLinkFromType,
|
getEntityLinkFromType,
|
||||||
getEntityOverview,
|
getEntityOverview,
|
||||||
highlightEntityNameAndDescription,
|
highlightEntityNameAndDescription,
|
||||||
|
highlightSearchArrayElement,
|
||||||
highlightSearchText,
|
highlightSearchText,
|
||||||
} from './EntityUtils';
|
} from './EntityUtils';
|
||||||
import {
|
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 <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);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -2551,5 +2551,32 @@ export const highlightSearchText = (
|
|||||||
|
|
||||||
const regex = new RegExp(`(${searchText})`, 'gi');
|
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
|
||||||
|
)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -143,4 +143,4 @@ export const mockText =
|
|||||||
export const mockSearchText = 'test';
|
export const mockSearchText = 'test';
|
||||||
|
|
||||||
export const mockHighlightedResult =
|
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…
x
Reference in New Issue
Block a user