From e621c7a2a594a90b82639c88475b04d860f15247 Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Wed, 5 Jun 2024 10:05:41 +0200 Subject: [PATCH] feat: Preview dependency (#7284) --- .../DependentFeaturesPreview.test.tsx | 62 +++++++++++ .../FeatureOverviewCell.tsx | 100 ++++++++++++++---- 2 files changed, 144 insertions(+), 18 deletions(-) create mode 100644 frontend/src/component/common/Table/cells/FeatureOverviewCell/DependentFeaturesPreview.test.tsx diff --git a/frontend/src/component/common/Table/cells/FeatureOverviewCell/DependentFeaturesPreview.test.tsx b/frontend/src/component/common/Table/cells/FeatureOverviewCell/DependentFeaturesPreview.test.tsx new file mode 100644 index 0000000000..282ad183aa --- /dev/null +++ b/frontend/src/component/common/Table/cells/FeatureOverviewCell/DependentFeaturesPreview.test.tsx @@ -0,0 +1,62 @@ +import { screen } from '@testing-library/react'; +import { render } from 'utils/testRenderer'; +import { PrimaryFeatureInfo } from './FeatureOverviewCell'; +import userEvent from '@testing-library/user-event'; +import { testServerRoute, testServerSetup } from 'utils/testServer'; + +const server = testServerSetup(); + +test('Preview parent feature', async () => { + testServerRoute(server, '/api/admin/projects/default/features/featureA', { + children: [], + dependencies: [{ feature: 'featureB' }], + }); + + render( + {}} + delay={0} + />, + ); + + const childBadge = screen.getByText('child'); + userEvent.hover(childBadge); + + await screen.findByText('Loading...'); + + await screen.findByText('Parent'); + await screen.findByText('featureB'); +}); + +test('Preview child features', async () => { + testServerRoute(server, '/api/admin/projects/default/features/featureA', { + children: ['featureB', 'featureC'], + dependencies: [], + }); + + render( + {}} + delay={0} + />, + ); + + const parentBadge = screen.getByText('parent'); + userEvent.hover(parentBadge); + + await screen.findByText('Loading...'); + + await screen.findByText('Children'); + await screen.findByText('featureB'); + await screen.findByText('featureC'); +}); diff --git a/frontend/src/component/common/Table/cells/FeatureOverviewCell/FeatureOverviewCell.tsx b/frontend/src/component/common/Table/cells/FeatureOverviewCell/FeatureOverviewCell.tsx index a80a40b91c..a8ab7f1716 100644 --- a/frontend/src/component/common/Table/cells/FeatureOverviewCell/FeatureOverviewCell.tsx +++ b/frontend/src/component/common/Table/cells/FeatureOverviewCell/FeatureOverviewCell.tsx @@ -1,6 +1,6 @@ import type { FC, ReactElement } from 'react'; import type { FeatureSearchResponseSchema } from '../../../../../openapi'; -import { Box, styled } from '@mui/material'; +import { Box, IconButton, styled } from '@mui/material'; import useFeatureTypes from 'hooks/api/getters/useFeatureTypes/useFeatureTypes'; import { getFeatureTypeIcons } from 'utils/getFeatureTypeIcons'; import { useSearchHighlightContext } from '../../SearchHighlightContext/SearchHighlightContext'; @@ -9,7 +9,8 @@ import { StyledDescription, StyledTitle } from '../LinkCell/LinkCell.styles'; import { Link } from 'react-router-dom'; import { Badge } from '../../../Badge/Badge'; import { HtmlTooltip } from '../../../HtmlTooltip/HtmlTooltip'; -import { ConditionallyRender } from '../../../ConditionallyRender/ConditionallyRender'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import { useFeature } from 'hooks/api/getters/useFeature/useFeature'; interface IFeatureNameCellProps { row: { @@ -191,14 +192,64 @@ const Tags: FC<{ ); }; -const PrimaryFeatureInfo: FC<{ +const StyledDependencyLink = styled(Link)({ + display: 'block', +}); + +const DependencyPreview: FC<{ feature: string; project: string }> = ({ + feature, + project, +}) => { + const { feature: fetchedFeature } = useFeature(project, feature); + const children = fetchedFeature.children; + const parents = fetchedFeature.dependencies; + + if (children.length > 0) { + return ( + <> + Children + + {children.map((child) => ( + + {child} + + ))} + + ); + } else if (parents[0]) { + const parentFeature = parents[0].feature; + return ( + <> + Parent + + {parentFeature} + + + ); + } + return <>Loading...; +}; + +export const PrimaryFeatureInfo: FC<{ project: string; feature: string; searchQuery: string; type: string; dependencyType: string; onTypeClick: (type: string) => void; -}> = ({ project, feature, type, searchQuery, dependencyType, onTypeClick }) => { + delay?: number; +}> = ({ + project, + feature, + type, + searchQuery, + dependencyType, + onTypeClick, + delay = 500, +}) => { const { featureTypes } = useFeatureTypes(); const IconComponent = getFeatureTypeIcons(type); const typeName = featureTypes.find( @@ -208,14 +259,16 @@ const PrimaryFeatureInfo: FC<{ const TypeIcon = () => ( - ({ - cursor: 'pointer', - fontSize: theme.spacing(2), - })} + onTypeClick(type)} - /> + > + ({ fontSize: theme.spacing(2) })} + data-testid='feature-type-icon' + /> + ); @@ -230,15 +283,26 @@ const PrimaryFeatureInfo: FC<{ } + enterDelay={delay} + enterNextDelay={delay} > - {dependencyType} - + + {dependencyType} + + } />