Merge pull request #7172 from backstage/blam/creating-custom-scaffolder-field-extensions

docs: Added Documentation for `customFieldExtensions`
This commit is contained in:
Ben Lambert
2021-09-15 11:23:01 +02:00
committed by GitHub
2 changed files with 155 additions and 0 deletions
@@ -0,0 +1,154 @@
---
id: writing-custom-field-extensions
title: Writing Custom Field Extensions
description: How to write your own field extensions
---
Collecting input from the user is a very large part of the scaffolding process
and Software Templates as a whole. Sometimes the built in components and fields
just aren't good enough, and sometimes you want to enrich the form that the
users sees with better inputs that fit better.
This is where `Custom Field Extensions` come in.
With them you can show your own `React` Components and use them to control the
state of the JSON schema, as well as provide your own validation functions to
validate the data too.
## Creating a Field Extension
Field extensions are a way to combine an ID, a `React` Component and a
`validation` function together in a modular way that you can then use to pass to
the `Scaffolder` frontend plugin in your own `App.tsx`.
You can create your own Field Extension by using the
[`createScaffolderFieldExtension`](https://backstage.io/docs/reference/plugin-scaffolder.createscaffolderfieldextension)
`API` like below:
```tsx
//packages/app/scaffolder/MyCustomExtension/MyCustomExtension.tsx
import { FieldProps, FieldValidation } from '@rjsf/core';
import { KubernetesValidatorFunctions } from '@backstage/catalog-model';
/*
This is the actual component that will get rendered in the form
*/
export const MyCustomExtension = ({ onChange, required }: FieldProps<string>) => {
return (
<FormControl
margin="normal"
required={required}
error={rawErrors?.length > 0 && !formData}
onChange={onChange}
>
)
};
/*
This is a validation function that will run when the form is submitted.
You will get the value from the `onChange` handler before as the value here to make sure that the types are aligned\
*/
export const myCustomValidation = (
value: string,
validation: FieldValidation,
) => {
if (!KubernetesValidatorFunctions.isValidObjectName(value)) {
validation.addError(
'must start and end with an alphanumeric character, and contain only alphanumeric characters, hyphens, underscores, and periods. Maximum length is 63 characters.',
);
}
};
```
```tsx
// packages/app/scaffolder/MyCustomExtension/extensions.ts
/*
This is where the magic happens and creates the custom field extension.
Note that if you're writing extensions part of a separate plugin,
then please use `plugin.provide` from there instead and export it part of your `plugin.ts` rather than re-using the `scaffolder.plugin`.
*/
import {
plugin,
createScaffolderFieldExtension,
} from '@backstage/plugin-scaffolder';
import { MyCustomExtension } from './MyCustomExtension';
import { myCustomValidation } from './validation';
export const MyCustomFieldExtension = plugin.provide(
createScaffolderFieldExtension({
name: 'MyCustomExtension',
component: MyCustomExtension,
validation: myCustomValidation,
}),
);
```
```tsx
// packages/app/scaffolder/MyCustomExtension/index.ts
export { MyCustomFieldExtension } from './extension';
```
Once all these files are in place, you then need to provide your custom
extension to the `scaffolder` plugin.
You do this in `packages/app/App.tsx`. You need to provide the
`customFieldExtensions` as children to the `ScaffolderPage`.
```tsx
const routes = (
<FlatRoutes>
...
<Route path="/create" element={<ScaffolderPage />} />
...
</FlatRoutes>
);
```
Should look something like this instead:
```tsx
import { MyCustomFieldExtension } from './scafffolder/MyCustomExtension';
const routes = (
<FlatRoutes>
...
<Route path="/create" element={<ScaffolderPage />}>
<ScaffolderFieldExtensions>
<MyCustomFieldExtension />
</ScaffolderFieldExtensions>
</Route>
...
</FlatRoutes>
);
```
## Using the Custom Field Extension
Once it's been passed to the `ScaffolderPage` you should now be able to use the
`ui:field` property in your templates to point it to the name of the
`customFieldExtension` that you registered.
Something like this:
```yaml
apiVersion: backstage.io/v1beta2
kind: Template
metadata:
name: Test template
title: Test template with custom extension
description: Test template
spec:
parameters:
- title: Fill in some steps
required:
- name
properties:
name:
title: Name
type: string
description: My custom name for the component
ui:field: MyCustomExtension
```
+1
View File
@@ -70,6 +70,7 @@
"features/software-templates/writing-templates",
"features/software-templates/builtin-actions",
"features/software-templates/writing-custom-actions",
"features/software-templates/writing-custom-field-extensions",
"features/software-templates/template-legacy",
"features/software-templates/migrating-from-v1alpha1-to-v1beta2"
]