diff --git a/.changeset/add-withApis-core-compat-api.md b/.changeset/add-withApis-core-compat-api.md new file mode 100644 index 0000000000..135dcf87c7 --- /dev/null +++ b/.changeset/add-withApis-core-compat-api.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-compat-api': patch +--- + +Added `withApis`, which is a Higher-Order Component for providing APIs as props to a component via `useApiHolder`. diff --git a/.changeset/deprecate-withApis-frontend-plugin-api.md b/.changeset/deprecate-withApis-frontend-plugin-api.md new file mode 100644 index 0000000000..5e0408fd04 --- /dev/null +++ b/.changeset/deprecate-withApis-frontend-plugin-api.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': patch +--- + +Deprecated `withApis`, use the `withApis` export from `@backstage/core-compat-api` instead. diff --git a/packages/core-compat-api/report.api.md b/packages/core-compat-api/report.api.md index ae202c96f7..13d0849000 100644 --- a/packages/core-compat-api/report.api.md +++ b/packages/core-compat-api/report.api.md @@ -22,11 +22,13 @@ import { FrontendPlugin } from '@backstage/frontend-plugin-api'; import { IconComponent } from '@backstage/core-plugin-api'; import { JSX as JSX_2 } from 'react'; import { JSX as JSX_3 } from 'react/jsx-runtime'; +import { PropsWithChildren } from 'react'; import { ReactNode } from 'react'; import { RouteRef } from '@backstage/core-plugin-api'; import { RouteRef as RouteRef_2 } from '@backstage/frontend-plugin-api'; import { SubRouteRef } from '@backstage/core-plugin-api'; import { SubRouteRef as SubRouteRef_2 } from '@backstage/frontend-plugin-api'; +import { TypesToApiRefs } from '@backstage/frontend-plugin-api'; // @public export function compatWrapper(element: ReactNode): JSX_3.Element; @@ -143,5 +145,15 @@ export type ToNewRouteRef = ? ExternalRouteRef_2 : never; +// @public +export function withApis( + apis: TypesToApiRefs, +): ( + WrappedComponent: ComponentType, +) => { + (props: PropsWithChildren>): JSX_3.Element; + displayName: string; +}; + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/core-compat-api/src/index.ts b/packages/core-compat-api/src/index.ts index 6d8041ef44..b273fae9e4 100644 --- a/packages/core-compat-api/src/index.ts +++ b/packages/core-compat-api/src/index.ts @@ -31,3 +31,4 @@ export { convertLegacyRouteRefs, type ToNewRouteRef, } from './convertLegacyRouteRef'; +export { withApis } from './withApis'; diff --git a/packages/core-compat-api/src/withApis.tsx b/packages/core-compat-api/src/withApis.tsx new file mode 100644 index 0000000000..a06b5cd7a9 --- /dev/null +++ b/packages/core-compat-api/src/withApis.tsx @@ -0,0 +1,60 @@ +/* + * Copyright 2020 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 { ComponentType, PropsWithChildren } from 'react'; +import { + ApiRef, + TypesToApiRefs, + useApiHolder, +} from '@backstage/frontend-plugin-api'; + +/** + * Wrapper for giving component an API context. + * + * @param apis - APIs for the context. + * @public + */ +export function withApis(apis: TypesToApiRefs) { + return function withApisWrapper( + WrappedComponent: ComponentType, + ) { + const Hoc = (props: PropsWithChildren>) => { + const apiHolder = useApiHolder(); + + const impls = {} as T; + + for (const key in apis) { + if (apis.hasOwnProperty(key)) { + const ref: ApiRef = apis[key]; + + const api = apiHolder.get(ref); + if (!api) { + throw new Error(`No implementation available for ${ref}`); + } + impls[key] = api; + } + } + + return ; + }; + const displayName = + WrappedComponent.displayName || WrappedComponent.name || 'Component'; + + Hoc.displayName = `withApis(${displayName})`; + + return Hoc; + }; +} diff --git a/packages/frontend-plugin-api/report.api.md b/packages/frontend-plugin-api/report.api.md index c83511fc47..b3801b070f 100644 --- a/packages/frontend-plugin-api/report.api.md +++ b/packages/frontend-plugin-api/report.api.md @@ -2313,7 +2313,7 @@ export const vmwareCloudAuthApiRef: ApiRef< SessionApi >; -// @public +// @public @deprecated export function withApis( apis: TypesToApiRefs, ): ( diff --git a/packages/frontend-plugin-api/src/apis/system/useApi.tsx b/packages/frontend-plugin-api/src/apis/system/useApi.tsx index 30e39033f7..fecf826c94 100644 --- a/packages/frontend-plugin-api/src/apis/system/useApi.tsx +++ b/packages/frontend-plugin-api/src/apis/system/useApi.tsx @@ -57,6 +57,7 @@ export function useApi(apiRef: ApiRef): T { * Wrapper for giving component an API context. * * @param apis - APIs for the context. + * @deprecated Use {@link withApis} from `@backstage/core-compat-api` instead. * @public */ export function withApis(apis: TypesToApiRefs) {