diff --git a/.changeset/long-socks-fold.md b/.changeset/long-socks-fold.md new file mode 100644 index 0000000000..23a7811685 --- /dev/null +++ b/.changeset/long-socks-fold.md @@ -0,0 +1,8 @@ +--- +'@backstage/integration-react': patch +'@backstage/plugin-catalog': patch +--- + +Move `ScmIntegrationIcon` from `@backstage/plugin-catalog` to +`@backstage/integration-react` and make it customizable using +`app.getSystemIcon()`. diff --git a/.changeset/nine-hornets-wave.md b/.changeset/nine-hornets-wave.md new file mode 100644 index 0000000000..7eab7fadc5 --- /dev/null +++ b/.changeset/nine-hornets-wave.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Provide a link to the template source on the `TemplateCard`. diff --git a/.changeset/pretty-dryers-turn.md b/.changeset/pretty-dryers-turn.md new file mode 100644 index 0000000000..9b80d8f6b7 --- /dev/null +++ b/.changeset/pretty-dryers-turn.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-catalog': patch +'@backstage/plugin-catalog-react': patch +--- + +Expose `getEntitySourceLocation`, `getEntityMetadataViewUrl`, and +`getEntityMetadataEditUrl` from `@backstage/plugin-catalog-react`. diff --git a/packages/integration-react/api-report.md b/packages/integration-react/api-report.md index 7c37ab0392..63a538213c 100644 --- a/packages/integration-react/api-report.md +++ b/packages/integration-react/api-report.md @@ -8,6 +8,11 @@ import { ApiRef } from '@backstage/core'; import { Config } from '@backstage/config'; import { ScmIntegrationRegistry } from '@backstage/integration'; +// @public (undocumented) +export const ScmIntegrationIcon: ({ type }: { + type?: string | undefined; +}) => JSX.Element; + // @public (undocumented) export class ScmIntegrationsApi { // (undocumented) diff --git a/packages/integration-react/src/ScmIntegrationsApi.test.ts b/packages/integration-react/src/api/ScmIntegrationsApi.test.ts similarity index 100% rename from packages/integration-react/src/ScmIntegrationsApi.test.ts rename to packages/integration-react/src/api/ScmIntegrationsApi.test.ts diff --git a/packages/integration-react/src/ScmIntegrationsApi.ts b/packages/integration-react/src/api/ScmIntegrationsApi.ts similarity index 100% rename from packages/integration-react/src/ScmIntegrationsApi.ts rename to packages/integration-react/src/api/ScmIntegrationsApi.ts diff --git a/packages/integration-react/src/api/index.ts b/packages/integration-react/src/api/index.ts new file mode 100644 index 0000000000..5483765b2e --- /dev/null +++ b/packages/integration-react/src/api/index.ts @@ -0,0 +1,20 @@ +/* + * 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 { + ScmIntegrationsApi, + scmIntegrationsApiRef, +} from './ScmIntegrationsApi'; diff --git a/packages/integration-react/src/components/ScmIntegrationIcon/ScmIntegrationIcon.test.tsx b/packages/integration-react/src/components/ScmIntegrationIcon/ScmIntegrationIcon.test.tsx new file mode 100644 index 0000000000..13ffd61264 --- /dev/null +++ b/packages/integration-react/src/components/ScmIntegrationIcon/ScmIntegrationIcon.test.tsx @@ -0,0 +1,34 @@ +/* + * 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 { renderInTestApp } from '@backstage/test-utils'; +import React from 'react'; +import { ScmIntegrationIcon } from './ScmIntegrationIcon'; + +describe('', () => { + it('renders without exploding (github)', async () => { + const { baseElement } = await renderInTestApp( + , + ); + expect(baseElement.querySelector('svg')).toBeInTheDocument(); + }); + + it('renders without exploding (unknown)', async () => { + const { baseElement } = await renderInTestApp( + , + ); + expect(baseElement.querySelector('svg')).toBeInTheDocument(); + }); +}); diff --git a/plugins/catalog/src/components/AboutCard/ScmIntegrationIcon.tsx b/packages/integration-react/src/components/ScmIntegrationIcon/ScmIntegrationIcon.tsx similarity index 64% rename from plugins/catalog/src/components/AboutCard/ScmIntegrationIcon.tsx rename to packages/integration-react/src/components/ScmIntegrationIcon/ScmIntegrationIcon.tsx index c96401135e..99745f977e 100644 --- a/plugins/catalog/src/components/AboutCard/ScmIntegrationIcon.tsx +++ b/packages/integration-react/src/components/ScmIntegrationIcon/ScmIntegrationIcon.tsx @@ -13,19 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { useApp } from '@backstage/core'; import CodeIcon from '@material-ui/icons/Code'; -import GitHubIcon from '@material-ui/icons/GitHub'; import React from 'react'; export const ScmIntegrationIcon = ({ type }: { type?: string }) => { - // TODO: In the future we might want to support more types here as a GitLab or - // Bitbucket icons were requested here in the past, or even use the icon - // customization feature of the app. But material UI react doesn't provide more. - - switch (type) { - case 'github': - return ; - default: - return ; - } + const app = useApp(); + const DefaultIcon = CodeIcon; + const Icon = type ? app.getSystemIcon(type) ?? DefaultIcon : DefaultIcon; + return ; }; diff --git a/plugins/catalog/src/utils/index.ts b/packages/integration-react/src/components/ScmIntegrationIcon/index.ts similarity index 78% rename from plugins/catalog/src/utils/index.ts rename to packages/integration-react/src/components/ScmIntegrationIcon/index.ts index bba42dac7e..1fd477c6d9 100644 --- a/plugins/catalog/src/utils/index.ts +++ b/packages/integration-react/src/components/ScmIntegrationIcon/index.ts @@ -13,8 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { - getEntityMetadataEditUrl, - getEntityMetadataViewUrl, -} from './getEntityMetadataUrl'; -export { getEntitySourceLocation } from './getEntitySourceLocation'; + +export { ScmIntegrationIcon } from './ScmIntegrationIcon'; diff --git a/packages/integration-react/src/components/index.ts b/packages/integration-react/src/components/index.ts new file mode 100644 index 0000000000..895df5c3b8 --- /dev/null +++ b/packages/integration-react/src/components/index.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +export * from './ScmIntegrationIcon'; diff --git a/packages/integration-react/src/index.ts b/packages/integration-react/src/index.ts index 5483765b2e..bb1addc8b8 100644 --- a/packages/integration-react/src/index.ts +++ b/packages/integration-react/src/index.ts @@ -14,7 +14,5 @@ * limitations under the License. */ -export { - ScmIntegrationsApi, - scmIntegrationsApiRef, -} from './ScmIntegrationsApi'; +export * from './api'; +export * from './components'; diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index eb01aba8af..8d7bc1f5d1 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -32,6 +32,7 @@ "@backstage/catalog-model": "^0.8.1", "@backstage/core": "^0.7.12", "@backstage/core-plugin-api": "^0.1.1", + "@backstage/integration": "^0.5.0", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", diff --git a/plugins/catalog/src/utils/getEntityMetadataUrl.ts b/plugins/catalog-react/src/utils/getEntityMetadataUrl.ts similarity index 100% rename from plugins/catalog/src/utils/getEntityMetadataUrl.ts rename to plugins/catalog-react/src/utils/getEntityMetadataUrl.ts diff --git a/plugins/catalog/src/utils/getEntitySourceLocation.ts b/plugins/catalog-react/src/utils/getEntitySourceLocation.ts similarity index 100% rename from plugins/catalog/src/utils/getEntitySourceLocation.ts rename to plugins/catalog-react/src/utils/getEntitySourceLocation.ts diff --git a/plugins/catalog-react/src/utils/index.ts b/plugins/catalog-react/src/utils/index.ts index 8d045e08ac..c91ef3c3e2 100644 --- a/plugins/catalog-react/src/utils/index.ts +++ b/plugins/catalog-react/src/utils/index.ts @@ -14,5 +14,11 @@ * limitations under the License. */ export * from './filters'; +export { + getEntityMetadataEditUrl, + getEntityMetadataViewUrl, +} from './getEntityMetadataUrl'; export { getEntityRelations } from './getEntityRelations'; +export { getEntitySourceLocation } from './getEntitySourceLocation'; +export type { EntitySourceLocation } from './getEntitySourceLocation'; export { isOwnerOf } from './isOwnerOf'; diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.tsx index 2bcf2a5453..3e8aec3e73 100644 --- a/plugins/catalog/src/components/AboutCard/AboutCard.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutCard.tsx @@ -26,8 +26,16 @@ import { InfoCardVariants, useApi, } from '@backstage/core'; -import { scmIntegrationsApiRef } from '@backstage/integration-react'; -import { getEntityRelations, useEntity } from '@backstage/plugin-catalog-react'; +import { + ScmIntegrationIcon, + scmIntegrationsApiRef, +} from '@backstage/integration-react'; +import { + getEntityMetadataEditUrl, + getEntityRelations, + getEntitySourceLocation, + useEntity, +} from '@backstage/plugin-catalog-react'; import { Card, CardContent, @@ -40,9 +48,7 @@ import DocsIcon from '@material-ui/icons/Description'; import EditIcon from '@material-ui/icons/Edit'; import ExtensionIcon from '@material-ui/icons/Extension'; import React from 'react'; -import { getEntityMetadataEditUrl, getEntitySourceLocation } from '../../utils'; import { AboutContent } from './AboutContent'; -import { ScmIntegrationIcon } from './ScmIntegrationIcon'; const useStyles = makeStyles({ gridItemCard: { diff --git a/plugins/catalog/src/components/AboutCard/index.ts b/plugins/catalog/src/components/AboutCard/index.ts index 262f0bedd3..8b7e5a1a81 100644 --- a/plugins/catalog/src/components/AboutCard/index.ts +++ b/plugins/catalog/src/components/AboutCard/index.ts @@ -17,4 +17,3 @@ export { AboutCard } from './AboutCard'; export { AboutContent } from './AboutContent'; export { AboutField } from './AboutField'; -export { ScmIntegrationIcon } from './ScmIntegrationIcon'; diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index b42dbe02b7..6d9f0c21bd 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -23,6 +23,8 @@ import { } from '@backstage/core'; import { formatEntityRefTitle, + getEntityMetadataEditUrl, + getEntityMetadataViewUrl, getEntityRelations, useEntityListProvider, useStarredEntities, @@ -31,10 +33,6 @@ import Edit from '@material-ui/icons/Edit'; import OpenInNew from '@material-ui/icons/OpenInNew'; import { capitalize } from 'lodash'; import React from 'react'; -import { - getEntityMetadataEditUrl, - getEntityMetadataViewUrl, -} from '../../utils'; import { favouriteEntityIcon, favouriteEntityTooltip, diff --git a/plugins/catalog/src/index.ts b/plugins/catalog/src/index.ts index c0cf0787c9..0958503077 100644 --- a/plugins/catalog/src/index.ts +++ b/plugins/catalog/src/index.ts @@ -14,15 +14,14 @@ * limitations under the License. */ -export { AboutCard } from './components/AboutCard'; +export * from './components/AboutCard'; export { CatalogResultListItem } from './components/CatalogResultListItem'; -export { EntityLayout } from './components/EntityLayout'; -export { EntityPageLayout } from './components/EntityPageLayout'; export { CatalogTable } from './components/CatalogTable'; +export { EntityLayout } from './components/EntityLayout'; +export * from './components/EntityOrphanWarning'; +export { EntityPageLayout } from './components/EntityPageLayout'; export * from './components/EntitySwitch'; export { Router } from './components/Router'; -export * from './components/EntityOrphanWarning'; -export * from './components/AboutCard'; export { CatalogEntityPage, CatalogIndexPage, @@ -39,4 +38,3 @@ export { EntityLinksCard, EntitySystemDiagramCard, } from './plugin'; -export * from './utils'; diff --git a/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx b/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx index 7e34c0d868..db0d583cb8 100644 --- a/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx +++ b/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx @@ -13,7 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Button, ItemCardHeader, useRouteRef } from '@backstage/core'; +import { + Entity, + RELATION_OWNED_BY, + TemplateEntityV1alpha1, +} from '@backstage/catalog-model'; +import { Button, ItemCardHeader, useApi, useRouteRef } from '@backstage/core'; +import { + ScmIntegrationIcon, + scmIntegrationsApiRef, +} from '@backstage/integration-react'; +import { + EntityRefLinks, + getEntityRelations, + getEntitySourceLocation, +} from '@backstage/plugin-catalog-react'; import { BackstageTheme, pageTheme } from '@backstage/theme'; import { Box, @@ -22,26 +36,18 @@ import { CardContent, CardMedia, Chip, + IconButton, Link, makeStyles, Tooltip, Typography, useTheme, } from '@material-ui/core'; -import React from 'react'; import WarningIcon from '@material-ui/icons/Warning'; +import React from 'react'; import { generatePath } from 'react-router'; import { rootRouteRef } from '../../routes'; -import { - TemplateEntityV1alpha1, - Entity, - RELATION_OWNED_BY, -} from '@backstage/catalog-model'; import { FavouriteTemplate } from '../FavouriteTemplate/FavouriteTemplate'; -import { - getEntityRelations, - EntityRefLinks, -} from '@backstage/plugin-catalog-react'; const useStyles = makeStyles(theme => ({ cardHeader: { @@ -67,6 +73,9 @@ const useStyles = makeStyles(theme => ({ lineHeight: 1, paddingBottom: '0.2rem', }, + leftButton: { + marginRight: 'auto', + }, })); const useDeprecationStyles = makeStyles(theme => ({ @@ -145,6 +154,9 @@ export const TemplateCard = ({ template, deprecated }: TemplateCardProps) => { templateName: templateProps.name, }); + const scmIntegrationsApi = useApi(scmIntegrationsApiRef); + const sourceLocation = getEntitySourceLocation(template, scmIntegrationsApi); + return ( @@ -179,6 +191,14 @@ export const TemplateCard = ({ template, deprecated }: TemplateCardProps) => { + {sourceLocation && ( + + + + )}