diff --git a/plugins/scaffolder/src/next/TemplateListPage/CategoryPicker.tsx b/plugins/scaffolder/src/next/TemplateListPage/CategoryPicker.tsx
index 94f0df2565..483751f2e2 100644
--- a/plugins/scaffolder/src/next/TemplateListPage/CategoryPicker.tsx
+++ b/plugins/scaffolder/src/next/TemplateListPage/CategoryPicker.tsx
@@ -34,6 +34,10 @@ import { alertApiRef, useApi } from '@backstage/core-plugin-api';
const icon = ;
const checkedIcon = ;
+/**
+ * The Category Picker that is rendered on the left side for picking
+ * categories and filtering the template list.
+ */
export const CategoryPicker = () => {
const alertApi = useApi(alertApiRef);
const { error, loading, availableTypes, selectedTypes, setSelectedTypes } =
diff --git a/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/CardHeader.test.tsx b/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/CardHeader.test.tsx
new file mode 100644
index 0000000000..460a41e092
--- /dev/null
+++ b/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/CardHeader.test.tsx
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2022 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 } from '@testing-library/react';
+import { CardHeader } from './CardHeader';
+import { ThemeProvider } from '@material-ui/core';
+import { lightTheme } from '@backstage/theme';
+
+describe('CardHeader', () => {
+ it('should select the correct theme from the theme provider from the header', () => {
+ // Can't really test what we want here.
+ // But we can check that we call the getPage theme with the right type of template at least.
+ const mockTheme = {
+ ...lightTheme,
+ getPageTheme: jest.fn(lightTheme.getPageTheme),
+ };
+
+ const { container } = render(
+
+
+ ,
+ );
+
+ expect(mockTheme.getPageTheme).toHaveBeenCalledWith({ themeId: 'service' });
+ });
+});
diff --git a/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/CardHeader.tsx b/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/CardHeader.tsx
new file mode 100644
index 0000000000..da1a1cb661
--- /dev/null
+++ b/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/CardHeader.tsx
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2022 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 { makeStyles, useTheme } from '@material-ui/core';
+import { ItemCardHeader } from '@backstage/core-components';
+import { BackstageTheme } from '@backstage/theme';
+
+const useStyles = makeStyles<{}, { cardBackgroundImage: string }>(theme => ({
+ header: {
+ backgroundImage: ({ cardBackgroundImage }) => cardBackgroundImage,
+ },
+}));
+
+/**
+ * Props for the CardHeader component
+ */
+export interface CardHeaderProps {
+ type?: string;
+ title: string;
+}
+
+/**
+ * The Card Header with the background for the TemplateCard.
+ */
+export const CardHeader = (props: CardHeaderProps) => {
+ const { type = 'other', title } = props;
+ const { getPageTheme } = useTheme();
+ const themeForType = getPageTheme({ themeId: type });
+ const styles = useStyles({
+ cardBackgroundImage: themeForType.backgroundImage,
+ });
+
+ return (
+
+ );
+};
diff --git a/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/TemplateCard.tsx b/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/TemplateCard.tsx
new file mode 100644
index 0000000000..86cb5dc050
--- /dev/null
+++ b/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/TemplateCard.tsx
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 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 { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common';
+import { Card } from '@material-ui/core';
+import { CardHeader } from './CardHeader';
+/**
+ * The Props for the Template Card component
+ * @public
+ */
+export interface TemplateCardProps {
+ template: TemplateEntityV1beta3;
+ deprecated?: boolean;
+}
+
+/**
+ * The Template Card component that is rendered in a list for each template
+ * @public
+ */
+export const TemplateCard = (props: TemplateCardProps) => {
+ const { template } = props;
+ return (
+
+
+
+ );
+};
diff --git a/plugins/scaffolder/src/next/TemplateListPage/TemplateCard.tsx b/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/index.ts
similarity index 70%
rename from plugins/scaffolder/src/next/TemplateListPage/TemplateCard.tsx
rename to plugins/scaffolder/src/next/TemplateListPage/TemplateCard/index.ts
index 53ea12d4d6..738c3fc5d4 100644
--- a/plugins/scaffolder/src/next/TemplateListPage/TemplateCard.tsx
+++ b/plugins/scaffolder/src/next/TemplateListPage/TemplateCard/index.ts
@@ -13,14 +13,4 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common';
-
-export interface TemplateCardProps {
- template: TemplateEntityV1beta3;
- deprecated?: boolean;
-}
-
-export const TemplateCard = (props: TemplateCardProps) => {
- return null;
-};
+export { TemplateCard } from './TemplateCard';
diff --git a/plugins/scaffolder/src/next/TemplateListPage/TemplateGroup.test.tsx b/plugins/scaffolder/src/next/TemplateListPage/TemplateGroup.test.tsx
index 0644e23e37..59c6989c0d 100644
--- a/plugins/scaffolder/src/next/TemplateListPage/TemplateGroup.test.tsx
+++ b/plugins/scaffolder/src/next/TemplateListPage/TemplateGroup.test.tsx
@@ -109,4 +109,35 @@ describe('TemplateGroup', () => {
);
}
});
+
+ it('should render the title when no templates passed', () => {
+ const { getByText } = render();
+ expect(getByText('Test')).toBeInTheDocument();
+ });
+
+ it('should render the title when there are templates in the list', () => {
+ const mockTemplates: TemplateEntityV1beta3[] = [
+ {
+ apiVersion: 'scaffolder.backstage.io/v1beta3',
+ kind: 'Template',
+ metadata: { name: 'test' },
+ spec: { parameters: [], steps: [], type: 'website' },
+ },
+ ];
+
+ const { getByText } = render(
+ ,
+ );
+
+ expect(getByText('Test')).toBeInTheDocument();
+ });
+
+ it('should allow for passing through a user given title component', () => {
+ const TitleComponent =
Im a custom header
;
+ const { getByText } = render(
+ ,
+ );
+
+ expect(getByText('Im a custom header')).toBeInTheDocument();
+ });
});
diff --git a/plugins/scaffolder/src/next/TemplateListPage/TemplateGroup.tsx b/plugins/scaffolder/src/next/TemplateListPage/TemplateGroup.tsx
index 389ec83231..4344c195ae 100644
--- a/plugins/scaffolder/src/next/TemplateListPage/TemplateGroup.tsx
+++ b/plugins/scaffolder/src/next/TemplateListPage/TemplateGroup.tsx
@@ -35,22 +35,24 @@ export interface TemplateGroupProps {
export const TemplateGroup = (props: TemplateGroupProps) => {
const { templates, title, components: { CardComponent } = {} } = props;
+ const titleComponent =
+ typeof title === 'string' ? : title;
if (templates.length === 0) {
return (
-
- No templates found that match your filter. Learn more about{' '}
-
- adding templates
-
- .
-
+
+ {titleComponent}
+
+ No templates found that match your filter. Learn more about{' '}
+
+ adding templates
+
+ .
+
+
);
}
- const titleComponent =
- typeof title === 'string' ? : title;
-
const Card = CardComponent || TemplateCard;
return (