@@ -2,4 +2,16 @@
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
---
|
||||
|
||||
Add swim lane filtering to the scaffolder page so that individuals can surface specific templates to end users ahead of others, or group templates together
|
||||
Add group filtering to the scaffolder page so that individuals can surface specific templates to end users ahead of others, or group templates together. This can be accomplished by passing in a `groups` prop to the `ScaffolderPage`
|
||||
|
||||
```
|
||||
<ScaffolderPage
|
||||
groups={[
|
||||
{
|
||||
title: "Recommended",
|
||||
filter: entity =>
|
||||
entity?.metadata?.tags?.includes('recommended') ?? false,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
```
|
||||
|
||||
@@ -58,12 +58,12 @@ RUN pip3 install cookiecutter
|
||||
|
||||
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 `swimLanes` and passing them to your
|
||||
`ScaffolderPage` like below
|
||||
accomplish this by creating `groups` and passing them to your `ScaffolderPage`
|
||||
like below
|
||||
|
||||
```
|
||||
<ScaffolderPage
|
||||
extraSwimlanes={[
|
||||
groups={[
|
||||
{
|
||||
title: "Recommended",
|
||||
filter: entity =>
|
||||
@@ -74,10 +74,9 @@ accomplish this by creating `swimLanes` and passing them to your
|
||||
```
|
||||
|
||||
This code will group all templates with the 'recommended' tag together at the
|
||||
top of the page above any other templates not filtered by this swimlane or
|
||||
others.
|
||||
top of the page above any other templates not filtered by this group or others.
|
||||
|
||||
You can also further customize swimlanes by passing in a `titleComponent`
|
||||
instead of a `title` which will be a component to use as the header instead of
|
||||
just the default `ContentHeader` with the `title` set as it's value.
|
||||
You can also further customize groups by passing in a `titleComponent` instead
|
||||
of a `title` which will be a component to use as the header instead of just the
|
||||
default `ContentHeader` with the `title` set as it's value.
|
||||

|
||||
|
||||
@@ -179,7 +179,7 @@ const routes = (
|
||||
path="/create"
|
||||
element={
|
||||
<ScaffolderPage
|
||||
extraSwimlanes={[
|
||||
groups={[
|
||||
{
|
||||
title: 'Recommended',
|
||||
filter: entity =>
|
||||
|
||||
@@ -214,14 +214,14 @@ export const ScaffolderFieldExtensions: React_2.ComponentType;
|
||||
// @public (undocumented)
|
||||
export const ScaffolderPage: ({
|
||||
TemplateCardComponent,
|
||||
extraSwimlanes,
|
||||
groups,
|
||||
}: {
|
||||
TemplateCardComponent?:
|
||||
| ComponentType<{
|
||||
template: TemplateEntityV1beta2;
|
||||
}>
|
||||
| undefined;
|
||||
extraSwimlanes?:
|
||||
groups?:
|
||||
| {
|
||||
title?: string | undefined;
|
||||
titleComponent?: ReactNode;
|
||||
@@ -249,7 +249,7 @@ export { scaffolderPlugin };
|
||||
// @public (undocumented)
|
||||
export const TemplateList: ({
|
||||
TemplateCardComponent,
|
||||
swimlane,
|
||||
group,
|
||||
}: TemplateListProps) => JSX.Element | null;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "TemplateListProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
@@ -261,7 +261,7 @@ export type TemplateListProps = {
|
||||
template: TemplateEntityV1beta2;
|
||||
}>
|
||||
| undefined;
|
||||
swimlane?: {
|
||||
group?: {
|
||||
title?: string;
|
||||
titleComponent?: React_2.ReactNode;
|
||||
filter: (entity: Entity) => boolean;
|
||||
|
||||
@@ -34,17 +34,14 @@ type RouterProps = {
|
||||
TemplateCardComponent?:
|
||||
| ComponentType<{ template: TemplateEntityV1beta2 }>
|
||||
| undefined;
|
||||
extraSwimlanes?: Array<{
|
||||
groups?: Array<{
|
||||
title?: string;
|
||||
titleComponent?: React.ReactNode;
|
||||
filter: (entity: Entity) => boolean;
|
||||
}>;
|
||||
};
|
||||
|
||||
export const Router = ({
|
||||
TemplateCardComponent,
|
||||
extraSwimlanes,
|
||||
}: RouterProps) => {
|
||||
export const Router = ({ TemplateCardComponent, groups }: RouterProps) => {
|
||||
const outlet = useOutlet();
|
||||
|
||||
const customFieldExtensions = useElementFilter(outlet, elements =>
|
||||
@@ -74,7 +71,7 @@ export const Router = ({
|
||||
element={
|
||||
<ScaffolderPage
|
||||
TemplateCardComponent={TemplateCardComponent}
|
||||
extraSwimlanes={extraSwimlanes}
|
||||
groups={groups}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -51,7 +51,7 @@ export type ScaffolderPageProps = {
|
||||
TemplateCardComponent?:
|
||||
| ComponentType<{ template: TemplateEntityV1beta2 }>
|
||||
| undefined;
|
||||
extraSwimlanes?: Array<{
|
||||
groups?: Array<{
|
||||
title?: string;
|
||||
titleComponent?: React.ReactNode;
|
||||
filter: (entity: Entity) => boolean;
|
||||
@@ -60,16 +60,14 @@ export type ScaffolderPageProps = {
|
||||
|
||||
export const ScaffolderPageContents = ({
|
||||
TemplateCardComponent,
|
||||
extraSwimlanes,
|
||||
groups,
|
||||
}: ScaffolderPageProps) => {
|
||||
const styles = useStyles();
|
||||
const registerComponentLink = useRouteRef(registerComponentRouteRef);
|
||||
const otherTemplatesSwimlane = {
|
||||
title: extraSwimlanes ? 'Other Templates' : 'Templates',
|
||||
const otherTemplatesGroup = {
|
||||
title: groups ? 'Other Templates' : 'Templates',
|
||||
filter: (entity: Entity) => {
|
||||
const filtered = (extraSwimlanes ?? []).map(swimlane =>
|
||||
swimlane.filter(entity),
|
||||
);
|
||||
const filtered = (groups ?? []).map(group => group.filter(entity));
|
||||
return !filtered.some(result => result === true);
|
||||
},
|
||||
};
|
||||
@@ -110,16 +108,16 @@ export const ScaffolderPageContents = ({
|
||||
<EntityTagPicker />
|
||||
</div>
|
||||
<div>
|
||||
{extraSwimlanes &&
|
||||
extraSwimlanes.map(swimlane => (
|
||||
{groups &&
|
||||
groups.map(group => (
|
||||
<TemplateList
|
||||
TemplateCardComponent={TemplateCardComponent}
|
||||
swimlane={swimlane}
|
||||
group={group}
|
||||
/>
|
||||
))}
|
||||
<TemplateList
|
||||
TemplateCardComponent={TemplateCardComponent}
|
||||
swimlane={otherTemplatesSwimlane}
|
||||
group={otherTemplatesGroup}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -130,12 +128,12 @@ export const ScaffolderPageContents = ({
|
||||
|
||||
export const ScaffolderPage = ({
|
||||
TemplateCardComponent,
|
||||
extraSwimlanes,
|
||||
groups,
|
||||
}: ScaffolderPageProps) => (
|
||||
<EntityListProvider>
|
||||
<ScaffolderPageContents
|
||||
TemplateCardComponent={TemplateCardComponent}
|
||||
extraSwimlanes={extraSwimlanes}
|
||||
groups={groups}
|
||||
/>
|
||||
</EntityListProvider>
|
||||
);
|
||||
|
||||
@@ -35,7 +35,7 @@ export type TemplateListProps = {
|
||||
TemplateCardComponent?:
|
||||
| ComponentType<{ template: TemplateEntityV1beta2 }>
|
||||
| undefined;
|
||||
swimlane?: {
|
||||
group?: {
|
||||
title?: string;
|
||||
titleComponent?: React.ReactNode;
|
||||
filter: (entity: Entity) => boolean;
|
||||
@@ -44,20 +44,20 @@ export type TemplateListProps = {
|
||||
|
||||
export const TemplateList = ({
|
||||
TemplateCardComponent,
|
||||
swimlane,
|
||||
group,
|
||||
}: TemplateListProps) => {
|
||||
const { loading, error, entities } = useEntityListProvider();
|
||||
const Card = TemplateCardComponent || TemplateCard;
|
||||
const maybeFilteredEntities = swimlane
|
||||
? entities.filter(e => swimlane.filter(e))
|
||||
const maybeFilteredEntities = group
|
||||
? entities.filter(e => group.filter(e))
|
||||
: entities;
|
||||
const title = swimlane ? (
|
||||
swimlane.titleComponent || <ContentHeader title={swimlane.title} />
|
||||
const title = group ? (
|
||||
group.titleComponent || <ContentHeader title={group.title} />
|
||||
) : (
|
||||
<ContentHeader title="Other Templates" />
|
||||
);
|
||||
|
||||
if (swimlane && maybeFilteredEntities.length === 0) {
|
||||
if (group && maybeFilteredEntities.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user