mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +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