feat(scaffolder): config-driven template groups and swappable TemplateCard (#34147)
* feat(scaffolder): config-driven template groups and swappable TemplateCard Signed-off-by: benjdlambert <ben@blam.sh> * refactor(scaffolder): keep createGroupsWithOther internal Signed-off-by: benjdlambert <ben@blam.sh> * docs(scaffolder): fix sub-page extension ID in changeset Signed-off-by: benjdlambert <ben@blam.sh> * address PR review feedback Signed-off-by: benjdlambert <ben@blam.sh> * split TemplateCard swappable contract from legacy props Signed-off-by: benjdlambert <ben@blam.sh> * address review feedback: dedupe tags, defensive groups copy, doc clarifications Signed-off-by: benjdlambert <ben@blam.sh> * regenerate api reports Signed-off-by: benjdlambert <ben@blam.sh> * align docs and changeset with actual default group titles Signed-off-by: benjdlambert <ben@blam.sh> * regen api reports after rebase Signed-off-by: benjdlambert <ben@blam.sh> --------- Signed-off-by: benjdlambert <ben@blam.sh>
This commit is contained in:
@@ -109,6 +109,10 @@ Default secrets are resolved from environment variables and accessible via `${{
|
||||
|
||||
## Customizing the ScaffolderPage with Grouping and Filtering
|
||||
|
||||
The sections below cover the legacy (JSX) frontend system. For the new
|
||||
frontend system, see [Customizing the templates page in the new frontend system](#customizing-the-templates-page-in-the-new-frontend-system)
|
||||
below.
|
||||
|
||||
Once you have more than a few software templates you may want to customize your
|
||||
`ScaffolderPage` by grouping and surfacing certain templates together. You can
|
||||
accomplish this by creating `groups` and passing them to your `ScaffolderPage`
|
||||
@@ -149,3 +153,74 @@ You can have several use cases for that:
|
||||
}
|
||||
/>
|
||||
```
|
||||
|
||||
## Customizing the templates page in the new frontend system
|
||||
|
||||
In the new frontend system the templates page is built from extensions, so
|
||||
customisations are configured rather than passed as JSX props.
|
||||
|
||||
### Defining template groups in `app-config.yaml`
|
||||
|
||||
The `sub-page:scaffolder/templates` extension accepts a `groups` config field.
|
||||
Each group has a `title` and a `filter` predicate (using
|
||||
[entity predicate queries](https://backstage.io/docs/features/software-catalog/catalog-customization#entity-predicate-queries)).
|
||||
Templates not matched by any group fall into an automatically appended
|
||||
"Other Templates" group. With no groups configured the page renders a single
|
||||
"Templates" group.
|
||||
|
||||
```yaml
|
||||
app:
|
||||
extensions:
|
||||
- sub-page:scaffolder/templates:
|
||||
config:
|
||||
groups:
|
||||
- title: Recommended Services
|
||||
filter:
|
||||
spec.type: service
|
||||
- title: Documentation
|
||||
filter:
|
||||
spec.type: documentation
|
||||
```
|
||||
|
||||
Predicate values are matched case-insensitively. The matchers `$exists`,
|
||||
`$in`, `$contains`, `$hasPrefix` and the logical operators `$all`, `$any`, `$not`
|
||||
are also supported — see the
|
||||
[entity predicate queries reference](https://backstage.io/docs/features/software-catalog/catalog-customization#entity-predicate-queries)
|
||||
for the full grammar.
|
||||
|
||||
### Replacing the default `TemplateCard`
|
||||
|
||||
The `TemplateCard` exported from `@backstage/plugin-scaffolder-react/alpha`
|
||||
is a swappable component. Apps can replace it by registering a
|
||||
`SwappableComponentBlueprint` extension that targets `TemplateCard`:
|
||||
|
||||
```tsx
|
||||
// packages/app/src/modules/appModuleScaffolder.tsx
|
||||
import { createFrontendModule } from '@backstage/frontend-plugin-api';
|
||||
import { SwappableComponentBlueprint } from '@backstage/plugin-app-react';
|
||||
import { TemplateCard } from '@backstage/plugin-scaffolder-react/alpha';
|
||||
|
||||
export const appModuleScaffolder = createFrontendModule({
|
||||
pluginId: 'app',
|
||||
extensions: [
|
||||
SwappableComponentBlueprint.make({
|
||||
name: 'scaffolder-template-card',
|
||||
params: defineParams =>
|
||||
defineParams({
|
||||
component: TemplateCard,
|
||||
loader: () => import('./MyTemplateCard').then(m => m.MyTemplateCard),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
Wire the module into your app by adding `appModuleScaffolder` to the
|
||||
`features` array of `createApp` in `packages/app/src/App.tsx`.
|
||||
|
||||
`MyTemplateCard` receives `TemplateCardComponentProps`
|
||||
(`{ template, additionalLinks?, onSelected? }`). The list takes care of
|
||||
binding the template to `onSelected`, so the card just calls
|
||||
`props.onSelected?.()` to choose itself. The example app under
|
||||
`packages/app/src/modules/BuiTemplateCard.tsx` shows a Backstage UI (BUI)
|
||||
implementation you can use as a starting point.
|
||||
|
||||
Reference in New Issue
Block a user