feat(search): highlight search result terms
Signed-off-by: Phil Kuang <pkuang@factset.com>
This commit is contained in:
@@ -15,7 +15,10 @@
|
||||
*/
|
||||
|
||||
import { Button } from '@backstage/core-components';
|
||||
import { lightTheme } from '@backstage/theme';
|
||||
import { Grid } from '@material-ui/core';
|
||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
||||
import { ThemeProvider } from '@material-ui/core/styles';
|
||||
import FindInPageIcon from '@material-ui/icons/FindInPage';
|
||||
import GroupIcon from '@material-ui/icons/Group';
|
||||
import React from 'react';
|
||||
@@ -77,3 +80,47 @@ export const WithSecondaryAction = () => {
|
||||
/>
|
||||
);
|
||||
};
|
||||
export const WithHighlightedResults = () => {
|
||||
return (
|
||||
<DefaultResultListItem
|
||||
result={mockSearchResult}
|
||||
highlight={{
|
||||
preTag: '<tag>',
|
||||
postTag: '</tag>',
|
||||
fields: { text: 'some <tag>text</tag> from the search result' },
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const WithCustomHighlightedResults = () => {
|
||||
const customTheme = {
|
||||
...lightTheme,
|
||||
overrides: {
|
||||
...lightTheme.overrides,
|
||||
BackstageHighlightedSearchResultText: {
|
||||
highlight: {
|
||||
color: 'inherit',
|
||||
backgroundColor: 'inherit',
|
||||
fontWeight: 'bold',
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={customTheme}>
|
||||
<CssBaseline>
|
||||
<DefaultResultListItem
|
||||
result={mockSearchResult}
|
||||
highlight={{
|
||||
preTag: '<tag>',
|
||||
postTag: '</tag>',
|
||||
fields: { text: 'some <tag>text</tag> from the search result' },
|
||||
}}
|
||||
/>
|
||||
</CssBaseline>
|
||||
</ThemeProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -20,11 +20,6 @@ import { renderInTestApp } from '@backstage/test-utils';
|
||||
import FindInPageIcon from '@material-ui/icons/FindInPage';
|
||||
import { DefaultResultListItem } from './DefaultResultListItem';
|
||||
|
||||
// Using canvas to render text..
|
||||
jest.mock('react-text-truncate', () => {
|
||||
return ({ text }: { text: string }) => <span>{text}</span>;
|
||||
});
|
||||
|
||||
describe('DefaultResultListItem', () => {
|
||||
const result = {
|
||||
title: 'title',
|
||||
|
||||
@@ -15,7 +15,11 @@
|
||||
*/
|
||||
|
||||
import React, { ReactNode } from 'react';
|
||||
import { SearchDocument } from '@backstage/plugin-search-common';
|
||||
import {
|
||||
ResultHighlight,
|
||||
SearchDocument,
|
||||
} from '@backstage/plugin-search-common';
|
||||
import { HighlightedSearchResultText } from '@backstage/plugin-search-react';
|
||||
import {
|
||||
ListItem,
|
||||
ListItemIcon,
|
||||
@@ -24,17 +28,18 @@ import {
|
||||
Divider,
|
||||
} from '@material-ui/core';
|
||||
import { Link } from '@backstage/core-components';
|
||||
import TextTruncate from 'react-text-truncate';
|
||||
|
||||
type Props = {
|
||||
icon?: ReactNode;
|
||||
secondaryAction?: ReactNode;
|
||||
result: SearchDocument;
|
||||
highlight?: ResultHighlight;
|
||||
lineClamp?: number;
|
||||
};
|
||||
|
||||
export const DefaultResultListItem = ({
|
||||
result,
|
||||
highlight,
|
||||
icon,
|
||||
secondaryAction,
|
||||
lineClamp = 5,
|
||||
@@ -45,14 +50,36 @@ export const DefaultResultListItem = ({
|
||||
{icon && <ListItemIcon>{icon}</ListItemIcon>}
|
||||
<ListItemText
|
||||
primaryTypographyProps={{ variant: 'h6' }}
|
||||
primary={result.title}
|
||||
primary={
|
||||
highlight?.fields.title ? (
|
||||
<HighlightedSearchResultText
|
||||
text={highlight.fields.title}
|
||||
preTag={highlight.preTag}
|
||||
postTag={highlight.postTag}
|
||||
/>
|
||||
) : (
|
||||
result.title
|
||||
)
|
||||
}
|
||||
secondary={
|
||||
<TextTruncate
|
||||
line={lineClamp}
|
||||
truncateText="…"
|
||||
text={result.text}
|
||||
element="span"
|
||||
/>
|
||||
<span
|
||||
style={{
|
||||
display: '-webkit-box',
|
||||
WebkitBoxOrient: 'vertical',
|
||||
WebkitLineClamp: lineClamp,
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{highlight?.fields.text ? (
|
||||
<HighlightedSearchResultText
|
||||
text={highlight.fields.text}
|
||||
preTag={highlight.preTag}
|
||||
postTag={highlight.postTag}
|
||||
/>
|
||||
) : (
|
||||
result.text
|
||||
)}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
{secondaryAction && <Box alignItems="flex-end">{secondaryAction}</Box>}
|
||||
|
||||
@@ -137,7 +137,7 @@ export const Modal = ({ toggleModal }: SearchModalProps) => {
|
||||
<SearchResult>
|
||||
{({ results }) => (
|
||||
<List>
|
||||
{results.map(({ document }) => (
|
||||
{results.map(({ document, highlight }) => (
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
@@ -148,6 +148,7 @@ export const Modal = ({ toggleModal }: SearchModalProps) => {
|
||||
<DefaultResultListItem
|
||||
key={document.location}
|
||||
result={document}
|
||||
highlight={highlight}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user