Move DefaultResultListItem from @backstage/plugin-search to @backstage/plugin-search-react and deprecate it in @backstage/plugin-search, and properly deprecate SearchResult in @backstage/plugin-search
Signed-off-by: Anders Näsman <andersn@spotify.com>
This commit is contained in:
committed by
Eric Peterson
parent
b3389f1eee
commit
bc6ce63e7b
+126
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
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';
|
||||
import { MemoryRouter } from 'react-router';
|
||||
import { DefaultResultListItem } from './DefaultResultListItem';
|
||||
|
||||
export default {
|
||||
title: 'Plugins/Search/DefaultResultListItem',
|
||||
component: DefaultResultListItem,
|
||||
decorators: [
|
||||
(Story: () => JSX.Element) => (
|
||||
<MemoryRouter>
|
||||
<Grid container direction="row">
|
||||
<Grid item xs={12}>
|
||||
<Story />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</MemoryRouter>
|
||||
),
|
||||
],
|
||||
};
|
||||
|
||||
const mockSearchResult = {
|
||||
location: 'search/search-result',
|
||||
title: 'Search Result 1',
|
||||
text: 'some text from the search result',
|
||||
owner: 'some-example-owner',
|
||||
};
|
||||
|
||||
export const Default = () => {
|
||||
return <DefaultResultListItem result={mockSearchResult} />;
|
||||
};
|
||||
|
||||
export const WithIcon = () => {
|
||||
return (
|
||||
<DefaultResultListItem
|
||||
result={mockSearchResult}
|
||||
icon={<FindInPageIcon color="primary" />}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const WithSecondaryAction = () => {
|
||||
return (
|
||||
<DefaultResultListItem
|
||||
result={mockSearchResult}
|
||||
secondaryAction={
|
||||
<Button
|
||||
to="#"
|
||||
size="small"
|
||||
aria-label="owner"
|
||||
variant="text"
|
||||
startIcon={<GroupIcon />}
|
||||
style={{ textTransform: 'lowercase' }}
|
||||
>
|
||||
{mockSearchResult.owner}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
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>
|
||||
);
|
||||
};
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { screen } from '@testing-library/react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import FindInPageIcon from '@material-ui/icons/FindInPage';
|
||||
import { DefaultResultListItem } from './DefaultResultListItem';
|
||||
|
||||
describe('DefaultResultListItem', () => {
|
||||
const result = {
|
||||
title: 'title',
|
||||
text: 'text',
|
||||
location: '/location',
|
||||
owner: 'owner',
|
||||
};
|
||||
|
||||
it('Links to result.location', async () => {
|
||||
await renderInTestApp(<DefaultResultListItem result={result} />);
|
||||
expect(screen.getByRole('link')).toHaveAttribute('href', result.location);
|
||||
});
|
||||
|
||||
it('Includes primary/secondary text (title / text)', async () => {
|
||||
await renderInTestApp(<DefaultResultListItem result={result} />);
|
||||
expect(screen.getByRole('listitem')).toHaveTextContent(
|
||||
result.title + result.text,
|
||||
);
|
||||
});
|
||||
|
||||
it('should render icon if prop is specified', async () => {
|
||||
await renderInTestApp(
|
||||
<DefaultResultListItem
|
||||
result={result}
|
||||
icon={<FindInPageIcon aria-label="icon" />}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByLabelText('icon')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render secondary action if prop is specified', async () => {
|
||||
await renderInTestApp(
|
||||
<DefaultResultListItem result={result} secondaryAction={result.owner} />,
|
||||
);
|
||||
expect(screen.getByText('owner')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { ReactNode } from 'react';
|
||||
import { AnalyticsContext } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
ResultHighlight,
|
||||
SearchDocument,
|
||||
} from '@backstage/plugin-search-common';
|
||||
import { HighlightedSearchResultText } from '../HighlightedSearchResultText';
|
||||
import {
|
||||
ListItem,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Box,
|
||||
Divider,
|
||||
} from '@material-ui/core';
|
||||
import { Link } from '@backstage/core-components';
|
||||
|
||||
/**
|
||||
* Props for {@link DefaultResultListItem}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type DefaultResultListItemProps = {
|
||||
icon?: ReactNode;
|
||||
secondaryAction?: ReactNode;
|
||||
result: SearchDocument;
|
||||
highlight?: ResultHighlight;
|
||||
lineClamp?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* A default result list item.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const DefaultResultListItemComponent = ({
|
||||
result,
|
||||
highlight,
|
||||
icon,
|
||||
secondaryAction,
|
||||
lineClamp = 5,
|
||||
}: DefaultResultListItemProps) => {
|
||||
return (
|
||||
<Link to={result.location}>
|
||||
<ListItem alignItems="center">
|
||||
{icon && <ListItemIcon>{icon}</ListItemIcon>}
|
||||
<ListItemText
|
||||
primaryTypographyProps={{ variant: 'h6' }}
|
||||
primary={
|
||||
highlight?.fields.title ? (
|
||||
<HighlightedSearchResultText
|
||||
text={highlight.fields.title}
|
||||
preTag={highlight.preTag}
|
||||
postTag={highlight.postTag}
|
||||
/>
|
||||
) : (
|
||||
result.title
|
||||
)
|
||||
}
|
||||
secondary={
|
||||
<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>}
|
||||
</ListItem>
|
||||
<Divider />
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* A higher order function providing AnalyticsContext to the DefaultResultListItemComponent.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
const HigherOrderDefaultResultListItem = (
|
||||
props: DefaultResultListItemProps,
|
||||
) => {
|
||||
return (
|
||||
<AnalyticsContext
|
||||
attributes={{
|
||||
pluginId: 'search',
|
||||
extension: 'DefaultResultListItem',
|
||||
}}
|
||||
>
|
||||
<DefaultResultListItemComponent {...props} />
|
||||
</AnalyticsContext>
|
||||
);
|
||||
};
|
||||
|
||||
export { HigherOrderDefaultResultListItem as DefaultResultListItem };
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { DefaultResultListItem } from './DefaultResultListItem';
|
||||
export type { DefaultResultListItemProps } from './DefaultResultListItem';
|
||||
@@ -14,16 +14,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Link } from '@backstage/core-components';
|
||||
import { List, ListItem } from '@material-ui/core';
|
||||
import React, { ComponentType } from 'react';
|
||||
import { List, ListItem } from '@material-ui/core';
|
||||
import { MemoryRouter } from 'react-router';
|
||||
import { DefaultResultListItem } from '@backstage/plugin-search';
|
||||
|
||||
import { Link } from '@backstage/core-components';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
|
||||
import { searchApiRef, MockSearchApi } from '../../api';
|
||||
import { SearchContextProvider } from '../../context';
|
||||
import { DefaultResultListItem } from '../DefaultResultListItem';
|
||||
import { SearchResult } from './SearchResult';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
|
||||
const mockResults = {
|
||||
results: [
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
|
||||
import { useSearch } from '../../context';
|
||||
import { SearchResult } from './SearchResult';
|
||||
|
||||
|
||||
@@ -14,16 +14,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
EmptyState,
|
||||
Progress,
|
||||
ResponseErrorPanel,
|
||||
} from '@backstage/core-components';
|
||||
import { AnalyticsContext } from '@backstage/core-plugin-api';
|
||||
import { SearchResult } from '@backstage/plugin-search-common';
|
||||
import React from 'react';
|
||||
|
||||
import { useSearch } from '../../context';
|
||||
|
||||
/**
|
||||
* Props for {@link SearchResultComponent}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type SearchResultProps = {
|
||||
@@ -31,6 +36,8 @@ export type SearchResultProps = {
|
||||
};
|
||||
|
||||
/**
|
||||
* A component returning the search result.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const SearchResultComponent = ({ children }: SearchResultProps) => {
|
||||
@@ -57,4 +64,22 @@ export const SearchResultComponent = ({ children }: SearchResultProps) => {
|
||||
return <>{children({ results: value.results })}</>;
|
||||
};
|
||||
|
||||
export { SearchResultComponent as SearchResult };
|
||||
/**
|
||||
* A higher order function providing AnalyticsContext to the SearchResultComponent.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
const HigherOrderSearchResult = (props: SearchResultProps) => {
|
||||
return (
|
||||
<AnalyticsContext
|
||||
attributes={{
|
||||
pluginId: 'search',
|
||||
extension: 'SearchResult',
|
||||
}}
|
||||
>
|
||||
<SearchResultComponent {...props} />
|
||||
</AnalyticsContext>
|
||||
);
|
||||
};
|
||||
|
||||
export { HigherOrderSearchResult as SearchResult };
|
||||
|
||||
@@ -14,5 +14,5 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { SearchResult } from './SearchResult';
|
||||
export { SearchResult, SearchResultComponent } from './SearchResult';
|
||||
export type { SearchResultProps } from './SearchResult';
|
||||
|
||||
@@ -20,3 +20,4 @@ export * from './SearchResult';
|
||||
export * from './SearchResultPager';
|
||||
export * from './SearchBar';
|
||||
export * from './SearchTracker';
|
||||
export * from './DefaultResultListItem';
|
||||
|
||||
Reference in New Issue
Block a user