Merge pull request #24467 from Nikunj0601/feat/MultiEntityPicker-uses-CatalogAPI

feat(MultiEntityPicker): use EntityPresentationApi instead of humanizeEntityRef to display entity
This commit is contained in:
Fredrik Adelöw
2024-05-10 14:56:52 +02:00
committed by GitHub
3 changed files with 51 additions and 35 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder': minor
---
`MultiEntityPicker` uses `EntityDisplayName` instead of `humanizeEntityRef` to display entity.
@@ -16,7 +16,11 @@
import { CATALOG_FILTER_EXISTS } from '@backstage/catalog-client';
import { Entity } from '@backstage/catalog-model';
import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog-react';
import {
CatalogApi,
catalogApiRef,
entityPresentationApiRef,
} from '@backstage/plugin-catalog-react';
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
import { fireEvent, screen } from '@testing-library/react';
@@ -24,6 +28,7 @@ import React from 'react';
import { MultiEntityPicker } from './MultiEntityPicker';
import { MultiEntityPickerProps } from './schema';
import { ScaffolderRJSFFieldProps as FieldProps } from '@backstage/plugin-scaffolder-react';
import { DefaultEntityPresentationApi } from '@backstage/plugin-catalog';
const makeEntity = (kind: string, namespace: string, name: string): Entity => ({
apiVersion: 'scaffolder.backstage.io/v1beta3',
@@ -59,7 +64,15 @@ describe('<MultiEntityPicker />', () => {
];
Wrapper = ({ children }: { children?: React.ReactNode }) => (
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
<TestApiProvider
apis={[
[catalogApiRef, catalogApi],
[
entityPresentationApiRef,
DefaultEntityPresentationApi.create({ catalogApi }),
],
]}
>
{children}
</TestApiProvider>
);
@@ -25,7 +25,9 @@ import {
import { useApi } from '@backstage/core-plugin-api';
import {
catalogApiRef,
humanizeEntityRef,
entityPresentationApiRef,
EntityDisplayName,
EntityRefPresentationSnapshot,
} from '@backstage/plugin-catalog-react';
import TextField from '@material-ui/core/TextField';
import FormControl from '@material-ui/core/FormControl';
@@ -64,33 +66,31 @@ export const MultiEntityPicker = (props: MultiEntityPickerProps) => {
uiSchema['ui:options']?.defaultNamespace || undefined;
const catalogApi = useApi(catalogApiRef);
const entityPresentationApi = useApi(entityPresentationApiRef);
const { value: entities, loading } = useAsync(async () => {
const { items } = await catalogApi.getEntities(
catalogFilter ? { filter: catalogFilter } : undefined,
);
return items;
const entityRefToPresentation = new Map<
string,
EntityRefPresentationSnapshot
>(
await Promise.all(
items.map(async item => {
const presentation = await entityPresentationApi.forEntity(item)
.promise;
return [stringifyEntityRef(item), presentation] as [
string,
EntityRefPresentationSnapshot,
];
}),
),
);
return { entities: items, entityRefToPresentation };
});
const allowArbitraryValues =
uiSchema['ui:options']?.allowArbitraryValues ?? true;
const getLabel = useCallback(
(ref: string) => {
try {
return humanizeEntityRef(
parseEntityRef(ref, { defaultKind, defaultNamespace }),
{
defaultKind,
defaultNamespace,
},
);
} catch (err) {
return ref;
}
},
[defaultKind, defaultNamespace],
);
const onSelect = useCallback(
(_: any, refs: (string | Entity)[], reason: AutocompleteChangeReason) => {
const values = refs
@@ -130,8 +130,8 @@ export const MultiEntityPicker = (props: MultiEntityPickerProps) => {
);
useEffect(() => {
if (entities?.length === 1) {
onChange([stringifyEntityRef(entities[0])]);
if (entities?.entities?.length === 1) {
onChange([stringifyEntityRef(entities?.entities[0])]);
}
}, [entities, onChange]);
@@ -144,23 +144,18 @@ export const MultiEntityPicker = (props: MultiEntityPickerProps) => {
<Autocomplete
multiple
filterSelectedOptions
disabled={entities?.length === 1}
disabled={entities?.entities?.length === 1}
id={idSchema?.$id}
value={
// Since free solo can be enabled, attempt to parse as a full entity ref first, then fall
// back to the given value.
entities?.filter(
e => formData && formData.includes(stringifyEntityRef(e)),
) ?? (allowArbitraryValues && formData ? formData.map(getLabel) : [])
}
loading={loading}
onChange={onSelect}
options={entities || []}
options={entities?.entities || []}
renderOption={option => <EntityDisplayName entityRef={option} />}
getOptionLabel={option =>
// option can be a string due to freeSolo.
typeof option === 'string'
? option
: humanizeEntityRef(option, { defaultKind, defaultNamespace })!
: entities?.entityRefToPresentation.get(stringifyEntityRef(option))
?.entityRef!
}
autoSelect
freeSolo={allowArbitraryValues}
@@ -170,7 +165,10 @@ export const MultiEntityPicker = (props: MultiEntityPickerProps) => {
label={title}
margin="dense"
helperText={description}
FormHelperTextProps={{ margin: 'dense', style: { marginLeft: 0 } }}
FormHelperTextProps={{
margin: 'dense',
style: { marginLeft: 0 },
}}
variant="outlined"
required={required}
InputProps={{