address comments

Signed-off-by: jrusso1020 <jrusso@brex.com>
This commit is contained in:
jrusso1020
2021-11-26 10:50:13 -05:00
parent ed5bef529e
commit fa683cab7d
8 changed files with 104 additions and 82 deletions
-1
View File
@@ -1,5 +1,4 @@
---
'example-app': patch
'@backstage/plugin-scaffolder': patch
---
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

@@ -53,3 +53,31 @@ You can do so by including the following lines in the last step of your
RUN apt-get update && apt-get install -y python3 python3-pip
RUN pip3 install cookiecutter
```
### Customizing the ScaffolderPage with Grouping and Filtering
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
```
<ScaffolderPage
extraSwimlanes={[
{
title: "Recommended",
filter: entity =>
entity?.metadata?.tags?.includes('recommended') ?? false,
},
]}
/>
```
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.
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.
![Grouped Templates](../../assets/software-templates/grouped-templates.png)
+3 -4
View File
@@ -32,7 +32,6 @@ import {
AlertDisplay,
OAuthRequestDialog,
SignInPage,
ContentHeader,
} from '@backstage/core-components';
import { apiDocsPlugin, ApiExplorerPage } from '@backstage/plugin-api-docs';
import {
@@ -180,11 +179,11 @@ const routes = (
path="/create"
element={
<ScaffolderPage
ExtraSwimlanes={[
extraSwimlanes={[
{
title: <ContentHeader title="Recommended" />,
title: 'Recommended',
filter: entity =>
entity?.metadata?.tags?.includes('recommended') || false,
entity?.metadata?.tags?.includes('recommended') ?? false,
},
]}
/>
+8 -6
View File
@@ -214,16 +214,17 @@ export const ScaffolderFieldExtensions: React_2.ComponentType;
// @public (undocumented)
export const ScaffolderPage: ({
TemplateCardComponent,
ExtraSwimlanes,
extraSwimlanes,
}: {
TemplateCardComponent?:
| ComponentType<{
template: TemplateEntityV1beta2;
}>
| undefined;
ExtraSwimlanes?:
extraSwimlanes?:
| {
title: ReactNode;
title?: string | undefined;
titleComponent?: ReactNode;
filter: (entity: Entity) => boolean;
}[]
| undefined;
@@ -248,7 +249,7 @@ export { scaffolderPlugin };
// @public (undocumented)
export const TemplateList: ({
TemplateCardComponent,
Swimlane,
swimlane,
}: 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)
@@ -260,8 +261,9 @@ export type TemplateListProps = {
template: TemplateEntityV1beta2;
}>
| undefined;
Swimlane?: {
title: React_2.ReactNode;
swimlane?: {
title?: string;
titleComponent?: React_2.ReactNode;
filter: (entity: Entity) => boolean;
};
};
+5 -4
View File
@@ -34,15 +34,16 @@ type RouterProps = {
TemplateCardComponent?:
| ComponentType<{ template: TemplateEntityV1beta2 }>
| undefined;
ExtraSwimlanes?: Array<{
title: React.ReactNode;
extraSwimlanes?: Array<{
title?: string;
titleComponent?: React.ReactNode;
filter: (entity: Entity) => boolean;
}>;
};
export const Router = ({
TemplateCardComponent,
ExtraSwimlanes,
extraSwimlanes,
}: RouterProps) => {
const outlet = useOutlet();
@@ -73,7 +74,7 @@ export const Router = ({
element={
<ScaffolderPage
TemplateCardComponent={TemplateCardComponent}
ExtraSwimlanes={ExtraSwimlanes}
extraSwimlanes={extraSwimlanes}
/>
}
/>
@@ -51,18 +51,28 @@ export type ScaffolderPageProps = {
TemplateCardComponent?:
| ComponentType<{ template: TemplateEntityV1beta2 }>
| undefined;
ExtraSwimlanes?: Array<{
title: React.ReactNode;
extraSwimlanes?: Array<{
title?: string;
titleComponent?: React.ReactNode;
filter: (entity: Entity) => boolean;
}>;
};
export const ScaffolderPageContents = ({
TemplateCardComponent,
ExtraSwimlanes,
extraSwimlanes,
}: ScaffolderPageProps) => {
const styles = useStyles();
const registerComponentLink = useRouteRef(registerComponentRouteRef);
const otherTemplatesSwimlane = {
title: extraSwimlanes ? 'Other Templates' : 'Templates',
filter: (entity: Entity) => {
const filtered = (extraSwimlanes ?? []).map(swimlane =>
swimlane.filter(entity),
);
return !filtered.some(result => result === true);
},
};
return (
<Page themeId="home">
@@ -100,14 +110,17 @@ export const ScaffolderPageContents = ({
<EntityTagPicker />
</div>
<div>
{ExtraSwimlanes &&
ExtraSwimlanes.map(swimlane => (
{extraSwimlanes &&
extraSwimlanes.map(swimlane => (
<TemplateList
TemplateCardComponent={TemplateCardComponent}
Swimlane={swimlane}
swimlane={swimlane}
/>
))}
<TemplateList TemplateCardComponent={TemplateCardComponent} />
<TemplateList
TemplateCardComponent={TemplateCardComponent}
swimlane={otherTemplatesSwimlane}
/>
</div>
</div>
</Content>
@@ -117,12 +130,12 @@ export const ScaffolderPageContents = ({
export const ScaffolderPage = ({
TemplateCardComponent,
ExtraSwimlanes,
extraSwimlanes,
}: ScaffolderPageProps) => (
<EntityListProvider>
<ScaffolderPageContents
TemplateCardComponent={TemplateCardComponent}
ExtraSwimlanes={ExtraSwimlanes}
extraSwimlanes={extraSwimlanes}
/>
</EntityListProvider>
);
@@ -35,84 +35,64 @@ export type TemplateListProps = {
TemplateCardComponent?:
| ComponentType<{ template: TemplateEntityV1beta2 }>
| undefined;
Swimlane?: {
title: React.ReactNode;
swimlane?: {
title?: string;
titleComponent?: React.ReactNode;
filter: (entity: Entity) => boolean;
};
};
type TemplateCardGridProps = {
Card: ComponentType<{ template: TemplateEntityV1beta2 }>;
entities: Entity[];
};
const TemplateCardGrid = ({ Card, entities }: TemplateCardGridProps) => {
return (
<ItemCardGrid>
{entities &&
entities?.length > 0 &&
entities.map((template: Entity) => (
<Card
key={stringifyEntityRef(template)}
template={template as TemplateEntityV1beta2}
/>
))}
</ItemCardGrid>
);
};
export const TemplateList = ({
TemplateCardComponent,
Swimlane,
swimlane,
}: TemplateListProps) => {
const { loading, error, entities } = useEntityListProvider();
const Card = TemplateCardComponent || TemplateCard;
if (Swimlane === null || Swimlane === undefined) {
return (
<>
{loading && <Progress />}
{error && (
<WarningPanel title="Oops! Something went wrong loading the templates">
{error.message}
</WarningPanel>
)}
{!error && !loading && !entities.length && (
<Typography variant="body2">
No templates found that match your filter. Learn more about{' '}
<Link href="https://backstage.io/docs/features/software-templates/adding-templates">
adding templates
</Link>
.
</Typography>
)}
<Content>
<ContentHeader title="All Templates" />
<TemplateCardGrid Card={Card} entities={entities} />
</Content>
</>
);
}
const filteredEntities = entities.filter((entity: Entity) =>
Swimlane.filter(entity),
const maybeFilteredEntities = swimlane
? entities.filter(e => swimlane.filter(e))
: entities;
const title = swimlane ? (
swimlane.titleComponent || <ContentHeader title={swimlane.title} />
) : (
<ContentHeader title="Other Templates" />
);
if (filteredEntities.length === 0) {
if (swimlane && maybeFilteredEntities.length === 0) {
return null;
}
return (
<Content>
{Swimlane.title}
<>
{loading && <Progress />}
{error && (
<WarningPanel title="Oops! Something went wrong loading the templates">
{error.message}
</WarningPanel>
)}
<TemplateCardGrid Card={Card} entities={filteredEntities} />
</Content>
{!error && !loading && !entities.length && (
<Typography variant="body2">
No templates found that match your filter. Learn more about{' '}
<Link href="https://backstage.io/docs/features/software-templates/adding-templates">
adding templates
</Link>
.
</Typography>
)}
<Content>
{title}
<ItemCardGrid>
{maybeFilteredEntities &&
maybeFilteredEntities?.length > 0 &&
maybeFilteredEntities.map((template: Entity) => (
<Card
key={stringifyEntityRef(template)}
template={template as TemplateEntityV1beta2}
/>
))}
</ItemCardGrid>
</Content>
</>
);
};