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 (