From 90f253aaf89bf220993d082a20c8c5e9d078fc21 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 9 Jun 2020 15:20:38 +0200 Subject: [PATCH] feat(catalog/star): Ability to star items in the catalog table --- .../components/CatalogPage/CatalogPage.tsx | 16 ++++++++-- .../catalog/src/hooks/useStarredEntites.ts | 15 ++++++++- .../src/hooks/useStarredEntities.test.tsx | 32 ++++++++++++++++++- 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx index 859449c715..d7c8825c9b 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx @@ -29,6 +29,8 @@ import { import { rootRoute as scaffolderRootRoute } from '@backstage/plugin-scaffolder'; import { Button, makeStyles, Typography, Link } from '@material-ui/core'; import GitHub from '@material-ui/icons/GitHub'; +import StarOutline from '@material-ui/icons/StarBorder'; +import Star from '@material-ui/icons/Star'; import React, { FC, useCallback, useState } from 'react'; import { Link as RouterLink } from 'react-router-dom'; import { useAsync } from 'react-use'; @@ -60,13 +62,14 @@ const useStyles = makeStyles(theme => ({ const CatalogPage: FC<{}> = () => { const catalogApi = useApi(catalogApiRef); - const { starredEntities } = useStarredEntities(); + const { starredEntities, toggleStarredEntity } = useStarredEntities(); const [selectedFilter, setSelectedFilter] = useState( defaultFilter, ); + const { value, error, loading } = useAsync( () => dataResolvers[selectedFilter.id]({ catalogApi, starredEntities }), - [selectedFilter.id], + [selectedFilter.id, starredEntities.size], ); const onFilterSelected = useCallback( @@ -89,6 +92,15 @@ const CatalogPage: FC<{}> = () => { hidden: location ? location?.type !== 'github' : true, }; }, + (rowData: Component) => { + return { + icon: starredEntities.has(rowData.metadata.name) ? Star : StarOutline, + toolTip: `${ + starredEntities.has(rowData.metadata.name) ? 'Unstar' : 'Star' + } ${rowData.metadata.name}`, + onClick: () => toggleStarredEntity(rowData.metadata.name), + }; + }, ]; // TODO: replace me with the proper tabs implemntation diff --git a/plugins/catalog/src/hooks/useStarredEntites.ts b/plugins/catalog/src/hooks/useStarredEntites.ts index 631991e9b0..3d1f902583 100644 --- a/plugins/catalog/src/hooks/useStarredEntites.ts +++ b/plugins/catalog/src/hooks/useStarredEntites.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useCallback } from 'react'; import { useApi, storageApiRef } from '@backstage/core'; import { useObservable } from 'react-use'; @@ -37,7 +37,20 @@ export const useStarredEntities = () => { } }, [observedItems?.newValue]); + const toggleStarredEntity = useCallback( + (entity: string) => { + if (starredEntities.has(entity)) { + starredEntities.delete(entity); + } else { + starredEntities.add(entity); + } + + settingsStore.set('starredEntities', Array.from(starredEntities)); + }, + [starredEntities, settingsStore], + ); return { starredEntities, + toggleStarredEntity, }; }; diff --git a/plugins/catalog/src/hooks/useStarredEntities.test.tsx b/plugins/catalog/src/hooks/useStarredEntities.test.tsx index 49890b7238..8127d71d50 100644 --- a/plugins/catalog/src/hooks/useStarredEntities.test.tsx +++ b/plugins/catalog/src/hooks/useStarredEntities.test.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import React from 'react'; -import { renderHook } from '@testing-library/react-hooks'; +import { renderHook, act } from '@testing-library/react-hooks'; import { useStarredEntities } from './useStarredEntites'; import { ApiProvider, @@ -77,4 +77,34 @@ describe('useStarredEntities', () => { expect(result.current.starredEntities.size).toBe(1); expect(result.current.starredEntities.has('something')).toBeTruthy(); }); + + it('should write new entries to the local store when adding a togglging entity', async () => { + const store = mockStorage?.forBucket('settings'); + + await store?.set('starredEntities', ['something1']); + + const { result } = renderHook(() => useStarredEntities(), { wrapper }); + + act(() => { + result.current.toggleStarredEntity('something2'); + }); + + expect(result.current.starredEntities.has('something2')).toBeTruthy(); + expect(result.current.starredEntities.has('something1')).toBeTruthy(); + }); + + it('should remove an existing entity when toggling entries', async () => { + const store = mockStorage?.forBucket('settings'); + + await store?.set('starredEntities', ['something1', 'something2']); + + const { result } = renderHook(() => useStarredEntities(), { wrapper }); + + act(() => { + result.current.toggleStarredEntity('something2'); + }); + + expect(result.current.starredEntities.has('something2')).toBeFalsy(); + expect(result.current.starredEntities.has('something1')).toBeTruthy(); + }); });