diff --git a/.changeset/search-new-fantastic-pov.md b/.changeset/search-new-fantastic-pov.md
new file mode 100644
index 0000000000..0baba045b7
--- /dev/null
+++ b/.changeset/search-new-fantastic-pov.md
@@ -0,0 +1,76 @@
+---
+'@backstage/plugin-search-backend': minor
+'@backstage/plugin-search-backend-node': minor
+---
+
+This release represents a move out of a pre-alpha phase of the Backstage Search
+plugin, into an alpha phase. With this release, you gain more control over the
+layout of your search page on the frontend, as well as the ability to extend
+search on the backend to encompass everything Backstage users may want to find.
+
+If you are updating to version `v0.4.0` of `@backstage/plugin-search` from a
+prior release, you will need to make modifications to your app backend.
+
+First, navigate to your backend package and install the two related search
+backend packages:
+
+```sh
+cd packages/backend
+yarn add @backstage/plugin-search-backend @backstage/plugin-search-backend-node
+```
+
+Wire up these new packages into your app backend by first creating a new
+`search.ts` file at `src/plugins/search.ts` with contents like the following:
+
+```typescript
+import { useHotCleanup } from '@backstage/backend-common';
+import { createRouter } from '@backstage/plugin-search-backend';
+import {
+ IndexBuilder,
+ LunrSearchEngine,
+} from '@backstage/plugin-search-backend-node';
+import { PluginEnvironment } from '../types';
+import { DefaultCatalogCollator } from '@backstage/plugin-catalog-backend';
+
+export default async function createPlugin({
+ logger,
+ discovery,
+}: PluginEnvironment) {
+ // Initialize a connection to a search engine.
+ const searchEngine = new LunrSearchEngine({ logger });
+ const indexBuilder = new IndexBuilder({ logger, searchEngine });
+
+ // Collators are responsible for gathering documents known to plugins. This
+ // particular collator gathers entities from the software catalog.
+ indexBuilder.addCollator({
+ defaultRefreshIntervalSeconds: 600,
+ collator: new DefaultCatalogCollator({ discovery }),
+ });
+
+ // The scheduler controls when documents are gathered from collators and sent
+ // to the search engine for indexing.
+ const { scheduler } = await indexBuilder.build();
+
+ // A 3 second delay gives the backend server a chance to initialize before
+ // any collators are executed, which may attempt requests against the API.
+ setTimeout(() => scheduler.start(), 3000);
+ useHotCleanup(module, () => scheduler.stop());
+
+ return await createRouter({
+ engine: indexBuilder.getSearchEngine(),
+ logger,
+ });
+}
+```
+
+Then, ensure the search plugin you configured above is initialized by modifying
+your backend's `index.ts` file in the following ways:
+
+```diff
++import search from './plugins/search';
+// ...
++const searchEnv = useHotMemoize(module, () => createEnv('search'));
+// ...
++apiRouter.use('/search', await search(searchEnv));
+// ...
+```
diff --git a/.changeset/search-whole-new-world.md b/.changeset/search-whole-new-world.md
new file mode 100644
index 0000000000..3dee24a371
--- /dev/null
+++ b/.changeset/search-whole-new-world.md
@@ -0,0 +1,118 @@
+---
+'@backstage/plugin-search': minor
+---
+
+This release represents a move out of a pre-alpha phase of the Backstage Search
+plugin, into an alpha phase. With this release, you gain more control over the
+layout of your search page on the frontend, as well as the ability to extend
+search on the backend to encompass everything Backstage users may want to find.
+
+If you are updating to this version of `@backstage/plugin-search` from a prior
+release, you will need to make the following modifications to your App:
+
+In your app package, create a new `searchPage` component at, for example,
+`packages/app/src/components/search/SearchPage.tsx` with contents like the
+following:
+
+```tsx
+import React from 'react';
+import { makeStyles, Theme, Grid, List, Paper } from '@material-ui/core';
+
+import { Content, Header, Lifecycle, Page } from '@backstage/core';
+import { CatalogResultListItem } from '@backstage/plugin-catalog';
+import {
+ SearchBar,
+ SearchFilter,
+ SearchResult,
+ DefaultResultListItem,
+} from '@backstage/plugin-search';
+
+const useStyles = makeStyles((theme: Theme) => ({
+ bar: {
+ padding: theme.spacing(1, 0),
+ },
+ filters: {
+ padding: theme.spacing(2),
+ },
+ filter: {
+ '& + &': {
+ marginTop: theme.spacing(2.5),
+ },
+ },
+}));
+
+const SearchPage = () => {
+ const classes = useStyles();
+
+ return (
+
+ } />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {({ results }) => (
+
+ {results.map(({ type, document }) => {
+ switch (type) {
+ case 'software-catalog':
+ return (
+
+ );
+ default:
+ return (
+
+ );
+ }
+ })}
+
+ )}
+
+
+
+
+
+ );
+};
+
+export const searchPage = ;
+```
+
+Then in `App.tsx`, import this new `searchPage` component, and set it as a
+child of the existing `` route so that it looks like this:
+
+```tsx
+import { searchPage } from './components/search/SearchPage';
+// ...
+}>
+ {searchPage}
+;
+```
+
+You will also need to update your backend. For details, check the changeset for
+`v0.2.0` of `@backstage/plugin-search-backend`.
diff --git a/docs/features/search/README.md b/docs/features/search/README.md
index bc0c9b3908..9f5a5d7009 100644
--- a/docs/features/search/README.md
+++ b/docs/features/search/README.md
@@ -24,22 +24,22 @@ Backstage ecosystem.
## Project roadmap
-| Version | Description |
-| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| Backstage Search v0 ✅ | Search Frontend letting you search through the entities of the software catalog. [See v0 Use Cases.](#backstage-search-v0) |
-| [Backstage Search V0.5 ✅ ][v0.5] | Foundations for the architecture. |
-| [Backstage Search v1 ⌛][v1] | Basic “out-of-the-box” in-memory indexing process of entities, and their metadata, registered to the Software Catalog. [See v1 Use Cases.](#backstage-search-v1) |
-| [Backstage Search v2 ⌛][v2] | Search Backend responsible for the indexing process of entities, and their metadata, registered to the Software Catalog. [See v2 Use Cases.](#backstage-search-v2) |
-| [Backstage Search v3 ⌛][v3] | Standardized Search API lets you index other plugins data to the search engine of choice. [See v3 Use Cases.](#backstage-search-v3) |
+| Version | Description |
+| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Backstage Search Pre-Alpha ✅ | Search Frontend letting you search through the entities of the software catalog. [See Pre-Alpha Use Cases.](#backstage-search-pre-alpha) |
+| Backstage Search Alpha ✅ | Basic “out-of-the-box” in-memory indexing process of entities, and their metadata, registered to the Software Catalog. [See Alpha Use Cases](#backstage-search-alpha). |
+| [Backstage Search Beta ⌛][beta] | At least one production-ready search engine that supports the same use-cases as in the alpha. [See Beta Use Cases](#backstage-search-beta). |
+| [Backstage Search GA ⌛][ga] | A stable Search API for plugin developers to add search to their plugins, and app integrators to expose that to their users. [See GA Use Cases](#backstage-search-ga). |
-[v0.5]: https://github.com/backstage/backstage/milestone/25
-[v1]: https://github.com/backstage/backstage/milestone/26
-[v2]: https://github.com/backstage/backstage/milestone/27
-[v3]: https://github.com/backstage/backstage/milestone/28
+[beta]: https://github.com/backstage/backstage/milestone/27
+[ga]: https://github.com/backstage/backstage/milestone/28
## Use Cases
-#### Backstage Search V.0
+#### Backstage Search Pre-Alpha
+
+The pre-alpha is intended to solve for the following user stories, but will get
+there by means of a front-end only, non-extensible MVP.
- As a software engineer I should be able to navigate to a search page and
search for entities registered in the Software Catalog.
@@ -52,28 +52,48 @@ Backstage ecosystem.
- As a software engineer I should be able to hide the filters if I don’t need to
use them.
-#### Backstage Search V.1
+#### Backstage Search Alpha
-- As a software engineer I should be able to get a match of a search on all
- entity metadata (e.g. owner, name, description, kind).
-- As an integrator I should not have to plug in any search engine, instead I can
- use the out of the box in-memory indexing process to index entities and their
- metadata registered in the Software Catalog.
+We will consider Backstage Search to be in alpha when the above use-cases are
+met, but built on top of a flexible, extensible platform.
-#### Backstage Search V.2
+- As an integrator, I should be able to provide all of the pre-alpha experiences
+ to my users if I choose, but also be able to customize the experience using a
+ composable set of components.
+- As a plugin developer, I should have a standard way to expose my plugin's data
+ to Backstage Search.
+- As an integrator, I should still be able to expose everything in the Software
+ Catalog in search, but it should be possible to customize what is searchable.
+- As an integrator, although I should be able to customize all of the above, it
+ should be possible to have the pre-alpha user experiences covered without
+ having to set up and configure a search engine.
-- As an integrator I should be able to spin up an instance of ElasticSearch.
-- As an integrator I should be able to define a ElasticSearch cluster in my
- app_config.yaml where my data gets indexed to.
+#### Backstage Search Beta
-more to come...
+We will consider Backstage Search to be in a beta phase when the above use-cases
+are met, and can be deployed using a production-ready search engine.
-#### Backstage Search V.3
+- As an integrator, I should be able to power my Backstage Search experience
+ (including querying and indexing) using a production-ready search engine like
+ ElasticSearch.
+- As an integrator, I should be able to configure the connection to my search
+ engine in `app_config.yaml`.
+- As an integrator, I should be able to tune the queries sent to my chosen
+ search engine according to my organization's needs, but a sensible default
+ query should be in place so that I am not required to do so.
-- As a contributor I should be able to integrate plugin data to the indexing
- process of Backstage Search by using the standardized API.
-- As a software engineer I should be able to search for all content (for
- example, entities, metadata, documentation) in Backstage search.
+#### Backstage Search GA
+
+We will consider Backstage Search to be generally available (GA) when the above
+use-cases are met, and an ecosystem of search-enabled plugins are available and
+stable.
+
+- As a plugin developer, there should be at least one example of a Backstage
+ plugin that integrates with search that I can use as inspiration for my own
+ plugin's search capabilities (for example, the TechDocs plugin).
+- As an app integrator, there should be plenty of examples and documentation on
+ how to customize and extend search in my Backstage instance to meet my
+ organization's needs.
more to come...
@@ -84,12 +104,19 @@ the search engines are used.
| Search Engine | Support Status |
| ------------- | -------------- |
-| Basic (lunr) | Not yet ❌ |
+| Basic (lunr) | ✅ |
| ElasticSearch | Not yet ❌ |
[Reach out to us](#feedback) if you want to chat about support for more search
engines.
+## Plugins Integrated with Search
+
+| Plugin | Support Status |
+| -------- | -------------- |
+| Catalog | ✅ |
+| TechDocs | Not yet ❌ |
+
## Tech Stack
| Stack | Location |
diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx
index 5874465ef6..b14f9a3163 100644
--- a/packages/app/src/App.tsx
+++ b/packages/app/src/App.tsx
@@ -43,7 +43,7 @@ import { GraphiQLPage } from '@backstage/plugin-graphiql';
import { LighthousePage } from '@backstage/plugin-lighthouse';
import { NewRelicPage } from '@backstage/plugin-newrelic';
import { ScaffolderPage, scaffolderPlugin } from '@backstage/plugin-scaffolder';
-import { SearchPage, SearchPageNext } from '@backstage/plugin-search';
+import { SearchPage } from '@backstage/plugin-search';
import { TechRadarPage } from '@backstage/plugin-tech-radar';
import { TechdocsPage } from '@backstage/plugin-techdocs';
import { UserSettingsPage } from '@backstage/plugin-user-settings';
@@ -119,8 +119,7 @@ const routes = (
} />
} />
} />
- } />
- }>
+ }>
{searchPage}
} />
diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/app/src/components/search/SearchPage.tsx
index 221d4a44c4..7b94e876e6 100644
--- a/packages/app/src/components/search/SearchPage.tsx
+++ b/packages/app/src/components/search/SearchPage.tsx
@@ -20,9 +20,9 @@ import { makeStyles, Theme, Grid, List, Paper } from '@material-ui/core';
import { Content, Header, Lifecycle, Page } from '@backstage/core';
import { CatalogResultListItem } from '@backstage/plugin-catalog';
import {
- SearchBarNext as SearchBar,
- SearchFilterNext as SearchFilter,
- SearchResultNext as SearchResult,
+ SearchBar,
+ SearchFilter,
+ SearchResult,
DefaultResultListItem,
} from '@backstage/plugin-search';
diff --git a/packages/backend/src/plugins/search.ts b/packages/backend/src/plugins/search.ts
index 6688b9c158..e587cdb606 100644
--- a/packages/backend/src/plugins/search.ts
+++ b/packages/backend/src/plugins/search.ts
@@ -26,17 +26,24 @@ export default async function createPlugin({
logger,
discovery,
}: PluginEnvironment) {
+ // Initialize a connection to a search engine.
const searchEngine = new LunrSearchEngine({ logger });
const indexBuilder = new IndexBuilder({ logger, searchEngine });
+ // Collators are responsible for gathering documents known to plugins. This
+ // particular collator gathers entities from the software catalog.
indexBuilder.addCollator({
defaultRefreshIntervalSeconds: 600,
collator: new DefaultCatalogCollator({ discovery }),
});
+ // The scheduler controls when documents are gathered from collators and sent
+ // to the search engine for indexing.
const { scheduler } = await indexBuilder.build();
- scheduler.start();
+ // A 3 second delay gives the backend server a chance to initialize before
+ // any collators are executed, which may attempt requests against the API.
+ setTimeout(() => scheduler.start(), 3000);
useHotCleanup(module, () => scheduler.stop());
return await createRouter({
diff --git a/plugins/search-backend-node/README.md b/plugins/search-backend-node/README.md
index 126e8b9988..264780fe93 100644
--- a/plugins/search-backend-node/README.md
+++ b/plugins/search-backend-node/README.md
@@ -1,17 +1,19 @@
# search-backend-node
This plugin is part of a suite of plugins that comprise the Backstage search
-platform, which is still very much under development. This plugin specifically
-is responsible for:
+platform. This particular plugin is responsible for all aspects of the search
+indexing process, including:
-- Allowing other backend plugins to register the fact that they have documents
- that they'd like to be indexed by a search engine (known as `collators`)
-- Allowing other backend plugins to register the fact that they have metadata
- that they'd like to augment existing documents in the search index with
- (known as `decorators`)
+- Providing connections to search engines where actual document indices live
+ and queries can be made.
+- Defining a mechanism for plugins to expose documents that they'd like to be
+ indexed (called `collators`).
+- Defining a mechanism for plugins to add extra metadata to documents that the
+ source plugin may not be aware of (known as `decorators`).
- A scheduler that, at configurable intervals, compiles documents to be indexed
- and passes them to a search engine for indexing
-- Types for all of the above
+ and passes them to a search engine for indexing.
+- A builder class to wire up all of the above.
+- Naturally, types for all of the above.
Documentation on how to develop and improve the search platform is currently
centralized in the `search` plugin README.md.
diff --git a/plugins/search-backend-node/src/Scheduler.test.ts b/plugins/search-backend-node/src/Scheduler.test.ts
index 43fdb7ea92..53a7e418e3 100644
--- a/plugins/search-backend-node/src/Scheduler.test.ts
+++ b/plugins/search-backend-node/src/Scheduler.test.ts
@@ -74,4 +74,21 @@ describe('Scheduler', () => {
expect(mockTask2).toHaveBeenCalled();
});
});
+
+ describe('start', () => {
+ it('should execute tasks on start', () => {
+ const mockTask1 = jest.fn();
+ const mockTask2 = jest.fn();
+
+ // Add tasks and interval to schedule
+ testScheduler.addToSchedule(mockTask1, 2);
+ testScheduler.addToSchedule(mockTask2, 2);
+
+ // Starts scheduling process
+ testScheduler.start();
+
+ expect(mockTask1).toHaveBeenCalled();
+ expect(mockTask2).toHaveBeenCalled();
+ });
+ });
});
diff --git a/plugins/search-backend-node/src/Scheduler.ts b/plugins/search-backend-node/src/Scheduler.ts
index e54a837cee..a5c5712999 100644
--- a/plugins/search-backend-node/src/Scheduler.ts
+++ b/plugins/search-backend-node/src/Scheduler.ts
@@ -54,6 +54,8 @@ export class Scheduler {
start() {
this.logger.info('Starting all scheduled search tasks.');
this.schedule.forEach(({ task, interval }) => {
+ // Fire the task immediately, then schedule it.
+ task();
this.intervalTimeouts.push(
setInterval(() => {
task();
diff --git a/plugins/search-backend/README.md b/plugins/search-backend/README.md
index 6c5e8c1bae..b89c6acf68 100644
--- a/plugins/search-backend/README.md
+++ b/plugins/search-backend/README.md
@@ -1,8 +1,8 @@
# search-backend
This plugin is part of a suite of plugins that comprise the Backstage search
-platform, which is still very much under development. This plugin specifically
-is responsible for exposing a JSON API for querying a search engine
+platform. This particular plugin responsible for exposing a JSON API for
+querying a search engine.
Documentation on how to develop and improve the search platform is currently
centralized in the `search` plugin README.md.
diff --git a/plugins/search/README.md b/plugins/search/README.md
index 04abee06ee..f4a32c9597 100644
--- a/plugins/search/README.md
+++ b/plugins/search/README.md
@@ -1,23 +1,26 @@
# Backstage Search
-**This plugin is still under development.**
+A flexible, extensible search across your whole Backstage ecosystem.
-You can follow the progress and contribute at the Backstage [Search Project Board](https://github.com/backstage/backstage/projects/6) or reach out to us in the [`#search` Discord channel](https://discord.com/channels/687207715902193673/770283289327566848).
+Development is ongoing. You can follow the progress and contribute at the Backstage [Search Project Board](https://github.com/backstage/backstage/projects/6) or reach out to us in the [`#search` Discord channel](https://discord.com/channels/687207715902193673/770283289327566848).
## Getting started
-Run `yarn start` in the root directory, and then navigate to [/search](http://localhost:3000/search) to check out the plugin.
+Run `yarn dev` in the root directory, and then navigate to [/search](http://localhost:3000/search) to check out the plugin.
-### Working on the search platform
+### Areas of Responsibility
-The above search experience is 100% client-side and only looks at the Software Catalog. We are actively developing [a more complete search platform](https://backstage.io/docs/features/search/search-overview), which will replace the current experience when ready.
+This search plugin is primarily responsible for the following:
-In order to work on this new search platform, you will need to be aware of the following:
+- Providing a `` routable extension.
+- Exposing various search-related components (like ``,
+ ``, etc), which can be composed by a Backstage App or by
+ other Backstage Plugins to power search experiences of all kinds.
+- Exposing a ``, which manages search state and API
+ communication with the Backstage backend.
-- **In-development app search route**: The new search platform will move the primary search page to the App-level, out of the search plugin. For now, to ensure the old experience is still easy to test, you can work on the new platform at `/search-next` instead of `/search`.
-- **App SearchPage Component**: You'll find a new search page in this plugin `SearchPageNext`. When sufficiently stable, we'll replace `SearchPage` with `SearchPageNext`
-- **Backend**: Don't forget, a lot of functionality will be made available in backend plugins:
- - `@backstage/plugin-search-backend-node`, which is responsible for the search index management
- - `@backstage/plugin-search-backend`, which is responsible for query processing
+Don't forget, a lot of functionality is available in backend plugins:
-As you work, be sure not to break the existing, frontend-only search page.
+- `@backstage/plugin-search-backend-node`, which is responsible for the search
+ index management
+- `@backstage/plugin-search-backend`, which is responsible for query processing
diff --git a/plugins/search/src/apis.test.ts b/plugins/search/src/apis.test.ts
index 32ec0f0d0e..64a5207d6d 100644
--- a/plugins/search/src/apis.test.ts
+++ b/plugins/search/src/apis.test.ts
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-import { CatalogApi } from '@backstage/plugin-catalog-react';
-
import { SearchClient } from './apis';
describe('apis', () => {
@@ -29,7 +27,6 @@ describe('apis', () => {
const baseUrl = 'https://base-url.com/';
const getBaseUrl = jest.fn().mockResolvedValue(baseUrl);
const client = new SearchClient({
- catalogApi: {} as CatalogApi,
discoveryApi: { getBaseUrl },
});
@@ -42,7 +39,7 @@ describe('apis', () => {
});
it('Fetch is called with expected URL (including stringified Q params)', async () => {
- await client._alphaPerformSearch(query);
+ await client.query(query);
expect(getBaseUrl).toHaveBeenLastCalledWith('search/query');
expect(fetch).toHaveBeenLastCalledWith(`${baseUrl}?term=&pageCursor=`);
});
@@ -50,6 +47,6 @@ describe('apis', () => {
it('Resolves JSON from fetch response', async () => {
const result = { loading: false, error: '', value: {} };
json.mockReturnValueOnce(result);
- expect(await client._alphaPerformSearch(query)).toStrictEqual(result);
+ expect(await client.query(query)).toStrictEqual(result);
});
});
diff --git a/plugins/search/src/apis.ts b/plugins/search/src/apis.ts
index b15be349cf..5e039cbb84 100644
--- a/plugins/search/src/apis.ts
+++ b/plugins/search/src/apis.ts
@@ -15,9 +15,6 @@
*/
import { createApiRef, DiscoveryApi } from '@backstage/core';
-import { Entity, ENTITY_DEFAULT_NAMESPACE } from '@backstage/catalog-model';
-
-import { CatalogApi } from '@backstage/plugin-catalog-react';
import { SearchQuery, SearchResultSet } from '@backstage/search-common';
import qs from 'qs';
@@ -26,55 +23,18 @@ export const searchApiRef = createApiRef({
description: 'Used to make requests against the search API',
});
-export type Result = {
- name: string;
- description: string | undefined;
- owner: string | undefined;
- kind: string;
- lifecycle: string | undefined;
- url: string;
-};
-
-export type SearchResults = Array;
-
export interface SearchApi {
- getSearchResult(): Promise;
- _alphaPerformSearch(query: SearchQuery): Promise;
+ query(query: SearchQuery): Promise;
}
export class SearchClient implements SearchApi {
- private readonly catalogApi: CatalogApi;
private readonly discoveryApi: DiscoveryApi;
- constructor(options: { catalogApi: CatalogApi; discoveryApi: DiscoveryApi }) {
- this.catalogApi = options.catalogApi;
+ constructor(options: { discoveryApi: DiscoveryApi }) {
this.discoveryApi = options.discoveryApi;
}
- private async entities() {
- const entities = await this.catalogApi.getEntities();
- return entities.items.map((entity: Entity) => ({
- name: entity.metadata.name,
- description: entity.metadata.description,
- owner:
- typeof entity.spec?.owner === 'string' ? entity.spec?.owner : undefined,
- kind: entity.kind,
- lifecycle:
- typeof entity.spec?.lifecycle === 'string'
- ? entity.spec?.lifecycle
- : undefined,
- url: `/catalog/${
- entity.metadata.namespace?.toLowerCase() || ENTITY_DEFAULT_NAMESPACE
- }/${entity.kind.toLowerCase()}/${entity.metadata.name}`,
- }));
- }
-
- getSearchResult(): Promise {
- return this.entities();
- }
-
- // TODO: Productionalize as we implement search milestones.
- async _alphaPerformSearch(query: SearchQuery): Promise {
+ async query(query: SearchQuery): Promise {
const queryString = qs.stringify(query);
const url = `${await this.discoveryApi.getBaseUrl(
'search/query',
diff --git a/plugins/search/src/components/LegacySearchPage/Filters/Filters.tsx b/plugins/search/src/components/LegacySearchPage/Filters/Filters.tsx
new file mode 100644
index 0000000000..888a583547
--- /dev/null
+++ b/plugins/search/src/components/LegacySearchPage/Filters/Filters.tsx
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * 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 {
+ makeStyles,
+ Typography,
+ Divider,
+ Card,
+ CardHeader,
+ Button,
+ CardContent,
+ Select,
+ Checkbox,
+ List,
+ ListItem,
+ ListItemText,
+ MenuItem,
+} from '@material-ui/core';
+
+const useStyles = makeStyles(theme => ({
+ filters: {
+ background: 'transparent',
+ boxShadow: '0px 0px 0px 0px',
+ },
+ checkbox: {
+ padding: theme.spacing(0, 1, 0, 1),
+ },
+ dropdown: {
+ width: '100%',
+ },
+}));
+
+export type FiltersState = {
+ selected: string;
+ checked: Array;
+};
+
+export type FilterOptions = {
+ kind: Array;
+ lifecycle: Array;
+};
+
+type FiltersProps = {
+ filters: FiltersState;
+ filterOptions: FilterOptions;
+ resetFilters: () => void;
+ updateSelected: (filter: string) => void;
+ updateChecked: (filter: string) => void;
+};
+
+export const Filters = ({
+ filters,
+ filterOptions,
+ resetFilters,
+ updateSelected,
+ updateChecked,
+}: FiltersProps) => {
+ const classes = useStyles();
+
+ return (
+
+ Filters}
+ action={
+
+ }
+ />
+
+ {filterOptions.kind.length === 0 && filterOptions.lifecycle.length === 0 && (
+
+
+ Filters cannot be applied to available results
+
+
+ )}
+ {filterOptions.kind.length > 0 && (
+
+ Kind
+
+
+ )}
+ {filterOptions.lifecycle.length > 0 && (
+
+ Lifecycle
+
+ {filterOptions.lifecycle.map(filter => (
+ updateChecked(filter)}
+ >
+
+
+
+ ))}
+
+
+ )}
+
+ );
+};
diff --git a/plugins/search/src/components/LegacySearchPage/Filters/FiltersButton.tsx b/plugins/search/src/components/LegacySearchPage/Filters/FiltersButton.tsx
new file mode 100644
index 0000000000..4775e9d8b8
--- /dev/null
+++ b/plugins/search/src/components/LegacySearchPage/Filters/FiltersButton.tsx
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * 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 FilterListIcon from '@material-ui/icons/FilterList';
+import { makeStyles, IconButton, Typography } from '@material-ui/core';
+
+const useStyles = makeStyles(theme => ({
+ filters: {
+ width: '250px',
+ display: 'flex',
+ },
+ icon: {
+ margin: theme.spacing(-1, 0, 0, 0),
+ },
+}));
+
+type FiltersButtonProps = {
+ numberOfSelectedFilters: number;
+ handleToggleFilters: () => void;
+};
+
+export const FiltersButton = ({
+ numberOfSelectedFilters,
+ handleToggleFilters,
+}: FiltersButtonProps) => {
+ const classes = useStyles();
+
+ return (
+
toggleFilters(!showFilters)}
- />
- }
- />
-
-
- >
- );
+ return children({ results: value.results });
};
+
+export { SearchResultComponent as SearchResult };
diff --git a/plugins/search/src/components/SearchResult/index.tsx b/plugins/search/src/components/SearchResult/index.tsx
index cf10135fd0..407700c19c 100644
--- a/plugins/search/src/components/SearchResult/index.tsx
+++ b/plugins/search/src/components/SearchResult/index.tsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 Spotify AB
+ * Copyright 2021 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/plugins/search/src/components/SearchResultNext/SearchResultNext.tsx b/plugins/search/src/components/SearchResultNext/SearchResultNext.tsx
deleted file mode 100644
index 84d3759ad3..0000000000
--- a/plugins/search/src/components/SearchResultNext/SearchResultNext.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2021 Spotify AB
- *
- * 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 { EmptyState, Progress } from '@backstage/core';
-import { SearchResult } from '@backstage/search-common';
-import { Alert } from '@material-ui/lab';
-
-import { useSearch } from '../SearchContext';
-
-type Props = {
- children: (results: { results: SearchResult[] }) => JSX.Element;
-};
-
-export const SearchResultNext = ({ children }: Props) => {
- const {
- result: { loading, error, value },
- } = useSearch();
-
- if (loading) {
- return ;
- }
- if (error) {
- return (
-
- Error encountered while fetching search results. {error.toString()}
-
- );
- }
-
- if (!value) {
- return ;
- }
-
- return children({ results: value.results });
-};
diff --git a/plugins/search/src/components/SearchResultNext/index.tsx b/plugins/search/src/components/SearchResultNext/index.tsx
deleted file mode 100644
index 73fabfdc7d..0000000000
--- a/plugins/search/src/components/SearchResultNext/index.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright 2021 Spotify AB
- *
- * 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 { SearchResultNext } from './SearchResultNext';
diff --git a/plugins/search/src/components/index.tsx b/plugins/search/src/components/index.tsx
index 92e24b60df..72bf9713ec 100644
--- a/plugins/search/src/components/index.tsx
+++ b/plugins/search/src/components/index.tsx
@@ -15,13 +15,10 @@
*/
export * from './Filters';
-export * from './SearchFilterNext';
+export * from './SearchFilter';
export * from './SearchBar';
-export * from './SearchBarNext';
export * from './SearchPage';
-export * from './SearchPageNext';
export * from './SearchResult';
-export * from './SearchResultNext';
export * from './DefaultResultListItem';
export * from './SidebarSearch';
export * from './SearchContext';
diff --git a/plugins/search/src/index.ts b/plugins/search/src/index.ts
index 27fd1706fc..75868d12fd 100644
--- a/plugins/search/src/index.ts
+++ b/plugins/search/src/index.ts
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-// TODO: export searchApiRef from ./apis once interface is stable and settled.
+export { searchApiRef } from './apis';
export {
searchPlugin,
searchPlugin as plugin,
SearchPage,
SearchPageNext,
SearchBarNext,
- SearchResultNext,
+ SearchResult,
DefaultResultListItem,
} from './plugin';
export {
@@ -31,8 +31,8 @@ export {
SearchContextProvider,
useSearch,
SearchPage as Router,
+ SearchFilter,
SearchFilterNext,
- SearchResult,
SidebarSearch,
} from './components';
export type { FiltersState } from './components';
diff --git a/plugins/search/src/plugin.ts b/plugins/search/src/plugin.ts
index 6ff40ebaf7..45987a3749 100644
--- a/plugins/search/src/plugin.ts
+++ b/plugins/search/src/plugin.ts
@@ -22,9 +22,6 @@ import {
createComponentExtension,
} from '@backstage/core';
import { SearchClient, searchApiRef } from './apis';
-import { catalogApiRef } from '@backstage/plugin-catalog-react';
-import { SearchPage as SearchPageComponent } from './components/SearchPage';
-import { SearchPageNext as SearchPageNextComponent } from './components/SearchPageNext';
export const rootRouteRef = createRouteRef({
path: '/search',
@@ -41,16 +38,12 @@ export const searchPlugin = createPlugin({
apis: [
createApiFactory({
api: searchApiRef,
- deps: { catalogApi: catalogApiRef, discoveryApi: discoveryApiRef },
- factory: ({ catalogApi, discoveryApi }) => {
- return new SearchClient({ catalogApi, discoveryApi });
+ deps: { discoveryApi: discoveryApiRef },
+ factory: ({ discoveryApi }) => {
+ return new SearchClient({ discoveryApi });
},
}),
],
- register({ router }) {
- router.addRoute(rootRouteRef, SearchPageComponent);
- router.addRoute(rootNextRouteRef, SearchPageNextComponent);
- },
routes: {
root: rootRouteRef,
nextRoot: rootNextRouteRef,
@@ -64,28 +57,59 @@ export const SearchPage = searchPlugin.provide(
}),
);
+/**
+ * @deprecated This component was used for rapid prototyping of the Backstage
+ * Search platform. Now that the API has stabilized, you should use the
+ * component instead. This component will be removed in an
+ * upcoming release.
+ */
export const SearchPageNext = searchPlugin.provide(
createRoutableExtension({
- component: () =>
- import('./components/SearchPageNext').then(m => m.SearchPageNext),
+ component: () => import('./components/SearchPage').then(m => m.SearchPage),
mountPoint: rootNextRouteRef,
}),
);
-export const SearchBarNext = searchPlugin.provide(
+export const SearchBar = searchPlugin.provide(
createComponentExtension({
component: {
- lazy: () =>
- import('./components/SearchBarNext').then(m => m.SearchBarNext),
+ lazy: () => import('./components/SearchBar').then(m => m.SearchBar),
},
}),
);
+/**
+ * @deprecated This component was used for rapid prototyping of the Backstage
+ * Search platform. Now that the API has stabilized, you should use the
+ * component instead. This component will be removed in an
+ * upcoming release.
+ */
+export const SearchBarNext = searchPlugin.provide(
+ createComponentExtension({
+ component: {
+ lazy: () => import('./components/SearchBar').then(m => m.SearchBar),
+ },
+ }),
+);
+
+export const SearchResult = searchPlugin.provide(
+ createComponentExtension({
+ component: {
+ lazy: () => import('./components/SearchResult').then(m => m.SearchResult),
+ },
+ }),
+);
+
+/**
+ * @deprecated This component was used for rapid prototyping of the Backstage
+ * Search platform. Now that the API has stabilized, you should use the
+ * component instead. This component will be removed in an
+ * upcoming release.
+ */
export const SearchResultNext = searchPlugin.provide(
createComponentExtension({
component: {
- lazy: () =>
- import('./components/SearchResultNext').then(m => m.SearchResultNext),
+ lazy: () => import('./components/SearchResult').then(m => m.SearchResult),
},
}),
);