From 3f11ae24ab6c157def394d118a273074acb21628 Mon Sep 17 00:00:00 2001 From: Malikah Montgomery Date: Mon, 15 May 2023 10:25:10 -0400 Subject: [PATCH 01/17] Create new package for plugin with react components Signed-off-by: Malikah Montgomery --- plugins/home-react/.eslintrc.js | 1 + plugins/home-react/CHANGELOG.md | 0 plugins/home-react/README.md | 244 ++++++++++++++++++ plugins/home-react/package.json | 78 ++++++ .../src/components/SettingsModal.tsx | 46 ++++ plugins/home-react/src/components/index.ts | 17 ++ plugins/home-react/src/extensions.tsx | 194 ++++++++++++++ plugins/home-react/src/index.ts | 31 +++ yarn.lock | 76 +++++- 9 files changed, 686 insertions(+), 1 deletion(-) create mode 100644 plugins/home-react/.eslintrc.js create mode 100644 plugins/home-react/CHANGELOG.md create mode 100644 plugins/home-react/README.md create mode 100644 plugins/home-react/package.json create mode 100644 plugins/home-react/src/components/SettingsModal.tsx create mode 100644 plugins/home-react/src/components/index.ts create mode 100644 plugins/home-react/src/extensions.tsx create mode 100644 plugins/home-react/src/index.ts diff --git a/plugins/home-react/.eslintrc.js b/plugins/home-react/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/home-react/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/home-react/CHANGELOG.md b/plugins/home-react/CHANGELOG.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/home-react/README.md b/plugins/home-react/README.md new file mode 100644 index 0000000000..7da0034662 --- /dev/null +++ b/plugins/home-react/README.md @@ -0,0 +1,244 @@ +# Home + +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. + +## Getting started + +If you have a standalone app (you didn't clone this repo), then do + +```bash +# From your Backstage root directory +yarn add --cwd packages/app @backstage/plugin-home +``` + +### 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'; + +// ... +}> + +; +// ... +``` + +### Creating Components + +The Home Page can be composed with regular React components, so there's no magic in creating components to be used for composition 🪄 🎩 . However, in order to assure that your component fits into a diverse set of Home Pages, there's an extension creator for this purpose, that creates a Card-based layout, for consistency between components (read more about extensions [here](https://backstage.io/docs/plugins/composability#extensions)). The extension creator requires two fields: `title` and `components`. The `components` field is expected to be an asynchronous import that should at least contain a `Content` field. Additionally, you can optionally provide `settings`, `actions` and `contextProvider` as well. These parts will be combined to create a card, where the `content`, `actions` and `settings` will be wrapped within the `contextProvider` in order to be able to access to context and effectively communicate with one another. + +Finally, the `createCardExtension` also accepts a generic, such that Component Developers can indicate to App Integrators what custom props their component will accept, such as the example below where the default category of the random jokes can be set. + +```tsx +export const RandomJokeHomePageComponent = homePlugin.provide( + createCardExtension<{ defaultCategory?: 'programming' | 'any' }>({ + title: 'Random Joke', + components: () => import('./homePageComponents/RandomJoke'), + }), +); +``` + +In summary: it is not necessary to use the `createCardExtension` extension creator to register a home page component, although it is convenient since it provides error boundary and lazy loading, and it also may hook into other functionality in the future. + +### Composing a 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, as described in the previous section. If created by the `createCardExtension` extension creator, they are rendered like so + +```tsx +import React from 'react'; +import Grid from '@material-ui/core/Grid'; +import { RandomJokeHomePageComponent } from '@backstage/plugin-home'; + +export const HomePage = () => { + return ( + + + + + + ); +}; +``` + +Additionally, the App Integrator is provided an escape hatch in case the way the card is rendered does not fit their requirements. They may optionally pass the `Renderer`-prop, which will receive the `title`, `content` and optionally `actions`, `settings` and `contextProvider`, if they exist for the component. This allows the App Integrator to render the content in any way they want. + +## Customizable home page + +If you want to allow users to customize the components that are shown in the home page, you can use CustomHomePageGrid component. +By adding the allowed components inside the grid, the user can add, configure, remove and move the components around in their +home page. The user configuration is also saved and restored in the process for later use. + +```tsx +import { + HomePageRandomJoke, + HomePageStarredEntities, + CustomHomepageGrid, +} from '@backstage/plugin-home'; +import { Content, Header, Page } from '@backstage/core-components'; +import { HomePageSearchBar } from '@backstage/plugin-search'; +import { HomePageCalendar } from '@backstage/plugin-gcalendar'; +import { MicrosoftCalendarCard } from '@backstage/plugin-microsoft-calendar'; + +export const HomePage = () => { + return ( + + // Insert the allowed widgets inside the grid + + + + + + + ); +}; +``` + +### Creating Customizable Components + +The custom home page can use the default components created by using the default `createCardExtension` method but if you +want to add additional configuration like component size or settings, you can define those in the `layout` +property: + +```tsx +export const RandomJokeHomePageComponent = homePlugin.provide( + createCardExtension<{ defaultCategory?: 'any' | 'programming' }>({ + name: 'HomePageRandomJoke', + title: 'Random Joke', + components: () => import('./homePageComponents/RandomJoke'), + layout: { + height: { minRows: 7 }, + width: { minColumns: 3 }, + }, + }), +); +``` + +These settings can also be defined for components that use `createReactExtension` instead `createCardExtension` by using +the data property: + +```tsx +export const HomePageSearchBar = searchPlugin.provide( + createReactExtension({ + name: 'HomePageSearchBar', + component: { + lazy: () => + import('./components/HomePageComponent').then(m => m.HomePageSearchBar), + }, + data: { + 'home.widget.config': { + layout: { + height: { maxRows: 1 }, + }, + }, + }, + }), +); +``` + +Available home page properties that are used for homepage widgets are: + +| Key | Type | Description | +| ----------------------------- | ------- | ------------------------------------------------------------ | +| `title` | string | User friend title. Shown when user adds widgets to homepage | +| `description` | string | Widget description. Shown when user adds widgets to homepage | +| `layout.width.defaultColumns` | integer | Default width of the widget (1-12) | +| `layout.width.minColumns` | integer | Minimum width of the widget (1-12) | +| `layout.width.maxColumns` | integer | Maximum width of the widget (1-12) | +| `layout.height.defaultRows` | integer | Default height of the widget (1-12) | +| `layout.height.minRows` | integer | Minimum height of the widget (1-12) | +| `layout.height.maxRows` | integer | Maximum height of the widget (1-12) | +| `settings.schema` | object | Customization settings of the widget, see below | + +#### Widget Specific Settings + +To define settings that the users can change for your component, you should define the `layout` and `settings` +properties. The `settings.schema` object should follow +[react-jsonschema-form](https://rjsf-team.github.io/react-jsonschema-form/docs/) definition and the type of the schema +must be `object`. + +```tsx +export const HomePageRandomJoke = homePlugin.provide( + createCardExtension<{ defaultCategory?: 'any' | 'programming' }>({ + name: 'HomePageRandomJoke', + title: 'Random Joke', + components: () => import('./homePageComponents/RandomJoke'), + description: 'Shows a random joke about optional category', + layout: { + height: { minRows: 4 }, + width: { minColumns: 3 }, + }, + settings: { + schema: { + title: 'Random Joke settings', + type: 'object', + properties: { + defaultCategory: { + title: 'Category', + type: 'string', + enum: ['any', 'programming', 'dad'], + default: 'any', + }, + }, + }, + }, + }), +); +``` + +This allows the user to select `defaultCategory` for the RandomJoke widgets that are added to the homepage. +Each widget has its own settings and the setting values are passed to the underlying React component in props. + +In case your `CardExtension` had `Settings` component defined, it will automatically disappear when you add the +`settingsSchema` to the component data structure. + +### Adding Default Layout + +You can set the default layout of the customizable home page by passing configuration to the `CustomHomepageGrid` +component: + +```tsx +const defaultConfig = [ + { + component: , // Or 'HomePageSearchBar' as a string if you know the component name + x: 0, + y: 0, + width: 12, + height: 1, + }, +]; + + +``` + +## 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](/packages/app/src/components/home/templates/DefaultTemplate.stories.tsx), and then open a PR with your suggestion. diff --git a/plugins/home-react/package.json b/plugins/home-react/package.json new file mode 100644 index 0000000000..0e94e3ee49 --- /dev/null +++ b/plugins/home-react/package.json @@ -0,0 +1,78 @@ +{ + "name": "@backstage/plugin-home-react", + "description": "A Backstage plugin that contains react components helps you build a home page", + "version": "0.0.1", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "frontend-plugin" + }, + "homepage": "https://github.com/backstage/backstage/tree/master/plugins/home-react#readme", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/home-react" + }, + "keywords": [ + "backstage", + "homepage" + ], + "scripts": { + "build": "backstage-cli package build", + "start": "backstage-cli package start", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "clean": "backstage-cli package clean" + }, + "dependencies": { + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/core-components": "workspace:^", + "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/theme": "workspace:^", + "@material-ui/core": "^4.12.2", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "4.0.0-alpha.61", + "@rjsf/core-v5": "npm:@rjsf/core@5.6.0", + "@rjsf/material-ui": "5.6.0", + "@rjsf/utils": "5.6.0", + "@rjsf/validator-ajv8": "5.6.0", + "@types/react": "^16.13.1 || ^17.0.0", + "lodash": "^4.17.21", + "react-grid-layout": "^1.3.4", + "react-resizable": "^3.0.4", + "react-use": "^17.2.4", + "zod": "~3.21.4" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0", + "react-dom": "^16.13.1 || ^17.0.0", + "react-router-dom": "6.0.0-beta.0 || ^6.3.0" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@backstage/core-app-api": "workspace:^", + "@backstage/dev-utils": "workspace:^", + "@backstage/test-utils": "workspace:^", + "@testing-library/dom": "^8.0.0", + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/react": "^12.1.3", + "@testing-library/user-event": "^14.0.0", + "@types/node": "^16.11.26", + "@types/react-grid-layout": "^1.3.2", + "cross-fetch": "^3.1.5", + "msw": "^1.0.0" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/home-react/src/components/SettingsModal.tsx b/plugins/home-react/src/components/SettingsModal.tsx new file mode 100644 index 0000000000..ace9944f28 --- /dev/null +++ b/plugins/home-react/src/components/SettingsModal.tsx @@ -0,0 +1,46 @@ +/* + * 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 React from 'react'; +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, +} from '@material-ui/core'; + +/** @public */ +export const SettingsModal = (props: { + open: boolean; + close: Function; + componentName: string; + children: JSX.Element; +}) => { + const { open, close, componentName, children } = props; + + return ( + close()}> + Settings - {componentName} + {children} + + + + + ); +}; diff --git a/plugins/home-react/src/components/index.ts b/plugins/home-react/src/components/index.ts new file mode 100644 index 0000000000..dce6968b36 --- /dev/null +++ b/plugins/home-react/src/components/index.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +export { SettingsModal } from './SettingsModal'; diff --git a/plugins/home-react/src/extensions.tsx b/plugins/home-react/src/extensions.tsx new file mode 100644 index 0000000000..380ff6713a --- /dev/null +++ b/plugins/home-react/src/extensions.tsx @@ -0,0 +1,194 @@ +/* + * 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 React, { Suspense } from 'react'; +import { IconButton } from '@material-ui/core'; +import SettingsIcon from '@material-ui/icons/Settings'; +import { InfoCard } from '@backstage/core-components'; +import { SettingsModal } from './components'; +import { createReactExtension, useApp } from '@backstage/core-plugin-api'; +import { RJSFSchema } from '@rjsf/utils'; + +/** + * @public + */ +export type ComponentRenderer = { + Renderer?: (props: RendererProps) => JSX.Element; +}; + +/** + * @public + */ +export type ComponentParts = { + Content: (props?: any) => JSX.Element; + Actions?: () => JSX.Element; + Settings?: () => JSX.Element; + ContextProvider?: (props: any) => JSX.Element; +}; + +/** + * @public + */ +export type RendererProps = { title: string } & ComponentParts; + +/** + * @public + */ +export type CardExtensionProps = ComponentRenderer & { title?: string } & T; + +/** + * @public + */ +export type CardLayout = { + width?: { minColumns?: number; maxColumns?: number; defaultColumns?: number }; + height?: { minRows?: number; maxRows?: number; defaultRows?: number }; +}; + +/** + * @public + */ +export type CardSettings = { + schema?: RJSFSchema; +}; + +/** + * @public + */ +export type CardConfig = { + layout?: CardLayout; + settings?: CardSettings; +}; + +/** + * An extension creator to create card based components for the homepage + * + * @public + */ +export function createCardExtension(options: { + title: string; + components: () => Promise; + name?: string; + description?: string; + layout?: CardLayout; + settings?: CardSettings; +}) { + const { title, components, name, description, layout, settings } = options; + // If widget settings schema is defined, we don't want to show the Settings icon or dialog + const isCustomizable = settings?.schema !== undefined; + + return createReactExtension({ + name, + data: { title, description, 'home.widget.config': { layout, settings } }, + component: { + lazy: () => + components().then(componentParts => { + return (props: CardExtensionProps) => { + return ( + + ); + }; + }), + }, + }); +} + +type CardExtensionComponentProps = CardExtensionProps & + ComponentParts & { + title: string; + isCustomizable?: boolean; + overrideTitle?: string; + }; + +function CardExtension(props: CardExtensionComponentProps) { + const { + Renderer, + Content, + Settings, + Actions, + ContextProvider, + isCustomizable, + title, + ...childProps + } = props; + const app = useApp(); + const { Progress } = app.getComponents(); + const [settingsOpen, setSettingsOpen] = React.useState(false); + + if (Renderer) { + return ( + }> + + + ); + } + + const cardProps = { + title: title, + ...(Settings && !isCustomizable + ? { + action: ( + setSettingsOpen(true)}> + Settings + + ), + } + : {}), + ...(Actions + ? { + actions: , + } + : {}), + }; + + const innerContent = ( + + {Settings && !isCustomizable && ( + setSettingsOpen(false)} + > + + + )} + + + ); + + return ( + }> + {ContextProvider ? ( + {innerContent} + ) : ( + innerContent + )} + + ); +} diff --git a/plugins/home-react/src/index.ts b/plugins/home-react/src/index.ts new file mode 100644 index 0000000000..90c76d0836 --- /dev/null +++ b/plugins/home-react/src/index.ts @@ -0,0 +1,31 @@ +/* + * 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. + */ + +/** + * A Backstage plugin that helps you build a home page + * + * @packageDocumentation + */ +export * from './components'; +export { createCardExtension } from './extensions'; +export type { + CardExtensionProps, + ComponentParts, + ComponentRenderer, + RendererProps, + CardLayout, + CardSettings, +} from './extensions'; diff --git a/yarn.lock b/yarn.lock index 2d4a940cea..78782deb48 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7036,7 +7036,81 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-home@^0.5.1, @backstage/plugin-home@workspace:^, @backstage/plugin-home@workspace:plugins/home": +"@backstage/plugin-home-react@workspace:plugins/home-react": + version: 0.0.0-use.local + resolution: "@backstage/plugin-home-react@workspace:plugins/home-react" + dependencies: + "@backstage/catalog-model": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" + "@backstage/core-app-api": "workspace:^" + "@backstage/core-components": "workspace:^" + "@backstage/core-plugin-api": "workspace:^" + "@backstage/dev-utils": "workspace:^" + "@backstage/plugin-catalog-react": "workspace:^" + "@backstage/test-utils": "workspace:^" + "@backstage/theme": "workspace:^" + "@material-ui/core": ^4.12.2 + "@material-ui/icons": ^4.9.1 + "@material-ui/lab": 4.0.0-alpha.61 + "@rjsf/core-v5": "npm:@rjsf/core@5.6.0" + "@rjsf/material-ui": 5.6.0 + "@rjsf/utils": 5.6.0 + "@rjsf/validator-ajv8": 5.6.0 + "@testing-library/dom": ^8.0.0 + "@testing-library/jest-dom": ^5.10.1 + "@testing-library/react": ^12.1.3 + "@testing-library/user-event": ^14.0.0 + "@types/node": ^16.11.26 + "@types/react": ^16.13.1 || ^17.0.0 + "@types/react-grid-layout": ^1.3.2 + cross-fetch: ^3.1.5 + lodash: ^4.17.21 + msw: ^1.0.0 + react-grid-layout: ^1.3.4 + react-resizable: ^3.0.4 + react-use: ^17.2.4 + zod: ~3.21.4 + peerDependencies: + react: ^16.13.1 || ^17.0.0 + react-dom: ^16.13.1 || ^17.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + languageName: unknown + linkType: soft + +"@backstage/plugin-home@npm:^0.5.1": + version: 0.5.1 + resolution: "@backstage/plugin-home@npm:0.5.1" + dependencies: + "@backstage/catalog-model": ^1.3.0 + "@backstage/config": ^1.0.7 + "@backstage/core-components": ^0.13.0 + "@backstage/core-plugin-api": ^1.5.1 + "@backstage/plugin-catalog-react": ^1.5.0 + "@backstage/theme": ^0.2.19 + "@material-ui/core": ^4.12.2 + "@material-ui/icons": ^4.9.1 + "@material-ui/lab": 4.0.0-alpha.61 + "@rjsf/core-v5": "npm:@rjsf/core@5.6.0" + "@rjsf/material-ui": 5.6.0 + "@rjsf/utils": 5.6.0 + "@rjsf/validator-ajv8": 5.6.0 + "@types/react": ^16.13.1 || ^17.0.0 + lodash: ^4.17.21 + object-hash: ^3.0.0 + react-grid-layout: ^1.3.4 + react-resizable: ^3.0.4 + react-use: ^17.2.4 + zod: ~3.21.4 + peerDependencies: + react: ^16.13.1 || ^17.0.0 + react-dom: ^16.13.1 || ^17.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 0470e0d4848818c59fdf5c3765b769c22bf2b47e26b0ed0c80398baac6a8e1ac703e94152dc20498bfda790aec18a2af65605fcd8c6eb2e7ba7b85eb6ce80b69 + languageName: node + linkType: hard + +"@backstage/plugin-home@workspace:^, @backstage/plugin-home@workspace:plugins/home": version: 0.0.0-use.local resolution: "@backstage/plugin-home@workspace:plugins/home" dependencies: From 838267c68c8be707c7f1b8e6af7d649e0715dceb Mon Sep 17 00:00:00 2001 From: Malikah Montgomery Date: Mon, 15 May 2023 10:49:36 -0400 Subject: [PATCH 02/17] deprecated createCardExtension in plugin-home and updated stack-overflow to use new plugin-home-react package Signed-off-by: Malikah Montgomery --- plugins/home-react/package.json | 15 +-------------- plugins/home/src/extensions.tsx | 6 ++++++ plugins/stack-overflow/package.json | 2 +- plugins/stack-overflow/src/plugin.ts | 2 +- yarn.lock | 17 ++--------------- 5 files changed, 11 insertions(+), 31 deletions(-) diff --git a/plugins/home-react/package.json b/plugins/home-react/package.json index 0e94e3ee49..e6f96e65f4 100644 --- a/plugins/home-react/package.json +++ b/plugins/home-react/package.json @@ -33,25 +33,12 @@ "clean": "backstage-cli package clean" }, "dependencies": { - "@backstage/catalog-model": "workspace:^", - "@backstage/config": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", - "@backstage/plugin-catalog-react": "workspace:^", - "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@material-ui/lab": "4.0.0-alpha.61", - "@rjsf/core-v5": "npm:@rjsf/core@5.6.0", - "@rjsf/material-ui": "5.6.0", "@rjsf/utils": "5.6.0", - "@rjsf/validator-ajv8": "5.6.0", - "@types/react": "^16.13.1 || ^17.0.0", - "lodash": "^4.17.21", - "react-grid-layout": "^1.3.4", - "react-resizable": "^3.0.4", - "react-use": "^17.2.4", - "zod": "~3.21.4" + "@types/react": "^16.13.1 || ^17.0.0" }, "peerDependencies": { "react": "^16.13.1 || ^17.0.0", diff --git a/plugins/home/src/extensions.tsx b/plugins/home/src/extensions.tsx index 380ff6713a..fdb9e9d67f 100644 --- a/plugins/home/src/extensions.tsx +++ b/plugins/home/src/extensions.tsx @@ -24,6 +24,7 @@ import { RJSFSchema } from '@rjsf/utils'; /** * @public + * @deprecated Please use the same type from `@backstage/plugin-home-react` instead */ export type ComponentRenderer = { Renderer?: (props: RendererProps) => JSX.Element; @@ -31,6 +32,7 @@ export type ComponentRenderer = { /** * @public + * @deprecated Please use the same type from `@backstage/plugin-home-react` instead */ export type ComponentParts = { Content: (props?: any) => JSX.Element; @@ -41,16 +43,19 @@ export type ComponentParts = { /** * @public + * @deprecated Please use the same type from `@backstage/plugin-home-react` instead */ export type RendererProps = { title: string } & ComponentParts; /** * @public + * @deprecated Please use the same type from `@backstage/plugin-home-react` instead */ export type CardExtensionProps = ComponentRenderer & { title?: string } & T; /** * @public + * @deprecated Please use the same type from `@backstage/plugin-home-react` instead */ export type CardLayout = { width?: { minColumns?: number; maxColumns?: number; defaultColumns?: number }; @@ -76,6 +81,7 @@ export type CardConfig = { * An extension creator to create card based components for the homepage * * @public + * @deprecated Please use the same type from `@backstage/plugin-home-react` instead */ export function createCardExtension(options: { title: string; diff --git a/plugins/stack-overflow/package.json b/plugins/stack-overflow/package.json index 5b9cdb0213..e550e15762 100644 --- a/plugins/stack-overflow/package.json +++ b/plugins/stack-overflow/package.json @@ -25,7 +25,7 @@ "@backstage/config": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", - "@backstage/plugin-home": "workspace:^", + "@backstage/plugin-home-react": "workspace:^", "@backstage/plugin-search-common": "workspace:^", "@backstage/plugin-search-react": "workspace:^", "@backstage/theme": "workspace:^", diff --git a/plugins/stack-overflow/src/plugin.ts b/plugins/stack-overflow/src/plugin.ts index 5be4c89cbe..017900bc02 100644 --- a/plugins/stack-overflow/src/plugin.ts +++ b/plugins/stack-overflow/src/plugin.ts @@ -20,7 +20,7 @@ import { createApiFactory, configApiRef, } from '@backstage/core-plugin-api'; -import { createCardExtension } from '@backstage/plugin-home'; +import { createCardExtension } from '@backstage/plugin-home-react'; import { StackOverflowQuestionsContentProps } from './types'; import { stackOverflowApiRef, StackOverflowClient } from './api'; import { SearchResultListItemExtensionProps } from '@backstage/plugin-search-react'; diff --git a/yarn.lock b/yarn.lock index 78782deb48..b5fbc69c3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7036,27 +7036,19 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-home-react@workspace:plugins/home-react": +"@backstage/plugin-home-react@workspace:^, @backstage/plugin-home-react@workspace:plugins/home-react": version: 0.0.0-use.local resolution: "@backstage/plugin-home-react@workspace:plugins/home-react" dependencies: - "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" - "@backstage/config": "workspace:^" "@backstage/core-app-api": "workspace:^" "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" - "@backstage/plugin-catalog-react": "workspace:^" "@backstage/test-utils": "workspace:^" - "@backstage/theme": "workspace:^" "@material-ui/core": ^4.12.2 "@material-ui/icons": ^4.9.1 - "@material-ui/lab": 4.0.0-alpha.61 - "@rjsf/core-v5": "npm:@rjsf/core@5.6.0" - "@rjsf/material-ui": 5.6.0 "@rjsf/utils": 5.6.0 - "@rjsf/validator-ajv8": 5.6.0 "@testing-library/dom": ^8.0.0 "@testing-library/jest-dom": ^5.10.1 "@testing-library/react": ^12.1.3 @@ -7065,12 +7057,7 @@ __metadata: "@types/react": ^16.13.1 || ^17.0.0 "@types/react-grid-layout": ^1.3.2 cross-fetch: ^3.1.5 - lodash: ^4.17.21 msw: ^1.0.0 - react-grid-layout: ^1.3.4 - react-resizable: ^3.0.4 - react-use: ^17.2.4 - zod: ~3.21.4 peerDependencies: react: ^16.13.1 || ^17.0.0 react-dom: ^16.13.1 || ^17.0.0 @@ -8918,7 +8905,7 @@ __metadata: "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" - "@backstage/plugin-home": "workspace:^" + "@backstage/plugin-home-react": "workspace:^" "@backstage/plugin-search-common": "workspace:^" "@backstage/plugin-search-react": "workspace:^" "@backstage/test-utils": "workspace:^" From 41e8037a8a6d20dde150b6a48c2cf3e3b8565d0e Mon Sep 17 00:00:00 2001 From: Malikah Montgomery Date: Mon, 15 May 2023 10:54:53 -0400 Subject: [PATCH 03/17] Extract new plugin-home-react package and deprecate createCardExtension in plugin-home Signed-off-by: Malikah Montgomery --- .changeset/violet-pots-push.md | 7 ++ plugins/home-react/api-report.md | 198 +++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 .changeset/violet-pots-push.md create mode 100644 plugins/home-react/api-report.md diff --git a/.changeset/violet-pots-push.md b/.changeset/violet-pots-push.md new file mode 100644 index 0000000000..8e668977a9 --- /dev/null +++ b/.changeset/violet-pots-push.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-stack-overflow': patch +'@backstage/plugin-home-react': patch +'@backstage/plugin-home': patch +--- + +Extract new plugin-home-react package and deprecate createCardExtension in plugin-home diff --git a/plugins/home-react/api-report.md b/plugins/home-react/api-report.md new file mode 100644 index 0000000000..43fdbf403c --- /dev/null +++ b/plugins/home-react/api-report.md @@ -0,0 +1,198 @@ +## API Report File for "@backstage/plugin-home" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { Extension } from '@backstage/core-plugin-api'; +import { default as React_2 } from 'react'; +import { ReactElement } from 'react'; +import { ReactNode } from 'react'; +import { RJSFSchema } from '@rjsf/utils'; +import { RouteRef } from '@backstage/core-plugin-api'; + +// @public (undocumented) +export type CardExtensionProps = ComponentRenderer & { + title?: string; +} & T; + +// @public (undocumented) +export type CardLayout = { + width?: { + minColumns?: number; + maxColumns?: number; + defaultColumns?: number; + }; + height?: { + minRows?: number; + maxRows?: number; + defaultRows?: number; + }; +}; + +// @public (undocumented) +export type CardSettings = { + schema?: RJSFSchema; +}; + +// @public (undocumented) +export type ClockConfig = { + label: string; + timeZone: string; +}; + +// @public (undocumented) +export const ComponentAccordion: (props: { + title: string; + expanded?: boolean | undefined; + Content: () => JSX.Element; + Actions?: (() => JSX.Element) | undefined; + Settings?: (() => JSX.Element) | undefined; + ContextProvider?: ((props: any) => JSX.Element) | undefined; +}) => JSX.Element; + +// @public (undocumented) +export type ComponentParts = { + Content: (props?: any) => JSX.Element; + Actions?: () => JSX.Element; + Settings?: () => JSX.Element; + ContextProvider?: (props: any) => JSX.Element; +}; + +// @public (undocumented) +export type ComponentRenderer = { + Renderer?: (props: RendererProps) => JSX.Element; +}; + +// @public (undocumented) +export const ComponentTab: (props: { + title: string; + Content: () => JSX.Element; + ContextProvider?: ((props: any) => JSX.Element) | undefined; +}) => JSX.Element; + +// @public (undocumented) +export const ComponentTabs: (props: { + title: string; + tabs: { + label: string; + Component: () => JSX.Element; + }[]; +}) => JSX.Element; + +// @public +export function createCardExtension(options: { + title: string; + components: () => Promise; + name?: string; + description?: string; + layout?: CardLayout; + settings?: CardSettings; +}): Extension<(props: CardExtensionProps) => JSX.Element>; + +// @public +export const CustomHomepageGrid: ( + props: CustomHomepageGridProps, +) => JSX.Element; + +// @public (undocumented) +export type CustomHomepageGridProps = { + children?: ReactNode; + config?: LayoutConfiguration[]; + rowHeight?: number; +}; + +// @public +export const HeaderWorldClock: (props: { + clockConfigs: ClockConfig[]; + customTimeFormat?: Intl.DateTimeFormatOptions | undefined; +}) => JSX.Element | null; + +// @public +export const HomePageCompanyLogo: (props: { + logo?: ReactNode; + className?: string | undefined; +}) => JSX.Element; + +// @public (undocumented) +export const HomepageCompositionRoot: (props: { + title?: string | undefined; + children?: ReactNode; +}) => JSX.Element; + +// @public (undocumented) +export const HomePageRandomJoke: ( + props: CardExtensionProps<{ + defaultCategory?: 'any' | 'programming' | undefined; + }>, +) => JSX.Element; + +// @public +export const HomePageStarredEntities: ( + props: CardExtensionProps, +) => JSX.Element; + +// @public +export const HomePageToolkit: ( + props: CardExtensionProps, +) => JSX.Element; + +// @public (undocumented) +export const homePlugin: BackstagePlugin< + { + root: RouteRef; + }, + {}, + {} +>; + +// @public +export type LayoutConfiguration = { + component: ReactElement | string; + x: number; + y: number; + width: number; + height: number; +}; + +// @public (undocumented) +export type RendererProps = { + title: string; +} & ComponentParts; + +// @public (undocumented) +export const SettingsModal: (props: { + open: boolean; + close: Function; + componentName: string; + children: JSX.Element; +}) => JSX.Element; + +// @public (undocumented) +export const TemplateBackstageLogo: (props: { + classes: { + svg: string; + path: string; + }; +}) => JSX.Element; + +// @public (undocumented) +export const TemplateBackstageLogoIcon: () => JSX.Element; + +// @public (undocumented) +export type Tool = { + label: string; + url: string; + icon: React_2.ReactNode; +}; + +// @public +export type ToolkitContentProps = { + tools: Tool[]; +}; + +// @public +export const WelcomeTitle: () => JSX.Element; +``` From 1f6ce3afa76df3563d4f1a5bfc62d0c0f0667ff2 Mon Sep 17 00:00:00 2001 From: Malikah Montgomery Date: Mon, 15 May 2023 12:08:42 -0400 Subject: [PATCH 04/17] generate api report Signed-off-by: Malikah Montgomery --- plugins/home-react/api-report.md | 130 +-------------------------- plugins/home/api-report.md | 12 +-- plugins/stack-overflow/api-report.md | 2 +- 3 files changed, 8 insertions(+), 136 deletions(-) diff --git a/plugins/home-react/api-report.md b/plugins/home-react/api-report.md index 43fdbf403c..9367fd61cc 100644 --- a/plugins/home-react/api-report.md +++ b/plugins/home-react/api-report.md @@ -1,17 +1,12 @@ -## API Report File for "@backstage/plugin-home" +## API Report File for "@backstage/plugin-home-react" > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts /// -import { BackstagePlugin } from '@backstage/core-plugin-api'; import { Extension } from '@backstage/core-plugin-api'; -import { default as React_2 } from 'react'; -import { ReactElement } from 'react'; -import { ReactNode } from 'react'; import { RJSFSchema } from '@rjsf/utils'; -import { RouteRef } from '@backstage/core-plugin-api'; // @public (undocumented) export type CardExtensionProps = ComponentRenderer & { @@ -37,22 +32,6 @@ export type CardSettings = { schema?: RJSFSchema; }; -// @public (undocumented) -export type ClockConfig = { - label: string; - timeZone: string; -}; - -// @public (undocumented) -export const ComponentAccordion: (props: { - title: string; - expanded?: boolean | undefined; - Content: () => JSX.Element; - Actions?: (() => JSX.Element) | undefined; - Settings?: (() => JSX.Element) | undefined; - ContextProvider?: ((props: any) => JSX.Element) | undefined; -}) => JSX.Element; - // @public (undocumented) export type ComponentParts = { Content: (props?: any) => JSX.Element; @@ -66,22 +45,6 @@ export type ComponentRenderer = { Renderer?: (props: RendererProps) => JSX.Element; }; -// @public (undocumented) -export const ComponentTab: (props: { - title: string; - Content: () => JSX.Element; - ContextProvider?: ((props: any) => JSX.Element) | undefined; -}) => JSX.Element; - -// @public (undocumented) -export const ComponentTabs: (props: { - title: string; - tabs: { - label: string; - Component: () => JSX.Element; - }[]; -}) => JSX.Element; - // @public export function createCardExtension(options: { title: string; @@ -92,71 +55,6 @@ export function createCardExtension(options: { settings?: CardSettings; }): Extension<(props: CardExtensionProps) => JSX.Element>; -// @public -export const CustomHomepageGrid: ( - props: CustomHomepageGridProps, -) => JSX.Element; - -// @public (undocumented) -export type CustomHomepageGridProps = { - children?: ReactNode; - config?: LayoutConfiguration[]; - rowHeight?: number; -}; - -// @public -export const HeaderWorldClock: (props: { - clockConfigs: ClockConfig[]; - customTimeFormat?: Intl.DateTimeFormatOptions | undefined; -}) => JSX.Element | null; - -// @public -export const HomePageCompanyLogo: (props: { - logo?: ReactNode; - className?: string | undefined; -}) => JSX.Element; - -// @public (undocumented) -export const HomepageCompositionRoot: (props: { - title?: string | undefined; - children?: ReactNode; -}) => JSX.Element; - -// @public (undocumented) -export const HomePageRandomJoke: ( - props: CardExtensionProps<{ - defaultCategory?: 'any' | 'programming' | undefined; - }>, -) => JSX.Element; - -// @public -export const HomePageStarredEntities: ( - props: CardExtensionProps, -) => JSX.Element; - -// @public -export const HomePageToolkit: ( - props: CardExtensionProps, -) => JSX.Element; - -// @public (undocumented) -export const homePlugin: BackstagePlugin< - { - root: RouteRef; - }, - {}, - {} ->; - -// @public -export type LayoutConfiguration = { - component: ReactElement | string; - x: number; - y: number; - width: number; - height: number; -}; - // @public (undocumented) export type RendererProps = { title: string; @@ -169,30 +67,4 @@ export const SettingsModal: (props: { componentName: string; children: JSX.Element; }) => JSX.Element; - -// @public (undocumented) -export const TemplateBackstageLogo: (props: { - classes: { - svg: string; - path: string; - }; -}) => JSX.Element; - -// @public (undocumented) -export const TemplateBackstageLogoIcon: () => JSX.Element; - -// @public (undocumented) -export type Tool = { - label: string; - url: string; - icon: React_2.ReactNode; -}; - -// @public -export type ToolkitContentProps = { - tools: Tool[]; -}; - -// @public -export const WelcomeTitle: () => JSX.Element; ``` diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index 43fdbf403c..30ee6732cd 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -13,12 +13,12 @@ import { ReactNode } from 'react'; import { RJSFSchema } from '@rjsf/utils'; import { RouteRef } from '@backstage/core-plugin-api'; -// @public (undocumented) +// @public @deprecated (undocumented) export type CardExtensionProps = ComponentRenderer & { title?: string; } & T; -// @public (undocumented) +// @public @deprecated (undocumented) export type CardLayout = { width?: { minColumns?: number; @@ -53,7 +53,7 @@ export const ComponentAccordion: (props: { ContextProvider?: ((props: any) => JSX.Element) | undefined; }) => JSX.Element; -// @public (undocumented) +// @public @deprecated (undocumented) export type ComponentParts = { Content: (props?: any) => JSX.Element; Actions?: () => JSX.Element; @@ -61,7 +61,7 @@ export type ComponentParts = { ContextProvider?: (props: any) => JSX.Element; }; -// @public (undocumented) +// @public @deprecated (undocumented) export type ComponentRenderer = { Renderer?: (props: RendererProps) => JSX.Element; }; @@ -82,7 +82,7 @@ export const ComponentTabs: (props: { }[]; }) => JSX.Element; -// @public +// @public @deprecated export function createCardExtension(options: { title: string; components: () => Promise; @@ -157,7 +157,7 @@ export type LayoutConfiguration = { height: number; }; -// @public (undocumented) +// @public @deprecated (undocumented) export type RendererProps = { title: string; } & ComponentParts; diff --git a/plugins/stack-overflow/api-report.md b/plugins/stack-overflow/api-report.md index b006f263ed..3e27318acb 100644 --- a/plugins/stack-overflow/api-report.md +++ b/plugins/stack-overflow/api-report.md @@ -7,7 +7,7 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; -import { CardExtensionProps } from '@backstage/plugin-home'; +import { CardExtensionProps } from '@backstage/plugin-home-react'; import { default as React_2 } from 'react'; import { ResultHighlight } from '@backstage/plugin-search-common'; import { SearchResultListItemExtensionProps } from '@backstage/plugin-search-react'; From e5e686f72df2cb3943a9f282b4bbf7ebc0970dc0 Mon Sep 17 00:00:00 2001 From: Malikah Montgomery Date: Mon, 15 May 2023 12:39:36 -0400 Subject: [PATCH 05/17] move react to dev depend fix error Signed-off-by: Malikah Montgomery --- plugins/home-react/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/home-react/package.json b/plugins/home-react/package.json index e6f96e65f4..24c75b05eb 100644 --- a/plugins/home-react/package.json +++ b/plugins/home-react/package.json @@ -37,8 +37,7 @@ "@backstage/core-plugin-api": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", - "@rjsf/utils": "5.6.0", - "@types/react": "^16.13.1 || ^17.0.0" + "@rjsf/utils": "5.6.0" }, "peerDependencies": { "react": "^16.13.1 || ^17.0.0", @@ -55,6 +54,7 @@ "@testing-library/react": "^12.1.3", "@testing-library/user-event": "^14.0.0", "@types/node": "^16.11.26", + "@types/react": "^16.13.1 || ^17.0.0", "@types/react-grid-layout": "^1.3.2", "cross-fetch": "^3.1.5", "msw": "^1.0.0" From 976b021d09c396d027f3e148daeb5d2599692f44 Mon Sep 17 00:00:00 2001 From: Malikah Montgomery Date: Tue, 16 May 2023 09:08:46 -0400 Subject: [PATCH 06/17] Update readme, remove changelog, and reset version to 0.0.0 Signed-off-by: Malikah Montgomery --- plugins/home-react/CHANGELOG.md | 0 plugins/home-react/README.md | 245 +------------------------------- plugins/home-react/package.json | 2 +- 3 files changed, 3 insertions(+), 244 deletions(-) delete mode 100644 plugins/home-react/CHANGELOG.md diff --git a/plugins/home-react/CHANGELOG.md b/plugins/home-react/CHANGELOG.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/home-react/README.md b/plugins/home-react/README.md index 7da0034662..a341de8f08 100644 --- a/plugins/home-react/README.md +++ b/plugins/home-react/README.md @@ -1,244 +1,3 @@ -# Home +# Home React -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. - -## Getting started - -If you have a standalone app (you didn't clone this repo), then do - -```bash -# From your Backstage root directory -yarn add --cwd packages/app @backstage/plugin-home -``` - -### 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'; - -// ... -}> - -; -// ... -``` - -### Creating Components - -The Home Page can be composed with regular React components, so there's no magic in creating components to be used for composition 🪄 🎩 . However, in order to assure that your component fits into a diverse set of Home Pages, there's an extension creator for this purpose, that creates a Card-based layout, for consistency between components (read more about extensions [here](https://backstage.io/docs/plugins/composability#extensions)). The extension creator requires two fields: `title` and `components`. The `components` field is expected to be an asynchronous import that should at least contain a `Content` field. Additionally, you can optionally provide `settings`, `actions` and `contextProvider` as well. These parts will be combined to create a card, where the `content`, `actions` and `settings` will be wrapped within the `contextProvider` in order to be able to access to context and effectively communicate with one another. - -Finally, the `createCardExtension` also accepts a generic, such that Component Developers can indicate to App Integrators what custom props their component will accept, such as the example below where the default category of the random jokes can be set. - -```tsx -export const RandomJokeHomePageComponent = homePlugin.provide( - createCardExtension<{ defaultCategory?: 'programming' | 'any' }>({ - title: 'Random Joke', - components: () => import('./homePageComponents/RandomJoke'), - }), -); -``` - -In summary: it is not necessary to use the `createCardExtension` extension creator to register a home page component, although it is convenient since it provides error boundary and lazy loading, and it also may hook into other functionality in the future. - -### Composing a 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, as described in the previous section. If created by the `createCardExtension` extension creator, they are rendered like so - -```tsx -import React from 'react'; -import Grid from '@material-ui/core/Grid'; -import { RandomJokeHomePageComponent } from '@backstage/plugin-home'; - -export const HomePage = () => { - return ( - - - - - - ); -}; -``` - -Additionally, the App Integrator is provided an escape hatch in case the way the card is rendered does not fit their requirements. They may optionally pass the `Renderer`-prop, which will receive the `title`, `content` and optionally `actions`, `settings` and `contextProvider`, if they exist for the component. This allows the App Integrator to render the content in any way they want. - -## Customizable home page - -If you want to allow users to customize the components that are shown in the home page, you can use CustomHomePageGrid component. -By adding the allowed components inside the grid, the user can add, configure, remove and move the components around in their -home page. The user configuration is also saved and restored in the process for later use. - -```tsx -import { - HomePageRandomJoke, - HomePageStarredEntities, - CustomHomepageGrid, -} from '@backstage/plugin-home'; -import { Content, Header, Page } from '@backstage/core-components'; -import { HomePageSearchBar } from '@backstage/plugin-search'; -import { HomePageCalendar } from '@backstage/plugin-gcalendar'; -import { MicrosoftCalendarCard } from '@backstage/plugin-microsoft-calendar'; - -export const HomePage = () => { - return ( - - // Insert the allowed widgets inside the grid - - - - - - - ); -}; -``` - -### Creating Customizable Components - -The custom home page can use the default components created by using the default `createCardExtension` method but if you -want to add additional configuration like component size or settings, you can define those in the `layout` -property: - -```tsx -export const RandomJokeHomePageComponent = homePlugin.provide( - createCardExtension<{ defaultCategory?: 'any' | 'programming' }>({ - name: 'HomePageRandomJoke', - title: 'Random Joke', - components: () => import('./homePageComponents/RandomJoke'), - layout: { - height: { minRows: 7 }, - width: { minColumns: 3 }, - }, - }), -); -``` - -These settings can also be defined for components that use `createReactExtension` instead `createCardExtension` by using -the data property: - -```tsx -export const HomePageSearchBar = searchPlugin.provide( - createReactExtension({ - name: 'HomePageSearchBar', - component: { - lazy: () => - import('./components/HomePageComponent').then(m => m.HomePageSearchBar), - }, - data: { - 'home.widget.config': { - layout: { - height: { maxRows: 1 }, - }, - }, - }, - }), -); -``` - -Available home page properties that are used for homepage widgets are: - -| Key | Type | Description | -| ----------------------------- | ------- | ------------------------------------------------------------ | -| `title` | string | User friend title. Shown when user adds widgets to homepage | -| `description` | string | Widget description. Shown when user adds widgets to homepage | -| `layout.width.defaultColumns` | integer | Default width of the widget (1-12) | -| `layout.width.minColumns` | integer | Minimum width of the widget (1-12) | -| `layout.width.maxColumns` | integer | Maximum width of the widget (1-12) | -| `layout.height.defaultRows` | integer | Default height of the widget (1-12) | -| `layout.height.minRows` | integer | Minimum height of the widget (1-12) | -| `layout.height.maxRows` | integer | Maximum height of the widget (1-12) | -| `settings.schema` | object | Customization settings of the widget, see below | - -#### Widget Specific Settings - -To define settings that the users can change for your component, you should define the `layout` and `settings` -properties. The `settings.schema` object should follow -[react-jsonschema-form](https://rjsf-team.github.io/react-jsonschema-form/docs/) definition and the type of the schema -must be `object`. - -```tsx -export const HomePageRandomJoke = homePlugin.provide( - createCardExtension<{ defaultCategory?: 'any' | 'programming' }>({ - name: 'HomePageRandomJoke', - title: 'Random Joke', - components: () => import('./homePageComponents/RandomJoke'), - description: 'Shows a random joke about optional category', - layout: { - height: { minRows: 4 }, - width: { minColumns: 3 }, - }, - settings: { - schema: { - title: 'Random Joke settings', - type: 'object', - properties: { - defaultCategory: { - title: 'Category', - type: 'string', - enum: ['any', 'programming', 'dad'], - default: 'any', - }, - }, - }, - }, - }), -); -``` - -This allows the user to select `defaultCategory` for the RandomJoke widgets that are added to the homepage. -Each widget has its own settings and the setting values are passed to the underlying React component in props. - -In case your `CardExtension` had `Settings` component defined, it will automatically disappear when you add the -`settingsSchema` to the component data structure. - -### Adding Default Layout - -You can set the default layout of the customizable home page by passing configuration to the `CustomHomepageGrid` -component: - -```tsx -const defaultConfig = [ - { - component: , // Or 'HomePageSearchBar' as a string if you know the component name - x: 0, - y: 0, - width: 12, - height: 1, - }, -]; - - -``` - -## 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](/packages/app/src/components/home/templates/DefaultTemplate.stories.tsx), and then open a PR with your suggestion. +A Library that contains React Components for [home plugin](/plugins/home/README.md) depend on. diff --git a/plugins/home-react/package.json b/plugins/home-react/package.json index 24c75b05eb..e32581bfd6 100644 --- a/plugins/home-react/package.json +++ b/plugins/home-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-home-react", "description": "A Backstage plugin that contains react components helps you build a home page", - "version": "0.0.1", + "version": "0.0.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", From 63b2dfd870a64ed299bb13ce0417b2f53b6fc003 Mon Sep 17 00:00:00 2001 From: Malikah Montgomery Date: Tue, 16 May 2023 10:17:55 -0400 Subject: [PATCH 07/17] update readme Signed-off-by: Malikah Montgomery --- plugins/home-react/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/home-react/README.md b/plugins/home-react/README.md index a341de8f08..c10ad8d323 100644 --- a/plugins/home-react/README.md +++ b/plugins/home-react/README.md @@ -1,3 +1,3 @@ # Home React -A Library that contains React Components for [home plugin](/plugins/home/README.md) depend on. +A new Library that contains React Components for [home plugin](/plugins/home/README.md) depend on. From e1e7e0a4aed3010f3b3b999f0e933640af87a360 Mon Sep 17 00:00:00 2001 From: Malikah Montgomery Date: Tue, 16 May 2023 14:33:16 -0400 Subject: [PATCH 08/17] update yarn lock after rebase Signed-off-by: Malikah Montgomery --- yarn.lock | 34 +--------------------------------- 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/yarn.lock b/yarn.lock index b5fbc69c3b..f644a4e4a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7065,39 +7065,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-home@npm:^0.5.1": - version: 0.5.1 - resolution: "@backstage/plugin-home@npm:0.5.1" - dependencies: - "@backstage/catalog-model": ^1.3.0 - "@backstage/config": ^1.0.7 - "@backstage/core-components": ^0.13.0 - "@backstage/core-plugin-api": ^1.5.1 - "@backstage/plugin-catalog-react": ^1.5.0 - "@backstage/theme": ^0.2.19 - "@material-ui/core": ^4.12.2 - "@material-ui/icons": ^4.9.1 - "@material-ui/lab": 4.0.0-alpha.61 - "@rjsf/core-v5": "npm:@rjsf/core@5.6.0" - "@rjsf/material-ui": 5.6.0 - "@rjsf/utils": 5.6.0 - "@rjsf/validator-ajv8": 5.6.0 - "@types/react": ^16.13.1 || ^17.0.0 - lodash: ^4.17.21 - object-hash: ^3.0.0 - react-grid-layout: ^1.3.4 - react-resizable: ^3.0.4 - react-use: ^17.2.4 - zod: ~3.21.4 - peerDependencies: - react: ^16.13.1 || ^17.0.0 - react-dom: ^16.13.1 || ^17.0.0 - react-router-dom: 6.0.0-beta.0 || ^6.3.0 - checksum: 0470e0d4848818c59fdf5c3765b769c22bf2b47e26b0ed0c80398baac6a8e1ac703e94152dc20498bfda790aec18a2af65605fcd8c6eb2e7ba7b85eb6ce80b69 - languageName: node - linkType: hard - -"@backstage/plugin-home@workspace:^, @backstage/plugin-home@workspace:plugins/home": +"@backstage/plugin-home@^0.5.1, @backstage/plugin-home@workspace:^, @backstage/plugin-home@workspace:plugins/home": version: 0.0.0-use.local resolution: "@backstage/plugin-home@workspace:plugins/home" dependencies: From 785694d143680dac9f21a6d6acbb5ef73dcdfe59 Mon Sep 17 00:00:00 2001 From: Malikah Montgomery Date: Wed, 17 May 2023 09:38:34 -0400 Subject: [PATCH 09/17] update changeset version, codeowners, and library type Signed-off-by: Malikah Montgomery --- .changeset/violet-pots-push.md | 2 +- .github/CODEOWNERS | 1 + plugins/home-react/package.json | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.changeset/violet-pots-push.md b/.changeset/violet-pots-push.md index 8e668977a9..f0074ed82e 100644 --- a/.changeset/violet-pots-push.md +++ b/.changeset/violet-pots-push.md @@ -1,6 +1,6 @@ --- '@backstage/plugin-stack-overflow': patch -'@backstage/plugin-home-react': patch +'@backstage/plugin-home-react': minor '@backstage/plugin-home': patch --- diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3880a0facd..7518fbab96 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -56,6 +56,7 @@ yarn.lock @backstage/maintainers @back /plugins/gcalendar @backstage/maintainers @szubster @ptychu @kielosz @alexrybch /plugins/git-release-manager @backstage/maintainers @erikengervall /plugins/home @backstage/discoverability-maintainers +/plugins/home-* @backstage/discoverability-maintainers /plugins/ilert @backstage/maintainers @yacut /plugins/jenkins @backstage/maintainers @timja /plugins/jenkins-backend @backstage/maintainers @timja diff --git a/plugins/home-react/package.json b/plugins/home-react/package.json index e32581bfd6..a001db5b53 100644 --- a/plugins/home-react/package.json +++ b/plugins/home-react/package.json @@ -11,7 +11,7 @@ "types": "dist/index.d.ts" }, "backstage": { - "role": "frontend-plugin" + "role": "web-library" }, "homepage": "https://github.com/backstage/backstage/tree/master/plugins/home-react#readme", "repository": { From b27f802f180e25fa3332939037b4bdb16853529d Mon Sep 17 00:00:00 2001 From: Malikah Montgomery Date: Wed, 17 May 2023 10:04:04 -0400 Subject: [PATCH 10/17] refactor how to export deprecated module Signed-off-by: Malikah Montgomery --- plugins/home/src/deprecated.ts | 21 +++++++++++++++++++++ plugins/home/src/index.ts | 10 +--------- 2 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 plugins/home/src/deprecated.ts diff --git a/plugins/home/src/deprecated.ts b/plugins/home/src/deprecated.ts new file mode 100644 index 0000000000..05c8537f99 --- /dev/null +++ b/plugins/home/src/deprecated.ts @@ -0,0 +1,21 @@ +/* + * 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 { createCardExtension } from './extensions'; + +// export { createCardExtension }; + +export * from './extensions'; diff --git a/plugins/home/src/index.ts b/plugins/home/src/index.ts index daa781f02c..63407c8531 100644 --- a/plugins/home/src/index.ts +++ b/plugins/home/src/index.ts @@ -36,12 +36,4 @@ export { export * from './components'; export * from './assets'; export * from './homePageComponents'; -export { createCardExtension } from './extensions'; -export type { - CardExtensionProps, - ComponentParts, - ComponentRenderer, - RendererProps, - CardLayout, - CardSettings, -} from './extensions'; +export * from './deprecated'; From b1c117375d9daa75d580d506843e2107c158ec6d Mon Sep 17 00:00:00 2001 From: kmarkow Date: Wed, 17 May 2023 11:16:22 -0400 Subject: [PATCH 11/17] Update api-report.md Signed-off-by: kmarkow --- plugins/home/api-report.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index 30ee6732cd..d06f5266eb 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -13,6 +13,12 @@ import { ReactNode } from 'react'; import { RJSFSchema } from '@rjsf/utils'; import { RouteRef } from '@backstage/core-plugin-api'; +// @public (undocumented) +export type CardConfig = { + layout?: CardLayout; + settings?: CardSettings; +}; + // @public @deprecated (undocumented) export type CardExtensionProps = ComponentRenderer & { title?: string; From 3c8761d5c64c656d4a8b981b0630b24a0d879a1c Mon Sep 17 00:00:00 2001 From: kmarkow Date: Wed, 17 May 2023 13:15:09 -0400 Subject: [PATCH 12/17] Code Review: Add depreacted.ts and remove the old duplicated code Signed-off-by: kmarkow --- plugins/home-react/api-report.md | 6 + plugins/home-react/src/index.ts | 1 + plugins/home/api-report.md | 94 ++++---- plugins/home/package.json | 1 + .../componentRenderers/ComponentAccordion.tsx | 3 +- .../CustomHomepage/CustomHomepageGrid.tsx | 2 +- plugins/home/src/components/index.ts | 1 - plugins/home/src/deprecated.ts | 60 +++++- plugins/home/src/extensions.tsx | 200 ------------------ plugins/home/src/plugin.ts | 2 +- 10 files changed, 109 insertions(+), 261 deletions(-) delete mode 100644 plugins/home/src/extensions.tsx diff --git a/plugins/home-react/api-report.md b/plugins/home-react/api-report.md index 9367fd61cc..42e8fd0b0f 100644 --- a/plugins/home-react/api-report.md +++ b/plugins/home-react/api-report.md @@ -8,6 +8,12 @@ import { Extension } from '@backstage/core-plugin-api'; import { RJSFSchema } from '@rjsf/utils'; +// @public (undocumented) +export type CardConfig = { + layout?: CardLayout; + settings?: CardSettings; +}; + // @public (undocumented) export type CardExtensionProps = ComponentRenderer & { title?: string; diff --git a/plugins/home-react/src/index.ts b/plugins/home-react/src/index.ts index 90c76d0836..1f02ded1c6 100644 --- a/plugins/home-react/src/index.ts +++ b/plugins/home-react/src/index.ts @@ -28,4 +28,5 @@ export type { RendererProps, CardLayout, CardSettings, + CardConfig, } from './extensions'; diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index d06f5266eb..20849501cd 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -6,42 +6,38 @@ /// import { BackstagePlugin } from '@backstage/core-plugin-api'; -import { Extension } from '@backstage/core-plugin-api'; +import { CardConfig as CardConfig_2 } from '@backstage/plugin-home-react'; +import { CardExtensionProps as CardExtensionProps_2 } from '@backstage/plugin-home-react'; +import { CardLayout as CardLayout_2 } from '@backstage/plugin-home-react'; +import { CardSettings as CardSettings_2 } from '@backstage/plugin-home-react'; +import { ComponentParts as ComponentParts_2 } from '@backstage/plugin-home-react'; +import { ComponentRenderer as ComponentRenderer_2 } from '@backstage/plugin-home-react'; +import { createCardExtension as createCardExtension_2 } from '@backstage/plugin-home-react'; import { default as React_2 } from 'react'; import { ReactElement } from 'react'; import { ReactNode } from 'react'; -import { RJSFSchema } from '@rjsf/utils'; +import { RendererProps as RendererProps_2 } from '@backstage/plugin-home-react'; import { RouteRef } from '@backstage/core-plugin-api'; -// @public (undocumented) -export type CardConfig = { - layout?: CardLayout; - settings?: CardSettings; -}; - +// Warning: (tsdoc-at-sign-in-word) The "@" character looks like part of a TSDoc tag; use a backslash to escape it +// // @public @deprecated (undocumented) -export type CardExtensionProps = ComponentRenderer & { - title?: string; -} & T; +export type CardConfig = CardConfig_2; +// Warning: (tsdoc-at-sign-in-word) The "@" character looks like part of a TSDoc tag; use a backslash to escape it +// // @public @deprecated (undocumented) -export type CardLayout = { - width?: { - minColumns?: number; - maxColumns?: number; - defaultColumns?: number; - }; - height?: { - minRows?: number; - maxRows?: number; - defaultRows?: number; - }; -}; +export type CardExtensionProps = CardExtensionProps_2; -// @public (undocumented) -export type CardSettings = { - schema?: RJSFSchema; -}; +// Warning: (tsdoc-at-sign-in-word) The "@" character looks like part of a TSDoc tag; use a backslash to escape it +// +// @public @deprecated (undocumented) +export type CardLayout = CardLayout_2; + +// Warning: (tsdoc-at-sign-in-word) The "@" character looks like part of a TSDoc tag; use a backslash to escape it +// +// @public @deprecated (undocumented) +export type CardSettings = CardSettings_2; // @public (undocumented) export type ClockConfig = { @@ -59,18 +55,15 @@ export const ComponentAccordion: (props: { ContextProvider?: ((props: any) => JSX.Element) | undefined; }) => JSX.Element; +// Warning: (tsdoc-at-sign-in-word) The "@" character looks like part of a TSDoc tag; use a backslash to escape it +// // @public @deprecated (undocumented) -export type ComponentParts = { - Content: (props?: any) => JSX.Element; - Actions?: () => JSX.Element; - Settings?: () => JSX.Element; - ContextProvider?: (props: any) => JSX.Element; -}; +export type ComponentParts = ComponentParts_2; +// Warning: (tsdoc-at-sign-in-word) The "@" character looks like part of a TSDoc tag; use a backslash to escape it +// // @public @deprecated (undocumented) -export type ComponentRenderer = { - Renderer?: (props: RendererProps) => JSX.Element; -}; +export type ComponentRenderer = ComponentRenderer_2; // @public (undocumented) export const ComponentTab: (props: { @@ -88,15 +81,10 @@ export const ComponentTabs: (props: { }[]; }) => JSX.Element; -// @public @deprecated -export function createCardExtension(options: { - title: string; - components: () => Promise; - name?: string; - description?: string; - layout?: CardLayout; - settings?: CardSettings; -}): Extension<(props: CardExtensionProps) => JSX.Element>; +// Warning: (tsdoc-at-sign-in-word) The "@" character looks like part of a TSDoc tag; use a backslash to escape it +// +// @public @deprecated (undocumented) +export const createCardExtension: typeof createCardExtension_2; // @public export const CustomHomepageGrid: ( @@ -130,19 +118,19 @@ export const HomepageCompositionRoot: (props: { // @public (undocumented) export const HomePageRandomJoke: ( - props: CardExtensionProps<{ + props: CardExtensionProps_2<{ defaultCategory?: 'any' | 'programming' | undefined; }>, ) => JSX.Element; // @public export const HomePageStarredEntities: ( - props: CardExtensionProps, + props: CardExtensionProps_2, ) => JSX.Element; // @public export const HomePageToolkit: ( - props: CardExtensionProps, + props: CardExtensionProps_2, ) => JSX.Element; // @public (undocumented) @@ -163,12 +151,14 @@ export type LayoutConfiguration = { height: number; }; +// Warning: (tsdoc-at-sign-in-word) The "@" character looks like part of a TSDoc tag; use a backslash to escape it +// // @public @deprecated (undocumented) -export type RendererProps = { - title: string; -} & ComponentParts; +export type RendererProps = RendererProps_2; -// @public (undocumented) +// Warning: (tsdoc-at-sign-in-word) The "@" character looks like part of a TSDoc tag; use a backslash to escape it +// +// @public @deprecated (undocumented) export const SettingsModal: (props: { open: boolean; close: Function; diff --git a/plugins/home/package.json b/plugins/home/package.json index ff714b16ca..c0b2b005df 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -38,6 +38,7 @@ "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", + "@backstage/plugin-home-react": "workspace:^", "@backstage/theme": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", diff --git a/plugins/home/src/componentRenderers/ComponentAccordion.tsx b/plugins/home/src/componentRenderers/ComponentAccordion.tsx index 8b9795c9c3..77e7561e55 100644 --- a/plugins/home/src/componentRenderers/ComponentAccordion.tsx +++ b/plugins/home/src/componentRenderers/ComponentAccordion.tsx @@ -15,6 +15,7 @@ */ import React from 'react'; +import { SettingsModal } from '@backstage/plugin-home-react'; import { Accordion, AccordionDetails, @@ -27,8 +28,6 @@ import { makeStyles } from '@material-ui/core/styles'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import SettingsIcon from '@material-ui/icons/Settings'; -import { SettingsModal } from '../components'; - const useStyles = makeStyles((theme: Theme) => ({ settingsIconButton: { padding: theme.spacing(0, 1, 0, 0), diff --git a/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx b/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx index 6af9044cbd..ae8e0dd9bb 100644 --- a/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx +++ b/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx @@ -42,7 +42,7 @@ import { LayoutConfigurationSchema, WidgetSchema, } from './types'; -import { CardConfig } from '../../extensions'; +import { CardConfig } from '@backstage/plugin-home-react'; // eslint-disable-next-line new-cap const ResponsiveGrid = WidthProvider(Responsive); diff --git a/plugins/home/src/components/index.ts b/plugins/home/src/components/index.ts index e06013d3fa..e528e0795d 100644 --- a/plugins/home/src/components/index.ts +++ b/plugins/home/src/components/index.ts @@ -15,5 +15,4 @@ */ export { HomepageCompositionRoot } from './HomepageCompositionRoot'; -export { SettingsModal } from './SettingsModal'; export * from './CustomHomepage'; diff --git a/plugins/home/src/deprecated.ts b/plugins/home/src/deprecated.ts index 05c8537f99..6fe9320316 100644 --- a/plugins/home/src/deprecated.ts +++ b/plugins/home/src/deprecated.ts @@ -14,8 +14,60 @@ * limitations under the License. */ -// import { createCardExtension } from './extensions'; +import { + createCardExtension as homeReactCreateCardExtension, + CardConfig as homeReactCardConfig, + CardExtensionProps as homeReactCardExtensionProps, + CardLayout as homeReactCardLayout, + CardSettings as homeReactCardSettings, + ComponentParts as homeReactComponentParts, + ComponentRenderer as homeReactComponentRenderer, + RendererProps as homeReactRendererProps, + SettingsModal as homeReactSettingsModal, +} from '@backstage/plugin-home-react'; -// export { createCardExtension }; - -export * from './extensions'; +/** + * @public + * @deprecated Import from '@backstage/plugin-home-react' instead + */ +export const createCardExtension = homeReactCreateCardExtension; +/** + * @public + * @deprecated Import from '@backstage/plugin-home-react' instead + */ +export type CardExtensionProps = homeReactCardExtensionProps; +/** + * @public + * @deprecated Import from '@backstage/plugin-home-react' instead + */ +export type CardLayout = homeReactCardLayout; +/** + * @public + * @deprecated Import from '@backstage/plugin-home-react' instead + */ +export type CardSettings = homeReactCardSettings; +/** + * @public + * @deprecated Import from '@backstage/plugin-home-react' instead + */ +export type CardConfig = homeReactCardConfig; +/** + * @public + * @deprecated Import from '@backstage/plugin-home-react' instead + */ +export type ComponentParts = homeReactComponentParts; +/** + * @public + * @deprecated Import from '@backstage/plugin-home-react' instead + */ +export type ComponentRenderer = homeReactComponentRenderer; +/** + * @public + * @deprecated Import from '@backstage/plugin-home-react' instead + */ +export type RendererProps = homeReactRendererProps; +/** + * @public + * @deprecated Import from '@backstage/plugin-home-react' instead + */ +export const SettingsModal = homeReactSettingsModal; diff --git a/plugins/home/src/extensions.tsx b/plugins/home/src/extensions.tsx deleted file mode 100644 index fdb9e9d67f..0000000000 --- a/plugins/home/src/extensions.tsx +++ /dev/null @@ -1,200 +0,0 @@ -/* - * 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 React, { Suspense } from 'react'; -import { IconButton } from '@material-ui/core'; -import SettingsIcon from '@material-ui/icons/Settings'; -import { InfoCard } from '@backstage/core-components'; -import { SettingsModal } from './components'; -import { createReactExtension, useApp } from '@backstage/core-plugin-api'; -import { RJSFSchema } from '@rjsf/utils'; - -/** - * @public - * @deprecated Please use the same type from `@backstage/plugin-home-react` instead - */ -export type ComponentRenderer = { - Renderer?: (props: RendererProps) => JSX.Element; -}; - -/** - * @public - * @deprecated Please use the same type from `@backstage/plugin-home-react` instead - */ -export type ComponentParts = { - Content: (props?: any) => JSX.Element; - Actions?: () => JSX.Element; - Settings?: () => JSX.Element; - ContextProvider?: (props: any) => JSX.Element; -}; - -/** - * @public - * @deprecated Please use the same type from `@backstage/plugin-home-react` instead - */ -export type RendererProps = { title: string } & ComponentParts; - -/** - * @public - * @deprecated Please use the same type from `@backstage/plugin-home-react` instead - */ -export type CardExtensionProps = ComponentRenderer & { title?: string } & T; - -/** - * @public - * @deprecated Please use the same type from `@backstage/plugin-home-react` instead - */ -export type CardLayout = { - width?: { minColumns?: number; maxColumns?: number; defaultColumns?: number }; - height?: { minRows?: number; maxRows?: number; defaultRows?: number }; -}; - -/** - * @public - */ -export type CardSettings = { - schema?: RJSFSchema; -}; - -/** - * @public - */ -export type CardConfig = { - layout?: CardLayout; - settings?: CardSettings; -}; - -/** - * An extension creator to create card based components for the homepage - * - * @public - * @deprecated Please use the same type from `@backstage/plugin-home-react` instead - */ -export function createCardExtension(options: { - title: string; - components: () => Promise; - name?: string; - description?: string; - layout?: CardLayout; - settings?: CardSettings; -}) { - const { title, components, name, description, layout, settings } = options; - // If widget settings schema is defined, we don't want to show the Settings icon or dialog - const isCustomizable = settings?.schema !== undefined; - - return createReactExtension({ - name, - data: { title, description, 'home.widget.config': { layout, settings } }, - component: { - lazy: () => - components().then(componentParts => { - return (props: CardExtensionProps) => { - return ( - - ); - }; - }), - }, - }); -} - -type CardExtensionComponentProps = CardExtensionProps & - ComponentParts & { - title: string; - isCustomizable?: boolean; - overrideTitle?: string; - }; - -function CardExtension(props: CardExtensionComponentProps) { - const { - Renderer, - Content, - Settings, - Actions, - ContextProvider, - isCustomizable, - title, - ...childProps - } = props; - const app = useApp(); - const { Progress } = app.getComponents(); - const [settingsOpen, setSettingsOpen] = React.useState(false); - - if (Renderer) { - return ( - }> - - - ); - } - - const cardProps = { - title: title, - ...(Settings && !isCustomizable - ? { - action: ( - setSettingsOpen(true)}> - Settings - - ), - } - : {}), - ...(Actions - ? { - actions: , - } - : {}), - }; - - const innerContent = ( - - {Settings && !isCustomizable && ( - setSettingsOpen(false)} - > - - - )} - - - ); - - return ( - }> - {ContextProvider ? ( - {innerContent} - ) : ( - innerContent - )} - - ); -} diff --git a/plugins/home/src/plugin.ts b/plugins/home/src/plugin.ts index 339e6dba2b..74a1c11390 100644 --- a/plugins/home/src/plugin.ts +++ b/plugins/home/src/plugin.ts @@ -19,7 +19,7 @@ import { createPlugin, createRoutableExtension, } from '@backstage/core-plugin-api'; -import { createCardExtension } from './extensions'; +import { createCardExtension } from '@backstage/plugin-home-react'; import { ToolkitContentProps } from './homePageComponents'; import { rootRouteRef } from './routes'; From d878bd873cbd43973e07b42ab6d353c7489299f0 Mon Sep 17 00:00:00 2001 From: kmarkow Date: Wed, 17 May 2023 13:17:53 -0400 Subject: [PATCH 13/17] Update yarn.lock Signed-off-by: kmarkow --- yarn.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/yarn.lock b/yarn.lock index f644a4e4a1..90101be828 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7077,6 +7077,7 @@ __metadata: "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" "@backstage/plugin-catalog-react": "workspace:^" + "@backstage/plugin-home-react": "workspace:^" "@backstage/test-utils": "workspace:^" "@backstage/theme": "workspace:^" "@material-ui/core": ^4.12.2 From 8d3ebb7b0c070d9fb88892bef56b5497ffcf202e Mon Sep 17 00:00:00 2001 From: kmarkow Date: Wed, 17 May 2023 13:27:25 -0400 Subject: [PATCH 14/17] Code Review: Update documentation Signed-off-by: kmarkow --- plugins/home/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/home/README.md b/plugins/home/README.md index 7da0034662..20b91289b1 100644 --- a/plugins/home/README.md +++ b/plugins/home/README.md @@ -51,6 +51,8 @@ The Home Page can be composed with regular React components, so there's no magic Finally, the `createCardExtension` also accepts a generic, such that Component Developers can indicate to App Integrators what custom props their component will accept, such as the example below where the default category of the random jokes can be set. ```tsx +import { createCardExtension } from '@backstage/plugin-home-react'; + export const RandomJokeHomePageComponent = homePlugin.provide( createCardExtension<{ defaultCategory?: 'programming' | 'any' }>({ title: 'Random Joke', @@ -121,6 +123,8 @@ want to add additional configuration like component size or settings, you can de property: ```tsx +import { createCardExtension } from '@backstage/plugin-home-react'; + export const RandomJokeHomePageComponent = homePlugin.provide( createCardExtension<{ defaultCategory?: 'any' | 'programming' }>({ name: 'HomePageRandomJoke', @@ -178,6 +182,8 @@ properties. The `settings.schema` object should follow must be `object`. ```tsx +import { createCardExtension } from '@backstage/plugin-home-react'; + export const HomePageRandomJoke = homePlugin.provide( createCardExtension<{ defaultCategory?: 'any' | 'programming' }>({ name: 'HomePageRandomJoke', From e37c2b4590d7b343d5ff1101b53f8f2269bf5b64 Mon Sep 17 00:00:00 2001 From: kmarkow Date: Wed, 17 May 2023 14:24:43 -0400 Subject: [PATCH 15/17] Fix api-report in plugins/home package Signed-off-by: kmarkow --- plugins/home/api-report.md | 18 ------------------ plugins/home/src/deprecated.ts | 18 +++++++++--------- 2 files changed, 9 insertions(+), 27 deletions(-) diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index 20849501cd..56b1f54331 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -19,23 +19,15 @@ import { ReactNode } from 'react'; import { RendererProps as RendererProps_2 } from '@backstage/plugin-home-react'; import { RouteRef } from '@backstage/core-plugin-api'; -// Warning: (tsdoc-at-sign-in-word) The "@" character looks like part of a TSDoc tag; use a backslash to escape it -// // @public @deprecated (undocumented) export type CardConfig = CardConfig_2; -// Warning: (tsdoc-at-sign-in-word) The "@" character looks like part of a TSDoc tag; use a backslash to escape it -// // @public @deprecated (undocumented) export type CardExtensionProps = CardExtensionProps_2; -// Warning: (tsdoc-at-sign-in-word) The "@" character looks like part of a TSDoc tag; use a backslash to escape it -// // @public @deprecated (undocumented) export type CardLayout = CardLayout_2; -// Warning: (tsdoc-at-sign-in-word) The "@" character looks like part of a TSDoc tag; use a backslash to escape it -// // @public @deprecated (undocumented) export type CardSettings = CardSettings_2; @@ -55,13 +47,9 @@ export const ComponentAccordion: (props: { ContextProvider?: ((props: any) => JSX.Element) | undefined; }) => JSX.Element; -// Warning: (tsdoc-at-sign-in-word) The "@" character looks like part of a TSDoc tag; use a backslash to escape it -// // @public @deprecated (undocumented) export type ComponentParts = ComponentParts_2; -// Warning: (tsdoc-at-sign-in-word) The "@" character looks like part of a TSDoc tag; use a backslash to escape it -// // @public @deprecated (undocumented) export type ComponentRenderer = ComponentRenderer_2; @@ -81,8 +69,6 @@ export const ComponentTabs: (props: { }[]; }) => JSX.Element; -// Warning: (tsdoc-at-sign-in-word) The "@" character looks like part of a TSDoc tag; use a backslash to escape it -// // @public @deprecated (undocumented) export const createCardExtension: typeof createCardExtension_2; @@ -151,13 +137,9 @@ export type LayoutConfiguration = { height: number; }; -// Warning: (tsdoc-at-sign-in-word) The "@" character looks like part of a TSDoc tag; use a backslash to escape it -// // @public @deprecated (undocumented) export type RendererProps = RendererProps_2; -// Warning: (tsdoc-at-sign-in-word) The "@" character looks like part of a TSDoc tag; use a backslash to escape it -// // @public @deprecated (undocumented) export const SettingsModal: (props: { open: boolean; diff --git a/plugins/home/src/deprecated.ts b/plugins/home/src/deprecated.ts index 6fe9320316..119e5ab731 100644 --- a/plugins/home/src/deprecated.ts +++ b/plugins/home/src/deprecated.ts @@ -28,46 +28,46 @@ import { /** * @public - * @deprecated Import from '@backstage/plugin-home-react' instead + * @deprecated Import from `@backstage/plugin-home-react` instead */ export const createCardExtension = homeReactCreateCardExtension; /** * @public - * @deprecated Import from '@backstage/plugin-home-react' instead + * @deprecated Import from `@backstage/plugin-home-react` instead */ export type CardExtensionProps = homeReactCardExtensionProps; /** * @public - * @deprecated Import from '@backstage/plugin-home-react' instead + * @deprecated Import from `@backstage/plugin-home-react` instead */ export type CardLayout = homeReactCardLayout; /** * @public - * @deprecated Import from '@backstage/plugin-home-react' instead + * @deprecated Import from `@backstage/plugin-home-react` instead */ export type CardSettings = homeReactCardSettings; /** * @public - * @deprecated Import from '@backstage/plugin-home-react' instead + * @deprecated Import from `@backstage/plugin-home-react` instead */ export type CardConfig = homeReactCardConfig; /** * @public - * @deprecated Import from '@backstage/plugin-home-react' instead + * @deprecated Import from `@backstage/plugin-home-react` instead */ export type ComponentParts = homeReactComponentParts; /** * @public - * @deprecated Import from '@backstage/plugin-home-react' instead + * @deprecated Import from `@backstage/plugin-home-react` instead */ export type ComponentRenderer = homeReactComponentRenderer; /** * @public - * @deprecated Import from '@backstage/plugin-home-react' instead + * @deprecated Import from `@backstage/plugin-home-react` instead */ export type RendererProps = homeReactRendererProps; /** * @public - * @deprecated Import from '@backstage/plugin-home-react' instead + * @deprecated Import from `@backstage/plugin-home-react` instead */ export const SettingsModal = homeReactSettingsModal; From 8ccd534ec095323dde4571542afe964e1b3921ad Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Wed, 17 May 2023 20:35:43 +0200 Subject: [PATCH 16/17] fix(home): remove duplicated settings modal Signed-off-by: Camila Belo --- plugins/home/src/components/SettingsModal.tsx | 46 ------------------- 1 file changed, 46 deletions(-) delete mode 100644 plugins/home/src/components/SettingsModal.tsx diff --git a/plugins/home/src/components/SettingsModal.tsx b/plugins/home/src/components/SettingsModal.tsx deleted file mode 100644 index ace9944f28..0000000000 --- a/plugins/home/src/components/SettingsModal.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 React from 'react'; -import { - Button, - Dialog, - DialogActions, - DialogContent, - DialogTitle, -} from '@material-ui/core'; - -/** @public */ -export const SettingsModal = (props: { - open: boolean; - close: Function; - componentName: string; - children: JSX.Element; -}) => { - const { open, close, componentName, children } = props; - - return ( - close()}> - Settings - {componentName} - {children} - - - - - ); -}; From 8a42303a0e4eea955785a9860bde3f78b4d691bf Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Wed, 17 May 2023 20:36:31 +0200 Subject: [PATCH 17/17] fix(home): remove api report warnings Signed-off-by: Camila Belo --- plugins/home/src/deprecated.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/home/src/deprecated.ts b/plugins/home/src/deprecated.ts index 119e5ab731..f116d2c9f9 100644 --- a/plugins/home/src/deprecated.ts +++ b/plugins/home/src/deprecated.ts @@ -31,41 +31,49 @@ import { * @deprecated Import from `@backstage/plugin-home-react` instead */ export const createCardExtension = homeReactCreateCardExtension; + /** * @public * @deprecated Import from `@backstage/plugin-home-react` instead */ export type CardExtensionProps = homeReactCardExtensionProps; + /** * @public * @deprecated Import from `@backstage/plugin-home-react` instead */ export type CardLayout = homeReactCardLayout; + /** * @public * @deprecated Import from `@backstage/plugin-home-react` instead */ export type CardSettings = homeReactCardSettings; + /** * @public * @deprecated Import from `@backstage/plugin-home-react` instead */ export type CardConfig = homeReactCardConfig; + /** * @public * @deprecated Import from `@backstage/plugin-home-react` instead */ export type ComponentParts = homeReactComponentParts; + /** * @public * @deprecated Import from `@backstage/plugin-home-react` instead */ export type ComponentRenderer = homeReactComponentRenderer; + /** * @public * @deprecated Import from `@backstage/plugin-home-react` instead */ export type RendererProps = homeReactRendererProps; + /** * @public * @deprecated Import from `@backstage/plugin-home-react` instead