mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
feat: Preview dependency (#7284)
This commit is contained in:
parent
c129541df6
commit
e621c7a2a5
@ -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(
|
||||||
|
<PrimaryFeatureInfo
|
||||||
|
feature='featureA'
|
||||||
|
project='default'
|
||||||
|
type='release'
|
||||||
|
searchQuery=''
|
||||||
|
dependencyType='child'
|
||||||
|
onTypeClick={() => {}}
|
||||||
|
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(
|
||||||
|
<PrimaryFeatureInfo
|
||||||
|
feature='featureA'
|
||||||
|
project='default'
|
||||||
|
type='release'
|
||||||
|
searchQuery=''
|
||||||
|
dependencyType='parent'
|
||||||
|
onTypeClick={() => {}}
|
||||||
|
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');
|
||||||
|
});
|
@ -1,6 +1,6 @@
|
|||||||
import type { FC, ReactElement } from 'react';
|
import type { FC, ReactElement } from 'react';
|
||||||
import type { FeatureSearchResponseSchema } from '../../../../../openapi';
|
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 useFeatureTypes from 'hooks/api/getters/useFeatureTypes/useFeatureTypes';
|
||||||
import { getFeatureTypeIcons } from 'utils/getFeatureTypeIcons';
|
import { getFeatureTypeIcons } from 'utils/getFeatureTypeIcons';
|
||||||
import { useSearchHighlightContext } from '../../SearchHighlightContext/SearchHighlightContext';
|
import { useSearchHighlightContext } from '../../SearchHighlightContext/SearchHighlightContext';
|
||||||
@ -9,7 +9,8 @@ import { StyledDescription, StyledTitle } from '../LinkCell/LinkCell.styles';
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { Badge } from '../../../Badge/Badge';
|
import { Badge } from '../../../Badge/Badge';
|
||||||
import { HtmlTooltip } from '../../../HtmlTooltip/HtmlTooltip';
|
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 {
|
interface IFeatureNameCellProps {
|
||||||
row: {
|
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 (
|
||||||
|
<>
|
||||||
|
<Box>Children</Box>
|
||||||
|
|
||||||
|
{children.map((child) => (
|
||||||
|
<StyledDependencyLink
|
||||||
|
to={`/projects/${project}/features/${child}`}
|
||||||
|
key={child}
|
||||||
|
>
|
||||||
|
{child}
|
||||||
|
</StyledDependencyLink>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else if (parents[0]) {
|
||||||
|
const parentFeature = parents[0].feature;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Box>Parent</Box>
|
||||||
|
<Link to={`/projects/${project}/features/${parentFeature}`}>
|
||||||
|
{parentFeature}
|
||||||
|
</Link>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <>Loading...</>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PrimaryFeatureInfo: FC<{
|
||||||
project: string;
|
project: string;
|
||||||
feature: string;
|
feature: string;
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
type: string;
|
type: string;
|
||||||
dependencyType: string;
|
dependencyType: string;
|
||||||
onTypeClick: (type: string) => void;
|
onTypeClick: (type: string) => void;
|
||||||
}> = ({ project, feature, type, searchQuery, dependencyType, onTypeClick }) => {
|
delay?: number;
|
||||||
|
}> = ({
|
||||||
|
project,
|
||||||
|
feature,
|
||||||
|
type,
|
||||||
|
searchQuery,
|
||||||
|
dependencyType,
|
||||||
|
onTypeClick,
|
||||||
|
delay = 500,
|
||||||
|
}) => {
|
||||||
const { featureTypes } = useFeatureTypes();
|
const { featureTypes } = useFeatureTypes();
|
||||||
const IconComponent = getFeatureTypeIcons(type);
|
const IconComponent = getFeatureTypeIcons(type);
|
||||||
const typeName = featureTypes.find(
|
const typeName = featureTypes.find(
|
||||||
@ -208,14 +259,16 @@ const PrimaryFeatureInfo: FC<{
|
|||||||
|
|
||||||
const TypeIcon = () => (
|
const TypeIcon = () => (
|
||||||
<HtmlTooltip arrow title={title} describeChild>
|
<HtmlTooltip arrow title={title} describeChild>
|
||||||
<IconComponent
|
<IconButton
|
||||||
data-testid='feature-type-icon'
|
sx={{ p: 0 }}
|
||||||
sx={(theme) => ({
|
aria-label={`add ${type} flag to filter`}
|
||||||
cursor: 'pointer',
|
|
||||||
fontSize: theme.spacing(2),
|
|
||||||
})}
|
|
||||||
onClick={() => onTypeClick(type)}
|
onClick={() => onTypeClick(type)}
|
||||||
/>
|
>
|
||||||
|
<IconComponent
|
||||||
|
sx={(theme) => ({ fontSize: theme.spacing(2) })}
|
||||||
|
data-testid='feature-type-icon'
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
</HtmlTooltip>
|
</HtmlTooltip>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -230,15 +283,26 @@ const PrimaryFeatureInfo: FC<{
|
|||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={Boolean(dependencyType)}
|
condition={Boolean(dependencyType)}
|
||||||
show={
|
show={
|
||||||
<DependencyBadge
|
<HtmlTooltip
|
||||||
color={
|
title={
|
||||||
dependencyType === 'parent'
|
<DependencyPreview
|
||||||
? 'warning'
|
feature={feature}
|
||||||
: 'secondary'
|
project={project}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
|
enterDelay={delay}
|
||||||
|
enterNextDelay={delay}
|
||||||
>
|
>
|
||||||
{dependencyType}
|
<DependencyBadge
|
||||||
</DependencyBadge>
|
color={
|
||||||
|
dependencyType === 'parent'
|
||||||
|
? 'warning'
|
||||||
|
: 'secondary'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{dependencyType}
|
||||||
|
</DependencyBadge>
|
||||||
|
</HtmlTooltip>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</FeatureNameAndType>
|
</FeatureNameAndType>
|
||||||
|
Loading…
Reference in New Issue
Block a user