From 24d9f44ccb12942cc9ddd77a7df2d7dfbe338762 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Sat, 23 Sep 2023 11:41:08 +0200 Subject: [PATCH 1/6] chore(search-react): configure alpha subpath Signed-off-by: Camila Belo --- plugins/search-react/package.json | 19 ++++++++++++++++--- plugins/search-react/src/alpha.ts | 15 +++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 plugins/search-react/src/alpha.ts diff --git a/plugins/search-react/package.json b/plugins/search-react/package.json index 4454d44574..6255ccb0ff 100644 --- a/plugins/search-react/package.json +++ b/plugins/search-react/package.json @@ -5,9 +5,22 @@ "types": "src/index.ts", "license": "Apache-2.0", "publishConfig": { - "access": "public", - "main": "dist/index.esm.js", - "types": "dist/index.d.ts" + "access": "public" + }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } }, "backstage": { "role": "web-library" diff --git a/plugins/search-react/src/alpha.ts b/plugins/search-react/src/alpha.ts new file mode 100644 index 0000000000..eb2f165350 --- /dev/null +++ b/plugins/search-react/src/alpha.ts @@ -0,0 +1,15 @@ +/* + * Copyright 2023 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. + */ From b54222b62d6a7bfbcb79f2aa3459ae35978efa96 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Mon, 25 Sep 2023 08:47:36 +0200 Subject: [PATCH 2/6] feat(search-react): create search result item extension Signed-off-by: Camila Belo --- plugins/search-react/package.json | 2 + plugins/search-react/src/alpha.ts | 2 + plugins/search-react/src/extensions.tsx | 4 +- ...eateSearchResultListItemExtension.test.tsx | 175 ++++++++++++++++++ .../createSearchResultListItemExtension.tsx | 111 +++++++++++ plugins/search-react/src/wiring/index.ts | 17 ++ yarn.lock | 2 + 7 files changed, 311 insertions(+), 2 deletions(-) create mode 100644 plugins/search-react/src/wiring/createSearchResultListItemExtension.test.tsx create mode 100644 plugins/search-react/src/wiring/createSearchResultListItemExtension.tsx create mode 100644 plugins/search-react/src/wiring/index.ts diff --git a/plugins/search-react/package.json b/plugins/search-react/package.json index 6255ccb0ff..31e12db763 100644 --- a/plugins/search-react/package.json +++ b/plugins/search-react/package.json @@ -47,6 +47,8 @@ "dependencies": { "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", + "@backstage/frontend-app-api": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", "@backstage/plugin-search-common": "workspace:^", "@backstage/theme": "workspace:^", "@backstage/types": "workspace:^", diff --git a/plugins/search-react/src/alpha.ts b/plugins/search-react/src/alpha.ts index eb2f165350..e006e249eb 100644 --- a/plugins/search-react/src/alpha.ts +++ b/plugins/search-react/src/alpha.ts @@ -13,3 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +export * from './wiring'; diff --git a/plugins/search-react/src/extensions.tsx b/plugins/search-react/src/extensions.tsx index a29311b1ff..5cd0aab76e 100644 --- a/plugins/search-react/src/extensions.tsx +++ b/plugins/search-react/src/extensions.tsx @@ -86,11 +86,11 @@ export type SearchResultListItemExtensionProps = Props & >; /** - * @internal + * @public * Extends children with extension capabilities. * @param props - see {@link SearchResultListItemExtensionProps}. */ -const SearchResultListItemExtension = ( +export const SearchResultListItemExtension = ( props: SearchResultListItemExtensionProps, ) => { const { diff --git a/plugins/search-react/src/wiring/createSearchResultListItemExtension.test.tsx b/plugins/search-react/src/wiring/createSearchResultListItemExtension.test.tsx new file mode 100644 index 0000000000..d7a2947e1f --- /dev/null +++ b/plugins/search-react/src/wiring/createSearchResultListItemExtension.test.tsx @@ -0,0 +1,175 @@ +/* + * Copyright 2023 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 { render, screen } from '@testing-library/react'; + +import { + createExtensionInput, + createPageExtension, + createPlugin, +} from '@backstage/frontend-plugin-api'; +import { SearchResult } from '@backstage/plugin-search-common'; +import { createApp } from '@backstage/frontend-app-api'; +import { MockConfigApi } from '@backstage/test-utils'; + +import { + BaseSearchResultListItemProps, + createSearchResultListItemExtension, + searchResultItemExtensionData, +} from './createSearchResultListItemExtension'; + +// TODO: Remove this mock when we have a permanent solution for nav items extensions +// The `GraphiQLIcon` used in "packages/frontend-app-api/src/extensions/CoreNav.tsx" file +// is throwing a "ReferenceError: ref is not defined" error during test +jest.mock('@backstage/plugin-graphiql', () => ({ + ...jest.requireActual('@backstage/plugin-graphiql'), + GraphiQLIcon: () => null, +})); + +describe('createSearchResultListItemExtension', () => { + it('Should use the correct result component', async () => { + const TechDocsSearchResultItemComponent = ( + props: BaseSearchResultListItemProps, + ) =>
TechDocs - Rank: {props.rank}
; + + const TechDocsSearchResultItemExtension = + createSearchResultListItemExtension({ + id: 'techdocs', + at: 'plugin.search.page/items', + predicate: result => result.type === 'techdocs', + component: async () => TechDocsSearchResultItemComponent, + }); + + const ExploreSearchResultItemComponent = ( + props: BaseSearchResultListItemProps, + ) =>
Explore - Rank: {props.rank}
; + + const ExploreSearchResultItemExtension = + createSearchResultListItemExtension({ + id: 'explore', + at: 'plugin.search.page/items', + predicate: result => result.type === 'explore', + component: async () => ExploreSearchResultItemComponent, + }); + + const SearchPageExtension = createPageExtension({ + id: 'plugin.search.page', + defaultPath: '/', + inputs: { + items: createExtensionInput({ + item: searchResultItemExtensionData, + }), + }, + loader: async ({ inputs }) => { + const results = [ + { + type: 'techdocs', + rank: 1, + document: { + title: 'Title1', + text: 'Text1', + location: '/location1', + }, + }, + { + type: 'explore', + rank: 2, + document: { + title: 'Title2', + text: 'Text2', + location: '/location2', + }, + }, + { + type: 'other', + rank: 3, + document: { + title: 'Title3', + text: 'Text3', + location: '/location3', + }, + }, + ]; + + const DefaultResultItem = (props: BaseSearchResultListItemProps) => ( +
Default - Rank: {props.rank}
+ ); + + const getResultItemComponent = (result: SearchResult) => { + const value = inputs.items.find(({ item }) => + item?.predicate?.(result), + ); + return value?.item.component ?? DefaultResultItem; + }; + + const Component = () => { + return ( +
+

Search Page

+
    + {results.map((result, index) => { + const SearchResultListItem = getResultItemComponent(result); + return ( + + ); + })} +
+
+ ); + }; + + return ; + }, + }); + + const SearchPlugin = createPlugin({ + id: 'search.plugin', + extensions: [ + SearchPageExtension, + ExploreSearchResultItemExtension, + TechDocsSearchResultItemExtension, + ], + }); + + const app = createApp({ + plugins: [SearchPlugin], + configLoader: async () => new MockConfigApi({}), + }); + + render(app.createRoot()); + + expect(await screen.findByText(/Search Page/)).toBeInTheDocument(); + + expect( + await screen.findByText(/TechDocs - Rank: 1/, { exact: false }), + ).toBeInTheDocument(); + + expect( + await screen.findByText(/Explore - Rank: 2/, { exact: false }), + ).toBeInTheDocument(); + + expect( + await screen.findByText(/Default - Rank: 3/, { exact: false }), + ).toBeInTheDocument(); + }); +}); diff --git a/plugins/search-react/src/wiring/createSearchResultListItemExtension.tsx b/plugins/search-react/src/wiring/createSearchResultListItemExtension.tsx new file mode 100644 index 0000000000..4ad0e54e1f --- /dev/null +++ b/plugins/search-react/src/wiring/createSearchResultListItemExtension.tsx @@ -0,0 +1,111 @@ +/* + * Copyright 2023 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, { lazy, Suspense } from 'react'; + +import { ListItemProps } from '@material-ui/core'; + +import { + ExtensionBoundary, + createExtension, + createExtensionDataRef, +} from '@backstage/frontend-plugin-api'; +import { Progress } from '@backstage/core-components'; +import { SearchDocument, SearchResult } from '@backstage/plugin-search-common'; + +import { SearchResultListItemExtension } from '../extensions'; + +/** @alpha */ +export type SearchResultItemExtensionComponent = < + P extends BaseSearchResultListItemProps, +>( + props: P, +) => JSX.Element | null; + +/** @alpha */ +export type SearchResultItemExtensionPredicate = ( + result: SearchResult, +) => boolean; + +/** @alpha */ +export const searchResultItemExtensionData = createExtensionDataRef<{ + predicate?: SearchResultItemExtensionPredicate; + component: SearchResultItemExtensionComponent; +}>('plugin.search.result.item.data'); + +/** @alpha */ +export type SearchResultItemExtensionOptions = { + /** + * The extension id. + */ + id: string; + /** + * The extension attachment point (e.g., search modal or page). + */ + at: string; + /** + * The extension component. + */ + component: () => Promise; + /** + * When an extension defines a predicate, it returns true if the result should be rendered by that extension. + * Defaults to a predicate that returns true, which means it renders all sorts of results. + */ + predicate?: SearchResultItemExtensionPredicate; +}; + +/** @alpha */ +export type BaseSearchResultListItemProps = { + rank?: number; + result?: SearchDocument; + noTrack?: boolean; +} & Omit; + +/** @alpha */ +export const createSearchResultListItemExtension = ( + options: SearchResultItemExtensionOptions, +) => + createExtension({ + id: `plugin.search.result.item.${options.id}`, + at: options.at, + output: { + item: searchResultItemExtensionData, + }, + factory({ bind, source }) { + const LazyComponent = lazy(() => + options.component().then(component => ({ default: component })), + ) as unknown as SearchResultItemExtensionComponent; + + bind({ + item: { + predicate: options.predicate, + component: props => ( + + }> + + + + + + ), + }, + }); + }, + }); diff --git a/plugins/search-react/src/wiring/index.ts b/plugins/search-react/src/wiring/index.ts new file mode 100644 index 0000000000..7cf5b0d6f5 --- /dev/null +++ b/plugins/search-react/src/wiring/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2023 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 * from './createSearchResultListItemExtension'; diff --git a/yarn.lock b/yarn.lock index 6f1a40c8c5..e429806816 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9079,6 +9079,8 @@ __metadata: "@backstage/core-app-api": "workspace:^" "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" + "@backstage/frontend-app-api": "workspace:^" + "@backstage/frontend-plugin-api": "workspace:^" "@backstage/plugin-search-common": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/theme": "workspace:^" From 0639f2840abaa7f1fce0d2c5d9bdb115aa9e3c39 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Mon, 25 Sep 2023 23:37:51 +0200 Subject: [PATCH 3/6] chore(search-react): update api reports Signed-off-by: Camila Belo --- plugins/search-react/alpha-api-report.md | 56 ++++++++++++++++++++++++ plugins/search-react/api-report.md | 5 +++ 2 files changed, 61 insertions(+) create mode 100644 plugins/search-react/alpha-api-report.md diff --git a/plugins/search-react/alpha-api-report.md b/plugins/search-react/alpha-api-report.md new file mode 100644 index 0000000000..90492f9434 --- /dev/null +++ b/plugins/search-react/alpha-api-report.md @@ -0,0 +1,56 @@ +## API Report File for "@backstage/plugin-search-react" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; +import { Extension } from '@backstage/frontend-plugin-api'; +import { ListItemProps } from '@material-ui/core'; +import { SearchDocument } from '@backstage/plugin-search-common'; +import { SearchResult } from '@backstage/plugin-search-common'; + +// @alpha (undocumented) +export type BaseSearchResultListItemProps = { + rank?: number; + result?: SearchDocument; + noTrack?: boolean; +} & Omit; + +// @alpha (undocumented) +export const createSearchResultListItemExtension: ( + options: SearchResultItemExtensionOptions, +) => Extension; + +// @alpha (undocumented) +export type SearchResultItemExtensionComponent = < + P extends BaseSearchResultListItemProps, +>( + props: P, +) => JSX.Element | null; + +// @alpha (undocumented) +export const searchResultItemExtensionData: ConfigurableExtensionDataRef< + { + predicate?: SearchResultItemExtensionPredicate | undefined; + component: SearchResultItemExtensionComponent; + }, + {} +>; + +// @alpha (undocumented) +export type SearchResultItemExtensionOptions = { + id: string; + at: string; + component: () => Promise; + predicate?: SearchResultItemExtensionPredicate; +}; + +// @alpha (undocumented) +export type SearchResultItemExtensionPredicate = ( + result: SearchResult, +) => boolean; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/search-react/api-report.md b/plugins/search-react/api-report.md index 97c8300c14..21dd6135a9 100644 --- a/plugins/search-react/api-report.md +++ b/plugins/search-react/api-report.md @@ -391,6 +391,11 @@ export const SearchResultList: ( props: SearchResultListProps, ) => React_2.JSX.Element; +// @public +export const SearchResultListItemExtension: ( + props: SearchResultListItemExtensionProps, +) => React_2.JSX.Element; + // @public export type SearchResultListItemExtensionOptions< Component extends (props: any) => JSX.Element | null, From 703a4ffc5beec70c7d6b013f8d421627ef2cf440 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 26 Sep 2023 00:01:56 +0200 Subject: [PATCH 4/6] chore(search-react): add changeset file Signed-off-by: Camila Belo --- .changeset/wicked-rings-flash.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/wicked-rings-flash.md diff --git a/.changeset/wicked-rings-flash.md b/.changeset/wicked-rings-flash.md new file mode 100644 index 0000000000..b872c0c71f --- /dev/null +++ b/.changeset/wicked-rings-flash.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-react': patch +--- + +Create `createSearchResultListItem` alpha version that only supports declarative integration. From 184be50385cf98e32b3bf5c5593b7d035510f75a Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Wed, 27 Sep 2023 14:29:54 +0200 Subject: [PATCH 5/6] refactor(search-react): apply review suggestions Signed-off-by: Camila Belo --- plugins/search-react/alpha-api-report.md | 23 +++++++--- plugins/search-react/api-report.md | 5 --- plugins/search-react/src/extensions.tsx | 2 +- ...eateSearchResultListItemExtension.test.tsx | 43 ++++++++++++++++--- .../createSearchResultListItemExtension.tsx | 43 ++++++++++++++----- 5 files changed, 86 insertions(+), 30 deletions(-) diff --git a/plugins/search-react/alpha-api-report.md b/plugins/search-react/alpha-api-report.md index 90492f9434..163247bf5e 100644 --- a/plugins/search-react/alpha-api-report.md +++ b/plugins/search-react/alpha-api-report.md @@ -8,20 +8,22 @@ import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { Extension } from '@backstage/frontend-plugin-api'; import { ListItemProps } from '@material-ui/core'; +import { PortableSchema } from '@backstage/frontend-plugin-api'; import { SearchDocument } from '@backstage/plugin-search-common'; import { SearchResult } from '@backstage/plugin-search-common'; // @alpha (undocumented) -export type BaseSearchResultListItemProps = { +export type BaseSearchResultListItemProps = T & { rank?: number; result?: SearchDocument; - noTrack?: boolean; } & Omit; // @alpha (undocumented) -export const createSearchResultListItemExtension: ( - options: SearchResultItemExtensionOptions, -) => Extension; +export function createSearchResultListItemExtension< + TConfig extends { + noTrack?: boolean; + }, +>(options: SearchResultItemExtensionOptions): Extension; // @alpha (undocumented) export type SearchResultItemExtensionComponent = < @@ -40,10 +42,17 @@ export const searchResultItemExtensionData: ConfigurableExtensionDataRef< >; // @alpha (undocumented) -export type SearchResultItemExtensionOptions = { +export type SearchResultItemExtensionOptions< + TConfig extends { + noTrack?: boolean; + }, +> = { id: string; at: string; - component: () => Promise; + configSchema?: PortableSchema; + component: (options: { + config: TConfig; + }) => Promise; predicate?: SearchResultItemExtensionPredicate; }; diff --git a/plugins/search-react/api-report.md b/plugins/search-react/api-report.md index 21dd6135a9..97c8300c14 100644 --- a/plugins/search-react/api-report.md +++ b/plugins/search-react/api-report.md @@ -391,11 +391,6 @@ export const SearchResultList: ( props: SearchResultListProps, ) => React_2.JSX.Element; -// @public -export const SearchResultListItemExtension: ( - props: SearchResultListItemExtensionProps, -) => React_2.JSX.Element; - // @public export type SearchResultListItemExtensionOptions< Component extends (props: any) => JSX.Element | null, diff --git a/plugins/search-react/src/extensions.tsx b/plugins/search-react/src/extensions.tsx index 5cd0aab76e..132fad25b5 100644 --- a/plugins/search-react/src/extensions.tsx +++ b/plugins/search-react/src/extensions.tsx @@ -86,7 +86,7 @@ export type SearchResultListItemExtensionProps = Props & >; /** - * @public + * @internal * Extends children with extension capabilities. * @param props - see {@link SearchResultListItemExtensionProps}. */ diff --git a/plugins/search-react/src/wiring/createSearchResultListItemExtension.test.tsx b/plugins/search-react/src/wiring/createSearchResultListItemExtension.test.tsx index d7a2947e1f..ca0e7e0672 100644 --- a/plugins/search-react/src/wiring/createSearchResultListItemExtension.test.tsx +++ b/plugins/search-react/src/wiring/createSearchResultListItemExtension.test.tsx @@ -22,6 +22,7 @@ import { createExtensionInput, createPageExtension, createPlugin, + createSchemaFromZod, } from '@backstage/frontend-plugin-api'; import { SearchResult } from '@backstage/plugin-search-common'; import { createApp } from '@backstage/frontend-app-api'; @@ -43,16 +44,32 @@ jest.mock('@backstage/plugin-graphiql', () => ({ describe('createSearchResultListItemExtension', () => { it('Should use the correct result component', async () => { + type TechDocsSearchReasulListItemProps = BaseSearchResultListItemProps<{ + lineClamp: number; + }>; const TechDocsSearchResultItemComponent = ( - props: BaseSearchResultListItemProps, - ) =>
TechDocs - Rank: {props.rank}
; + props: TechDocsSearchReasulListItemProps, + ) => ( +
+ TechDocs - Rank: {props.rank} - Line clamp: {props.lineClamp} +
+ ); const TechDocsSearchResultItemExtension = createSearchResultListItemExtension({ id: 'techdocs', at: 'plugin.search.page/items', + configSchema: createSchemaFromZod(z => + z.object({ + noTrack: z.boolean().default(true), + lineClamp: z.number().default(5), + }), + ), predicate: result => result.type === 'techdocs', - component: async () => TechDocsSearchResultItemComponent, + component: + async ({ config }) => + props => + , }); const ExploreSearchResultItemComponent = ( @@ -129,7 +146,6 @@ describe('createSearchResultListItemExtension', () => { key={index} rank={result.rank} result={result.document} - noTrack /> ); })} @@ -153,7 +169,20 @@ describe('createSearchResultListItemExtension', () => { const app = createApp({ plugins: [SearchPlugin], - configLoader: async () => new MockConfigApi({}), + configLoader: async () => + new MockConfigApi({ + app: { + extensions: [ + { + 'plugin.search.result.item.techdocs': { + config: { + lineClamp: 3, + }, + }, + }, + ], + }, + }), }); render(app.createRoot()); @@ -161,7 +190,9 @@ describe('createSearchResultListItemExtension', () => { expect(await screen.findByText(/Search Page/)).toBeInTheDocument(); expect( - await screen.findByText(/TechDocs - Rank: 1/, { exact: false }), + await screen.findByText(/TechDocs - Rank: 1 - Line clamp: 3/, { + exact: false, + }), ).toBeInTheDocument(); expect( diff --git a/plugins/search-react/src/wiring/createSearchResultListItemExtension.tsx b/plugins/search-react/src/wiring/createSearchResultListItemExtension.tsx index 4ad0e54e1f..b276c5e60a 100644 --- a/plugins/search-react/src/wiring/createSearchResultListItemExtension.tsx +++ b/plugins/search-react/src/wiring/createSearchResultListItemExtension.tsx @@ -20,8 +20,10 @@ import { ListItemProps } from '@material-ui/core'; import { ExtensionBoundary, + PortableSchema, createExtension, createExtensionDataRef, + createSchemaFromZod, } from '@backstage/frontend-plugin-api'; import { Progress } from '@backstage/core-components'; import { SearchDocument, SearchResult } from '@backstage/plugin-search-common'; @@ -47,7 +49,9 @@ export const searchResultItemExtensionData = createExtensionDataRef<{ }>('plugin.search.result.item.data'); /** @alpha */ -export type SearchResultItemExtensionOptions = { +export type SearchResultItemExtensionOptions< + TConfig extends { noTrack?: boolean }, +> = { /** * The extension id. */ @@ -56,10 +60,16 @@ export type SearchResultItemExtensionOptions = { * The extension attachment point (e.g., search modal or page). */ at: string; + /** + * Optional extension config schema. + */ + configSchema?: PortableSchema; /** * The extension component. */ - component: () => Promise; + component: (options: { + config: TConfig; + }) => Promise; /** * When an extension defines a predicate, it returns true if the result should be rendered by that extension. * Defaults to a predicate that returns true, which means it renders all sorts of results. @@ -68,25 +78,35 @@ export type SearchResultItemExtensionOptions = { }; /** @alpha */ -export type BaseSearchResultListItemProps = { +export type BaseSearchResultListItemProps = T & { rank?: number; result?: SearchDocument; - noTrack?: boolean; } & Omit; /** @alpha */ -export const createSearchResultListItemExtension = ( - options: SearchResultItemExtensionOptions, -) => - createExtension({ +export function createSearchResultListItemExtension< + TConfig extends { noTrack?: boolean }, +>(options: SearchResultItemExtensionOptions) { + const configSchema = + 'configSchema' in options + ? options.configSchema + : (createSchemaFromZod(z => + z.object({ + noTrack: z.boolean().default(true), + }), + ) as PortableSchema); + return createExtension({ id: `plugin.search.result.item.${options.id}`, at: options.at, + configSchema, output: { item: searchResultItemExtensionData, }, - factory({ bind, source }) { + factory({ bind, config, source }) { const LazyComponent = lazy(() => - options.component().then(component => ({ default: component })), + options + .component({ config }) + .then(component => ({ default: component })), ) as unknown as SearchResultItemExtensionComponent; bind({ @@ -98,7 +118,7 @@ export const createSearchResultListItemExtension = ( @@ -109,3 +129,4 @@ export const createSearchResultListItemExtension = ( }); }, }); +} From d36f92de248c885544e900a9ac4907ef06975dd3 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Thu, 28 Sep 2023 09:37:16 +0200 Subject: [PATCH 6/6] refactor(search-react): move everything to alpha file Signed-off-by: Camila Belo --- plugins/search-react/package.json | 4 ++-- ...istItemExtension.test.tsx => alpha.test.tsx} | 6 +++--- plugins/search-react/src/alpha.ts | 17 ----------------- ...rchResultListItemExtension.tsx => alpha.tsx} | 16 ++++++++-------- plugins/search-react/src/wiring/index.ts | 17 ----------------- 5 files changed, 13 insertions(+), 47 deletions(-) rename plugins/search-react/src/{wiring/createSearchResultListItemExtension.test.tsx => alpha.test.tsx} (97%) delete mode 100644 plugins/search-react/src/alpha.ts rename plugins/search-react/src/{wiring/createSearchResultListItemExtension.tsx => alpha.tsx} (97%) delete mode 100644 plugins/search-react/src/wiring/index.ts diff --git a/plugins/search-react/package.json b/plugins/search-react/package.json index 31e12db763..da4092fcbd 100644 --- a/plugins/search-react/package.json +++ b/plugins/search-react/package.json @@ -9,13 +9,13 @@ }, "exports": { ".": "./src/index.ts", - "./alpha": "./src/alpha.ts", + "./alpha": "./src/alpha.tsx", "./package.json": "./package.json" }, "typesVersions": { "*": { "alpha": [ - "src/alpha.ts" + "src/alpha.tsx" ], "package.json": [ "package.json" diff --git a/plugins/search-react/src/wiring/createSearchResultListItemExtension.test.tsx b/plugins/search-react/src/alpha.test.tsx similarity index 97% rename from plugins/search-react/src/wiring/createSearchResultListItemExtension.test.tsx rename to plugins/search-react/src/alpha.test.tsx index ca0e7e0672..b61a400832 100644 --- a/plugins/search-react/src/wiring/createSearchResultListItemExtension.test.tsx +++ b/plugins/search-react/src/alpha.test.tsx @@ -31,8 +31,8 @@ import { MockConfigApi } from '@backstage/test-utils'; import { BaseSearchResultListItemProps, createSearchResultListItemExtension, - searchResultItemExtensionData, -} from './createSearchResultListItemExtension'; + searchResultItemExtensionData as searchResultListItemExtensionData, +} from './alpha'; // TODO: Remove this mock when we have a permanent solution for nav items extensions // The `GraphiQLIcon` used in "packages/frontend-app-api/src/extensions/CoreNav.tsx" file @@ -89,7 +89,7 @@ describe('createSearchResultListItemExtension', () => { defaultPath: '/', inputs: { items: createExtensionInput({ - item: searchResultItemExtensionData, + item: searchResultListItemExtensionData, }), }, loader: async ({ inputs }) => { diff --git a/plugins/search-react/src/alpha.ts b/plugins/search-react/src/alpha.ts deleted file mode 100644 index e006e249eb..0000000000 --- a/plugins/search-react/src/alpha.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2023 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 * from './wiring'; diff --git a/plugins/search-react/src/wiring/createSearchResultListItemExtension.tsx b/plugins/search-react/src/alpha.tsx similarity index 97% rename from plugins/search-react/src/wiring/createSearchResultListItemExtension.tsx rename to plugins/search-react/src/alpha.tsx index b276c5e60a..4b5bb79669 100644 --- a/plugins/search-react/src/wiring/createSearchResultListItemExtension.tsx +++ b/plugins/search-react/src/alpha.tsx @@ -28,7 +28,13 @@ import { import { Progress } from '@backstage/core-components'; import { SearchDocument, SearchResult } from '@backstage/plugin-search-common'; -import { SearchResultListItemExtension } from '../extensions'; +import { SearchResultListItemExtension } from './extensions'; + +/** @alpha */ +export type BaseSearchResultListItemProps = T & { + rank?: number; + result?: SearchDocument; +} & Omit; /** @alpha */ export type SearchResultItemExtensionComponent = < @@ -77,12 +83,6 @@ export type SearchResultItemExtensionOptions< predicate?: SearchResultItemExtensionPredicate; }; -/** @alpha */ -export type BaseSearchResultListItemProps = T & { - rank?: number; - result?: SearchDocument; -} & Omit; - /** @alpha */ export function createSearchResultListItemExtension< TConfig extends { noTrack?: boolean }, @@ -92,7 +92,7 @@ export function createSearchResultListItemExtension< ? options.configSchema : (createSchemaFromZod(z => z.object({ - noTrack: z.boolean().default(true), + noTrack: z.boolean().default(false), }), ) as PortableSchema); return createExtension({ diff --git a/plugins/search-react/src/wiring/index.ts b/plugins/search-react/src/wiring/index.ts deleted file mode 100644 index 7cf5b0d6f5..0000000000 --- a/plugins/search-react/src/wiring/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2023 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 * from './createSearchResultListItemExtension';