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:
Anders Näsman
2022-06-03 09:41:14 +02:00
committed by Eric Peterson
parent b3389f1eee
commit bc6ce63e7b
18 changed files with 131 additions and 267 deletions
+25 -3
View File
@@ -12,6 +12,9 @@ import { JsonObject } from '@backstage/types';
import { PropsWithChildren } from 'react';
import { default as React_2 } from 'react';
import { ReactElement } from 'react';
import { ReactNode } from 'react';
import { ResultHighlight } from '@backstage/plugin-search-common';
import { SearchDocument } from '@backstage/plugin-search-common';
import { SearchQuery } from '@backstage/plugin-search-common';
import { SearchResult as SearchResult_2 } from '@backstage/plugin-search-common';
import { SearchResultSet } from '@backstage/plugin-search-common';
@@ -24,6 +27,20 @@ export const AutocompleteFilter: (
// @public (undocumented)
export const CheckboxFilter: (props: SearchFilterComponentProps) => JSX.Element;
// @public
export const DefaultResultListItem: (
props: DefaultResultListItemProps,
) => JSX.Element;
// @public
export type DefaultResultListItemProps = {
icon?: ReactNode;
secondaryAction?: ReactNode;
result: SearchDocument;
highlight?: ResultHighlight;
lineClamp?: number;
};
// @public (undocumented)
export const HighlightedSearchResultText: ({
text,
@@ -151,13 +168,18 @@ export type SearchFilterWrapperProps = SearchFilterComponentProps & {
debug?: boolean;
};
// @public (undocumented)
export const SearchResult: ({ children }: SearchResultProps) => JSX.Element;
// @public
export const SearchResult: (props: SearchResultProps) => JSX.Element;
// @public
export const SearchResultComponent: ({
children,
}: SearchResultProps) => JSX.Element;
// @public (undocumented)
export const SearchResultPager: () => JSX.Element;
// @public (undocumented)
// @public
export type SearchResultProps = {
children: (results: { results: SearchResult_2[] }) => JSX.Element;
};
+1 -1
View File
@@ -31,11 +31,11 @@
"start": "backstage-cli package start"
},
"dependencies": {
"@backstage/plugin-search": "0.8.2-next.1",
"@backstage/plugin-search-common": "^0.3.5-next.0",
"@backstage/core-components": "^0.9.5-next.1",
"@backstage/core-plugin-api": "^1.0.3-next.0",
"@backstage/version-bridge": "^1.0.1",
"@backstage/theme": "^0.2.15",
"@backstage/types": "^1.0.0",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
@@ -1,5 +1,5 @@
/*
* Copyright 2021 The Backstage Authors
* 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2021 The Backstage Authors
* 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2021 The Backstage Authors
* 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.
@@ -15,11 +15,12 @@
*/
import React, { ReactNode } from 'react';
import { AnalyticsContext } from '@backstage/core-plugin-api';
import {
ResultHighlight,
SearchDocument,
} from '@backstage/plugin-search-common';
import { HighlightedSearchResultText } from '@backstage/plugin-search-react';
import { HighlightedSearchResultText } from '../HighlightedSearchResultText';
import {
ListItem,
ListItemIcon,
@@ -29,7 +30,12 @@ import {
} from '@material-ui/core';
import { Link } from '@backstage/core-components';
type Props = {
/**
* Props for {@link DefaultResultListItem}
*
* @public
*/
export type DefaultResultListItemProps = {
icon?: ReactNode;
secondaryAction?: ReactNode;
result: SearchDocument;
@@ -37,13 +43,18 @@ type Props = {
lineClamp?: number;
};
export const DefaultResultListItem = ({
/**
* A default result list item.
*
* @public
*/
export const DefaultResultListItemComponent = ({
result,
highlight,
icon,
secondaryAction,
lineClamp = 5,
}: Props) => {
}: DefaultResultListItemProps) => {
return (
<Link to={result.location}>
<ListItem alignItems="center">
@@ -88,3 +99,25 @@ export const DefaultResultListItem = ({
</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 };
@@ -1,5 +1,5 @@
/*
* Copyright 2021 The Backstage Authors
* 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.
@@ -15,3 +15,4 @@
*/
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';
+8 -23
View File
@@ -6,33 +6,22 @@
/// <reference types="react" />
import { BackstagePlugin } from '@backstage/core-plugin-api';
import { DefaultResultListItemProps } from '@backstage/plugin-search-react/src/components/DefaultResultListItem/DefaultResultListItem';
import { IconComponent } from '@backstage/core-plugin-api';
import { InputBaseProps } from '@material-ui/core';
import { ReactElement } from 'react';
import { ReactNode } from 'react';
import { ResultHighlight } from '@backstage/plugin-search-common';
import { RouteRef } from '@backstage/core-plugin-api';
import { SearchAutocompleteFilterProps as SearchAutocompleteFilterProps_2 } from '@backstage/plugin-search-react';
import { SearchDocument } from '@backstage/plugin-search-common';
import { SearchFilterComponentProps as SearchFilterComponentProps_2 } from '@backstage/plugin-search-react';
import { SearchResult as SearchResult_2 } from '@backstage/plugin-search-common';
import { SearchResultProps } from '@backstage/plugin-search-react';
// Warning: (ae-missing-release-tag) "DefaultResultListItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const DefaultResultListItem: ({
result,
highlight,
icon,
secondaryAction,
lineClamp,
}: {
icon?: ReactNode;
secondaryAction?: ReactNode;
result: SearchDocument;
highlight?: ResultHighlight | undefined;
lineClamp?: number | undefined;
}) => JSX.Element;
// @public @deprecated (undocumented)
export const DefaultResultListItem: (
props: DefaultResultListItemProps,
) => JSX.Element;
// Warning: (ae-forgotten-export) The symbol "FiltersProps" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "Filters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -214,12 +203,8 @@ export { searchPlugin };
// Warning: (ae-missing-release-tag) "SearchResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const SearchResult: ({
children,
}: {
children: (results: { results: SearchResult_2[] }) => JSX.Element;
}) => JSX.Element;
// @public @deprecated (undocumented)
export const SearchResult: (props: SearchResultProps) => JSX.Element;
// Warning: (ae-missing-release-tag) "SearchResultPager" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -27,16 +27,16 @@ import {
import { makeStyles } from '@material-ui/core/styles';
import React, { ComponentType } from 'react';
import { rootRouteRef } from '../../plugin';
import { DefaultResultListItem } from '../DefaultResultListItem';
import { SearchBar } from '../SearchBar';
import {
DefaultResultListItem,
searchApiRef,
MockSearchApi,
SearchContextProvider,
SearchResult,
} from '@backstage/plugin-search-react';
import { TestApiProvider } from '@backstage/test-utils';
import { SearchModal } from './SearchModal';
import { SearchResult } from '../SearchResult';
import { SearchResultPager } from '../SearchResultPager';
import { SearchType } from '../SearchType';
import { useSearchModal } from './useSearchModal';
@@ -29,10 +29,10 @@ import {
import LaunchIcon from '@material-ui/icons/Launch';
import { makeStyles } from '@material-ui/core/styles';
import { SearchBar } from '../SearchBar';
import { DefaultResultListItem } from '../DefaultResultListItem';
import { SearchResult } from '../SearchResult';
import {
DefaultResultListItem,
SearchContextProvider,
SearchResult,
useSearch,
} from '@backstage/plugin-search-react';
import { SearchResultPager } from '../SearchResultPager';
@@ -1,125 +0,0 @@
/*
* Copyright 2021 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 { renderInTestApp } from '@backstage/test-utils';
import { waitFor } from '@testing-library/react';
import React from 'react';
import { useSearch } from '@backstage/plugin-search-react';
import { SearchResult } from './SearchResult';
jest.mock('@backstage/plugin-search-react', () => ({
...jest.requireActual('@backstage/plugin-search-react'),
useSearch: jest.fn().mockReturnValue({
result: {},
}),
}));
describe('SearchResult', () => {
it('Progress rendered on Loading state', async () => {
(useSearch as jest.Mock).mockReturnValueOnce({
result: { loading: true },
});
const { getByRole } = await renderInTestApp(
<SearchResult>{() => <></>}</SearchResult>,
);
await waitFor(() => {
expect(getByRole('progressbar')).toBeInTheDocument();
});
});
it('Alert rendered on Error state', async () => {
const error = new Error('some error');
(useSearch as jest.Mock).mockReturnValueOnce({
result: { loading: false, error },
});
const { getByRole } = await renderInTestApp(
<SearchResult>{() => <></>}</SearchResult>,
);
await waitFor(() => {
expect(getByRole('alert')).toHaveTextContent(
new RegExp(`Error encountered while fetching search results.*${error}`),
);
});
});
it('On no result value state', async () => {
(useSearch as jest.Mock).mockReturnValueOnce({
result: { loading: false, error: '', value: undefined },
});
const { getByRole } = await renderInTestApp(
<SearchResult>{() => <></>}</SearchResult>,
);
await waitFor(() => {
expect(
getByRole('heading', { name: 'Sorry, no results were found' }),
).toBeInTheDocument();
});
});
it('On empty result value state', async () => {
(useSearch as jest.Mock).mockReturnValueOnce({
result: { loading: false, error: '', value: { results: [] } },
});
const { getByRole } = await renderInTestApp(
<SearchResult>{() => <></>}</SearchResult>,
);
await waitFor(() => {
expect(
getByRole('heading', { name: 'Sorry, no results were found' }),
).toBeInTheDocument();
});
});
it('Calls children with results set to result.value', async () => {
(useSearch as jest.Mock).mockReturnValueOnce({
result: {
loading: false,
error: '',
value: {
totalCount: 1,
results: [
{
type: 'some-type',
document: {
title: 'some-title',
text: 'some-text',
location: 'some-location',
},
},
],
},
},
});
const { getByText } = await renderInTestApp(
<SearchResult>
{({ results }) => {
return <>Results {results.length}</>;
}}
</SearchResult>,
);
expect(getByText('Results 1')).toBeInTheDocument();
});
});
@@ -1,57 +0,0 @@
/*
* Copyright 2021 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 {
EmptyState,
Progress,
ResponseErrorPanel,
} from '@backstage/core-components';
import { SearchResult } from '@backstage/plugin-search-common';
import React from 'react';
import { useSearch } from '@backstage/plugin-search-react';
type Props = {
children: (results: { results: SearchResult[] }) => JSX.Element;
};
/**
* @deprecated Moved to `@backstage/plugin-search-react`.
*/
export const SearchResultComponent = ({ children }: Props) => {
const {
result: { loading, error, value },
} = useSearch();
if (loading) {
return <Progress />;
}
if (error) {
return (
<ResponseErrorPanel
title="Error encountered while fetching search results"
error={error}
/>
);
}
if (!value?.results.length) {
return <EmptyState missing="data" title="Sorry, no results were found" />;
}
return <>{children({ results: value.results })}</>;
};
export { SearchResultComponent as SearchResult };
@@ -1,17 +0,0 @@
/*
* Copyright 2021 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 { SearchResult } from './SearchResult';
+13 -20
View File
@@ -15,7 +15,11 @@
*/
import { SearchClient } from './apis';
import { searchApiRef } from '@backstage/plugin-search-react';
import {
searchApiRef,
SearchResult as RealSearchResult,
DefaultResultListItem as RealDefaultResultListItem,
} from '@backstage/plugin-search-react';
import {
createApiFactory,
createPlugin,
@@ -63,14 +67,10 @@ export const SearchBar = searchPlugin.provide(
}),
);
export const SearchResult = searchPlugin.provide(
createComponentExtension({
name: 'SearchResult',
component: {
lazy: () => import('./components/SearchResult').then(m => m.SearchResult),
},
}),
);
/**
* @deprecated Import from `@backstage/plugin-search-react` instead.
*/
export const SearchResult = RealSearchResult;
export const SidebarSearchModal = searchPlugin.provide(
createComponentExtension({
@@ -84,17 +84,10 @@ export const SidebarSearchModal = searchPlugin.provide(
}),
);
export const DefaultResultListItem = searchPlugin.provide(
createComponentExtension({
name: 'DefaultResultListItem',
component: {
lazy: () =>
import('./components/DefaultResultListItem').then(
m => m.DefaultResultListItem,
),
},
}),
);
/**
* @deprecated Import from `@backstage/plugin-search-react` instead.
*/
export const DefaultResultListItem = RealDefaultResultListItem;
export const HomePageSearchBar = searchPlugin.provide(
createComponentExtension({