From 6522e459aae698f2e1da1c04ac3cc25d387e5ca7 Mon Sep 17 00:00:00 2001 From: Phil Kuang Date: Thu, 8 Sep 2022 14:45:09 -0400 Subject: [PATCH] feat(EntityTagPicker): support displaying and ordering by counts Signed-off-by: Phil Kuang --- .changeset/lucky-ads-worry.md | 5 +++ plugins/scaffolder/api-report.md | 4 ++ .../EntityTagsPicker/EntityTagsPicker.tsx | 40 +++++++++++++------ 3 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 .changeset/lucky-ads-worry.md diff --git a/.changeset/lucky-ads-worry.md b/.changeset/lucky-ads-worry.md new file mode 100644 index 0000000000..a7f749da17 --- /dev/null +++ b/.changeset/lucky-ads-worry.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Support displaying and ordering by counts in `EntityTagPicker` field. Add the `showCounts` option to enable this. Also support configuring `helperText`. diff --git a/plugins/scaffolder/api-report.md b/plugins/scaffolder/api-report.md index d611309df3..9541098934 100644 --- a/plugins/scaffolder/api-report.md +++ b/plugins/scaffolder/api-report.md @@ -76,8 +76,12 @@ export const EntityTagsPickerFieldExtension: FieldExtensionComponent< // @public export interface EntityTagsPickerUiOptions { + // (undocumented) + helperText?: string; // (undocumented) kinds?: string[]; + // (undocumented) + showCounts?: boolean; } // @public diff --git a/plugins/scaffolder/src/components/fields/EntityTagsPicker/EntityTagsPicker.tsx b/plugins/scaffolder/src/components/fields/EntityTagsPicker/EntityTagsPicker.tsx index 5cf98cb8aa..c0e3aab436 100644 --- a/plugins/scaffolder/src/components/fields/EntityTagsPicker/EntityTagsPicker.tsx +++ b/plugins/scaffolder/src/components/fields/EntityTagsPicker/EntityTagsPicker.tsx @@ -16,8 +16,8 @@ import React, { useState } from 'react'; import useAsync from 'react-use/lib/useAsync'; import useEffectOnce from 'react-use/lib/useEffectOnce'; -import { GetEntitiesRequest } from '@backstage/catalog-client'; -import { Entity, makeValidator } from '@backstage/catalog-model'; +import { GetEntityFacetsRequest } from '@backstage/catalog-client'; +import { makeValidator } from '@backstage/catalog-model'; import { useApi } from '@backstage/core-plugin-api'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { FormControl, TextField } from '@material-ui/core'; @@ -32,6 +32,8 @@ import { FieldExtensionComponentProps } from '../../../extensions'; */ export interface EntityTagsPickerUiOptions { kinds?: string[]; + showCounts?: boolean; + helperText?: string; } /** @@ -45,26 +47,34 @@ export const EntityTagsPicker = ( ) => { const { formData, onChange, uiSchema } = props; const catalogApi = useApi(catalogApiRef); + const [tagOptions, setTagOptions] = useState([]); const [inputValue, setInputValue] = useState(''); const [inputError, setInputError] = useState(false); const tagValidator = makeValidator().isValidTag; const kinds = uiSchema['ui:options']?.kinds; + const showCounts = uiSchema['ui:options']?.showCounts; + const helperText = uiSchema['ui:options']?.helperText; const { loading, value: existingTags } = useAsync(async () => { - const tagsRequest: GetEntitiesRequest = { fields: ['metadata.tags'] }; + const facet = 'metadata.tags'; + const tagsRequest: GetEntityFacetsRequest = { facets: [facet] }; if (kinds) { tagsRequest.filter = { kind: kinds }; } - const entities = await catalogApi.getEntities(tagsRequest); + const { facets } = await catalogApi.getEntityFacets(tagsRequest); - return [ - ...new Set( - entities.items - .flatMap((e: Entity) => e.metadata?.tags) - .filter(Boolean) as string[], + const tagFacets = Object.fromEntries( + facets[facet].map(({ value, count }) => [value, count]), + ); + + setTagOptions( + Object.keys(tagFacets).sort((a, b) => + showCounts ? tagFacets[b] - tagFacets[a] : a.localeCompare(b), ), - ].sort(); + ); + + return tagFacets; }); const setTags = (_: React.ChangeEvent<{}>, values: string[] | null) => { @@ -102,15 +112,21 @@ export const EntityTagsPicker = ( value={formData || []} inputValue={inputValue} loading={loading} - options={existingTags || []} + options={tagOptions} ChipProps={{ size: 'small' }} + renderOption={option => + showCounts ? `${option} (${existingTags?.[option]})` : option + } renderInput={params => ( setInputValue(e.target.value)} error={inputError} - helperText="Add any relevant tags, hit 'Enter' to add new tags. Valid format: [a-z0-9+#] separated by [-], at most 63 characters" + helperText={ + helperText ?? + "Add any relevant tags, hit 'Enter' to add new tags. Valid format: [a-z0-9+#] separated by [-], at most 63 characters" + } /> )} />