mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-31 00:16:47 +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. ![image](https://github.com/Unleash/unleash/assets/17786332/c74bb1c9-07f9-4bca-95bb-4ca020398444)
This commit is contained in:
parent
20bfa73b82
commit
b021e7cf85
@ -18,9 +18,10 @@ export const SegmentDelete = ({
|
||||
onClose,
|
||||
onRemove,
|
||||
}: ISegmentDeleteProps) => {
|
||||
const { strategies, loading } = useStrategiesBySegment(segment.id);
|
||||
const canDeleteSegment = strategies?.length === 0;
|
||||
|
||||
const { strategies, changeRequestStrategies, loading } =
|
||||
useStrategiesBySegment(segment.id);
|
||||
const canDeleteSegment =
|
||||
strategies?.length === 0 && changeRequestStrategies?.length === 0;
|
||||
if (loading) {
|
||||
return null;
|
||||
}
|
||||
@ -42,6 +43,7 @@ export const SegmentDelete = ({
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
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 { formatStrategyName } from 'utils/strategyNames';
|
||||
import { styled } from '@mui/material';
|
||||
import {
|
||||
ChangeRequestNewStrategy,
|
||||
ChangeRequestStrategy,
|
||||
ChangeRequestUpdatedStrategy,
|
||||
} from 'hooks/api/getters/useStrategiesBySegment/useStrategiesBySegment';
|
||||
import { sortStrategiesByFeature } from './sort-strategies';
|
||||
|
||||
const StyledUl = styled('ul')({
|
||||
marginBottom: 0,
|
||||
@ -21,14 +27,29 @@ interface ISegmentDeleteUsedSegmentProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
strategies: IFeatureStrategy[] | undefined;
|
||||
changeRequestStrategies: ChangeRequestStrategy[] | undefined;
|
||||
}
|
||||
|
||||
export const formatChangeRequestPath = (
|
||||
projectId: string,
|
||||
changeRequestId: number,
|
||||
): string => {
|
||||
return `/projects/${projectId}/change-requests/${changeRequestId}`;
|
||||
};
|
||||
|
||||
export const SegmentDeleteUsedSegment = ({
|
||||
segment,
|
||||
open,
|
||||
onClose,
|
||||
strategies,
|
||||
changeRequestStrategies,
|
||||
}: ISegmentDeleteUsedSegmentProps) => {
|
||||
const sortedStrategies = sortStrategiesByFeature<
|
||||
IFeatureStrategy,
|
||||
ChangeRequestUpdatedStrategy,
|
||||
ChangeRequestNewStrategy
|
||||
>(strategies ?? [], changeRequestStrategies ?? []);
|
||||
|
||||
return (
|
||||
<Dialogue
|
||||
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:
|
||||
</p>
|
||||
<StyledUl>
|
||||
{strategies?.map((strategy) => (
|
||||
<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>
|
||||
))}
|
||||
{sortedStrategies.map((strategy, index) =>
|
||||
strategyListItem(strategy, index),
|
||||
)}
|
||||
</StyledUl>
|
||||
</Dialogue>
|
||||
);
|
||||
};
|
||||
|
||||
const formatStrategyNameParens = (strategy: IFeatureStrategy): string => {
|
||||
const formatStrategyNameParens = (strategy: {
|
||||
strategyName?: string;
|
||||
}): string => {
|
||||
if (!strategy.strategyName) {
|
||||
return '';
|
||||
}
|
||||
|
||||
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 { 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 {
|
||||
strategies: IFeatureStrategy[];
|
||||
changeRequestStrategies: ChangeRequestStrategy[];
|
||||
refetchUsedSegments: () => void;
|
||||
loading: boolean;
|
||||
error?: Error;
|
||||
@ -26,6 +44,7 @@ export const useStrategiesBySegment = (
|
||||
|
||||
return {
|
||||
strategies: data?.strategies || [],
|
||||
changeRequestStrategies: data?.changeRequestStrategies || [],
|
||||
refetchUsedSegments,
|
||||
loading: !error && !data,
|
||||
error,
|
||||
|
Loading…
Reference in New Issue
Block a user