diff --git a/plugins/scaffolder/src/components/ResultsFilter/ResultsFilter.tsx b/plugins/scaffolder/src/components/ResultsFilter/ResultsFilter.tsx index 8c5737b7b6..5df70bd203 100644 --- a/plugins/scaffolder/src/components/ResultsFilter/ResultsFilter.tsx +++ b/plugins/scaffolder/src/components/ResultsFilter/ResultsFilter.tsx @@ -50,28 +50,28 @@ const useStyles = makeStyles(theme => ({ })); type Props = { - availableTags: string[]; + availableCategories: string[]; }; /** * The additional results filter in the sidebar. */ -export const ResultsFilter = ({ availableTags }: Props) => { +export const ResultsFilter = ({ availableCategories }: Props) => { const classes = useStyles(); - const [selectedTags, setSelectedTags] = useState([]); + const [selectedCategories, setSelectedCategories] = useState([]); const context = useContext(filterGroupsContext); if (!context) { throw new Error(`Must be used inside an EntityFilterGroupsProvider`); } - const setSelectedTagsFilter = context?.setSelectedTags; + const setSelectedCatgoriesFilter = context?.setSelectedCategories; - const updateSelectedTags = useCallback( - (tags: string[]) => { - setSelectedTags(tags); - setSelectedTagsFilter(tags); + const updateSelectedCategories = useCallback( + (categories: string[]) => { + setSelectedCategories(categories); + setSelectedCatgoriesFilter(categories); }, - [setSelectedTags, setSelectedTagsFilter], + [setSelectedCategories, setSelectedCatgoriesFilter], ); return ( @@ -80,14 +80,14 @@ export const ResultsFilter = ({ availableTags }: Props) => { Refine Results {' '} - + - Tags + Categories - {availableTags.map(t => { + {availableCategories.map(t => { const labelId = `checkbox-list-label-${t}`; return ( { dense button onClick={() => - updateSelectedTags( - selectedTags.includes(t) - ? selectedTags.filter(s => s !== t) - : [...selectedTags, t], + updateSelectedCategories( + selectedCategories.includes(t) + ? selectedCategories.filter(s => s !== t) + : [...selectedCategories, t], ) } > - + ); })} diff --git a/plugins/scaffolder/src/components/CatalogFilter/AllServicesCount.tsx b/plugins/scaffolder/src/components/ScaffolderFilter/AllServicesCount.tsx similarity index 100% rename from plugins/scaffolder/src/components/CatalogFilter/AllServicesCount.tsx rename to plugins/scaffolder/src/components/ScaffolderFilter/AllServicesCount.tsx diff --git a/plugins/scaffolder/src/components/CatalogFilter/CatalogFilter.test.tsx b/plugins/scaffolder/src/components/ScaffolderFilter/ScaffolderFilter.test.tsx similarity index 94% rename from plugins/scaffolder/src/components/CatalogFilter/CatalogFilter.test.tsx rename to plugins/scaffolder/src/components/ScaffolderFilter/ScaffolderFilter.test.tsx index 0cc2108985..1c9a89c517 100644 --- a/plugins/scaffolder/src/components/CatalogFilter/CatalogFilter.test.tsx +++ b/plugins/scaffolder/src/components/ScaffolderFilter/ScaffolderFilter.test.tsx @@ -28,7 +28,7 @@ import { MockStorageApi, wrapInTestApp } from '@backstage/test-utils'; import { fireEvent, render, waitFor } from '@testing-library/react'; import React from 'react'; import { EntityFilterGroupsProvider } from '../../filter'; -import { ButtonGroup, CatalogFilter } from './CatalogFilter'; +import { ButtonGroup, ScaffolderFilter } from './ScaffolderFilter'; describe('Catalog Filter', () => { const catalogApi: Partial = { @@ -86,7 +86,7 @@ describe('Catalog Filter', () => { { name: 'Test Group 2', items: [] }, ]; const { findByText } = renderWrapped( - , + , ); for (const group of mockGroups) { expect(await findByText(group.name)).toBeInTheDocument(); @@ -113,7 +113,7 @@ describe('Catalog Filter', () => { ]; const { findByText } = renderWrapped( - , + , ); for (const item of mockGroups[0].items) { @@ -143,7 +143,7 @@ describe('Catalog Filter', () => { const onChange = jest.fn(); renderWrapped( - { const onChange = jest.fn(); renderWrapped( - { const onChange = jest.fn(); const { findByText } = renderWrapped( - { ]; const { findByText } = renderWrapped( - , + , ); expect(await findByText('1')).toBeInTheDocument(); diff --git a/plugins/scaffolder/src/components/CatalogFilter/CatalogFilter.tsx b/plugins/scaffolder/src/components/ScaffolderFilter/ScaffolderFilter.tsx similarity index 99% rename from plugins/scaffolder/src/components/CatalogFilter/CatalogFilter.tsx rename to plugins/scaffolder/src/components/ScaffolderFilter/ScaffolderFilter.tsx index 90a1e15ad0..14093462b8 100644 --- a/plugins/scaffolder/src/components/CatalogFilter/CatalogFilter.tsx +++ b/plugins/scaffolder/src/components/ScaffolderFilter/ScaffolderFilter.tsx @@ -83,7 +83,7 @@ type Props = { /** * The main filter group in the sidebar, toggling owned/starred/all. */ -export const CatalogFilter = ({ +export const ScaffolderFilter = ({ buttonGroups, onChange, initiallySelected, diff --git a/plugins/scaffolder/src/components/CatalogFilter/index.ts b/plugins/scaffolder/src/components/ScaffolderFilter/index.ts similarity index 91% rename from plugins/scaffolder/src/components/CatalogFilter/index.ts rename to plugins/scaffolder/src/components/ScaffolderFilter/index.ts index 5103b16307..f53e4be89f 100644 --- a/plugins/scaffolder/src/components/CatalogFilter/index.ts +++ b/plugins/scaffolder/src/components/ScaffolderFilter/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export { CatalogFilter } from './CatalogFilter'; +export { ScaffolderFilter } from './ScaffolderFilter'; diff --git a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx index 59480d1aba..1184c44263 100644 --- a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx +++ b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx @@ -19,7 +19,6 @@ import { configApiRef, Content, ContentHeader, - errorApiRef, Header, Lifecycle, Page, @@ -28,7 +27,7 @@ import { useApi, WarningPanel, } from '@backstage/core'; -import { catalogApiRef, isOwnerOf } from '@backstage/plugin-catalog-react'; +import { isOwnerOf } from '@backstage/plugin-catalog-react'; import { Button, FormControl, @@ -36,27 +35,18 @@ import { IconButton, Input, InputAdornment, - InputLabel, Link, makeStyles, - TextField, Toolbar, Typography, } from '@material-ui/core'; -import React, { - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { Link as RouterLink } from 'react-router-dom'; -import useStaleWhileRevalidate from 'swr'; import { EntityFilterGroupsProvider, useFilteredEntities } from '../../filter'; import { TemplateCard, TemplateCardProps } from '../TemplateCard'; import { ResultsFilter } from '../ResultsFilter/ResultsFilter'; -import { CatalogFilter } from '../CatalogFilter'; -import { ButtonGroup } from '../CatalogFilter/CatalogFilter'; +import { ScaffolderFilter } from '../ScaffolderFilter'; +import { ButtonGroup } from '../ScaffolderFilter/ScaffolderFilter'; import SettingsIcon from '@material-ui/icons/Settings'; import StarIcon from '@material-ui/icons/Star'; import { useOwnUser } from '../useOwnUser'; @@ -96,29 +86,14 @@ export const ScaffolderPageContents = () => { const { loading, error, - reload, + reload, // TODO: Configure reload matchingEntities, - availableTags, // TODO: Change tags to Categories + availableCategories, // TODO: Change tags to Categories isCatalogEmpty, } = useFilteredEntities(); // TODO: use Selected Sidebar Item const [selectedSidebarItem, setSelectedSidebarItem] = useState(); - const catalogApi = useApi(catalogApiRef); - const errorApi = useApi(errorApiRef); - const { - data: templates /* isValidating*/ /* , error */, - } = useStaleWhileRevalidate('templates/all', async () => { - const response = await catalogApi.getEntities({ - filter: { kind: 'Template' }, - }); - return response.items as TemplateEntityV1alpha1[]; - }); - useEffect(() => { - if (!error) return; - errorApi.post(error); - }, [error, errorApi]); - const { value: user } = useOwnUser(); const configApi = useApi(configApiRef); @@ -237,12 +212,12 @@ export const ScaffolderPageContents = () => { - setSelectedSidebarItem(label)} // TODO: setSelecteSidebarItem initiallySelected="all" /> - +
{!filteredTemplates && loading && } diff --git a/plugins/scaffolder/src/filter/EntityFilterGroupsProvider.tsx b/plugins/scaffolder/src/filter/EntityFilterGroupsProvider.tsx index 92b99c74be..1e5474f327 100644 --- a/plugins/scaffolder/src/filter/EntityFilterGroupsProvider.tsx +++ b/plugins/scaffolder/src/filter/EntityFilterGroupsProvider.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Entity } from '@backstage/catalog-model'; +import { Entity, TemplateEntityV1alpha1 } from '@backstage/catalog-model'; import { useApi } from '@backstage/core'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; import React, { useCallback, useEffect, useRef, useState } from 'react'; @@ -59,12 +59,12 @@ function useProvideEntityFilters(): FilterGroupsContext { const selectedFilterKeys = useRef<{ [filterGroupId: string]: Set; }>({}); - const selectedTags = useRef([]); + const selectedCategories = useRef([]); const [filterGroupStates, setFilterGroupStates] = useState<{ [filterGroupId: string]: FilterGroupStates; }>({}); const [matchingEntities, setMatchingEntities] = useState([]); - const [availableTags, setAvailableTags] = useState([]); + const [availableCategories, setAvailableCategories] = useState([]); const [isCatalogEmpty, setCatalogEmpty] = useState(false); useEffect(() => { @@ -76,7 +76,7 @@ function useProvideEntityFilters(): FilterGroupsContext { buildStates( filterGroups.current, selectedFilterKeys.current, - selectedTags.current, + selectedCategories.current, entities, error, ), @@ -85,11 +85,11 @@ function useProvideEntityFilters(): FilterGroupsContext { buildMatchingEntities( filterGroups.current, selectedFilterKeys.current, - selectedTags.current, + selectedCategories.current, entities, ), ); - setAvailableTags(collectTags(entities)); + setAvailableCategories(collectCategories(entities)); setCatalogEmpty(entities !== undefined && entities.length === 0); }, [entities, error]); @@ -125,9 +125,9 @@ function useProvideEntityFilters(): FilterGroupsContext { [rebuild], ); - const setSelectedTags = useCallback( - (tags: string[]) => { - selectedTags.current = tags; + const setSelectedCategories = useCallback( + (categories: string[]) => { + selectedCategories.current = categories; rebuild(); }, [rebuild], @@ -141,13 +141,13 @@ function useProvideEntityFilters(): FilterGroupsContext { register, unregister, setGroupSelectedFilters, - setSelectedTags, + setSelectedCategories, reload, loading: !error && !entities, error, filterGroupStates, matchingEntities, - availableTags, + availableCategories, isCatalogEmpty, }; } @@ -157,7 +157,7 @@ function useProvideEntityFilters(): FilterGroupsContext { function buildStates( filterGroups: { [filterGroupId: string]: FilterGroup }, selectedFilterKeys: { [filterGroupId: string]: Set }, - selectedTags: string[], + selectedCategories: string[], entities?: Entity[], error?: Error, ): { [filterGroupId: string]: FilterGroupStates } { @@ -186,7 +186,7 @@ function buildStates( const otherMatchingEntities = buildMatchingEntities( filterGroups, selectedFilterKeys, - selectedTags, + selectedCategories, entities, filterGroupId, ); @@ -204,15 +204,15 @@ function buildStates( return result; } -// Given all entites, find all possible tags and provide them in a sorted list. -function collectTags(entities?: Entity[]): string[] { - const tags = new Set(); +// Given all entites, find all possible categories and provide them in a sorted list. +function collectCategories(entities?: Entity[]): string[] { + const categories = new Set(); (entities || []).forEach(e => { - if (e.metadata.tags) { - e.metadata.tags.forEach(t => tags.add(t)); + if (e.spec?.type) { + categories.add(e.spec.type as string); } }); - return Array.from(tags).sort(); + return Array.from(categories).sort(); } // Given all filter groups and what filters are actually selected, extract all @@ -220,7 +220,7 @@ function collectTags(entities?: Entity[]): string[] { function buildMatchingEntities( filterGroups: { [filterGroupId: string]: FilterGroup }, selectedFilterKeys: { [filterGroupId: string]: Set }, - selectedTags: string[], + selectedCategories: string[], entities?: Entity[], excludeFilterGroupId?: string, ): Entity[] { @@ -247,13 +247,10 @@ function buildMatchingEntities( } } - // Filter by tags, if at least one tag is selected. Include all entities - // that have at least one of the selected tags - if (selectedTags.length > 0) { - allFilters.push( - entity => - !!entity.metadata.tags && - entity.metadata.tags.some(t => selectedTags.includes(t)), + // Filter by categories, if at least one category is selected. + if (selectedCategories.length > 0) { + allFilters.push(entity => + selectedCategories.some(c => entity.spec?.type === c), ); } diff --git a/plugins/scaffolder/src/filter/context.ts b/plugins/scaffolder/src/filter/context.ts index c025480fa6..e46ebb5507 100644 --- a/plugins/scaffolder/src/filter/context.ts +++ b/plugins/scaffolder/src/filter/context.ts @@ -26,13 +26,13 @@ export type FilterGroupsContext = { ) => void; unregister: (filterGroupId: string) => void; setGroupSelectedFilters: (filterGroupId: string, filterIds: string[]) => void; - setSelectedTags: (tags: string[]) => void; + setSelectedCategories: (categories: string[]) => void; reload: () => Promise; loading: boolean; error?: Error; filterGroupStates: { [filterGroupId: string]: FilterGroupStates }; matchingEntities: Entity[]; - availableTags: string[]; + availableCategories: string[]; isCatalogEmpty: boolean; }; diff --git a/plugins/scaffolder/src/filter/useFilteredEntities.ts b/plugins/scaffolder/src/filter/useFilteredEntities.ts index 2d7dcfd89d..cc2c739546 100644 --- a/plugins/scaffolder/src/filter/useFilteredEntities.ts +++ b/plugins/scaffolder/src/filter/useFilteredEntities.ts @@ -30,7 +30,7 @@ export function useFilteredEntities() { loading: context.loading, error: context.error, matchingEntities: context.matchingEntities, - availableTags: context.availableTags, + availableCategories: context.availableCategories, isCatalogEmpty: context.isCatalogEmpty, reload: context.reload, };