Skip to content

Instantly share code, notes, and snippets.

@gbzarelli
Created October 24, 2022 19:29
Show Gist options
  • Save gbzarelli/c79413b5cfcd485ceb77b6b80633d377 to your computer and use it in GitHub Desktop.
Save gbzarelli/c79413b5cfcd485ceb77b6b80633d377 to your computer and use it in GitHub Desktop.

Custom EntityPicker with Type filter

  • Create folder: packages/app/src/scaffolder/CustomEntityPickerExtension
  • Put files: CustomEntityPickerExtension.tsx and index.ts
  • Edit packages/app/src/App.tsx and insert:
[...]

import { CustomEntityPickerExtension } from './scaffolder/CustomEntityPickerExtension';

[...]

    <Route path="/create" element={<ScaffolderPage />}>
      <ScaffolderFieldExtensions>
        <CustomEntityPickerExtension />
        [...]
      </ScaffolderFieldExtensions>
    </Route>

[...]
  • use in your /template.yaml
[...]
        my-prop:
          title: "My Prop"
          type: string
          description: 'Select my-prop'
          ui:field: CustomEntityPicker
          ui:options:
            allowedTypes:
              - my-type
            allowedKinds:
              - Resource
            defaultKind: Resource
[...]
import React, { useCallback, useEffect } from 'react';
import { useApi } from '@backstage/core-plugin-api';
import {
catalogApiRef,
humanizeEntityRef,
} from '@backstage/plugin-catalog-react';
import useAsync from 'react-use/lib/useAsync';
import {
createScaffolderFieldExtension,
FieldExtensionComponentProps, scaffolderPlugin,
} from '@backstage/plugin-scaffolder';
import { TextField } from '@material-ui/core';
import FormControl from '@material-ui/core/FormControl';
import Autocomplete from '@material-ui/lab/Autocomplete';
/**
* The input props that can be specified under `ui:options` for the
* `EntityPicker` field extension.
*
* @public
*/
export interface EntityPickerUiOptions {
allowedKinds?: string[];
defaultKind?: string;
allowedTypes?: string[];
allowArbitraryValues?: boolean;
defaultNamespace?: string | false;
}
export const CustomEntityPicker = (
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;
const allowedTypes = uiSchema['ui:options']?.allowedTypes;
const defaultNamespace = uiSchema['ui:options']?.defaultNamespace;
const catalogApi = useApi(catalogApiRef);
const { value: entities, loading } = useAsync(() =>
catalogApi.getEntities(
allowedKinds ? { filter: { 'spec.type': allowedTypes!, kind: allowedKinds } } : undefined,
),
);
const entityRefs = entities?.items.map(e =>
humanizeEntityRef(e, { defaultKind, defaultNamespace }),
);
const onSelect = useCallback(
(_: any, value: string | null) => {
onChange(value ?? undefined);
},
[onChange],
);
useEffect(() => {
if (entityRefs?.length === 1) {
onChange(entityRefs[0]);
}
}, [entityRefs, onChange]);
return (
<FormControl
margin="normal"
required={required}
error={rawErrors?.length > 0 && !formData}
>
<Autocomplete
disabled={entityRefs?.length === 1}
id={idSchema?.$id}
value={(formData as string) || ''}
loading={loading}
onChange={onSelect}
options={entityRefs || []}
autoSelect
freeSolo={uiSchema['ui:options']?.allowArbitraryValues ?? true}
renderInput={params => (
<TextField
{...params}
label={title}
margin="dense"
helperText={description}
FormHelperTextProps={{ margin: 'dense', style: { marginLeft: 0 } }}
variant="outlined"
required={required}
InputProps={params.InputProps}
/>
)}
/>
</FormControl>
);
};
export const CustomEntityPickerExtension = scaffolderPlugin.provide(
createScaffolderFieldExtension({
name: 'CustomEntityPicker',
component: CustomEntityPicker,
}),
);
export { CustomEntityPickerExtension } from './CustomEntityPickerExtension';
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment