diff --git a/.changeset/large-eggs-help.md b/.changeset/large-eggs-help.md new file mode 100644 index 0000000000..e310ad7645 --- /dev/null +++ b/.changeset/large-eggs-help.md @@ -0,0 +1,19 @@ +--- +'@backstage/plugin-explore': minor +--- + +Introduce external route for linking to the entity page from the explore plugin. + +To use the explore plugin you have to bind the external route in your app: + +```typescript +const app = createApp({ + ... + bindRoutes({ bind }) { + ... + bind(explorePlugin.externalRoutes, { + catalogEntity: catalogPlugin.routes.catalogEntity, + }); + }, +}); +``` diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 5b6f7b95fe..7acde03953 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -33,7 +33,7 @@ import { CostInsightsPage, CostInsightsProjectGrowthInstructionsPage, } from '@backstage/plugin-cost-insights'; -import { ExplorePage } from '@backstage/plugin-explore'; +import { ExplorePage, explorePlugin } from '@backstage/plugin-explore'; import { GcpProjectsPage } from '@backstage/plugin-gcp-projects'; import { GraphiQLPage } from '@backstage/plugin-graphiql'; import { LighthousePage } from '@backstage/plugin-lighthouse'; @@ -79,6 +79,9 @@ const app = createApp({ bind(apiDocsPlugin.externalRoutes, { createComponent: scaffolderPlugin.routes.root, }); + bind(explorePlugin.externalRoutes, { + catalogEntity: catalogPlugin.routes.catalogEntity, + }); }, }); diff --git a/plugins/explore/src/components/DomainCard/DomainCard.test.tsx b/plugins/explore/src/components/DomainCard/DomainCard.test.tsx index 9d409051f6..56a02a3642 100644 --- a/plugins/explore/src/components/DomainCard/DomainCard.test.tsx +++ b/plugins/explore/src/components/DomainCard/DomainCard.test.tsx @@ -15,13 +15,13 @@ */ import { DomainEntity } from '@backstage/catalog-model'; -import { render } from '@testing-library/react'; +import { renderInTestApp } from '@backstage/test-utils'; import React from 'react'; -import { MemoryRouter } from 'react-router-dom'; +import { catalogEntityRouteRef } from '../../routes'; import { DomainCard } from './DomainCard'; describe('', () => { - it('renders a domain card', () => { + it('renders a domain card', async () => { const entity: DomainEntity = { apiVersion: 'backstage.io/v1alpha1', kind: 'Domain', @@ -34,9 +34,14 @@ describe('', () => { owner: 'guest', }, }; - const { getByText } = render(, { - wrapper: MemoryRouter, - }); + const { getByText } = await renderInTestApp( + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': catalogEntityRouteRef, + }, + }, + ); expect(getByText('artists')).toBeInTheDocument(); expect(getByText('Everything about artists')).toBeInTheDocument(); diff --git a/plugins/explore/src/components/DomainCard/DomainCard.tsx b/plugins/explore/src/components/DomainCard/DomainCard.tsx index e5efc543b2..67bee13696 100644 --- a/plugins/explore/src/components/DomainCard/DomainCard.tsx +++ b/plugins/explore/src/components/DomainCard/DomainCard.tsx @@ -14,15 +14,14 @@ * limitations under the License. */ import { DomainEntity, RELATION_OWNED_BY } from '@backstage/catalog-model'; -import { ItemCard } from '@backstage/core'; +import { ItemCard, useRouteRef } from '@backstage/core'; import { EntityRefLinks, - entityRoute, entityRouteParams, getEntityRelations, } from '@backstage/plugin-catalog-react'; import React from 'react'; -import { generatePath } from 'react-router-dom'; +import { catalogEntityRouteRef } from '../../routes'; type DomainCardProps = { entity: DomainEntity; @@ -30,6 +29,7 @@ type DomainCardProps = { export const DomainCard = ({ entity }: DomainCardProps) => { const ownedByRelations = getEntityRelations(entity, RELATION_OWNED_BY); + const catalogEntityRoute = useRouteRef(catalogEntityRouteRef); return ( { /> } label="Explore" - // TODO: Use useRouteRef here to generate the path - href={generatePath( - `/catalog/${entityRoute.path}`, - entityRouteParams(entity), - )} + href={catalogEntityRoute(entityRouteParams(entity))} /> ); }; diff --git a/plugins/explore/src/components/DomainCard/DomainCardGrid.test.tsx b/plugins/explore/src/components/DomainCard/DomainCardGrid.test.tsx index bad3ca581b..ee06a50427 100644 --- a/plugins/explore/src/components/DomainCard/DomainCardGrid.test.tsx +++ b/plugins/explore/src/components/DomainCard/DomainCardGrid.test.tsx @@ -15,13 +15,13 @@ */ import { DomainEntity } from '@backstage/catalog-model'; -import { render } from '@testing-library/react'; +import { renderInTestApp } from '@backstage/test-utils'; import React from 'react'; -import { MemoryRouter } from 'react-router-dom'; +import { catalogEntityRouteRef } from '../../routes'; import { DomainCardGrid } from './DomainCardGrid'; describe('', () => { - it('renders a grid of domain cards', () => { + it('renders a grid of domain cards', async () => { const entities: DomainEntity[] = [ { apiVersion: 'backstage.io/v1alpha1', @@ -44,9 +44,14 @@ describe('', () => { }, }, ]; - const { getByText } = render(, { - wrapper: MemoryRouter, - }); + const { getByText } = await renderInTestApp( + , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': catalogEntityRouteRef, + }, + }, + ); expect(getByText('artists')).toBeInTheDocument(); expect(getByText('playback')).toBeInTheDocument(); diff --git a/plugins/explore/src/components/DomainExplorerContent/DomainExplorerContent.test.tsx b/plugins/explore/src/components/DomainExplorerContent/DomainExplorerContent.test.tsx index f29ad2cd6a..cd3f2847c6 100644 --- a/plugins/explore/src/components/DomainExplorerContent/DomainExplorerContent.test.tsx +++ b/plugins/explore/src/components/DomainExplorerContent/DomainExplorerContent.test.tsx @@ -20,6 +20,7 @@ import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { renderInTestApp } from '@backstage/test-utils'; import { waitFor } from '@testing-library/react'; import React from 'react'; +import { catalogEntityRouteRef } from '../../routes'; import { DomainExplorerContent } from './DomainExplorerContent'; describe('', () => { @@ -71,6 +72,11 @@ describe('', () => { , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': catalogEntityRouteRef, + }, + }, ); await waitFor(() => { @@ -86,6 +92,11 @@ describe('', () => { , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': catalogEntityRouteRef, + }, + }, ); await waitFor(() => @@ -101,6 +112,11 @@ describe('', () => { , + { + mountedRoutes: { + '/catalog/:namespace/:kind/:name': catalogEntityRouteRef, + }, + }, ); await waitFor(() => diff --git a/plugins/explore/src/plugin.ts b/plugins/explore/src/plugin.ts index 27573eaeb9..c5dd6644e7 100644 --- a/plugins/explore/src/plugin.ts +++ b/plugins/explore/src/plugin.ts @@ -16,7 +16,7 @@ import { createApiFactory, createPlugin } from '@backstage/core'; import { exploreToolsConfigRef } from '@backstage/plugin-explore-react'; -import { exploreRouteRef } from './routes'; +import { catalogEntityRouteRef, exploreRouteRef } from './routes'; import { exampleTools } from './util/examples'; export const explorePlugin = createPlugin({ @@ -37,4 +37,7 @@ export const explorePlugin = createPlugin({ routes: { explore: exploreRouteRef, }, + externalRoutes: { + catalogEntity: catalogEntityRouteRef, + }, }); diff --git a/plugins/explore/src/routes.ts b/plugins/explore/src/routes.ts index c41a53128f..fc7868c4c9 100644 --- a/plugins/explore/src/routes.ts +++ b/plugins/explore/src/routes.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createRouteRef } from '@backstage/core'; +import { createExternalRouteRef, createRouteRef } from '@backstage/core'; const NoIcon = () => null; @@ -22,3 +22,8 @@ export const exploreRouteRef = createRouteRef({ icon: NoIcon, title: 'Explore', }); + +export const catalogEntityRouteRef = createExternalRouteRef({ + id: 'catalog-entity', + params: ['namespace', 'kind', 'name'], +});