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:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder': minor
|
||||
---
|
||||
|
||||
`MultiEntityPicker` uses `EntityDisplayName` instead of `humanizeEntityRef` to display entity.
|
||||
+15
-2
@@ -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={{
|
||||
|
||||
Reference in New Issue
Block a user