@backstage/plugin-home 0.4.33-next.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # @backstage/plugin-home
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 760f521b979: Add support for customizable homepage.
8
+
9
+ Allows customizing homepage components, their placement, size and
10
+ individual settings. For maximum size and settings, the existing home
11
+ components should add necessary data attributes to their components.
12
+
13
+ See `plugins/home/README.md` for more information how to configure
14
+ the customizable homepage.
15
+
16
+ ### Patch Changes
17
+
18
+ - 8e00acb28db: Small tweaks to remove warnings in the console during development (mainly focusing on techdocs)
19
+ - e0c6e8b9c3c: Update peer dependencies
20
+ - Updated dependencies
21
+ - @backstage/core-components@0.13.0
22
+ - @backstage/plugin-catalog-react@1.5.0
23
+ - @backstage/theme@0.2.19
24
+ - @backstage/core-plugin-api@1.5.1
25
+ - @backstage/catalog-model@1.3.0
26
+ - @backstage/config@1.0.7
27
+
3
28
  ## 0.4.33-next.3
4
29
 
5
30
  ### Patch Changes
package/README.md CHANGED
@@ -66,23 +66,171 @@ In summary: it is not necessary to use the `createCardExtension` extension creat
66
66
  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
67
67
 
68
68
  ```tsx
69
- import React from 'react'
70
- import Grid from '@material-ui/core/Grid'
69
+ import React from 'react';
70
+ import Grid from '@material-ui/core/Grid';
71
71
  import { RandomJokeHomePageComponent } from '@backstage/plugin-home';
72
72
 
73
73
  export const HomePage = () => {
74
74
  return (
75
75
  <Grid container spacing={3}>
76
76
  <Grid item xs={12} md={4}>
77
- <RandomJokeHomePageComponent>
77
+ <RandomJokeHomePageComponent />
78
78
  </Grid>
79
79
  </Grid>
80
- )
81
- }
80
+ );
81
+ };
82
82
  ```
83
83
 
84
84
  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.
85
85
 
86
+ ## Customizable home page
87
+
88
+ If you want to allow users to customize the components that are shown in the home page, you can use CustomHomePageGrid component.
89
+ By adding the allowed components inside the grid, the user can add, configure, remove and move the components around in their
90
+ home page. The user configuration is also saved and restored in the process for later use.
91
+
92
+ ```tsx
93
+ import {
94
+ HomePageRandomJoke,
95
+ HomePageStarredEntities,
96
+ CustomHomepageGrid,
97
+ } from '@backstage/plugin-home';
98
+ import { Content, Header, Page } from '@backstage/core-components';
99
+ import { HomePageSearchBar } from '@backstage/plugin-search';
100
+ import { HomePageCalendar } from '@backstage/plugin-gcalendar';
101
+ import { MicrosoftCalendarCard } from '@backstage/plugin-microsoft-calendar';
102
+
103
+ export const HomePage = () => {
104
+ return (
105
+ <CustomHomepageGrid>
106
+ // Insert the allowed widgets inside the grid
107
+ <HomePageSearchBar />
108
+ <HomePageRandomJoke />
109
+ <HomePageCalendar />
110
+ <MicrosoftCalendarCard />
111
+ <HomePageStarredEntities />
112
+ </CustomHomepageGrid>
113
+ );
114
+ };
115
+ ```
116
+
117
+ ### Creating Customizable Components
118
+
119
+ The custom home page can use the default components created by using the default `createCardExtension` method but if you
120
+ want to add additional configuration like component size or settings, you can define those in the `layout`
121
+ property:
122
+
123
+ ```tsx
124
+ export const RandomJokeHomePageComponent = homePlugin.provide(
125
+ createCardExtension<{ defaultCategory?: 'any' | 'programming' }>({
126
+ name: 'HomePageRandomJoke',
127
+ title: 'Random Joke',
128
+ components: () => import('./homePageComponents/RandomJoke'),
129
+ layout: {
130
+ height: { minRows: 7 },
131
+ width: { minColumns: 3 },
132
+ },
133
+ }),
134
+ );
135
+ ```
136
+
137
+ These settings can also be defined for components that use `createReactExtension` instead `createCardExtension` by using
138
+ the data property:
139
+
140
+ ```tsx
141
+ export const HomePageSearchBar = searchPlugin.provide(
142
+ createReactExtension({
143
+ name: 'HomePageSearchBar',
144
+ component: {
145
+ lazy: () =>
146
+ import('./components/HomePageComponent').then(m => m.HomePageSearchBar),
147
+ },
148
+ data: {
149
+ 'home.widget.config': {
150
+ layout: {
151
+ height: { maxRows: 1 },
152
+ },
153
+ },
154
+ },
155
+ }),
156
+ );
157
+ ```
158
+
159
+ Available home page properties that are used for homepage widgets are:
160
+
161
+ | Key | Type | Description |
162
+ | ----------------------------- | ------- | ------------------------------------------------------------ |
163
+ | `title` | string | User friend title. Shown when user adds widgets to homepage |
164
+ | `description` | string | Widget description. Shown when user adds widgets to homepage |
165
+ | `layout.width.defaultColumns` | integer | Default width of the widget (1-12) |
166
+ | `layout.width.minColumns` | integer | Minimum width of the widget (1-12) |
167
+ | `layout.width.maxColumns` | integer | Maximum width of the widget (1-12) |
168
+ | `layout.height.defaultRows` | integer | Default height of the widget (1-12) |
169
+ | `layout.height.minRows` | integer | Minimum height of the widget (1-12) |
170
+ | `layout.height.maxRows` | integer | Maximum height of the widget (1-12) |
171
+ | `settings.schema` | object | Customization settings of the widget, see below |
172
+
173
+ #### Widget Specific Settings
174
+
175
+ To define settings that the users can change for your component, you should define the `layout` and `settings`
176
+ properties. The `settings.schema` object should follow
177
+ [react-jsonschema-form](https://rjsf-team.github.io/react-jsonschema-form/docs/) definition and the type of the schema
178
+ must be `object`.
179
+
180
+ ```tsx
181
+ export const HomePageRandomJoke = homePlugin.provide(
182
+ createCardExtension<{ defaultCategory?: 'any' | 'programming' }>({
183
+ name: 'HomePageRandomJoke',
184
+ title: 'Random Joke',
185
+ components: () => import('./homePageComponents/RandomJoke'),
186
+ description: 'Shows a random joke about optional category',
187
+ layout: {
188
+ height: { minRows: 4 },
189
+ width: { minColumns: 3 },
190
+ },
191
+ settings: {
192
+ schema: {
193
+ title: 'Random Joke settings',
194
+ type: 'object',
195
+ properties: {
196
+ defaultCategory: {
197
+ title: 'Category',
198
+ type: 'string',
199
+ enum: ['any', 'programming', 'dad'],
200
+ default: 'any',
201
+ },
202
+ },
203
+ },
204
+ },
205
+ }),
206
+ );
207
+ ```
208
+
209
+ This allows the user to select `defaultCategory` for the RandomJoke widgets that are added to the homepage.
210
+ Each widget has its own settings and the setting values are passed to the underlying React component in props.
211
+
212
+ In case your `CardExtension` had `Settings` component defined, it will automatically disappear when you add the
213
+ `settingsSchema` to the component data structure.
214
+
215
+ ### Adding Default Layout
216
+
217
+ You can set the default layout of the customizable home page by passing configuration to the `CustomHomepageGrid`
218
+ component:
219
+
220
+ ```tsx
221
+ const defaultConfig = [
222
+ {
223
+ component: <HomePageSearchBar />, // Or 'HomePageSearchBar' as a string if you know the component name
224
+ x: 0,
225
+ y: 0,
226
+ width: 12,
227
+ height: 1,
228
+ },
229
+ ];
230
+
231
+ <CustomHomepageGrid config={defaultConfig}>
232
+ ```
233
+
86
234
  ## Contributing
87
235
 
88
236
  ### Homepage Components
@@ -7,6 +7,23 @@ import 'react-router-dom';
7
7
  import { SettingsModal } from '../index.esm.js';
8
8
  import { InfoCard } from '@backstage/core-components';
9
9
  import '@backstage/core-plugin-api';
10
+ import 'react-grid-layout';
11
+ import 'react-grid-layout/css/styles.css';
12
+ import 'react-resizable/css/styles.css';
13
+ import 'lodash';
14
+ import 'react-use/lib/useObservable';
15
+ import '@material-ui/core/Typography';
16
+ import '@material-ui/core/IconButton';
17
+ import '@material-ui/icons/Delete';
18
+ import '@rjsf/material-ui';
19
+ import '@material-ui/core/List';
20
+ import '@material-ui/core/ListItem';
21
+ import '@material-ui/icons/Add';
22
+ import '@material-ui/core/ListItemText';
23
+ import '@material-ui/core/Button';
24
+ import '@material-ui/icons/Save';
25
+ import '@material-ui/icons/Edit';
26
+ import 'zod';
10
27
 
11
28
  const useStyles = makeStyles((theme) => ({
12
29
  settingsIconButton: {
@@ -82,4 +99,4 @@ const ComponentTab = (props) => {
82
99
  };
83
100
 
84
101
  export { ComponentAccordion, ComponentTab, ComponentTabs };
85
- //# sourceMappingURL=index-6bbcec36.esm.js.map
102
+ //# sourceMappingURL=index-93442718.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-6bbcec36.esm.js","sources":["../../src/componentRenderers/ComponentAccordion.tsx","../../src/componentRenderers/ComponentTabs/ComponentTabs.tsx","../../src/componentRenderers/ComponentTabs/ComponentTab.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React from 'react';\nimport {\n Accordion,\n AccordionDetails,\n AccordionSummary,\n Typography,\n IconButton,\n Theme,\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport SettingsIcon from '@material-ui/icons/Settings';\n\nimport { SettingsModal } from '../components';\n\nconst useStyles = makeStyles((theme: Theme) => ({\n settingsIconButton: {\n padding: theme.spacing(0, 1, 0, 0),\n },\n contentContainer: {\n width: '100%',\n },\n}));\n\nexport const ComponentAccordion = (props: {\n title: string;\n expanded?: boolean;\n Content: () => JSX.Element;\n Actions?: () => JSX.Element;\n Settings?: () => JSX.Element;\n ContextProvider?: (props: any) => JSX.Element;\n}) => {\n const {\n title,\n expanded = false,\n Content,\n Actions,\n Settings,\n ContextProvider,\n ...childProps\n } = props;\n\n const classes = useStyles();\n const [settingsIsExpanded, setSettingsIsExpanded] = React.useState(false);\n const [isExpanded, setIsExpanded] = React.useState(expanded);\n\n const handleOpenSettings = (e: any) => {\n e.stopPropagation();\n setSettingsIsExpanded(prevState => !prevState);\n };\n\n const innerContent = (\n <>\n {Settings && (\n <SettingsModal\n open={settingsIsExpanded}\n close={() => setSettingsIsExpanded(false)}\n componentName={title}\n >\n <Settings />\n </SettingsModal>\n )}\n <Accordion\n expanded={isExpanded}\n onChange={(_e: any, expandedValue: boolean) =>\n setIsExpanded(expandedValue)\n }\n >\n <AccordionSummary expandIcon={<ExpandMoreIcon />}>\n {Settings && (\n <IconButton\n onClick={handleOpenSettings}\n className={classes.settingsIconButton}\n >\n <SettingsIcon />\n </IconButton>\n )}\n <Typography>{title}</Typography>\n </AccordionSummary>\n <AccordionDetails>\n <div className={classes.contentContainer}>\n <Content />\n {Actions && <Actions />}\n </div>\n </AccordionDetails>\n </Accordion>\n </>\n );\n\n return ContextProvider ? (\n <ContextProvider {...childProps}>{innerContent}</ContextProvider>\n ) : (\n innerContent\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React from 'react';\nimport { Tabs, Tab } from '@material-ui/core';\nimport { InfoCard } from '@backstage/core-components';\n\ntype TabType = {\n label: string;\n Component: () => JSX.Element;\n};\n\nexport const ComponentTabs = (props: { title: string; tabs: TabType[] }) => {\n const { title, tabs } = props;\n\n const [value, setValue] = React.useState(0);\n\n const handleChange = (_event: any, newValue: number) => {\n setValue(newValue);\n };\n\n return (\n <InfoCard title={title}>\n <Tabs value={value} onChange={handleChange}>\n {tabs.map(t => (\n <Tab key={t.label} label={t.label} />\n ))}\n </Tabs>\n {tabs.map(({ Component }, idx) => (\n <div\n key={idx}\n {...(idx !== value ? { style: { display: 'none' } } : {})}\n >\n <Component />\n </div>\n ))}\n </InfoCard>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React from 'react';\n\nexport const ComponentTab = (props: {\n title: string;\n Content: () => JSX.Element;\n ContextProvider?: (props: any) => JSX.Element;\n}) => {\n const { title, Content, ContextProvider, ...childProps } = props;\n\n return ContextProvider ? (\n <ContextProvider {...childProps}>\n <Content />\n </ContextProvider>\n ) : (\n <Content />\n );\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index-93442718.esm.js","sources":["../../src/componentRenderers/ComponentAccordion.tsx","../../src/componentRenderers/ComponentTabs/ComponentTabs.tsx","../../src/componentRenderers/ComponentTabs/ComponentTab.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React from 'react';\nimport {\n Accordion,\n AccordionDetails,\n AccordionSummary,\n Typography,\n IconButton,\n Theme,\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\nimport ExpandMoreIcon from '@material-ui/icons/ExpandMore';\nimport SettingsIcon from '@material-ui/icons/Settings';\n\nimport { SettingsModal } from '../components';\n\nconst useStyles = makeStyles((theme: Theme) => ({\n settingsIconButton: {\n padding: theme.spacing(0, 1, 0, 0),\n },\n contentContainer: {\n width: '100%',\n },\n}));\n\nexport const ComponentAccordion = (props: {\n title: string;\n expanded?: boolean;\n Content: () => JSX.Element;\n Actions?: () => JSX.Element;\n Settings?: () => JSX.Element;\n ContextProvider?: (props: any) => JSX.Element;\n}) => {\n const {\n title,\n expanded = false,\n Content,\n Actions,\n Settings,\n ContextProvider,\n ...childProps\n } = props;\n\n const classes = useStyles();\n const [settingsIsExpanded, setSettingsIsExpanded] = React.useState(false);\n const [isExpanded, setIsExpanded] = React.useState(expanded);\n\n const handleOpenSettings = (e: any) => {\n e.stopPropagation();\n setSettingsIsExpanded(prevState => !prevState);\n };\n\n const innerContent = (\n <>\n {Settings && (\n <SettingsModal\n open={settingsIsExpanded}\n close={() => setSettingsIsExpanded(false)}\n componentName={title}\n >\n <Settings />\n </SettingsModal>\n )}\n <Accordion\n expanded={isExpanded}\n onChange={(_e: any, expandedValue: boolean) =>\n setIsExpanded(expandedValue)\n }\n >\n <AccordionSummary expandIcon={<ExpandMoreIcon />}>\n {Settings && (\n <IconButton\n onClick={handleOpenSettings}\n className={classes.settingsIconButton}\n >\n <SettingsIcon />\n </IconButton>\n )}\n <Typography>{title}</Typography>\n </AccordionSummary>\n <AccordionDetails>\n <div className={classes.contentContainer}>\n <Content />\n {Actions && <Actions />}\n </div>\n </AccordionDetails>\n </Accordion>\n </>\n );\n\n return ContextProvider ? (\n <ContextProvider {...childProps}>{innerContent}</ContextProvider>\n ) : (\n innerContent\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React from 'react';\nimport { Tabs, Tab } from '@material-ui/core';\nimport { InfoCard } from '@backstage/core-components';\n\ntype TabType = {\n label: string;\n Component: () => JSX.Element;\n};\n\nexport const ComponentTabs = (props: { title: string; tabs: TabType[] }) => {\n const { title, tabs } = props;\n\n const [value, setValue] = React.useState(0);\n\n const handleChange = (_event: any, newValue: number) => {\n setValue(newValue);\n };\n\n return (\n <InfoCard title={title}>\n <Tabs value={value} onChange={handleChange}>\n {tabs.map(t => (\n <Tab key={t.label} label={t.label} />\n ))}\n </Tabs>\n {tabs.map(({ Component }, idx) => (\n <div\n key={idx}\n {...(idx !== value ? { style: { display: 'none' } } : {})}\n >\n <Component />\n </div>\n ))}\n </InfoCard>\n );\n};\n","/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React from 'react';\n\nexport const ComponentTab = (props: {\n title: string;\n Content: () => JSX.Element;\n ContextProvider?: (props: any) => JSX.Element;\n}) => {\n const { title, Content, ContextProvider, ...childProps } = props;\n\n return ContextProvider ? (\n <ContextProvider {...childProps}>\n <Content />\n </ContextProvider>\n ) : (\n <Content />\n );\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import React from 'react';
2
+ import { useOutlet } from 'react-router-dom';
3
+ export { CustomHomepageGrid, SettingsModal } from '../index.esm.js';
4
+ import '@backstage/core-plugin-api';
5
+ import '@material-ui/core';
6
+ import '@material-ui/icons/Settings';
7
+ import '@backstage/core-components';
8
+ import 'react-grid-layout';
9
+ import 'react-grid-layout/css/styles.css';
10
+ import 'react-resizable/css/styles.css';
11
+ import 'lodash';
12
+ import 'react-use/lib/useObservable';
13
+ import '@material-ui/core/Typography';
14
+ import '@material-ui/core/IconButton';
15
+ import '@material-ui/icons/Delete';
16
+ import '@rjsf/material-ui';
17
+ import '@material-ui/core/List';
18
+ import '@material-ui/core/ListItem';
19
+ import '@material-ui/icons/Add';
20
+ import '@material-ui/core/ListItemText';
21
+ import '@material-ui/core/Button';
22
+ import '@material-ui/icons/Save';
23
+ import '@material-ui/icons/Edit';
24
+ import 'zod';
25
+
26
+ const HomepageCompositionRoot = (props) => {
27
+ var _a;
28
+ const outlet = useOutlet();
29
+ const children = (_a = props.children) != null ? _a : outlet;
30
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, children);
31
+ };
32
+
33
+ export { HomepageCompositionRoot };
34
+ //# sourceMappingURL=index-a42ec1b1.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-236adacf.esm.js","sources":["../../src/components/HomepageCompositionRoot.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { ReactNode } from 'react';\nimport { useOutlet } from 'react-router-dom';\n\nexport const HomepageCompositionRoot = (props: {\n title?: string;\n children?: ReactNode;\n}) => {\n const outlet = useOutlet();\n const children = props.children ?? outlet;\n return <>{children}</>;\n};\n"],"names":[],"mappings":";;;;;;;;AAmBa,MAAA,uBAAA,GAA0B,CAAC,KAGlC,KAAA;AAtBN,EAAA,IAAA,EAAA,CAAA;AAuBE,EAAA,MAAM,SAAS,SAAU,EAAA,CAAA;AACzB,EAAM,MAAA,QAAA,GAAA,CAAW,EAAM,GAAA,KAAA,CAAA,QAAA,KAAN,IAAkB,GAAA,EAAA,GAAA,MAAA,CAAA;AACnC,EAAA,iEAAU,QAAS,CAAA,CAAA;AACrB;;;;"}
1
+ {"version":3,"file":"index-a42ec1b1.esm.js","sources":["../../src/components/HomepageCompositionRoot.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { ReactNode } from 'react';\nimport { useOutlet } from 'react-router-dom';\n\nexport const HomepageCompositionRoot = (props: {\n title?: string;\n children?: ReactNode;\n}) => {\n const outlet = useOutlet();\n const children = props.children ?? outlet;\n return <>{children}</>;\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAmBa,MAAA,uBAAA,GAA0B,CAAC,KAGlC,KAAA;AAtBN,EAAA,IAAA,EAAA,CAAA;AAuBE,EAAA,MAAM,SAAS,SAAU,EAAA,CAAA;AACzB,EAAM,MAAA,QAAA,GAAA,CAAW,EAAM,GAAA,KAAA,CAAA,QAAA,KAAN,IAAkB,GAAA,EAAA,GAAA,MAAA,CAAA;AACnC,EAAA,iEAAU,QAAS,CAAA,CAAA;AACrB;;;;"}
package/dist/index.d.ts CHANGED
@@ -1,13 +1,14 @@
1
1
  /// <reference types="react" />
2
- import * as React from 'react';
3
- import React__default from 'react';
2
+ import * as react from 'react';
3
+ import react__default, { ReactElement, ReactNode } from 'react';
4
4
  import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
5
+ import { RJSFSchema } from '@rjsf/utils';
5
6
 
6
7
  /** @public */
7
8
  type Tool = {
8
9
  label: string;
9
10
  url: string;
10
- icon: React__default.ReactNode;
11
+ icon: react__default.ReactNode;
11
12
  };
12
13
 
13
14
  /**
@@ -52,6 +53,27 @@ type RendererProps = {
52
53
  type CardExtensionProps<T> = ComponentRenderer & {
53
54
  title?: string;
54
55
  } & T;
56
+ /**
57
+ * @public
58
+ */
59
+ type CardLayout = {
60
+ width?: {
61
+ minColumns?: number;
62
+ maxColumns?: number;
63
+ defaultColumns?: number;
64
+ };
65
+ height?: {
66
+ minRows?: number;
67
+ maxRows?: number;
68
+ defaultRows?: number;
69
+ };
70
+ };
71
+ /**
72
+ * @public
73
+ */
74
+ type CardSettings = {
75
+ schema?: RJSFSchema;
76
+ };
55
77
  /**
56
78
  * An extension creator to create card based components for the homepage
57
79
  *
@@ -61,6 +83,9 @@ declare function createCardExtension<T>(options: {
61
83
  title: string;
62
84
  components: () => Promise<ComponentParts>;
63
85
  name?: string;
86
+ description?: string;
87
+ layout?: CardLayout;
88
+ settings?: CardSettings;
64
89
  }): _backstage_core_plugin_api.Extension<(props: CardExtensionProps<T>) => JSX.Element>;
65
90
 
66
91
  /** @public */
@@ -70,7 +95,7 @@ declare const homePlugin: _backstage_core_plugin_api.BackstagePlugin<{
70
95
  /** @public */
71
96
  declare const HomepageCompositionRoot: (props: {
72
97
  title?: string | undefined;
73
- children?: React.ReactNode;
98
+ children?: react.ReactNode;
74
99
  }) => JSX.Element;
75
100
  /** @public */
76
101
  declare const ComponentAccordion: (props: {
@@ -107,7 +132,7 @@ declare const WelcomeTitle: () => JSX.Element;
107
132
  * @public
108
133
  */
109
134
  declare const HomePageCompanyLogo: (props: {
110
- logo?: React.ReactNode;
135
+ logo?: react.ReactNode;
111
136
  className?: string | undefined;
112
137
  }) => JSX.Element;
113
138
  /** @public */
@@ -144,6 +169,34 @@ declare const SettingsModal: (props: {
144
169
  children: JSX.Element;
145
170
  }) => JSX.Element;
146
171
 
172
+ /**
173
+ * Layout configuration that can be passed to the custom home page.
174
+ *
175
+ * @public
176
+ */
177
+ type LayoutConfiguration = {
178
+ component: ReactElement | string;
179
+ x: number;
180
+ y: number;
181
+ width: number;
182
+ height: number;
183
+ };
184
+
185
+ /**
186
+ * @public
187
+ */
188
+ type CustomHomepageGridProps = {
189
+ children?: ReactNode;
190
+ config?: LayoutConfiguration[];
191
+ rowHeight?: number;
192
+ };
193
+ /**
194
+ * A component that allows customizing components in home grid layout.
195
+ *
196
+ * @public
197
+ */
198
+ declare const CustomHomepageGrid: (props: CustomHomepageGridProps) => JSX.Element;
199
+
147
200
  /** @public */
148
201
  declare const TemplateBackstageLogo: (props: {
149
202
  classes: {
@@ -155,4 +208,4 @@ declare const TemplateBackstageLogo: (props: {
155
208
  /** @public */
156
209
  declare const TemplateBackstageLogoIcon: () => JSX.Element;
157
210
 
158
- export { CardExtensionProps, ClockConfig, ComponentAccordion, ComponentParts, ComponentRenderer, ComponentTab, ComponentTabs, HeaderWorldClock, HomePageCompanyLogo, HomePageRandomJoke, HomePageStarredEntities, HomePageToolkit, HomepageCompositionRoot, RendererProps, SettingsModal, TemplateBackstageLogo, TemplateBackstageLogoIcon, Tool, ToolkitContentProps, WelcomeTitle, createCardExtension, homePlugin };
211
+ export { CardExtensionProps, CardLayout, CardSettings, ClockConfig, ComponentAccordion, ComponentParts, ComponentRenderer, ComponentTab, ComponentTabs, CustomHomepageGrid, CustomHomepageGridProps, HeaderWorldClock, HomePageCompanyLogo, HomePageRandomJoke, HomePageStarredEntities, HomePageToolkit, HomepageCompositionRoot, LayoutConfiguration, RendererProps, SettingsModal, TemplateBackstageLogo, TemplateBackstageLogoIcon, Tool, ToolkitContentProps, WelcomeTitle, createCardExtension, homePlugin };