Merge pull request #20615 from acierto/configure-template
Make it possible to define control buttons text (Back, Create, Review) per template
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
'@backstage/plugin-scaffolder-common': patch
|
||||
'@backstage/plugin-scaffolder-react': patch
|
||||
---
|
||||
|
||||
Make it possible to define control buttons text (Back, Create, Review) per template
|
||||
@@ -363,8 +363,12 @@ export async function createRouter(
|
||||
const template = await authorizeTemplate(req.params, token);
|
||||
|
||||
const parameters = [template.spec.parameters ?? []].flat();
|
||||
|
||||
const presentation = template.spec.presentation;
|
||||
|
||||
res.json({
|
||||
title: template.metadata.title ?? template.metadata.name,
|
||||
...(presentation ? { presentation } : {}),
|
||||
description: template.metadata.description,
|
||||
'ui:options': template.metadata['ui:options'],
|
||||
steps: parameters.map(schema => ({
|
||||
|
||||
@@ -66,6 +66,7 @@ export interface TemplateEntityV1beta3 extends Entity {
|
||||
kind: 'Template';
|
||||
spec: {
|
||||
type: string;
|
||||
presentation?: TemplatePresentationV1beta3;
|
||||
parameters?: TemplateParametersV1beta3 | TemplateParametersV1beta3[];
|
||||
steps: Array<TemplateEntityStepV1beta3>;
|
||||
output?: {
|
||||
@@ -98,4 +99,13 @@ export interface TemplatePermissionsV1beta3 extends JsonObject {
|
||||
// (undocumented)
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface TemplatePresentationV1beta3 extends JsonObject {
|
||||
buttonLabels?: {
|
||||
backButtonText?: string;
|
||||
createButtonText?: string;
|
||||
reviewButtonText?: string;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
@@ -45,6 +45,12 @@ export interface TemplateEntityV1beta3 extends Entity {
|
||||
* The type that the Template will create. For example service, website or library.
|
||||
*/
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* Template specific configuration of the presentation layer.
|
||||
*/
|
||||
presentation?: TemplatePresentationV1beta3;
|
||||
|
||||
/**
|
||||
* This is a JSONSchema or an array of JSONSchema's which is used to render a form in the frontend
|
||||
* to collect user input and validate it against that schema. This can then be used in the `steps` part below to template
|
||||
@@ -67,6 +73,31 @@ export interface TemplateEntityV1beta3 extends Entity {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The presentation of the template.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface TemplatePresentationV1beta3 extends JsonObject {
|
||||
/**
|
||||
* Overrides default buttons' text
|
||||
*/
|
||||
buttonLabels?: {
|
||||
/**
|
||||
* The text for the button which leads to the previous template page
|
||||
*/
|
||||
backButtonText?: string;
|
||||
/**
|
||||
* The text for the button which starts the execution of the template
|
||||
*/
|
||||
createButtonText?: string;
|
||||
/**
|
||||
* The text for the button which opens template's review/summary
|
||||
*/
|
||||
reviewButtonText?: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Step that is part of a Template Entity.
|
||||
*
|
||||
|
||||
@@ -27,6 +27,7 @@ export {
|
||||
isTemplateEntityV1beta3,
|
||||
} from './TemplateEntityV1beta3';
|
||||
export type {
|
||||
TemplatePresentationV1beta3,
|
||||
TemplateEntityV1beta3,
|
||||
TemplateEntityStepV1beta3,
|
||||
TemplateParametersV1beta3,
|
||||
|
||||
@@ -33,6 +33,7 @@ import { TaskStep } from '@backstage/plugin-scaffolder-common';
|
||||
import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common';
|
||||
import { TemplateGroupFilter } from '@backstage/plugin-scaffolder-react';
|
||||
import { TemplateParameterSchema } from '@backstage/plugin-scaffolder-react';
|
||||
import { TemplatePresentationV1beta3 } from '@backstage/plugin-scaffolder-common';
|
||||
import { UiSchema } from '@rjsf/utils';
|
||||
|
||||
// @alpha (undocumented)
|
||||
@@ -189,6 +190,7 @@ export type StepperProps = {
|
||||
components?: {
|
||||
ReviewStepComponent?: ComponentType<ReviewStepProps>;
|
||||
ReviewStateComponent?: (props: ReviewStateProps) => JSX.Element;
|
||||
backButtonText?: ReactNode;
|
||||
createButtonText?: ReactNode;
|
||||
reviewButtonText?: ReactNode;
|
||||
};
|
||||
@@ -303,6 +305,7 @@ export const useTemplateParameterSchema: (templateRef: string) => {
|
||||
// @alpha
|
||||
export const useTemplateSchema: (manifest: TemplateParameterSchema) => {
|
||||
steps: ParsedTemplateSchema[];
|
||||
presentation?: TemplatePresentationV1beta3 | undefined;
|
||||
};
|
||||
|
||||
// @alpha (undocumented)
|
||||
|
||||
@@ -39,6 +39,7 @@ import { StrictRJSFSchema } from '@rjsf/utils';
|
||||
import { TaskSpec } from '@backstage/plugin-scaffolder-common';
|
||||
import { TaskStep } from '@backstage/plugin-scaffolder-common';
|
||||
import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common';
|
||||
import { TemplatePresentationV1beta3 } from '@backstage/plugin-scaffolder-common';
|
||||
import { TemplatesType } from '@rjsf/utils';
|
||||
import { UIOptionsType } from '@rjsf/utils';
|
||||
import { UiSchema } from '@rjsf/utils';
|
||||
@@ -482,6 +483,7 @@ export type TemplateGroupFilter = {
|
||||
export type TemplateParameterSchema = {
|
||||
title: string;
|
||||
description?: string;
|
||||
presentation?: TemplatePresentationV1beta3;
|
||||
steps: Array<{
|
||||
title: string;
|
||||
description?: string;
|
||||
|
||||
@@ -81,6 +81,7 @@ export type StepperProps = {
|
||||
components?: {
|
||||
ReviewStepComponent?: ComponentType<ReviewStepProps>;
|
||||
ReviewStateComponent?: (props: ReviewStateProps) => JSX.Element;
|
||||
backButtonText?: ReactNode;
|
||||
createButtonText?: ReactNode;
|
||||
reviewButtonText?: ReactNode;
|
||||
};
|
||||
@@ -96,11 +97,12 @@ export const Stepper = (stepperProps: StepperProps) => {
|
||||
const {
|
||||
ReviewStateComponent = ReviewState,
|
||||
ReviewStepComponent,
|
||||
backButtonText = 'Back',
|
||||
createButtonText = 'Create',
|
||||
reviewButtonText = 'Review',
|
||||
} = components;
|
||||
const analytics = useAnalytics();
|
||||
const { steps } = useTemplateSchema(props.manifest);
|
||||
const { presentation, steps } = useTemplateSchema(props.manifest);
|
||||
const apiHolder = useApiHolder();
|
||||
const [activeStep, setActiveStep] = useState(0);
|
||||
const [isValidating, setIsValidating] = useState(false);
|
||||
@@ -178,6 +180,13 @@ export const Stepper = (stepperProps: StepperProps) => {
|
||||
setFormState(current => ({ ...current, ...formData }));
|
||||
};
|
||||
|
||||
const backLabel =
|
||||
presentation?.buttonLabels?.backButtonText ?? backButtonText;
|
||||
const createLabel =
|
||||
presentation?.buttonLabels?.createButtonText ?? createButtonText;
|
||||
const reviewLabel =
|
||||
presentation?.buttonLabels?.reviewButtonText ?? reviewButtonText;
|
||||
|
||||
return (
|
||||
<>
|
||||
{isValidating && <LinearProgress variant="indeterminate" />}
|
||||
@@ -213,7 +222,7 @@ export const Stepper = (stepperProps: StepperProps) => {
|
||||
className={styles.backButton}
|
||||
disabled={activeStep < 1 || isValidating}
|
||||
>
|
||||
Back
|
||||
{backLabel}
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
@@ -221,7 +230,7 @@ export const Stepper = (stepperProps: StepperProps) => {
|
||||
type="submit"
|
||||
disabled={isValidating}
|
||||
>
|
||||
{activeStep === steps.length - 1 ? reviewButtonText : 'Next'}
|
||||
{activeStep === steps.length - 1 ? reviewLabel : 'Next'}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
@@ -251,7 +260,7 @@ export const Stepper = (stepperProps: StepperProps) => {
|
||||
color="primary"
|
||||
onClick={handleCreate}
|
||||
>
|
||||
{createButtonText}
|
||||
{createLabel}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { featureFlagsApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
import { TemplatePresentationV1beta3 } from '@backstage/plugin-scaffolder-common';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { UiSchema } from '@rjsf/utils';
|
||||
import { TemplateParameterSchema } from '@backstage/plugin-scaffolder-react';
|
||||
@@ -39,7 +40,10 @@ export interface ParsedTemplateSchema {
|
||||
*/
|
||||
export const useTemplateSchema = (
|
||||
manifest: TemplateParameterSchema,
|
||||
): { steps: ParsedTemplateSchema[] } => {
|
||||
): {
|
||||
steps: ParsedTemplateSchema[];
|
||||
presentation?: TemplatePresentationV1beta3;
|
||||
} => {
|
||||
const featureFlags = useApi(featureFlagsApiRef);
|
||||
const steps = manifest.steps.map(({ title, description, schema }) => ({
|
||||
title,
|
||||
@@ -76,6 +80,7 @@ export const useTemplateSchema = (
|
||||
}));
|
||||
|
||||
return {
|
||||
presentation: manifest.presentation,
|
||||
steps: returningSteps,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
import { JsonObject } from '@backstage/types';
|
||||
|
||||
import { TemplatePresentationV1beta3 } from '@backstage/plugin-scaffolder-common';
|
||||
|
||||
/**
|
||||
* The shape of each entry of parameters which gets rendered
|
||||
* as a separate step in the wizard input
|
||||
@@ -25,6 +27,7 @@ import { JsonObject } from '@backstage/types';
|
||||
export type TemplateParameterSchema = {
|
||||
title: string;
|
||||
description?: string;
|
||||
presentation?: TemplatePresentationV1beta3;
|
||||
steps: Array<{
|
||||
title: string;
|
||||
description?: string;
|
||||
|
||||
@@ -405,11 +405,10 @@ describe('SearchContext', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current).toEqual(expect.objectContaining(initialState));
|
||||
expect(result.current.fetchNextPage).toBeUndefined();
|
||||
expect(result.current.fetchPreviousPage).toBeDefined();
|
||||
});
|
||||
|
||||
expect(result.current.fetchNextPage).toBeUndefined();
|
||||
expect(result.current.fetchPreviousPage).toBeDefined();
|
||||
|
||||
await act(async () => {
|
||||
result.current.fetchPreviousPage!();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user