diff --git a/frontend/src/component/common/Table/cells/FeatureOverviewCell/FeatureOverviewCell.test.tsx b/frontend/src/component/common/Table/cells/FeatureOverviewCell/FeatureOverviewCell.test.tsx index 53f520dcac..cd218865bf 100644 --- a/frontend/src/component/common/Table/cells/FeatureOverviewCell/FeatureOverviewCell.test.tsx +++ b/frontend/src/component/common/Table/cells/FeatureOverviewCell/FeatureOverviewCell.test.tsx @@ -1,8 +1,10 @@ import { screen } from '@testing-library/react'; import { render } from 'utils/testRenderer'; -import { FeatureOverviewCell } from './FeatureOverviewCell'; +import { FeatureOverviewCell as makeFeatureOverviewCell } from './FeatureOverviewCell'; test('Display full overview information', () => { + const FeatureOverviewCell = makeFeatureOverviewCell(() => {}); + render( { }); test('Display minimal overview information', () => { + const FeatureOverviewCell = makeFeatureOverviewCell(() => {}); + render( ({ +const Tag = styled('button')(({ theme }) => ({ marginRight: theme.spacing(0.5), border: `1px solid ${theme.palette.divider}`, borderRadius: theme.shape.borderRadius, @@ -42,6 +42,9 @@ const Tag = styled('div')(({ theme }) => ({ textWrap: 'nowrap', maxWidth: '250px', padding: theme.spacing(0.25, 0.5), + cursor: 'pointer', + background: 'inherit', + color: 'inherit', })); const CappedDescription: FC<{ text: string; searchQuery: string }> = ({ @@ -73,16 +76,15 @@ const CappedDescription: FC<{ text: string; searchQuery: string }> = ({ ); }; -const CappedTag: FC<{ tag: string }> = ({ tag }) => { +const CappedTag: FC<{ tag: string; children: ReactElement }> = ({ + tag, + children, +}) => { return ( 30} - show={ - - {tag} - - } - elseShow={{tag}} + show={{children}} + elseShow={children} /> ); }; @@ -135,27 +137,55 @@ const FeatureName: FC<{ ); }; -const RestTags: FC<{ tags: string[] }> = ({ tags }) => { +const RestTags: FC<{ tags: string[]; onClick: (tag: string) => void }> = ({ + tags, + onClick, +}) => { return ( -
{tag}
)}> - {tags.length} more... + ( + onClick(tag)} + key={tag} + > + {tag} + + ))} + > + {tags.length} more... ); }; -const Tags: FC<{ tags: FeatureSearchResponseSchema['tags'] }> = ({ tags }) => { +const Tags: FC<{ + tags: FeatureSearchResponseSchema['tags']; + onClick: (tag: string) => void; +}> = ({ tags, onClick }) => { const [tag1, tag2, tag3, ...restTags] = (tags || []).map( ({ type, value }) => `${type}:${value}`, ); return ( - {tag1 && } - {tag2 && } - {tag3 && } + {tag1 && ( + + onClick(tag1)}>{tag1} + + )} + {tag2 && ( + + onClick(tag2)}>{tag2} + + )} + {tag3 && ( + + onClick(tag3)}>{tag3} + + )} 0} - show={} + show={} /> ); @@ -228,23 +258,25 @@ const SecondaryFeatureInfo: FC<{ ); }; -export const FeatureOverviewCell: FC = ({ row }) => { - const { searchQuery } = useSearchHighlightContext(); +export const FeatureOverviewCell = + (onClick: (tag: string) => void): FC => + ({ row }) => { + const { searchQuery } = useSearchHighlightContext(); - return ( - - - - - - ); -}; + return ( + + + + + + ); + }; diff --git a/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/ProjectFeatureToggles.test.tsx b/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/ProjectFeatureToggles.test.tsx index b3936fa43f..ce5e71637f 100644 --- a/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/ProjectFeatureToggles.test.tsx +++ b/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/ProjectFeatureToggles.test.tsx @@ -8,12 +8,18 @@ import { BATCH_SELECTED_COUNT } from 'utils/testIds'; const server = testServerSetup(); const setupApi = () => { - const features = [{ name: 'featureA' }, { name: 'featureB' }]; + const features = [ + { name: 'featureA', tags: [{ type: 'backend', value: 'sdk' }] }, + { name: 'featureB' }, + ]; testServerRoute(server, '/api/admin/search/features', { features, total: features.length, }); testServerRoute(server, '/api/admin/ui-config', {}); + testServerRoute(server, '/api/admin/tags', { + tags: [{ type: 'backend', value: 'sdk' }], + }); }; test('selects project features', async () => { @@ -58,3 +64,28 @@ test('selects project features', async () => { selectFeatureA.click(); expect(screen.queryByTestId(BATCH_SELECTED_COUNT)).not.toBeInTheDocument(); }); + +test('filters by tag', async () => { + setupApi(); + render( + + + } + /> + , + { + route: '/projects/default', + }, + ); + const tag = await screen.findByText('backend:sdk'); + + tag.click(); + + await screen.findByText('include'); + expect(screen.getAllByText('backend:sdk')).toHaveLength(2); +}); diff --git a/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/ProjectFeatureToggles.tsx b/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/ProjectFeatureToggles.tsx index 71fafc0fe1..2ed8ccedb3 100644 --- a/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/ProjectFeatureToggles.tsx +++ b/frontend/src/component/project/Project/PaginatedProjectFeatureToggles/ProjectFeatureToggles.tsx @@ -93,6 +93,24 @@ export const ProjectFeatureToggles = ({ const featureLifecycleEnabled = useUiFlag('featureLifecycle'); + const onTagClick = (tag: string) => { + if ( + tableState.tag && + tableState.tag.values.length > 0 && + !tableState.tag.values.includes(tag) + ) { + setTableState({ + tag: { + operator: tableState.tag.operator, + values: [...tableState.tag.values, tag], + }, + }); + } + if (!tableState.tag) { + setTableState({ tag: { operator: 'INCLUDE', values: [tag] } }); + } + }; + const columns = useMemo( () => [ columnHelper.display({ @@ -144,7 +162,7 @@ export const ProjectFeatureToggles = ({ columnHelper.accessor('name', { id: 'name', header: 'Name', - cell: FeatureOverviewCell, + cell: FeatureOverviewCell(onTagClick), enableHiding: false, meta: { width: '50%',