diff --git a/docs/features/software-templates/writing-custom-field-extensions.md b/docs/features/software-templates/writing-custom-field-extensions.md new file mode 100644 index 0000000000..4f2d3bdf5d --- /dev/null +++ b/docs/features/software-templates/writing-custom-field-extensions.md @@ -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) => { + return ( + 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 = ( + + ... + } /> + ... + +); +``` + +Should look something like this instead: + +```tsx +import { MyCustomFieldExtension } from './scafffolder/MyCustomExtension'; +const routes = ( + + ... + }> + + + + + ... + +); +``` + +## 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 +``` diff --git a/microsite/sidebars.json b/microsite/sidebars.json index d0d77e9b59..93af2c0be3 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -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" ]