mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: show strategies used by segments (#5407)
This PR displays change request usage of segments when such usage is returned from the API. It expects at least #5406 to have been merged before it can be merged. 
This commit is contained in:
		
							parent
							
								
									20bfa73b82
								
							
						
					
					
						commit
						b021e7cf85
					
				@ -18,9 +18,10 @@ export const SegmentDelete = ({
 | 
				
			|||||||
    onClose,
 | 
					    onClose,
 | 
				
			||||||
    onRemove,
 | 
					    onRemove,
 | 
				
			||||||
}: ISegmentDeleteProps) => {
 | 
					}: ISegmentDeleteProps) => {
 | 
				
			||||||
    const { strategies, loading } = useStrategiesBySegment(segment.id);
 | 
					    const { strategies, changeRequestStrategies, loading } =
 | 
				
			||||||
    const canDeleteSegment = strategies?.length === 0;
 | 
					        useStrategiesBySegment(segment.id);
 | 
				
			||||||
 | 
					    const canDeleteSegment =
 | 
				
			||||||
 | 
					        strategies?.length === 0 && changeRequestStrategies?.length === 0;
 | 
				
			||||||
    if (loading) {
 | 
					    if (loading) {
 | 
				
			||||||
        return null;
 | 
					        return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -42,6 +43,7 @@ export const SegmentDelete = ({
 | 
				
			|||||||
                    open={open}
 | 
					                    open={open}
 | 
				
			||||||
                    onClose={onClose}
 | 
					                    onClose={onClose}
 | 
				
			||||||
                    strategies={strategies}
 | 
					                    strategies={strategies}
 | 
				
			||||||
 | 
					                    changeRequestStrategies={changeRequestStrategies}
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					import { render } from 'utils/testRenderer';
 | 
				
			||||||
 | 
					import { screen } from '@testing-library/react';
 | 
				
			||||||
 | 
					import { SegmentDeleteUsedSegment } from './SegmentDeleteUsedSegment';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('SegmentDeleteUsedSegment', () => {
 | 
				
			||||||
 | 
					    it('should link to change requests for change request strategies', async () => {
 | 
				
			||||||
 | 
					        const projectId = 'project1';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const crStrategies = [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                projectId,
 | 
				
			||||||
 | 
					                featureName: 'feature1',
 | 
				
			||||||
 | 
					                strategyName: 'flexible rollout',
 | 
				
			||||||
 | 
					                environment: 'default',
 | 
				
			||||||
 | 
					                changeRequest: { id: 1, title: null },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                projectId,
 | 
				
			||||||
 | 
					                featureName: 'feature1',
 | 
				
			||||||
 | 
					                strategyName: 'flexible rollout',
 | 
				
			||||||
 | 
					                environment: 'default',
 | 
				
			||||||
 | 
					                changeRequest: { id: 2, title: 'My cool CR' },
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        render(
 | 
				
			||||||
 | 
					            <SegmentDeleteUsedSegment
 | 
				
			||||||
 | 
					                changeRequestStrategies={crStrategies}
 | 
				
			||||||
 | 
					                segment={{
 | 
				
			||||||
 | 
					                    id: 1,
 | 
				
			||||||
 | 
					                    name: 'segment',
 | 
				
			||||||
 | 
					                    description: 'description',
 | 
				
			||||||
 | 
					                    project: projectId,
 | 
				
			||||||
 | 
					                    constraints: [],
 | 
				
			||||||
 | 
					                    createdAt: '',
 | 
				
			||||||
 | 
					                    createdBy: '',
 | 
				
			||||||
 | 
					                }}
 | 
				
			||||||
 | 
					                open={true}
 | 
				
			||||||
 | 
					                strategies={[]}
 | 
				
			||||||
 | 
					                onClose={() => {}}
 | 
				
			||||||
 | 
					            />,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const links = await screen.findAllByRole('link');
 | 
				
			||||||
 | 
					        expect(links).toHaveLength(crStrategies.length);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const [link1, link2] = links;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        expect(link1).toHaveTextContent('#1');
 | 
				
			||||||
 | 
					        expect(link1).toHaveAccessibleDescription('Change request 1');
 | 
				
			||||||
 | 
					        expect(link1).toHaveAttribute(
 | 
				
			||||||
 | 
					            'href',
 | 
				
			||||||
 | 
					            `/projects/${projectId}/change-requests/1`,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        expect(link2).toHaveTextContent('#2 (My cool CR)');
 | 
				
			||||||
 | 
					        expect(link2).toHaveAccessibleDescription('Change request 2');
 | 
				
			||||||
 | 
					        expect(link2).toHaveAttribute(
 | 
				
			||||||
 | 
					            'href',
 | 
				
			||||||
 | 
					            `/projects/${projectId}/change-requests/2`,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@ -5,6 +5,12 @@ import { Link } from 'react-router-dom';
 | 
				
			|||||||
import { formatEditStrategyPath } from 'component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit';
 | 
					import { formatEditStrategyPath } from 'component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit';
 | 
				
			||||||
import { formatStrategyName } from 'utils/strategyNames';
 | 
					import { formatStrategyName } from 'utils/strategyNames';
 | 
				
			||||||
import { styled } from '@mui/material';
 | 
					import { styled } from '@mui/material';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    ChangeRequestNewStrategy,
 | 
				
			||||||
 | 
					    ChangeRequestStrategy,
 | 
				
			||||||
 | 
					    ChangeRequestUpdatedStrategy,
 | 
				
			||||||
 | 
					} from 'hooks/api/getters/useStrategiesBySegment/useStrategiesBySegment';
 | 
				
			||||||
 | 
					import { sortStrategiesByFeature } from './sort-strategies';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledUl = styled('ul')({
 | 
					const StyledUl = styled('ul')({
 | 
				
			||||||
    marginBottom: 0,
 | 
					    marginBottom: 0,
 | 
				
			||||||
@ -21,14 +27,29 @@ interface ISegmentDeleteUsedSegmentProps {
 | 
				
			|||||||
    open: boolean;
 | 
					    open: boolean;
 | 
				
			||||||
    onClose: () => void;
 | 
					    onClose: () => void;
 | 
				
			||||||
    strategies: IFeatureStrategy[] | undefined;
 | 
					    strategies: IFeatureStrategy[] | undefined;
 | 
				
			||||||
 | 
					    changeRequestStrategies: ChangeRequestStrategy[] | undefined;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const formatChangeRequestPath = (
 | 
				
			||||||
 | 
					    projectId: string,
 | 
				
			||||||
 | 
					    changeRequestId: number,
 | 
				
			||||||
 | 
					): string => {
 | 
				
			||||||
 | 
					    return `/projects/${projectId}/change-requests/${changeRequestId}`;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const SegmentDeleteUsedSegment = ({
 | 
					export const SegmentDeleteUsedSegment = ({
 | 
				
			||||||
    segment,
 | 
					    segment,
 | 
				
			||||||
    open,
 | 
					    open,
 | 
				
			||||||
    onClose,
 | 
					    onClose,
 | 
				
			||||||
    strategies,
 | 
					    strategies,
 | 
				
			||||||
 | 
					    changeRequestStrategies,
 | 
				
			||||||
}: ISegmentDeleteUsedSegmentProps) => {
 | 
					}: ISegmentDeleteUsedSegmentProps) => {
 | 
				
			||||||
 | 
					    const sortedStrategies = sortStrategiesByFeature<
 | 
				
			||||||
 | 
					        IFeatureStrategy,
 | 
				
			||||||
 | 
					        ChangeRequestUpdatedStrategy,
 | 
				
			||||||
 | 
					        ChangeRequestNewStrategy
 | 
				
			||||||
 | 
					    >(strategies ?? [], changeRequestStrategies ?? []);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Dialogue
 | 
					        <Dialogue
 | 
				
			||||||
            title="You can't delete a segment that's currently in use"
 | 
					            title="You can't delete a segment that's currently in use"
 | 
				
			||||||
@ -41,32 +62,74 @@ export const SegmentDeleteUsedSegment = ({
 | 
				
			|||||||
                <strong>{segment.name}</strong> segment for their strategies:
 | 
					                <strong>{segment.name}</strong> segment for their strategies:
 | 
				
			||||||
            </p>
 | 
					            </p>
 | 
				
			||||||
            <StyledUl>
 | 
					            <StyledUl>
 | 
				
			||||||
                {strategies?.map((strategy) => (
 | 
					                {sortedStrategies.map((strategy, index) =>
 | 
				
			||||||
                    <li key={strategy.id}>
 | 
					                    strategyListItem(strategy, index),
 | 
				
			||||||
                        <StyledLink
 | 
					                )}
 | 
				
			||||||
                            to={formatEditStrategyPath(
 | 
					 | 
				
			||||||
                                strategy.projectId!,
 | 
					 | 
				
			||||||
                                strategy.featureName!,
 | 
					 | 
				
			||||||
                                strategy.environment!,
 | 
					 | 
				
			||||||
                                strategy.id,
 | 
					 | 
				
			||||||
                            )}
 | 
					 | 
				
			||||||
                            target='_blank'
 | 
					 | 
				
			||||||
                            rel='noopener noreferrer'
 | 
					 | 
				
			||||||
                        >
 | 
					 | 
				
			||||||
                            {strategy.featureName!}{' '}
 | 
					 | 
				
			||||||
                            {formatStrategyNameParens(strategy)}
 | 
					 | 
				
			||||||
                        </StyledLink>
 | 
					 | 
				
			||||||
                    </li>
 | 
					 | 
				
			||||||
                ))}
 | 
					 | 
				
			||||||
            </StyledUl>
 | 
					            </StyledUl>
 | 
				
			||||||
        </Dialogue>
 | 
					        </Dialogue>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const formatStrategyNameParens = (strategy: IFeatureStrategy): string => {
 | 
					const formatStrategyNameParens = (strategy: {
 | 
				
			||||||
 | 
					    strategyName?: string;
 | 
				
			||||||
 | 
					}): string => {
 | 
				
			||||||
    if (!strategy.strategyName) {
 | 
					    if (!strategy.strategyName) {
 | 
				
			||||||
        return '';
 | 
					        return '';
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return `(${formatStrategyName(strategy.strategyName)})`;
 | 
					    return `(${formatStrategyName(strategy.strategyName)})`;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const strategyListItem = (
 | 
				
			||||||
 | 
					    strategy:
 | 
				
			||||||
 | 
					        | IFeatureStrategy
 | 
				
			||||||
 | 
					        | ChangeRequestUpdatedStrategy
 | 
				
			||||||
 | 
					        | ChangeRequestNewStrategy,
 | 
				
			||||||
 | 
					    index: number,
 | 
				
			||||||
 | 
					) => {
 | 
				
			||||||
 | 
					    const isChangeRequest = (
 | 
				
			||||||
 | 
					        strategy: IFeatureStrategy | ChangeRequestStrategy,
 | 
				
			||||||
 | 
					    ): strategy is ChangeRequestStrategy => 'changeRequest' in strategy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (isChangeRequest(strategy)) {
 | 
				
			||||||
 | 
					        const { id, title } = strategy.changeRequest;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const text = title ? `#${id} (${title})` : `#${id}`;
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					            <li key={`#${strategy.changeRequest.id}@${index}`}>
 | 
				
			||||||
 | 
					                <p>
 | 
				
			||||||
 | 
					                    {strategy.featureName}{' '}
 | 
				
			||||||
 | 
					                    {`${formatStrategyNameParens(
 | 
				
			||||||
 | 
					                        strategy,
 | 
				
			||||||
 | 
					                    )} — in change request `}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <StyledLink
 | 
				
			||||||
 | 
					                        to={formatChangeRequestPath(strategy.projectId, id)}
 | 
				
			||||||
 | 
					                        target='_blank'
 | 
				
			||||||
 | 
					                        rel='noopener noreferrer'
 | 
				
			||||||
 | 
					                        title={`Change request ${id}`}
 | 
				
			||||||
 | 
					                    >
 | 
				
			||||||
 | 
					                        {text}
 | 
				
			||||||
 | 
					                    </StyledLink>
 | 
				
			||||||
 | 
					                </p>
 | 
				
			||||||
 | 
					            </li>
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					            <li key={strategy.id}>
 | 
				
			||||||
 | 
					                <StyledLink
 | 
				
			||||||
 | 
					                    to={formatEditStrategyPath(
 | 
				
			||||||
 | 
					                        strategy.projectId!,
 | 
				
			||||||
 | 
					                        strategy.featureName!,
 | 
				
			||||||
 | 
					                        strategy.environment!,
 | 
				
			||||||
 | 
					                        strategy.id,
 | 
				
			||||||
 | 
					                    )}
 | 
				
			||||||
 | 
					                    target='_blank'
 | 
				
			||||||
 | 
					                    rel='noopener noreferrer'
 | 
				
			||||||
 | 
					                >
 | 
				
			||||||
 | 
					                    {strategy.featureName!} {formatStrategyNameParens(strategy)}
 | 
				
			||||||
 | 
					                </StyledLink>
 | 
				
			||||||
 | 
					            </li>
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -5,8 +5,26 @@ import handleErrorResponses from '../httpErrorResponseHandler';
 | 
				
			|||||||
import { IFeatureStrategy } from 'interfaces/strategy';
 | 
					import { IFeatureStrategy } from 'interfaces/strategy';
 | 
				
			||||||
import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR';
 | 
					import { useConditionalSWR } from '../useConditionalSWR/useConditionalSWR';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type ChangeRequestInfo = { id: number; title: string | null };
 | 
				
			||||||
 | 
					export type ChangeRequestNewStrategy = {
 | 
				
			||||||
 | 
					    projectId: string;
 | 
				
			||||||
 | 
					    featureName: string;
 | 
				
			||||||
 | 
					    strategyName: string;
 | 
				
			||||||
 | 
					    environment: string;
 | 
				
			||||||
 | 
					    changeRequest: ChangeRequestInfo;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type ChangeRequestUpdatedStrategy = ChangeRequestNewStrategy & {
 | 
				
			||||||
 | 
					    id: string;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type ChangeRequestStrategy =
 | 
				
			||||||
 | 
					    | ChangeRequestNewStrategy
 | 
				
			||||||
 | 
					    | ChangeRequestUpdatedStrategy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IUseStrategiesBySegmentOutput {
 | 
					export interface IUseStrategiesBySegmentOutput {
 | 
				
			||||||
    strategies: IFeatureStrategy[];
 | 
					    strategies: IFeatureStrategy[];
 | 
				
			||||||
 | 
					    changeRequestStrategies: ChangeRequestStrategy[];
 | 
				
			||||||
    refetchUsedSegments: () => void;
 | 
					    refetchUsedSegments: () => void;
 | 
				
			||||||
    loading: boolean;
 | 
					    loading: boolean;
 | 
				
			||||||
    error?: Error;
 | 
					    error?: Error;
 | 
				
			||||||
@ -26,6 +44,7 @@ export const useStrategiesBySegment = (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
        strategies: data?.strategies || [],
 | 
					        strategies: data?.strategies || [],
 | 
				
			||||||
 | 
					        changeRequestStrategies: data?.changeRequestStrategies || [],
 | 
				
			||||||
        refetchUsedSegments,
 | 
					        refetchUsedSegments,
 | 
				
			||||||
        loading: !error && !data,
 | 
					        loading: !error && !data,
 | 
				
			||||||
        error,
 | 
					        error,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user