From 512a04ec3a606af34e70a949cc0cb52dd8ae6d07 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 27 Jul 2021 10:40:57 +0200 Subject: [PATCH 1/6] chore: started some documentation on FieldExtensions Signed-off-by: blam --- .../writing-custom-field-extensions.md | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 docs/features/software-templates/writing-custom-field-extensions.md 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..af703029e3 --- /dev/null +++ b/docs/features/software-templates/writing-custom-field-extensions.md @@ -0,0 +1,24 @@ +--- +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 us From 16db8192ff230dc3a8e9e6dbed2345c47fcf153c Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 9 Aug 2021 17:20:18 +0200 Subject: [PATCH 2/6] chore: added some more of an example Signed-off-by: blam --- .../writing-custom-field-extensions.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/features/software-templates/writing-custom-field-extensions.md b/docs/features/software-templates/writing-custom-field-extensions.md index af703029e3..56c7310809 100644 --- a/docs/features/software-templates/writing-custom-field-extensions.md +++ b/docs/features/software-templates/writing-custom-field-extensions.md @@ -21,4 +21,12 @@ 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 us +You can create your own Field Extension by using the `createScaffolderFieldExtension` `API` like below: + +```tsx +//packages/app/scaffolder/ +const CustomFieldExtensionComponent = () => { + +} + + From df1504c9ca657facc3065feb5ea238a10ab45f43 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 30 Aug 2021 13:52:36 +0200 Subject: [PATCH 3/6] chore: added some more lines of documentation Signed-off-by: blam --- .../writing-custom-field-extensions.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/features/software-templates/writing-custom-field-extensions.md b/docs/features/software-templates/writing-custom-field-extensions.md index 56c7310809..e32aa4e78d 100644 --- a/docs/features/software-templates/writing-custom-field-extensions.md +++ b/docs/features/software-templates/writing-custom-field-extensions.md @@ -21,12 +21,14 @@ 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` `API` like below: +You can create your own Field Extension by using the +`createScaffolderFieldExtension` `API` like below: ```tsx -//packages/app/scaffolder/ -const CustomFieldExtensionComponent = () => { - -} - +//packages/app/scaffolder/MyCustomExtension/MyCustomExtension.tsx +export const MyCustomExtension = () => {}; +``` +```tsx +// packages/app/scaffolder/MyCustomExtension/validation.ts +``` From 67ee34dcaede5012d4e1d75883878919e5746d01 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 7 Sep 2021 15:07:40 +0200 Subject: [PATCH 4/6] docs: added some more examples Signed-off-by: blam --- .../writing-custom-field-extensions.md | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/features/software-templates/writing-custom-field-extensions.md b/docs/features/software-templates/writing-custom-field-extensions.md index e32aa4e78d..cf0a1e5d8f 100644 --- a/docs/features/software-templates/writing-custom-field-extensions.md +++ b/docs/features/software-templates/writing-custom-field-extensions.md @@ -26,9 +26,31 @@ You can create your own Field Extension by using the ```tsx //packages/app/scaffolder/MyCustomExtension/MyCustomExtension.tsx -export const MyCustomExtension = () => {}; +import { FieldProps } from '@rjsf/core'; +export const MyCustomExtension = ({ onChange, required }: FieldProps) => { + return ( + 0 && !formData} + > + ) +}; ``` ```tsx // packages/app/scaffolder/MyCustomExtension/validation.ts +import { FieldValidation } from '@rjsf/core'; +import { KubernetesValidatorFunctions } from '@backstage/catalog-model'; + +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.', + ); + } +}; ``` From 114a10fe9c083596a67324943267d6eacba26571 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 14 Sep 2021 12:10:36 +0200 Subject: [PATCH 5/6] chore: added some more docs around field extensions Signed-off-by: blam Signed-off-by: blam --- .../writing-custom-field-extensions.md | 96 +++++++++++++++++++ microsite/sidebars.json | 1 + 2 files changed, 97 insertions(+) diff --git a/docs/features/software-templates/writing-custom-field-extensions.md b/docs/features/software-templates/writing-custom-field-extensions.md index cf0a1e5d8f..d9636f864b 100644 --- a/docs/features/software-templates/writing-custom-field-extensions.md +++ b/docs/features/software-templates/writing-custom-field-extensions.md @@ -27,12 +27,17 @@ You can create your own Field Extension by using the ```tsx //packages/app/scaffolder/MyCustomExtension/MyCustomExtension.tsx import { FieldProps } from '@rjsf/core'; + +/* + This is the actual component that will get rendered in the form +*/ export const MyCustomExtension = ({ onChange, required }: FieldProps) => { return ( 0 && !formData} + onChange={onChange} > ) }; @@ -43,6 +48,10 @@ export const MyCustomExtension = ({ onChange, required }: FieldProps) => import { FieldValidation } from '@rjsf/core'; import { KubernetesValidatorFunctions } from '@backstage/catalog-model'; +/* + 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, @@ -54,3 +63,90 @@ export const myCustomValidation = ( } }; ``` + +```tsx +// packages/app/scaffolder/MyCustomExtension/index.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, + }), +); +``` + +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" ] From cf4f80d50bcf190f5538fd9711ec9bfe07dd1930 Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 15 Sep 2021 10:52:29 +0200 Subject: [PATCH 6/6] chore: updating review emcomments Signed-off-by: blam --- .../writing-custom-field-extensions.md | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/docs/features/software-templates/writing-custom-field-extensions.md b/docs/features/software-templates/writing-custom-field-extensions.md index d9636f864b..4f2d3bdf5d 100644 --- a/docs/features/software-templates/writing-custom-field-extensions.md +++ b/docs/features/software-templates/writing-custom-field-extensions.md @@ -22,12 +22,13 @@ Field extensions are a way to combine an ID, a `React` Component and a the `Scaffolder` frontend plugin in your own `App.tsx`. You can create your own Field Extension by using the -`createScaffolderFieldExtension` `API` like below: +[`createScaffolderFieldExtension`](https://backstage.io/docs/reference/plugin-scaffolder.createscaffolderfieldextension) +`API` like below: ```tsx //packages/app/scaffolder/MyCustomExtension/MyCustomExtension.tsx -import { FieldProps } from '@rjsf/core'; - +import { FieldProps, FieldValidation } from '@rjsf/core'; +import { KubernetesValidatorFunctions } from '@backstage/catalog-model'; /* This is the actual component that will get rendered in the form */ @@ -41,17 +42,12 @@ export const MyCustomExtension = ({ onChange, required }: FieldProps) => > ) }; -``` - -```tsx -// packages/app/scaffolder/MyCustomExtension/validation.ts -import { FieldValidation } from '@rjsf/core'; -import { KubernetesValidatorFunctions } from '@backstage/catalog-model'; /* 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, @@ -65,7 +61,7 @@ export const myCustomValidation = ( ``` ```tsx -// packages/app/scaffolder/MyCustomExtension/index.ts +// packages/app/scaffolder/MyCustomExtension/extensions.ts /* This is where the magic happens and creates the custom field extension. @@ -90,6 +86,12 @@ export const MyCustomFieldExtension = plugin.provide( ); ``` +```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.