diff --git a/.changeset/nervous-starfishes-report.md b/.changeset/nervous-starfishes-report.md new file mode 100644 index 0000000000..2ac4179a2c --- /dev/null +++ b/.changeset/nervous-starfishes-report.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': patch +--- + +Adds two new home components - CompanyLogo and Toolkit. diff --git a/docs/getting-started/app-custom-theme.md b/docs/getting-started/app-custom-theme.md index b9b74f92dd..d05a14cc35 100644 --- a/docs/getting-started/app-custom-theme.md +++ b/docs/getting-started/app-custom-theme.md @@ -253,3 +253,73 @@ const LogoFull = () => { return ; }; ``` + +## Custom Homepage + +In addition to a custom theme, a custom logo, you can also customize the +homepage of your app. To do that we need to go through a few steps. + +### Setting up the Home Page + +1. Create a Home Page Component that will be used for composition. + +`packages/app/src/components/home/HomePage.tsx` + +```tsx +import React from 'react'; + +export const HomePage = () => { + return { + /* TODO: Compose a Home Page here */ + }; +}; +``` + +2. Add a route where the homepage will live, presumably `/`. + +`packages/app/src/App.tsx` + +```tsx +import { HomepageCompositionRoot } from '@backstage/plugin-home'; +import { HomePage } from './components/home/HomePage'; + +// ... +}> + +; +// ... +``` + +### Composing your Home Page + +Composing a Home Page is no different from creating a regular React Component, +i.e. the App Integrator is free to include whatever content they like. However, +there are components developed with the Home Page in mind. If you are looking +for components to use when composing your homepage, you can take a look at the +[collection of Homepage components](https://backstage.io/?path=/story/plugins-home-components) +in storybook. If you don't find a component that suits your needs but want to +contribute, check the +[Contributing documentation](https://github.com/backstage/backstage/blob/master/plugins/home/README.md#contributing). + +!!! tip If you want to use one of the available homepage templates you can find +the +[templates](https://backstage.io/storybook/?path=/story/plugins-home-templates) +in the storybook under the "Home" plugin. And if you would like to contribute a +template, please see the +[Contributing documentation](https://github.com/backstage/backstage/blob/master/plugins/home/README.md#contributing) + +```tsx +import React from 'react'; +import Grid from '@material-ui/core/Grid'; +import { HomePageCompanyLogo } from '@backstage/plugin-home'; + +export const HomePage = () => { + return ( + + + + + + ); +}; +``` diff --git a/packages/storybook/.storybook/main.js b/packages/storybook/.storybook/main.js index f5852b0164..6e0a2b92fa 100644 --- a/packages/storybook/.storybook/main.js +++ b/packages/storybook/.storybook/main.js @@ -8,6 +8,7 @@ const BACKSTAGE_CORE_STORIES = [ 'packages/core-components', 'plugins/org', 'plugins/search', + 'plugins/home', ]; module.exports = ({ args }) => { diff --git a/plugins/home/README.md b/plugins/home/README.md index a09c85775b..b3b492be8e 100644 --- a/plugins/home/README.md +++ b/plugins/home/README.md @@ -1,9 +1,5 @@ # Home -Development is ongoing. You can follow the progress and contribute at the Backstage [Home Project Board](https://github.com/backstage/backstage/projects/7) or reach out to us in the [`#support` Discord channel](https://discord.com/channels/687207715902193673/687235481154617364). - -## Overview - The Home plugin introduces a system for composing a Home Page for Backstage in order to surface relevant info and provide convenient shortcuts for common tasks. It's designed with composability in mind with an open ecosystem that allows anyone to contribute with any component, to be included in any Home Page. For App Integrators, the system is designed to be composable to give total freedom in designing a Home Page that suits the needs of the organization. From the perspective of a Component Developer who wishes to contribute with building blocks to be included in Home Pages, there's a convenient interface for bundling the different parts and exporting them with both error boundary and lazy loading handled under the surface. @@ -90,6 +86,12 @@ Additionally, the App Integrator is provided an escape hatch in case the way the ## Contributing +### Homepage Components + We believe that people have great ideas for what makes a useful Home Page, and we want to make it easy for every to benefit from the effort you put in to create something cool for the Home Page. Therefore, a great way of contributing is by simply creating more Home Page Components, than can then be used by everyone when composing their own Home Page. If they are tightly coupled to an existing plugin, it is recommended to allow them to live within that plugin, for convenience and to limit complex dependencies. On the other hand, if there's no clear plugin that the component is based on, it's also fine to contribute them into the [home plugin](/plugins/home/src/homePageComponents) Additionally, the API is at a very early state, so contributing with additional use cases may expose weaknesses in the current solution that we may iterate on, to provide more flexibility and ease of use for those who wish to develop components for the Home Page. + +### Homepage Templates + +We are hoping that we together can build up a collection of Homepage templates. We therefore put together a place where we can collect all the templates for the Home Plugin in the [storybook](https://backstage.io/storybook/?path=/story/plugins-home-templates). If you would like to contribute with a template, start by taking a look at the [DefaultTemplate storybook example to create your own](/plugins/home/src/templates/DefaultTemplate.stories.tsx), and then open a PR with your suggestion. diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index 99d88fa4b5..fab2b05bb6 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -7,6 +7,7 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { Extension } from '@backstage/core-plugin-api'; +import { default as React_2 } from 'react'; import { ReactNode } from 'react'; import { RouteRef } from '@backstage/core-plugin-api'; @@ -64,10 +65,9 @@ export const ComponentTabs: ({ }[]; }) => JSX.Element; -// Warning: (ae-forgotten-export) The symbol "ComponentRenderer" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "createCardExtension" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-forgotten-export) The symbol "CardExtensionProps" needs to be exported by the entry point index.d.ts // -// @public (undocumented) +// @public export function createCardExtension({ title, components, @@ -76,15 +76,7 @@ export function createCardExtension({ title: string; components: () => Promise; name?: string; -}): Extension< - ({ - Renderer, - title: overrideTitle, - ...childProps - }: ComponentRenderer & { - title?: string; - } & T) => JSX.Element ->; +}): Extension<(props: CardExtensionProps) => JSX.Element>; // Warning: (ae-missing-release-tag) "HeaderWorldClock" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -95,6 +87,12 @@ export const HeaderWorldClock: ({ clockConfigs: ClockConfig[]; }) => JSX.Element | null; +// @public +export const HomePageCompanyLogo: (props: { + logo?: ReactNode; + className?: string | undefined; +}) => JSX.Element; + // Warning: (ae-missing-release-tag) "HomepageCompositionRoot" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -103,18 +101,26 @@ export const HomepageCompositionRoot: (props: { children?: ReactNode; }) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "ComponentRenderer" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "HomePageRandomJoke" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const HomePageRandomJoke: ({ - Renderer, - title: overrideTitle, - ...childProps -}: ComponentRenderer & { - title?: string | undefined; -} & { - defaultCategory?: 'any' | 'programming' | undefined; -}) => JSX.Element; +export const HomePageRandomJoke: ( + props: ComponentRenderer & { + title?: string | undefined; + } & { + defaultCategory?: 'any' | 'programming' | undefined; + }, +) => JSX.Element; + +// Warning: (ae-forgotten-export) The symbol "ToolkitContentProps" needs to be exported by the entry point index.d.ts +// +// @public +export const HomePageToolkit: ( + props: ComponentRenderer & { + title?: string | undefined; + } & ToolkitContentProps, +) => JSX.Element; // Warning: (ae-missing-release-tag) "homePlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -146,5 +152,5 @@ export const WelcomeTitle: () => JSX.Element; // Warnings were encountered during analysis: // -// src/extensions.d.ts:16:5 - (ae-forgotten-export) The symbol "ComponentParts" needs to be exported by the entry point index.d.ts +// src/extensions.d.ts:24:5 - (ae-forgotten-export) The symbol "ComponentParts" needs to be exported by the entry point index.d.ts ``` diff --git a/plugins/home/package.json b/plugins/home/package.json index 8b2f829efa..298b1860e9 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -24,6 +24,7 @@ "@backstage/core-components": "^0.8.3", "@backstage/core-plugin-api": "^0.4.1", "@backstage/theme": "^0.2.14", + "@backstage/plugin-search": "^0.5.1", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", diff --git a/plugins/home/src/extensions.tsx b/plugins/home/src/extensions.tsx index 82394cf1ea..4a234f9813 100644 --- a/plugins/home/src/extensions.tsx +++ b/plugins/home/src/extensions.tsx @@ -26,7 +26,7 @@ export type ComponentRenderer = { }; type ComponentParts = { - Content: () => JSX.Element; + Content: (props?: any) => JSX.Element; Actions?: () => JSX.Element; Settings?: () => JSX.Element; ContextProvider?: (props: any) => JSX.Element; @@ -34,6 +34,13 @@ type ComponentParts = { type RendererProps = { title: string } & ComponentParts; +type CardExtensionProps = ComponentRenderer & { title?: string } & T; + +/** + * An extension creator to create card based components for the homepage + * + * @public + */ export function createCardExtension({ title, components, @@ -48,11 +55,8 @@ export function createCardExtension({ component: { lazy: () => components().then(({ Content, Actions, Settings, ContextProvider }) => { - const CardExtension = ({ - Renderer, - title: overrideTitle, - ...childProps - }: ComponentRenderer & { title?: string } & T) => { + const CardExtension = (props: CardExtensionProps) => { + const { Renderer, title: overrideTitle, ...childProps } = props; const app = useApp(); const { Progress } = app.getComponents(); const [settingsOpen, setSettingsOpen] = React.useState(false); @@ -103,7 +107,7 @@ export function createCardExtension({ )} - + ); diff --git a/plugins/home/src/homePageComponents/CompanyLogo/CompanyLogo.stories.tsx b/plugins/home/src/homePageComponents/CompanyLogo/CompanyLogo.stories.tsx new file mode 100644 index 0000000000..ab71eea54e --- /dev/null +++ b/plugins/home/src/homePageComponents/CompanyLogo/CompanyLogo.stories.tsx @@ -0,0 +1,79 @@ +/* + * Copyright 2021 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 { TemplateBackstageLogo } from '../../templates'; +import { HomePageCompanyLogo } from '../../plugin'; +import { rootRouteRef } from '../../routes'; +import { wrapInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { configApiRef } from '@backstage/core-plugin-api'; +import { ConfigReader } from '@backstage/core-app-api'; +import { Grid, makeStyles } from '@material-ui/core'; +import React, { ComponentType } from 'react'; + +export default { + title: 'Plugins/Home/Components/CompanyLogo', + decorators: [ + (Story: ComponentType<{}>) => + wrapInTestApp( + + + , + { + mountedRoutes: { '/hello-company-logo': rootRouteRef }, + }, + ), + ], +}; + +const useLogoStyles = makeStyles(theme => ({ + container: { + margin: theme.spacing(5, 0), + }, + svg: { + width: 'auto', + height: 100, + }, + path: { + fill: '#7df3e1', + }, +})); + +export const Default = () => { + const { container } = useLogoStyles(); + + return ( + + + + ); +}; + +export const CustomLogo = () => { + const { container, svg, path } = useLogoStyles(); + + return ( + + } + /> + + ); +}; diff --git a/plugins/home/src/homePageComponents/CompanyLogo/CompanyLogo.test.tsx b/plugins/home/src/homePageComponents/CompanyLogo/CompanyLogo.test.tsx new file mode 100644 index 0000000000..3cd08bc70b --- /dev/null +++ b/plugins/home/src/homePageComponents/CompanyLogo/CompanyLogo.test.tsx @@ -0,0 +1,43 @@ +/* + * Copyright 2021 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 { CompanyLogo } from './CompanyLogo'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { configApiRef } from '@backstage/core-plugin-api'; +import { ConfigReader } from '@backstage/core-app-api'; +import { Typography } from '@material-ui/core'; +import React from 'react'; + +describe('', () => { + it('should have a fall back if logo is not provided', async () => { + const { getByRole } = await renderInTestApp( + + + , + ); + + expect(getByRole('heading', { name: 'My App' })).toBeInTheDocument(); + }); + + it('should show provided company logo', async () => { + const { getByRole } = await renderInTestApp( + Backstage} />, + ); + + expect(getByRole('heading', { name: 'Backstage' })).toBeInTheDocument(); + }); +}); diff --git a/plugins/home/src/homePageComponents/CompanyLogo/CompanyLogo.tsx b/plugins/home/src/homePageComponents/CompanyLogo/CompanyLogo.tsx new file mode 100644 index 0000000000..998c2d46b1 --- /dev/null +++ b/plugins/home/src/homePageComponents/CompanyLogo/CompanyLogo.tsx @@ -0,0 +1,43 @@ +/* + * Copyright 2021 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 { Typography } from '@material-ui/core'; +import { configApiRef, useApi } from '@backstage/core-plugin-api'; +import React from 'react'; + +type CompanyLogoProps = { + logo?: React.ReactNode; + className?: string; +}; + +/** + * A component to display a company logo for the user. + * + * @public + */ +export const CompanyLogo = (props: CompanyLogoProps) => { + const { logo, className } = props; + const configApi = useApi(configApiRef); + + return ( +
+ {logo ? ( + <>{logo} + ) : ( + {configApi.getString('app.title')} + )} +
+ ); +}; diff --git a/plugins/home/src/homePageComponents/CompanyLogo/index.ts b/plugins/home/src/homePageComponents/CompanyLogo/index.ts new file mode 100644 index 0000000000..50a8869720 --- /dev/null +++ b/plugins/home/src/homePageComponents/CompanyLogo/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2021 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. + */ +export { CompanyLogo } from './CompanyLogo'; diff --git a/plugins/home/src/homePageComponents/Toolkit/Content.test.tsx b/plugins/home/src/homePageComponents/Toolkit/Content.test.tsx new file mode 100644 index 0000000000..7fbaff08f9 --- /dev/null +++ b/plugins/home/src/homePageComponents/Toolkit/Content.test.tsx @@ -0,0 +1,36 @@ +/* + * Copyright 2021 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 { renderInTestApp } from '@backstage/test-utils'; +import React from 'react'; +import { Content } from './Content'; + +describe('', () => { + test('should render list of tools', async () => { + const { getByText } = await renderInTestApp( + icon }, + { label: 'tool 2', url: '/url-2', icon:
icon 2
}, + ]} + />, + ); + + expect(getByText('tool')).toBeInTheDocument(); + expect(getByText('tool 2')).toBeInTheDocument(); + expect(getByText('tool').closest('a')).toHaveAttribute('href', '/url'); + expect(getByText('tool 2').closest('a')).toHaveAttribute('href', '/url-2'); + }); +}); diff --git a/plugins/home/src/homePageComponents/Toolkit/Content.tsx b/plugins/home/src/homePageComponents/Toolkit/Content.tsx new file mode 100644 index 0000000000..4ee1884bd0 --- /dev/null +++ b/plugins/home/src/homePageComponents/Toolkit/Content.tsx @@ -0,0 +1,88 @@ +/* + * Copyright 2021 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 { Link } from '@backstage/core-components'; +import { + makeStyles, + List, + ListItemIcon, + ListItemText, +} from '@material-ui/core'; +import React from 'react'; + +const useStyles = makeStyles(theme => ({ + toolkit: { + display: 'flex', + flexWrap: 'wrap', + textAlign: 'center', + }, + tool: { + margin: theme.spacing(0.5, 1), + }, + label: { + marginTop: theme.spacing(1), + fontSize: '0.9em', + lineHeight: '1.25', + color: theme.palette.text.secondary, + }, + icon: { + width: '64px', + height: '64px', + borderRadius: '50px', + justifyContent: 'center', + alignItems: 'center', + boxShadow: theme.shadows[1], + backgroundColor: theme.palette.background.default, + }, +})); + +type Tool = { + label: string; + url: string; + icon: React.ReactNode; +}; + +/** + * Props for Toolkit content component {@link Content}. + * + * @public + */ +export type ToolkitContentProps = { + tools: Tool[]; +}; + +/** + * A component to display a list of tools for the user. + * + * @public + */ +export const Content = (props: ToolkitContentProps) => { + const classes = useStyles(); + + return ( + + {props.tools.map((tool: Tool) => ( + + {tool.icon} + + + ))} + + ); +}; diff --git a/plugins/home/src/homePageComponents/Toolkit/Toolkit.stories.tsx b/plugins/home/src/homePageComponents/Toolkit/Toolkit.stories.tsx new file mode 100644 index 0000000000..0a8e6fffbe --- /dev/null +++ b/plugins/home/src/homePageComponents/Toolkit/Toolkit.stories.tsx @@ -0,0 +1,40 @@ +/* + * Copyright 2021 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 { TemplateBackstageLogoIcon } from '../../templates'; +import { HomePageToolkit } from '../../plugin'; +import { wrapInTestApp } from '@backstage/test-utils'; +import { Grid } from '@material-ui/core'; +import React, { ComponentType } from 'react'; + +export default { + title: 'Plugins/Home/Components/Toolkit', + decorators: [(Story: ComponentType<{}>) => wrapInTestApp()], +}; + +export const Default = () => { + return ( + + , + })} + /> + + ); +}; diff --git a/plugins/home/src/homePageComponents/Toolkit/index.ts b/plugins/home/src/homePageComponents/Toolkit/index.ts new file mode 100644 index 0000000000..98e0869825 --- /dev/null +++ b/plugins/home/src/homePageComponents/Toolkit/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 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. + */ + +export { Content } from './Content'; +export type { ToolkitContentProps } from './Content'; diff --git a/plugins/home/src/homePageComponents/index.ts b/plugins/home/src/homePageComponents/index.ts new file mode 100644 index 0000000000..4d2802fb98 --- /dev/null +++ b/plugins/home/src/homePageComponents/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 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. + */ + +export * from './CompanyLogo'; +export * from './Toolkit'; diff --git a/plugins/home/src/index.ts b/plugins/home/src/index.ts index a8b8e7ab14..dcd61f1fa3 100644 --- a/plugins/home/src/index.ts +++ b/plugins/home/src/index.ts @@ -24,6 +24,8 @@ export { homePlugin, HomepageCompositionRoot, HomePageRandomJoke, + HomePageToolkit, + HomePageCompanyLogo, ComponentAccordion, ComponentTabs, ComponentTab, diff --git a/plugins/home/src/plugin.ts b/plugins/home/src/plugin.ts index b4b9f9b6ee..135ac97af0 100644 --- a/plugins/home/src/plugin.ts +++ b/plugins/home/src/plugin.ts @@ -19,6 +19,7 @@ import { createRoutableExtension, } from '@backstage/core-plugin-api'; import { createCardExtension } from './extensions'; +import { ToolkitContentProps } from './homePageComponents'; import { rootRouteRef } from './routes'; @@ -79,6 +80,21 @@ export const WelcomeTitle = homePlugin.provide( }), ); +/** + * A component to display a company logo for the user. + * + * @public + */ +export const HomePageCompanyLogo = homePlugin.provide( + createComponentExtension({ + name: 'CompanyLogo', + component: { + lazy: () => + import('./homePageComponents/CompanyLogo').then(m => m.CompanyLogo), + }, + }), +); + export const HomePageRandomJoke = homePlugin.provide( createCardExtension<{ defaultCategory?: 'any' | 'programming' }>({ name: 'HomePageRandomJoke', @@ -86,3 +102,16 @@ export const HomePageRandomJoke = homePlugin.provide( components: () => import('./homePageComponents/RandomJoke'), }), ); + +/** + * A component to display a list of tools for the user. + * + * @public + */ +export const HomePageToolkit = homePlugin.provide( + createCardExtension({ + name: 'HomePageToolkit', + title: 'Toolkit', + components: () => import('./homePageComponents/Toolkit'), + }), +); diff --git a/plugins/home/src/templates/DefaultTemplate.stories.tsx b/plugins/home/src/templates/DefaultTemplate.stories.tsx new file mode 100644 index 0000000000..18518e47a9 --- /dev/null +++ b/plugins/home/src/templates/DefaultTemplate.stories.tsx @@ -0,0 +1,130 @@ +/* + * Copyright 2021 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 {TemplateBackstageLogo} from './TemplateBackstageLogo'; +import {TemplateBackstageLogoIcon} from './TemplateBackstageLogoIcon'; +import { HomePageToolkit, HomePageCompanyLogo } from '../plugin'; +import { wrapInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { Content, Page, InfoCard } from '@backstage/core-components'; +import { + HomePageSearchBar, + SearchContextProvider, + searchApiRef, + searchPlugin +} from '@backstage/plugin-search'; +import { Grid, makeStyles } from '@material-ui/core'; +import React, { ComponentType} from 'react'; + + +export default { + title: 'Plugins/Home/Templates', + decorators: [ + (Story: ComponentType<{}>) => + wrapInTestApp( + <> + Promise.resolve({results: []})}]]}> + + + , + { + mountedRoutes: {'/hello-company': searchPlugin.routes.root } + } + ), + ], +}; + +const useStyles = makeStyles(theme => ({ + search: { + backgroundColor: theme.palette.background.paper, + boxShadow: theme.shadows[1], + maxWidth: '60vw', + display: 'flex', + justifyContent: 'space-between', + padding: '8px 0', + borderColor: 'transparent', + borderRadius: '50px', + margin: 'auto', + }, +})); + +const useLogoStyles = makeStyles(theme => ({ + container: { + margin: theme.spacing(5, 0), + }, + svg: { + width: 'auto', + height: 100, + }, + path: { + fill: '#7df3e1', + }, +})); + +export const DefaultTemplate = () => { + const { search } = useStyles(); + const { svg, path, container } = useLogoStyles(); + + return ( + + + + + } + /> + + + + + + + {/* placeholder for content */} +
+ + + + , + })} + /> + + + + {/* placeholder for content */} +
+ + + + + {/* placeholder for content */} +
+ + + + + + + + ); +}; + diff --git a/plugins/home/src/templates/TemplateBackstageLogo.tsx b/plugins/home/src/templates/TemplateBackstageLogo.tsx new file mode 100644 index 0000000000..c5bca1f773 --- /dev/null +++ b/plugins/home/src/templates/TemplateBackstageLogo.tsx @@ -0,0 +1,37 @@ +/* + * Copyright 2020 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'; + +type Classes = { + svg: string; + path: string; +} + +export const TemplateBackstageLogo = ({ classes }: { classes: Classes }) => { + return ( + + + + ); +}; diff --git a/plugins/home/src/templates/TemplateBackstageLogoIcon.tsx b/plugins/home/src/templates/TemplateBackstageLogoIcon.tsx new file mode 100644 index 0000000000..2116b48784 --- /dev/null +++ b/plugins/home/src/templates/TemplateBackstageLogoIcon.tsx @@ -0,0 +1,46 @@ +/* + * Copyright 2020 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 } from '@material-ui/core'; + +const useStyles = makeStyles({ + svg: { + width: 'auto', + height: 28, + }, + path: { + fill: '#7df3e1', + }, +}); + +export const TemplateBackstageLogoIcon = () => { + const classes = useStyles(); + + return ( + + + + ); +}; + diff --git a/plugins/home/src/templates/index.ts b/plugins/home/src/templates/index.ts new file mode 100644 index 0000000000..bfebe73cd8 --- /dev/null +++ b/plugins/home/src/templates/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 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. + */ + +export { TemplateBackstageLogoIcon } from './TemplateBackstageLogoIcon'; +export { TemplateBackstageLogo } from './TemplateBackstageLogo' diff --git a/plugins/search/src/components/HomePageComponent/HomePageSearchBar.stories.tsx b/plugins/search/src/components/HomePageComponent/HomePageSearchBar.stories.tsx new file mode 100644 index 0000000000..48f0cbaa1e --- /dev/null +++ b/plugins/search/src/components/HomePageComponent/HomePageSearchBar.stories.tsx @@ -0,0 +1,68 @@ +/* + * Copyright 2021 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 { rootRouteRef, HomePageSearchBar } from '../../plugin'; +import { searchApiRef } from '../../apis'; +import { wrapInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { Grid, makeStyles } from '@material-ui/core'; +import React, { ComponentType } from 'react'; + +export default { + title: 'Plugins/Home/Components/SearchBar', + decorators: [ + (Story: ComponentType<{}>) => + wrapInTestApp( + <> + Promise.resolve({ results: [] }) }], + ]} + > + + + , + { + mountedRoutes: { '/hello-search': rootRouteRef }, + }, + ), + ], +}; + +const useStyles = makeStyles(theme => ({ + search: { + backgroundColor: theme.palette.background.paper, + boxShadow: theme.shadows[1], + maxWidth: '60vw', + display: 'flex', + justifyContent: 'space-between', + padding: '8px 0', + borderColor: 'transparent', + borderRadius: '50px', + margin: 'auto', + }, +})); + +export const Default = () => { + const { search } = useStyles(); + + return ( + + + + + + ); +};