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 && (
+
+
+
+ )}