@backstage/plugin-home 0.9.9-next.0 → 0.9.9-next.2
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 +23 -0
- package/README.md +3 -642
- package/dist/alpha/DefaultHomePageLayout.esm.js +16 -3
- package/dist/alpha/DefaultHomePageLayout.esm.js.map +1 -1
- package/dist/alpha.d.ts +79 -2
- package/dist/alpha.esm.js +112 -21
- package/dist/alpha.esm.js.map +1 -1
- package/dist/components/CustomHomepage/CustomHomepageGrid.esm.js +32 -11
- package/dist/components/CustomHomepage/CustomHomepageGrid.esm.js.map +1 -1
- package/dist/homePageComponents/HeaderWorldClock/HeaderWorldClock.esm.js +1 -37
- package/dist/homePageComponents/HeaderWorldClock/HeaderWorldClock.esm.js.map +1 -1
- package/dist/homePageComponents/Toolkit/Content.esm.js +4 -1
- package/dist/homePageComponents/Toolkit/Content.esm.js.map +1 -1
- package/dist/homePageComponents/VisitedByType/Content.esm.js +20 -6
- package/dist/homePageComponents/VisitedByType/Content.esm.js.map +1 -1
- package/dist/homePageComponents/VisitedByType/RecentlyVisited.esm.js +3 -3
- package/dist/homePageComponents/VisitedByType/TopVisited.esm.js +3 -3
- package/dist/homePageComponents/WorldClock/WorldClock.esm.js +55 -0
- package/dist/homePageComponents/WorldClock/WorldClock.esm.js.map +1 -0
- package/dist/homePageComponents/WorldClock/clocks.esm.js +40 -0
- package/dist/homePageComponents/WorldClock/clocks.esm.js.map +1 -0
- package/dist/homePageComponents/WorldClock/index.esm.js +6 -0
- package/dist/homePageComponents/WorldClock/index.esm.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/package.json.esm.js +1 -1
- package/dist/translation.esm.js +4 -0
- package/dist/translation.esm.js.map +1 -1
- package/package.json +13 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# @backstage/plugin-home
|
|
2
2
|
|
|
3
|
+
## 0.9.9-next.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- a178067: Added new frontend system widget blueprints for Most Visited, Recently Visited, World Clocks, and a configurable Toolkit. The home page now supports a `defaultConfig` for defining the initial grid layout via app-config.
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @backstage/core-plugin-api@1.12.9-next.1
|
|
10
|
+
- @backstage/plugin-catalog-react@3.2.1-next.3
|
|
11
|
+
- @backstage/plugin-home-react@0.1.41-next.2
|
|
12
|
+
|
|
13
|
+
## 0.9.9-next.1
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies
|
|
18
|
+
- @backstage/plugin-catalog-react@3.2.1-next.2
|
|
19
|
+
- @backstage/frontend-plugin-api@0.18.0-next.0
|
|
20
|
+
- @backstage/core-compat-api@0.5.14-next.1
|
|
21
|
+
- @backstage/core-plugin-api@1.12.9-next.0
|
|
22
|
+
- @backstage/plugin-home-react@0.1.41-next.1
|
|
23
|
+
- @backstage/core-app-api@1.20.4-next.1
|
|
24
|
+
- @backstage/core-components@0.18.13-next.2
|
|
25
|
+
|
|
3
26
|
## 0.9.9-next.0
|
|
4
27
|
|
|
5
28
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -18,131 +18,9 @@ The plugin will automatically provide:
|
|
|
18
18
|
- A homepage at `/home` with customizable widget grid
|
|
19
19
|
- A "Home" navigation item in the sidebar
|
|
20
20
|
|
|
21
|
-
##
|
|
21
|
+
## Documentation
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
create custom homepage layouts. A layout receives the installed widgets and is
|
|
25
|
-
responsible for arranging them on the page. If no custom layout is installed, the
|
|
26
|
-
plugin provides a built-in default.
|
|
27
|
-
|
|
28
|
-
```ts
|
|
29
|
-
import { HomePageLayoutBlueprint } from '@backstage/plugin-home-react/alpha';
|
|
30
|
-
import { CustomHomepageGrid } from '@backstage/plugin-home';
|
|
31
|
-
import { Content, Header, Page } from '@backstage/core-components';
|
|
32
|
-
import { Fragment } from 'react';
|
|
33
|
-
|
|
34
|
-
const myHomePageLayout = HomePageLayoutBlueprint.make({
|
|
35
|
-
params: {
|
|
36
|
-
loader: async () =>
|
|
37
|
-
function MyHomePageLayout({ widgets }) {
|
|
38
|
-
return (
|
|
39
|
-
<Page themeId="home">
|
|
40
|
-
<Header title="Welcome" />
|
|
41
|
-
<Content>
|
|
42
|
-
<CustomHomepageGrid>
|
|
43
|
-
{widgets.map((widget, index) => (
|
|
44
|
-
<Fragment key={widget.name ?? index}>
|
|
45
|
-
{widget.component}
|
|
46
|
-
</Fragment>
|
|
47
|
-
))}
|
|
48
|
-
</CustomHomepageGrid>
|
|
49
|
-
</Content>
|
|
50
|
-
</Page>
|
|
51
|
-
);
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Then register the layout in a frontend module:
|
|
58
|
-
|
|
59
|
-
```ts
|
|
60
|
-
import { createFrontendModule } from '@backstage/frontend-plugin-api';
|
|
61
|
-
|
|
62
|
-
const homeModule = createFrontendModule({
|
|
63
|
-
pluginId: 'home',
|
|
64
|
-
extensions: [myHomePageLayout],
|
|
65
|
-
});
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## Visit Tracking (Optional)
|
|
69
|
-
|
|
70
|
-
Visit tracking is an **optional feature** that must be explicitly enabled. When enabled, it provides intelligent storage fallbacks:
|
|
71
|
-
|
|
72
|
-
**Enabling Visit Tracking:**
|
|
73
|
-
|
|
74
|
-
Add the following to your `app-config.yaml`:
|
|
75
|
-
|
|
76
|
-
```yaml
|
|
77
|
-
app:
|
|
78
|
-
extensions:
|
|
79
|
-
# Enable visit tracking API (disabled by default)
|
|
80
|
-
- api:home/visits: true
|
|
81
|
-
# Enable visit listener (disabled by default)
|
|
82
|
-
- app-root-element:home/visit-listener: true
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
**Storage Strategy (when enabled):**
|
|
86
|
-
|
|
87
|
-
1. **Custom Storage API**: If you have `storageApiRef` configured (like database-backed `UserSettingsStorage`), visit data uses your custom storage
|
|
88
|
-
2. **Browser Local Storage Fallback**: If no custom storage is configured, automatically falls back to browser local storage
|
|
89
|
-
|
|
90
|
-
**Note**: Visit tracking extensions are disabled by default to give users control over data collection and storage.
|
|
91
|
-
|
|
92
|
-
## Creating Homepage Widgets
|
|
93
|
-
|
|
94
|
-
Homepage widgets are React components that can be added to customizable home pages.
|
|
95
|
-
|
|
96
|
-
Create widgets using the `HomePageWidgetBlueprint`:
|
|
97
|
-
|
|
98
|
-
```ts
|
|
99
|
-
import { HomePageWidgetBlueprint } from '@backstage/plugin-home-react/alpha';
|
|
100
|
-
|
|
101
|
-
const myWidget = HomePageWidgetBlueprint.make({
|
|
102
|
-
name: 'my-widget',
|
|
103
|
-
params: {
|
|
104
|
-
name: 'MyWidget',
|
|
105
|
-
title: 'My Custom Widget',
|
|
106
|
-
description: 'A custom widget for the homepage',
|
|
107
|
-
components: () =>
|
|
108
|
-
import('./MyWidgetComponent').then(m => ({
|
|
109
|
-
Content: m.Content,
|
|
110
|
-
})),
|
|
111
|
-
layout: {
|
|
112
|
-
height: { minRows: 4 },
|
|
113
|
-
width: { minColumns: 3 },
|
|
114
|
-
},
|
|
115
|
-
settings: {
|
|
116
|
-
schema: {
|
|
117
|
-
title: 'Widget Settings',
|
|
118
|
-
type: 'object',
|
|
119
|
-
properties: {
|
|
120
|
-
color: {
|
|
121
|
-
title: 'Color',
|
|
122
|
-
type: 'string',
|
|
123
|
-
default: 'blue',
|
|
124
|
-
enum: ['blue', 'red', 'green'],
|
|
125
|
-
},
|
|
126
|
-
},
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
});
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
> **Example**: See [dev/index.tsx](dev/index.tsx) for a comprehensive example of creating multiple homepage widgets and layouts.
|
|
134
|
-
|
|
135
|
-
### Preventing Duplicate Widgets
|
|
136
|
-
|
|
137
|
-
The `CustomHomepageGrid` supports preventing duplicate widgets from being added to the homepage via the `preventDuplicateWidgets` prop (default: `false`):
|
|
138
|
-
|
|
139
|
-
```tsx
|
|
140
|
-
<CustomHomepageGrid preventDuplicateWidgets={true}>
|
|
141
|
-
<HomePageSearchBar />
|
|
142
|
-
<HomePageRandomJoke />
|
|
143
|
-
<HomePageStarredEntities />
|
|
144
|
-
</CustomHomepageGrid>
|
|
145
|
-
```
|
|
23
|
+
For full setup instructions, available widgets, configuration options, and how to create custom widgets and layouts, see the [Homepage documentation](https://backstage.io/docs/getting-started/homepage).
|
|
146
24
|
|
|
147
25
|
## Contributing
|
|
148
26
|
|
|
@@ -159,521 +37,4 @@ If you would like to contribute with a template, start by taking a look at the [
|
|
|
159
37
|
|
|
160
38
|
## Old Frontend System
|
|
161
39
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
### Setting up the Home Page
|
|
165
|
-
|
|
166
|
-
1. Create a Home Page Component that will be used for composition.
|
|
167
|
-
|
|
168
|
-
`packages/app/src/components/home/HomePage.tsx`
|
|
169
|
-
|
|
170
|
-
```tsx
|
|
171
|
-
export const homePage = (
|
|
172
|
-
/* TODO: Compose a Home Page here */
|
|
173
|
-
);
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
2. Add a route where the homepage will live, presumably `/`.
|
|
177
|
-
|
|
178
|
-
`packages/app/src/App.tsx`
|
|
179
|
-
|
|
180
|
-
```tsx
|
|
181
|
-
import { HomepageCompositionRoot } from '@backstage/plugin-home';
|
|
182
|
-
import { homePage } from './components/home/HomePage';
|
|
183
|
-
|
|
184
|
-
// ...
|
|
185
|
-
<Route path="/" element={<HomepageCompositionRoot />}>
|
|
186
|
-
{homePage}
|
|
187
|
-
</Route>;
|
|
188
|
-
// ...
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
### Creating Widgets (Old Frontend System)
|
|
192
|
-
|
|
193
|
-
In the old frontend system, use the `createCardExtension` helper to create homepage widgets:
|
|
194
|
-
|
|
195
|
-
```tsx
|
|
196
|
-
import { createCardExtension } from '@backstage/plugin-home-react';
|
|
197
|
-
|
|
198
|
-
export const MyWidget = homePlugin.provide(
|
|
199
|
-
createCardExtension<{ defaultCategory?: 'programming' | 'any' }>({
|
|
200
|
-
title: 'My Custom Widget',
|
|
201
|
-
components: () => import('./homePageComponents/MyWidget'),
|
|
202
|
-
}),
|
|
203
|
-
);
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
The `createCardExtension` provides error boundary and lazy loading, and accepts generics for custom props that App Integrators can configure.
|
|
207
|
-
|
|
208
|
-
### Composing a Home Page (Old Frontend System)
|
|
209
|
-
|
|
210
|
-
Composing a Home Page is done by creating regular React components. Components created with `createCardExtension` are rendered like so:
|
|
211
|
-
|
|
212
|
-
```tsx
|
|
213
|
-
import Grid from '@material-ui/core/Grid';
|
|
214
|
-
import { RandomJokeHomePageComponent } from '@backstage/plugin-home';
|
|
215
|
-
|
|
216
|
-
export const homePage = (
|
|
217
|
-
<Grid container spacing={3}>
|
|
218
|
-
<Grid item xs={12} md={4}>
|
|
219
|
-
<RandomJokeHomePageComponent />
|
|
220
|
-
</Grid>
|
|
221
|
-
</Grid>
|
|
222
|
-
);
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
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.
|
|
226
|
-
|
|
227
|
-
### Customizable Home Page (Old Frontend System)
|
|
228
|
-
|
|
229
|
-
If you want to allow users to customize the components that are shown in the home page, you can use CustomHomePageGrid component.
|
|
230
|
-
By adding the allowed components inside the grid, the user can add, configure, remove and move the components around in their
|
|
231
|
-
home page. The user configuration is also saved and restored in the process for later use.
|
|
232
|
-
|
|
233
|
-
```tsx
|
|
234
|
-
import {
|
|
235
|
-
HomePageRandomJoke,
|
|
236
|
-
HomePageStarredEntities,
|
|
237
|
-
CustomHomepageGrid,
|
|
238
|
-
} from '@backstage/plugin-home';
|
|
239
|
-
import { Content, Header, Page } from '@backstage/core-components';
|
|
240
|
-
import { HomePageSearchBar } from '@backstage/plugin-search';
|
|
241
|
-
import { HomePageCalendar } from '@backstage/plugin-gcalendar';
|
|
242
|
-
import { MicrosoftCalendarCard } from '@backstage/plugin-microsoft-calendar';
|
|
243
|
-
|
|
244
|
-
export const homePage = (
|
|
245
|
-
<CustomHomepageGrid>
|
|
246
|
-
// Insert the allowed widgets inside the grid
|
|
247
|
-
<HomePageSearchBar />
|
|
248
|
-
<HomePageRandomJoke />
|
|
249
|
-
<HomePageCalendar />
|
|
250
|
-
<MicrosoftCalendarCard />
|
|
251
|
-
<HomePageStarredEntities />
|
|
252
|
-
</CustomHomepageGrid>
|
|
253
|
-
);
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
> [!NOTE]
|
|
257
|
-
> You can provide a title to the grid by passing it as a prop: `<CustomHomepageGrid title="Your Dashboard" />`. This will be displayed as a header above the grid layout.
|
|
258
|
-
|
|
259
|
-
#### Creating Customizable Components (Old Frontend System)
|
|
260
|
-
|
|
261
|
-
The custom home page can use the default components created by using the default `createCardExtension` method but if you
|
|
262
|
-
want to add additional configuration like component size or settings, you can define those in the `layout`
|
|
263
|
-
property:
|
|
264
|
-
|
|
265
|
-
```tsx
|
|
266
|
-
import { createCardExtension } from '@backstage/plugin-home-react';
|
|
267
|
-
|
|
268
|
-
export const RandomJokeHomePageComponent = homePlugin.provide(
|
|
269
|
-
createCardExtension<{ defaultCategory?: 'any' | 'programming' }>({
|
|
270
|
-
name: 'HomePageRandomJoke',
|
|
271
|
-
title: 'Random Joke',
|
|
272
|
-
components: () => import('./homePageComponents/RandomJoke'),
|
|
273
|
-
layout: {
|
|
274
|
-
height: { minRows: 7 },
|
|
275
|
-
width: { minColumns: 3 },
|
|
276
|
-
},
|
|
277
|
-
}),
|
|
278
|
-
);
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
These settings can also be defined for components that use `createReactExtension` instead of `createCardExtension` by using
|
|
282
|
-
the data property:
|
|
283
|
-
|
|
284
|
-
```tsx
|
|
285
|
-
export const HomePageSearchBar = searchPlugin.provide(
|
|
286
|
-
createReactExtension({
|
|
287
|
-
name: 'HomePageSearchBar',
|
|
288
|
-
component: {
|
|
289
|
-
lazy: () =>
|
|
290
|
-
import('./components/HomePageComponent').then(m => m.HomePageSearchBar),
|
|
291
|
-
},
|
|
292
|
-
data: {
|
|
293
|
-
'home.widget.config': {
|
|
294
|
-
layout: {
|
|
295
|
-
height: { maxRows: 1 },
|
|
296
|
-
},
|
|
297
|
-
},
|
|
298
|
-
},
|
|
299
|
-
}),
|
|
300
|
-
);
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
Available home page properties that are used for homepage widgets are:
|
|
304
|
-
|
|
305
|
-
| Key | Type | Description |
|
|
306
|
-
| ----------------------------- | ------- | ------------------------------------------------------------ |
|
|
307
|
-
| `title` | string | User friend title. Shown when user adds widgets to homepage |
|
|
308
|
-
| `description` | string | Widget description. Shown when user adds widgets to homepage |
|
|
309
|
-
| `layout.width.defaultColumns` | integer | Default width of the widget (1-12) |
|
|
310
|
-
| `layout.width.minColumns` | integer | Minimum width of the widget (1-12) |
|
|
311
|
-
| `layout.width.maxColumns` | integer | Maximum width of the widget (1-12) |
|
|
312
|
-
| `layout.height.defaultRows` | integer | Default height of the widget (1-12) |
|
|
313
|
-
| `layout.height.minRows` | integer | Minimum height of the widget (1-12) |
|
|
314
|
-
| `layout.height.maxRows` | integer | Maximum height of the widget (1-12) |
|
|
315
|
-
| `settings.schema` | object | Customization settings of the widget, see below |
|
|
316
|
-
|
|
317
|
-
#### Widget Specific Settings (Old Frontend System)
|
|
318
|
-
|
|
319
|
-
To define settings that the users can change for your component, you should define the `layout` and `settings`
|
|
320
|
-
properties. The `settings.schema` object should follow
|
|
321
|
-
[react-jsonschema-form](https://rjsf-team.github.io/react-jsonschema-form/docs/) definition and the type of the schema
|
|
322
|
-
must be `object`. As well, the `uiSchema` can be defined if a certain UI style needs to be applied for any of the defined
|
|
323
|
-
properties. More documentation [here](https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/uiSchema).
|
|
324
|
-
|
|
325
|
-
If you want to hide the card title, you can do it by setting a `name` and leaving the `title` empty.
|
|
326
|
-
|
|
327
|
-
```tsx
|
|
328
|
-
import { createCardExtension } from '@backstage/plugin-home-react';
|
|
329
|
-
|
|
330
|
-
export const HomePageRandomJoke = homePlugin.provide(
|
|
331
|
-
createCardExtension<{ defaultCategory?: 'any' | 'programming' }>({
|
|
332
|
-
name: 'HomePageRandomJoke',
|
|
333
|
-
title: 'Random Joke',
|
|
334
|
-
components: () => import('./homePageComponents/RandomJoke'),
|
|
335
|
-
description: 'Shows a random joke about optional category',
|
|
336
|
-
layout: {
|
|
337
|
-
height: { minRows: 4 },
|
|
338
|
-
width: { minColumns: 3 },
|
|
339
|
-
},
|
|
340
|
-
settings: {
|
|
341
|
-
schema: {
|
|
342
|
-
title: 'Random Joke settings',
|
|
343
|
-
type: 'object',
|
|
344
|
-
properties: {
|
|
345
|
-
defaultCategory: {
|
|
346
|
-
title: 'Category',
|
|
347
|
-
type: 'string',
|
|
348
|
-
enum: ['any', 'programming', 'dad'],
|
|
349
|
-
default: 'any',
|
|
350
|
-
},
|
|
351
|
-
},
|
|
352
|
-
},
|
|
353
|
-
uiSchema: {
|
|
354
|
-
defaultCategory: {
|
|
355
|
-
'ui:widget': 'radio', // Instead of the default 'select'
|
|
356
|
-
},
|
|
357
|
-
},
|
|
358
|
-
},
|
|
359
|
-
}),
|
|
360
|
-
);
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
This allows the user to select `defaultCategory` for the RandomJoke widgets that are added to the homepage.
|
|
364
|
-
Each widget has its own settings and the setting values are passed to the underlying React component in props.
|
|
365
|
-
|
|
366
|
-
In case your `CardExtension` had `Settings` component defined, it will automatically disappear when you add the
|
|
367
|
-
`settingsSchema` to the component data structure.
|
|
368
|
-
|
|
369
|
-
#### Adding Default Layout (Old Frontend System)
|
|
370
|
-
|
|
371
|
-
You can set the default layout of the customizable home page by passing configuration to the `CustomHomepageGrid`
|
|
372
|
-
component:
|
|
373
|
-
|
|
374
|
-
```tsx
|
|
375
|
-
const defaultConfig = [
|
|
376
|
-
{
|
|
377
|
-
component: <HomePageSearchBar />, // Or 'HomePageSearchBar' as a string if you know the component name
|
|
378
|
-
x: 0,
|
|
379
|
-
y: 0,
|
|
380
|
-
width: 12,
|
|
381
|
-
height: 1,
|
|
382
|
-
movable: true,
|
|
383
|
-
resizable: false,
|
|
384
|
-
deletable: false,
|
|
385
|
-
},
|
|
386
|
-
];
|
|
387
|
-
|
|
388
|
-
<CustomHomepageGrid config={defaultConfig}>
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
### Page Visit Homepage Component (Old Frontend System)
|
|
392
|
-
|
|
393
|
-
This component shows the homepage user a view for "Recently visited" or "Top visited".
|
|
394
|
-
Being provided by the `<HomePageTopVisited/>` and `<HomePageRecentlyVisited/>` component, see it in use on a homepage example below:
|
|
395
|
-
|
|
396
|
-
```tsx
|
|
397
|
-
// packages/app/src/components/home/HomePage.tsx
|
|
398
|
-
import Grid from '@material-ui/core/Grid';
|
|
399
|
-
import {
|
|
400
|
-
HomePageTopVisited,
|
|
401
|
-
HomePageRecentlyVisited,
|
|
402
|
-
} from '@backstage/plugin-home';
|
|
403
|
-
|
|
404
|
-
export const homePage = (
|
|
405
|
-
<Grid container spacing={3}>
|
|
406
|
-
<Grid item xs={12} md={4}>
|
|
407
|
-
<HomePageTopVisited />
|
|
408
|
-
</Grid>
|
|
409
|
-
<Grid item xs={12} md={4}>
|
|
410
|
-
<HomePageRecentlyVisited />
|
|
411
|
-
</Grid>
|
|
412
|
-
</Grid>
|
|
413
|
-
);
|
|
414
|
-
```
|
|
415
|
-
|
|
416
|
-
There are some requirements to provide its functionality, so please ensure the following:
|
|
417
|
-
|
|
418
|
-
These components need an API to handle visit data, please refer to the [utility-apis](../../docs/api/utility-apis.md)
|
|
419
|
-
documentation for more information. Bellow you can see an example for two options:
|
|
420
|
-
|
|
421
|
-
```ts
|
|
422
|
-
// packages/app/src/apis.ts
|
|
423
|
-
// ...
|
|
424
|
-
import {
|
|
425
|
-
VisitsStorageApi,
|
|
426
|
-
VisitsWebStorageApi,
|
|
427
|
-
visitsApiRef,
|
|
428
|
-
} from '@backstage/plugin-home';
|
|
429
|
-
// ...
|
|
430
|
-
export const apis: AnyApiFactory[] = [
|
|
431
|
-
// Implementation that relies on a provided storageApi
|
|
432
|
-
createApiFactory({
|
|
433
|
-
api: visitsApiRef,
|
|
434
|
-
deps: {
|
|
435
|
-
storageApi: storageApiRef,
|
|
436
|
-
identityApi: identityApiRef,
|
|
437
|
-
},
|
|
438
|
-
factory: ({ storageApi, identityApi }) =>
|
|
439
|
-
VisitsStorageApi.create({ storageApi, identityApi }),
|
|
440
|
-
}),
|
|
441
|
-
|
|
442
|
-
// Or a local storage data implementation, relies on WebStorage implementation of storageApi
|
|
443
|
-
createApiFactory({
|
|
444
|
-
api: visitsApiRef,
|
|
445
|
-
deps: {
|
|
446
|
-
identityApi: identityApiRef,
|
|
447
|
-
errorApi: errorApiRef
|
|
448
|
-
},
|
|
449
|
-
factory: ({ identityApi, errorApi }) => VisitsWebStorageApi.create({ identityApi, errorApi }),
|
|
450
|
-
}),
|
|
451
|
-
// ...
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
To monitor page visit activity and save it on behalf of the user a component is provided, please add it to your app.
|
|
455
|
-
See the example usage:
|
|
456
|
-
|
|
457
|
-
```ts
|
|
458
|
-
// packages/app/src/App.tsx
|
|
459
|
-
import { VisitListener } from '@backstage/plugin-home';
|
|
460
|
-
// ...
|
|
461
|
-
export default app.createRoot(
|
|
462
|
-
<>
|
|
463
|
-
<AlertDisplay />
|
|
464
|
-
<OAuthRequestDialog />
|
|
465
|
-
<AppRouter>
|
|
466
|
-
<VisitListener />
|
|
467
|
-
<Root>{routes}</Root>
|
|
468
|
-
</AppRouter>
|
|
469
|
-
</>,
|
|
470
|
-
);
|
|
471
|
-
```
|
|
472
|
-
|
|
473
|
-
You can filter the items that are shown in the component.
|
|
474
|
-
this can be done by using the config file.
|
|
475
|
-
Filtering is done by using 3 parameters:
|
|
476
|
-
|
|
477
|
-
- `field` - define which field to filter. can be one of the following
|
|
478
|
-
- `id`: string
|
|
479
|
-
- `name`: string
|
|
480
|
-
- `pathname`: string
|
|
481
|
-
- `hits`: number
|
|
482
|
-
- `timestamp`: number
|
|
483
|
-
- `entityRef`: string
|
|
484
|
-
- `operator` - can be one of the following `'<' | '<=' | '==' | '!=' | '>' | '>=' | 'contains'`
|
|
485
|
-
- `value` - the value of the filter
|
|
486
|
-
|
|
487
|
-
```yaml
|
|
488
|
-
home:
|
|
489
|
-
recentVisits:
|
|
490
|
-
filterBy:
|
|
491
|
-
- field:
|
|
492
|
-
operator:
|
|
493
|
-
value:
|
|
494
|
-
topVisits:
|
|
495
|
-
filterBy:
|
|
496
|
-
- field:
|
|
497
|
-
operator:
|
|
498
|
-
value:
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
`filterBy` configs that are not defined in the above format will be ignored.
|
|
502
|
-
|
|
503
|
-
In order to validate the config you can use `backstage/cli config:check`
|
|
504
|
-
|
|
505
|
-
#### Customizing the VisitList (Old Frontend System)
|
|
506
|
-
|
|
507
|
-
If you want more control over the recent and top visited lists, you can write your own functions to transform the path names and determine which visits to save. You can also enrich each visit with other fields and customize the chip colors/labels in the visit lists.
|
|
508
|
-
|
|
509
|
-
##### Transform Pathname Function
|
|
510
|
-
|
|
511
|
-
Provide a `transformPathname` function to transform the pathname before it's processed for visit tracking. This can be used for transforming the pathname for the visit (before any other consideration). As an example, you can treat multiple sub-path visits to be counted as a singular path, e.g. `/entity-path/sub1` , `/entity-path/sub-2`, `/entity-path/sub-2/sub-sub-2` can all be mapped to `/entity-path` so visits to any of those routes are all counted as the same.
|
|
512
|
-
|
|
513
|
-
```tsx
|
|
514
|
-
import {
|
|
515
|
-
AnyApiFactory,
|
|
516
|
-
createApiFactory,
|
|
517
|
-
identityApiRef,
|
|
518
|
-
storageApiRef,
|
|
519
|
-
} from '@backstage/core-plugin-api';
|
|
520
|
-
import { VisitsStorageApi } from '@backstage/plugin-home';
|
|
521
|
-
|
|
522
|
-
const transformPathname = (pathname: string) => {
|
|
523
|
-
const pathnameParts = pathname.split('/').filter(part => part !== '');
|
|
524
|
-
const rootPathFromPathname = pathnameParts[0] ?? '';
|
|
525
|
-
if (rootPathFromPathname === 'catalog' && pathnameParts.length >= 4) {
|
|
526
|
-
return `/${pathnameParts.slice(0, 4).join('/')}`;
|
|
527
|
-
}
|
|
528
|
-
return pathname;
|
|
529
|
-
};
|
|
530
|
-
|
|
531
|
-
export const apis: AnyApiFactory[] = [
|
|
532
|
-
createApiFactory({
|
|
533
|
-
api: visitsApiRef,
|
|
534
|
-
deps: {
|
|
535
|
-
storageApi: storageApiRef,
|
|
536
|
-
identityApi: identityApiRef,
|
|
537
|
-
},
|
|
538
|
-
factory: ({ storageApi, identityApi }) =>
|
|
539
|
-
VisitsStorageApi.create({
|
|
540
|
-
storageApi,
|
|
541
|
-
identityApi,
|
|
542
|
-
transformPathname,
|
|
543
|
-
}),
|
|
544
|
-
}),
|
|
545
|
-
];
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
##### Can Save Function
|
|
549
|
-
|
|
550
|
-
Provide a `canSave` function to determine which visits should be tracked and saved. This allows you to conditionally save visits to the list:
|
|
551
|
-
|
|
552
|
-
```tsx
|
|
553
|
-
import {
|
|
554
|
-
AnyApiFactory,
|
|
555
|
-
createApiFactory,
|
|
556
|
-
identityApiRef,
|
|
557
|
-
storageApiRef,
|
|
558
|
-
} from '@backstage/core-plugin-api';
|
|
559
|
-
import { VisitInput, VisitsStorageApi } from '@backstage/plugin-home';
|
|
560
|
-
|
|
561
|
-
const canSave = (visit: VisitInput) => {
|
|
562
|
-
// Don't save visits to admin or settings pages
|
|
563
|
-
return (
|
|
564
|
-
!visit.pathname.startsWith('/admin') &&
|
|
565
|
-
!visit.pathname.startsWith('/settings')
|
|
566
|
-
);
|
|
567
|
-
};
|
|
568
|
-
|
|
569
|
-
export const apis: AnyApiFactory[] = [
|
|
570
|
-
createApiFactory({
|
|
571
|
-
api: visitsApiRef,
|
|
572
|
-
deps: {
|
|
573
|
-
storageApi: storageApiRef,
|
|
574
|
-
identityApi: identityApiRef,
|
|
575
|
-
},
|
|
576
|
-
factory: ({ storageApi, identityApi }) =>
|
|
577
|
-
VisitsStorageApi.create({
|
|
578
|
-
storageApi,
|
|
579
|
-
identityApi,
|
|
580
|
-
canSave,
|
|
581
|
-
}),
|
|
582
|
-
}),
|
|
583
|
-
];
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
##### Enrich Visit Function
|
|
587
|
-
|
|
588
|
-
You can also add the `enrichVisit` function to put additional values on each `Visit`. The values could later be used to customize the chips in the `VisitList`. For example, you could add the entity `type` on the `Visit` so that `type` is used for labels instead of `kind`.
|
|
589
|
-
|
|
590
|
-
```tsx
|
|
591
|
-
import {
|
|
592
|
-
AnyApiFactory,
|
|
593
|
-
createApiFactory,
|
|
594
|
-
identityApiRef,
|
|
595
|
-
storageApiRef,
|
|
596
|
-
} from '@backstage/core-plugin-api';
|
|
597
|
-
import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog-react';
|
|
598
|
-
import { VisitsStorageApi } from '@backstage/plugin-home';
|
|
599
|
-
|
|
600
|
-
type EnrichedVisit = VisitInput & {
|
|
601
|
-
type?: string;
|
|
602
|
-
};
|
|
603
|
-
|
|
604
|
-
const createEnrichVisit =
|
|
605
|
-
(catalogApi: CatalogApi) =>
|
|
606
|
-
async (visit: VisitInput): Promise<EnrichedVisit> => {
|
|
607
|
-
if (!visit.entityRef) {
|
|
608
|
-
return visit;
|
|
609
|
-
}
|
|
610
|
-
try {
|
|
611
|
-
const entity = await catalogApi.getEntityByRef(visit.entityRef);
|
|
612
|
-
const type = entity?.spec?.type?.toString();
|
|
613
|
-
return { ...visit, type };
|
|
614
|
-
} catch (error) {
|
|
615
|
-
return visit;
|
|
616
|
-
}
|
|
617
|
-
};
|
|
618
|
-
|
|
619
|
-
export const apis: AnyApiFactory[] = [
|
|
620
|
-
createApiFactory({
|
|
621
|
-
api: visitsApiRef,
|
|
622
|
-
deps: {
|
|
623
|
-
storageApi: storageApiRef,
|
|
624
|
-
identityApi: identityApiRef,
|
|
625
|
-
catalogApi: catalogApiRef,
|
|
626
|
-
},
|
|
627
|
-
factory: ({ storageApi, identityApi, catalogApi }) =>
|
|
628
|
-
VisitsStorageApi.create({
|
|
629
|
-
storageApi,
|
|
630
|
-
identityApi,
|
|
631
|
-
enrichVisit: createEnrichVisit(catalogApi),
|
|
632
|
-
}),
|
|
633
|
-
}),
|
|
634
|
-
];
|
|
635
|
-
```
|
|
636
|
-
|
|
637
|
-
##### Custom Chip Colors and Labels
|
|
638
|
-
|
|
639
|
-
To provide your own chip colors and/or labels for the recent and top visited lists, wrap the components in `VisitDisplayProvider` with `getChipColor` and `getChipLabel` functions. The colors provided will be used instead of the hard coded [`colorVariants`](https://github.com/backstage/backstage/blob/2da352043425bcab4c4422e4d2820c26c0a83382/packages/theme/src/base/pageTheme.ts#L46) provided via `@backstage/theme`.
|
|
640
|
-
|
|
641
|
-
```tsx
|
|
642
|
-
import {
|
|
643
|
-
CustomHomepageGrid,
|
|
644
|
-
HomePageTopVisited,
|
|
645
|
-
HomePageRecentlyVisited,
|
|
646
|
-
VisitDisplayProvider,
|
|
647
|
-
} from '@backstage/plugin-home';
|
|
648
|
-
|
|
649
|
-
const getChipColor = (visit: any) => {
|
|
650
|
-
const type = visit.type;
|
|
651
|
-
switch (type) {
|
|
652
|
-
case 'application':
|
|
653
|
-
return '#b39ddb';
|
|
654
|
-
case 'service':
|
|
655
|
-
return '#90caf9';
|
|
656
|
-
case 'account':
|
|
657
|
-
return '#a5d6a7';
|
|
658
|
-
case 'suite':
|
|
659
|
-
return '#fff59d';
|
|
660
|
-
default:
|
|
661
|
-
return '#ef9a9a';
|
|
662
|
-
}
|
|
663
|
-
};
|
|
664
|
-
|
|
665
|
-
const getChipLabel = (visit?: any) => {
|
|
666
|
-
return visit?.type ? visit.type : 'Other';
|
|
667
|
-
};
|
|
668
|
-
|
|
669
|
-
export default function HomePage() {
|
|
670
|
-
return (
|
|
671
|
-
<VisitDisplayProvider getChipColor={getChipColor} getLabel={getChipLabel}>
|
|
672
|
-
<CustomHomepageGrid title="Your Dashboard">
|
|
673
|
-
<HomePageRecentlyVisited />
|
|
674
|
-
<HomePageTopVisited />
|
|
675
|
-
</CustomHomepageGrid>
|
|
676
|
-
</VisitDisplayProvider>
|
|
677
|
-
);
|
|
678
|
-
}
|
|
679
|
-
```
|
|
40
|
+
For setup and usage with the old frontend system, see the [old frontend system homepage guide](https://backstage.io/docs/getting-started/homepage--old).
|