Merge pull request #9752 from backstage/blam/deprecations/scaffolder/frontend

🧹 some tidying up in the `scaffolder` plugin
This commit is contained in:
Fredrik Adelöw
2022-02-24 11:54:06 +01:00
committed by GitHub
22 changed files with 314 additions and 283 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-scaffolder': patch
---
- Reworking the `FieldExtensionComponentType` so we can export the `ui:schema:options` props in the `api-report.md`.
- Exporting all of the `UiOptions` types for the `FieldExtensions` so we can see them in the `api-report.md`.
- Removing the redundant type in the `CustomFieldValidator` union.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-scaffolder': minor
---
- **BREAKING** - Removed the `plugin` export, use `scaffolderPlugin` instead.
- **BREAKING** - Removed the `TextValuePicker` component export, you can inline this component instead as it's a simple wrapper around a `TextField` from `@material-ui/core`.
@@ -13,14 +13,44 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import type { FieldValidation } from '@rjsf/core';
import {
createScaffolderFieldExtension,
TextValuePicker,
FieldExtensionComponentProps,
scaffolderPlugin,
} from '@backstage/plugin-scaffolder';
import { TextField } from '@material-ui/core';
const TextValuePicker = (props: FieldExtensionComponentProps<string>) => {
const {
onChange,
required,
schema: { title, description },
rawErrors,
formData,
uiSchema: { 'ui:autofocus': autoFocus },
idSchema,
placeholder,
} = props;
return (
<TextField
id={idSchema?.$id}
label={title}
placeholder={placeholder}
helperText={description}
required={required}
value={formData ?? ''}
onChange={({ target: { value } }) => onChange(value)}
margin="normal"
error={rawErrors?.length > 0 && !formData}
inputProps={{ autoFocus }}
/>
);
};
export const LowerCaseValuePickerFieldExtension = scaffolderPlugin.provide(
createScaffolderFieldExtension({
name: 'LowerCaseValuePicker',
+91 -76
View File
@@ -32,28 +32,21 @@ import { TemplateEntityV1beta2 } from '@backstage/plugin-scaffolder-common';
// Warning: (ae-missing-release-tag) "createScaffolderFieldExtension" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function createScaffolderFieldExtension<T = any>(
options: FieldExtensionOptions<T>,
export function createScaffolderFieldExtension<
TReturnValue = unknown,
TInputProps = unknown,
>(
options: FieldExtensionOptions<TReturnValue, TInputProps>,
): Extension<() => null>;
// @public
export type CustomFieldValidator<T> =
| ((data: T, field: FieldValidation) => void)
| ((
data: T,
field: FieldValidation,
context: {
apiHolder: ApiHolder;
},
) => void);
// Warning: (ae-missing-release-tag) "EntityNamePicker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const EntityNamePicker: ({
schema: { title, description },
...props
}: FieldProps<string>) => JSX.Element;
export type CustomFieldValidator<TFieldReturnValue> = (
data: TFieldReturnValue,
field: FieldValidation,
context: {
apiHolder: ApiHolder;
},
) => void;
// Warning: (ae-missing-release-tag) "EntityNamePickerFieldExtension" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -62,32 +55,46 @@ export const EntityNamePickerFieldExtension: () => null;
// Warning: (ae-missing-release-tag) "EntityPicker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const EntityPicker: ({
onChange,
schema: { title, description },
required,
uiSchema,
rawErrors,
formData,
idSchema,
}: FieldProps<string>) => JSX.Element;
// @public
export const EntityPicker: (
props: FieldExtensionComponentProps<string, EntityPickerUiOptions>,
) => JSX.Element;
// Warning: (ae-missing-release-tag) "EntityPickerFieldExtension" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const EntityPickerFieldExtension: () => null;
// Warning: (ae-missing-release-tag) "EntityPickerUiOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface EntityPickerUiOptions {
// (undocumented)
allowArbitraryValues?: boolean;
// (undocumented)
allowedKinds?: string[];
// (undocumented)
defaultKind?: string;
}
// Warning: (ae-missing-release-tag) "EntityTagsPicker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export const EntityTagsPicker: ({
formData,
onChange,
uiSchema,
}: FieldProps<string[]>) => JSX.Element;
export const EntityTagsPicker: (
props: FieldExtensionComponentProps<string[], EntityTagsPickerUiOptions>,
) => JSX.Element;
// @public
export const EntityTagsPickerFieldExtension: () => null;
// Warning: (ae-missing-release-tag) "EntityTagsPickerUiOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface EntityTagsPickerUiOptions {
// (undocumented)
kinds?: string[];
}
// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "FavouriteTemplate" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -96,57 +103,81 @@ export const FavouriteTemplate: (props: Props) => JSX.Element;
// @public
export interface FieldExtensionComponentProps<
ReturnValue,
UiOptions extends {} = {},
> extends FieldProps<ReturnValue> {
TFieldReturnValue,
TUiOptions extends {} = {},
> extends FieldProps<TFieldReturnValue> {
// (undocumented)
uiSchema: {
'ui:options'?: UiOptions;
uiSchema: FieldProps['uiSchema'] & {
'ui:options'?: TUiOptions;
};
}
// @public
export type FieldExtensionOptions<T = any> = {
export type FieldExtensionOptions<
TFieldReturnValue = unknown,
TProps = FieldProps<TFieldReturnValue>,
> = {
name: string;
component: (props: FieldProps<T>) => JSX.Element | null;
validation?: CustomFieldValidator<T>;
component: (props: TProps) => JSX.Element | null;
validation?: CustomFieldValidator<TFieldReturnValue>;
};
// Warning: (ae-missing-release-tag) "OwnedEntityPicker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const OwnedEntityPicker: ({
onChange,
schema: { title, description },
required,
uiSchema,
rawErrors,
formData,
idSchema,
}: FieldProps<string>) => JSX.Element;
// @public
export const OwnedEntityPicker: (
props: FieldExtensionComponentProps<string, OwnedEntityPickerUiOptions>,
) => JSX.Element;
// Warning: (ae-missing-release-tag) "OwnedEntityPickerFieldExtension" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const OwnedEntityPickerFieldExtension: () => null;
// Warning: (ae-missing-release-tag) "OwnerPicker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
// Warning: (ae-missing-release-tag) "OwnedEntityPickerUiOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const OwnerPicker: ({
schema: { title, description },
uiSchema,
...props
}: FieldProps<string>) => JSX.Element;
export interface OwnedEntityPickerUiOptions {
// (undocumented)
allowedKinds?: string[];
// (undocumented)
defaultKind?: string;
}
// Warning: (ae-missing-release-tag) "OwnerPicker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export const OwnerPicker: (
props: FieldExtensionComponentProps<string, OwnerPickerUiOptions>,
) => JSX.Element;
// Warning: (ae-missing-release-tag) "OwnerPickerFieldExtension" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const OwnerPickerFieldExtension: () => null;
// Warning: (ae-missing-release-tag) "RepoUrlPicker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
// Warning: (ae-missing-release-tag) "OwnerPickerUiOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface OwnerPickerUiOptions {
// (undocumented)
allowedKinds?: string[];
}
// Warning: (ae-missing-release-tag) "repoPickerValidation" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const repoPickerValidation: (
value: string,
validation: FieldValidation,
context: {
apiHolder: ApiHolder;
},
) => void;
// Warning: (ae-missing-release-tag) "RepoUrlPicker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public
export const RepoUrlPicker: (
props: FieldExtensionComponentProps<string, RepoUrlPickerUiOptions>,
) => JSX.Element;
@@ -276,7 +307,7 @@ export const ScaffolderPage: ({
// Warning: (ae-missing-release-tag) "scaffolderPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
const scaffolderPlugin: BackstagePlugin<
export const scaffolderPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
},
@@ -284,8 +315,6 @@ const scaffolderPlugin: BackstagePlugin<
registerComponent: ExternalRouteRef<undefined, true>;
}
>;
export { scaffolderPlugin as plugin };
export { scaffolderPlugin };
// @public
export const TaskPage: ({ loadingText }: TaskPageProps) => JSX.Element;
@@ -324,20 +353,6 @@ export type TemplateListProps = {
// @public (undocumented)
export const TemplateTypePicker: () => JSX.Element | null;
// Warning: (ae-missing-release-tag) "TextValuePicker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const TextValuePicker: ({
onChange,
required,
schema: { title, description },
rawErrors,
formData,
uiSchema: { 'ui:autofocus': autoFocus },
idSchema,
placeholder,
}: FieldProps<string>) => JSX.Element;
// @public
export const useTemplateSecrets: () => {
setSecret: (input: Record<string, string>) => void;
@@ -14,12 +14,38 @@
* limitations under the License.
*/
import React from 'react';
import { FieldProps } from '@rjsf/core';
import { TextValuePicker } from '../TextValuePicker';
import { FieldExtensionComponentProps } from '../../../extensions';
import { TextField } from '@material-ui/core';
export const EntityNamePicker = ({
schema: { title = 'Name', description = 'Unique name of the component' },
...props
}: FieldProps<string>) => (
<TextValuePicker schema={{ title, description }} {...props} />
);
/**
* EntityName Picker
*/
export const EntityNamePicker = (
props: FieldExtensionComponentProps<string>,
) => {
const {
onChange,
required,
schema: { title = 'Name', description = 'Unique name of the component' },
rawErrors,
formData,
uiSchema: { 'ui:autofocus': autoFocus },
idSchema,
placeholder,
} = props;
return (
<TextField
id={idSchema?.$id}
label={title}
placeholder={placeholder}
helperText={description}
required={required}
value={formData ?? ''}
onChange={({ target: { value } }) => onChange(value)}
margin="normal"
error={rawErrors?.length > 0 && !formData}
inputProps={{ autoFocus }}
/>
);
};
@@ -18,9 +18,9 @@ import { Entity } from '@backstage/catalog-model';
import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog-react';
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
import { FieldProps } from '@rjsf/core';
import userEvent from '@testing-library/user-event';
import { fireEvent } from '@testing-library/react';
import React from 'react';
import { EntityPicker, allowArbitraryValues } from './EntityPicker';
import { EntityPicker } from './EntityPicker';
const makeEntity = (kind: string, namespace: string, name: string): Entity => ({
apiVersion: 'backstage.io/v1beta1',
@@ -92,15 +92,16 @@ describe('<EntityPicker />', () => {
});
it('updates even if there is not an exact match', async () => {
const { getByLabelText } = await renderInTestApp(
const { getByRole } = await renderInTestApp(
<Wrapper>
<EntityPicker {...props} />
</Wrapper>,
);
const input = getByLabelText('Entity');
userEvent.type(input, 'squ');
input.blur();
const input = getByRole('textbox');
fireEvent.change(input, { target: { value: 'squ' } });
fireEvent.blur(input);
expect(onChange).toHaveBeenCalledWith('squ');
});
@@ -136,37 +137,3 @@ describe('<EntityPicker />', () => {
});
});
});
describe('allowArbitraryValues', () => {
describe('without ui:options', () => {
it('defaults to true', () => {
const uiSchema = {};
const result = allowArbitraryValues(uiSchema);
expect(result).toBe(true);
});
});
describe('without ui:options.allowArbitraryValues', () => {
it('defaults to true', () => {
const uiSchema = { 'ui:options': {} };
const result = allowArbitraryValues(uiSchema);
expect(result).toBe(true);
});
});
describe('with ui:options.allowArbitraryValues set to true', () => {
it('is true', () => {
const uiSchema = { 'ui:options': { allowArbitraryValues: true } };
const result = allowArbitraryValues(uiSchema);
expect(result).toBe(true);
});
});
describe('with ui:options.allowArbitraryValues set to false', () => {
it('is false', () => {
const uiSchema = { 'ui:options': { allowArbitraryValues: false } };
const result = allowArbitraryValues(uiSchema);
expect(result).toBe(false);
});
});
});
@@ -21,24 +21,34 @@ import {
import { TextField } from '@material-ui/core';
import FormControl from '@material-ui/core/FormControl';
import Autocomplete from '@material-ui/lab/Autocomplete';
import { FieldProps, UiSchema } from '@rjsf/core';
import React, { useCallback, useEffect } from 'react';
import useAsync from 'react-use/lib/useAsync';
import { FieldExtensionComponentProps } from '../../../extensions';
export const allowArbitraryValues = (uiSchema: UiSchema): boolean =>
(uiSchema['ui:options']?.allowArbitraryValues as boolean) ?? true;
export interface EntityPickerUiOptions {
allowedKinds?: string[];
defaultKind?: string;
allowArbitraryValues?: boolean;
}
/**
* Entity Picker
*/
export const EntityPicker = (
props: FieldExtensionComponentProps<string, EntityPickerUiOptions>,
) => {
const {
onChange,
schema: { title = 'Entity', description = 'An entity from the catalog' },
required,
uiSchema,
rawErrors,
formData,
idSchema,
} = props;
const allowedKinds = uiSchema['ui:options']?.allowedKinds;
const defaultKind = uiSchema['ui:options']?.defaultKind;
export const EntityPicker = ({
onChange,
schema: { title = 'Entity', description = 'An entity from the catalog' },
required,
uiSchema,
rawErrors,
formData,
idSchema,
}: FieldProps<string>) => {
const allowedKinds = uiSchema['ui:options']?.allowedKinds as string[];
const defaultKind = uiSchema['ui:options']?.defaultKind as string | undefined;
const catalogApi = useApi(catalogApiRef);
const { value: entities, loading } = useAsync(() =>
@@ -78,7 +88,7 @@ export const EntityPicker = ({
onChange={onSelect}
options={entityRefs || []}
autoSelect
freeSolo={allowArbitraryValues(uiSchema)}
freeSolo={uiSchema['ui:options']?.allowArbitraryValues ?? true}
renderInput={params => (
<TextField
{...params}
@@ -14,3 +14,4 @@
* limitations under the License.
*/
export { EntityPicker } from './EntityPicker';
export type { EntityPickerUiOptions } from './EntityPicker';
@@ -22,22 +22,23 @@ import { useApi } from '@backstage/core-plugin-api';
import { catalogApiRef } from '@backstage/plugin-catalog-react';
import { FormControl, TextField } from '@material-ui/core';
import { Autocomplete } from '@material-ui/lab';
import { FieldProps } from '@rjsf/core';
import { FieldExtensionComponentProps } from '../../../extensions';
export interface EntityTagsPickerUiOptions {
kinds?: string[];
}
/**
* EntityTagsPicker
* @public
*/
export const EntityTagsPicker = ({
formData,
onChange,
uiSchema,
}: FieldProps<string[]>) => {
export const EntityTagsPicker = (
props: FieldExtensionComponentProps<string[], EntityTagsPickerUiOptions>,
) => {
const { formData, onChange, uiSchema } = props;
const catalogApi = useApi(catalogApiRef);
const [inputValue, setInputValue] = useState('');
const [inputError, setInputError] = useState(false);
const tagValidator = makeValidator().isValidTag;
const kinds = uiSchema['ui:options']?.kinds as string[];
const kinds = uiSchema['ui:options']?.kinds;
const { loading, value: existingTags } = useAsync(async () => {
const tagsRequest: GetEntitiesRequest = { fields: ['metadata.tags'] };
@@ -14,3 +14,4 @@
* limitations under the License.
*/
export { EntityTagsPicker } from './EntityTagsPicker';
export type { EntityTagsPickerUiOptions } from './EntityTagsPicker';
@@ -20,20 +20,33 @@ import {
import { TextField } from '@material-ui/core';
import FormControl from '@material-ui/core/FormControl';
import Autocomplete from '@material-ui/lab/Autocomplete';
import { FieldProps } from '@rjsf/core';
import React from 'react';
export const OwnedEntityPicker = ({
onChange,
schema: { title = 'Entity', description = 'An entity from the catalog' },
required,
uiSchema,
rawErrors,
formData,
idSchema,
}: FieldProps<string>) => {
const allowedKinds = uiSchema['ui:options']?.allowedKinds as string[];
const defaultKind = uiSchema['ui:options']?.defaultKind as string | undefined;
import { FieldExtensionComponentProps } from '../../../extensions';
export interface OwnedEntityPickerUiOptions {
allowedKinds?: string[];
defaultKind?: string;
}
/**
* Owned Entity Picker
*/
export const OwnedEntityPicker = (
props: FieldExtensionComponentProps<string, OwnedEntityPickerUiOptions>,
) => {
const {
onChange,
schema: { title = 'Entity', description = 'An entity from the catalog' },
required,
uiSchema,
rawErrors,
formData,
idSchema,
} = props;
const allowedKinds = uiSchema['ui:options']?.allowedKinds;
const defaultKind = uiSchema['ui:options']?.defaultKind;
const { ownedEntities, loading } = useOwnedEntities(allowedKinds);
const entityRefs = ownedEntities?.items
@@ -14,3 +14,4 @@
* limitations under the License.
*/
export { OwnedEntityPicker } from './OwnedEntityPicker';
export type { OwnedEntityPickerUiOptions } from './OwnedEntityPicker';
@@ -13,15 +13,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { FieldProps } from '@rjsf/core';
import React from 'react';
import { EntityPicker } from '../EntityPicker';
import { FieldExtensionComponentProps } from '../../../extensions';
export interface OwnerPickerUiOptions {
allowedKinds?: string[];
}
/**
* Owner Picker
*/
export const OwnerPicker = (
props: FieldExtensionComponentProps<string, OwnerPickerUiOptions>,
) => {
const {
schema: { title = 'Owner', description = 'The owner of the component' },
uiSchema,
...restProps
} = props;
export const OwnerPicker = ({
schema: { title = 'Owner', description = 'The owner of the component' },
uiSchema,
...props
}: FieldProps<string>) => {
const ownerUiSchema = {
...uiSchema,
'ui:options': {
@@ -35,7 +46,7 @@ export const OwnerPicker = ({
return (
<EntityPicker
{...props}
{...restProps}
schema={{ title, description }}
uiSchema={ownerUiSchema}
/>
@@ -14,3 +14,4 @@
* limitations under the License.
*/
export { OwnerPicker } from './OwnerPicker';
export type { OwnerPickerUiOptions } from './OwnerPicker';
@@ -44,6 +44,9 @@ export interface RepoUrlPickerUiOptions {
};
}
/**
* Repo Url Picker
*/
export const RepoUrlPicker = (
props: FieldExtensionComponentProps<string, RepoUrlPickerUiOptions>,
) => {
@@ -1,42 +0,0 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TextField } from '@material-ui/core';
import { FieldProps } from '@rjsf/core';
import React from 'react';
export const TextValuePicker = ({
onChange,
required,
schema: { title, description },
rawErrors,
formData,
uiSchema: { 'ui:autofocus': autoFocus },
idSchema,
placeholder,
}: FieldProps<string>) => (
<TextField
id={idSchema?.$id}
label={title}
placeholder={placeholder}
helperText={description}
required={required}
value={formData ?? ''}
onChange={({ target: { value } }) => onChange(value)}
margin="normal"
error={rawErrors?.length > 0 && !formData}
inputProps={{ autoFocus }}
/>
);
@@ -1,16 +0,0 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { TextValuePicker } from './TextValuePicker';
@@ -13,10 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './EntityNamePicker';
export * from './EntityPicker';
export * from './OwnerPicker';
export * from './RepoUrlPicker';
export * from './TextValuePicker';
export * from './OwnedEntityPicker';
export * from './EntityTagsPicker';
+29 -28
View File
@@ -27,31 +27,32 @@ import {
import { FieldExtensionOptions } from './types';
import { OwnedEntityPicker } from '../components/fields/OwnedEntityPicker';
export const DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS: FieldExtensionOptions[] = [
{
component: EntityPicker,
name: 'EntityPicker',
},
{
component: EntityNamePicker,
name: 'EntityNamePicker',
validation: entityNamePickerValidation,
},
{
component: EntityTagsPicker,
name: 'EntityTagsPicker',
},
{
component: RepoUrlPicker,
name: 'RepoUrlPicker',
validation: repoPickerValidation,
},
{
component: OwnerPicker,
name: 'OwnerPicker',
},
{
component: OwnedEntityPicker,
name: 'OwnedEntityPicker',
},
];
export const DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS: FieldExtensionOptions<any>[] =
[
{
component: EntityPicker,
name: 'EntityPicker',
},
{
component: EntityNamePicker,
name: 'EntityNamePicker',
validation: entityNamePickerValidation,
},
{
component: EntityTagsPicker,
name: 'EntityTagsPicker',
},
{
component: RepoUrlPicker,
name: 'RepoUrlPicker',
validation: repoPickerValidation,
},
{
component: OwnerPicker,
name: 'OwnerPicker',
},
{
component: OwnedEntityPicker,
name: 'OwnedEntityPicker',
},
];
+7 -2
View File
@@ -25,8 +25,13 @@ import { Extension, attachComponentData } from '@backstage/core-plugin-api';
export const FIELD_EXTENSION_WRAPPER_KEY = 'scaffolder.extensions.wrapper.v1';
export const FIELD_EXTENSION_KEY = 'scaffolder.extensions.field.v1';
export function createScaffolderFieldExtension<T = any>(
options: FieldExtensionOptions<T>,
export function createScaffolderFieldExtension<
TReturnValue = unknown,
TInputProps = unknown,
>(
options: FieldExtensionOptions<TReturnValue, TInputProps>,
// TODO: need know how to embed these types nicely so the api report looks nice.
// then we can remove the export of the components
): Extension<() => null> {
return {
expose() {
+16 -15
View File
@@ -21,13 +21,11 @@ import { FieldValidation, FieldProps } from '@rjsf/core';
*
* @public
*/
export type CustomFieldValidator<T> =
| ((data: T, field: FieldValidation) => void)
| ((
data: T,
field: FieldValidation,
context: { apiHolder: ApiHolder },
) => void);
export type CustomFieldValidator<TFieldReturnValue> = (
data: TFieldReturnValue,
field: FieldValidation,
context: { apiHolder: ApiHolder },
) => void;
/**
* Type for the Custom Field Extension with the
@@ -35,10 +33,13 @@ export type CustomFieldValidator<T> =
*
* @public
*/
export type FieldExtensionOptions<T = any> = {
export type FieldExtensionOptions<
TFieldReturnValue = unknown,
TProps = FieldProps<TFieldReturnValue>,
> = {
name: string;
component: (props: FieldProps<T>) => JSX.Element | null;
validation?: CustomFieldValidator<T>;
component: (props: TProps) => JSX.Element | null;
validation?: CustomFieldValidator<TFieldReturnValue>;
};
/**
@@ -48,10 +49,10 @@ export type FieldExtensionOptions<T = any> = {
* @public
*/
export interface FieldExtensionComponentProps<
ReturnValue,
UiOptions extends {} = {},
> extends FieldProps<ReturnValue> {
uiSchema: {
'ui:options'?: UiOptions;
TFieldReturnValue,
TUiOptions extends {} = {},
> extends FieldProps<TFieldReturnValue> {
uiSchema: FieldProps['uiSchema'] & {
'ui:options'?: TUiOptions;
};
}
+1 -10
View File
@@ -39,18 +39,9 @@ export {
OwnedEntityPickerFieldExtension,
RepoUrlPickerFieldExtension,
ScaffolderPage,
scaffolderPlugin as plugin,
scaffolderPlugin,
} from './plugin';
export {
EntityNamePicker,
EntityPicker,
EntityTagsPicker,
OwnerPicker,
RepoUrlPicker,
TextValuePicker,
OwnedEntityPicker,
} from './components/fields';
export * from './components/fields';
export type { RepoUrlPickerUiOptions } from './components/fields';
export { FavouriteTemplate } from './components/FavouriteTemplate';
export { TemplateList } from './components/TemplateList';