mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +02:00
Add various fixes to the CR view (#10231)
Adds a number of small corrections and fixes to (mainly) CR-related component. Discovered as part of adding the new JSON diff view. Refer to the various inline comments for explanations. --------- Co-authored-by: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com>
This commit is contained in:
parent
55b8941306
commit
f2766b6b3b
@ -16,7 +16,7 @@ export const ArchiveFeatureChange: FC<IArchiveFeatureChange> = ({
|
||||
actions,
|
||||
}) => (
|
||||
<ChangeItemWrapper>
|
||||
<ArchiveBox>Archiving feature</ArchiveBox>
|
||||
<ArchiveBox>Archiving flag</ArchiveBox>
|
||||
{actions}
|
||||
</ChangeItemWrapper>
|
||||
);
|
||||
|
@ -26,6 +26,7 @@ import { FeatureStrategyForm } from '../../../../feature/FeatureStrategy/Feature
|
||||
import { NewStrategyVariants } from 'component/feature/StrategyTypes/NewStrategyVariants';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { constraintId } from 'constants/constraintId.ts';
|
||||
import { apiPayloadConstraintReplacer } from 'utils/api-payload-constraint-replacer.ts';
|
||||
|
||||
interface IEditChangeProps {
|
||||
change: IChangeRequestAddStrategy | IChangeRequestUpdateStrategy;
|
||||
@ -208,7 +209,7 @@ export const formatUpdateStrategyApiCode = (
|
||||
}
|
||||
|
||||
const url = `${unleashUrl}/api/admin/projects/${projectId}/change-requests/${changeRequestId}/changes/${changeId}`;
|
||||
const payload = JSON.stringify(strategy, undefined, 2);
|
||||
const payload = JSON.stringify(strategy, apiPayloadConstraintReplacer, 2);
|
||||
|
||||
return `curl --location --request PUT '${url}' \\
|
||||
--header 'Authorization: INSERT_API_KEY' \\
|
||||
|
@ -6,6 +6,7 @@ import { Box, styled } from '@mui/material';
|
||||
import { EnvironmentStrategyOrderDiff } from './EnvironmentStrategyOrderDiff.tsx';
|
||||
import { StrategyExecution } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution';
|
||||
import { formatStrategyName } from '../../../../../../utils/strategyNames.tsx';
|
||||
import type { IFeatureStrategy } from 'interfaces/strategy.ts';
|
||||
|
||||
const ChangeItemInfo = styled(Box)({
|
||||
display: 'flex',
|
||||
@ -74,14 +75,14 @@ export const EnvironmentStrategyExecutionOrder = ({
|
||||
.map((strategy) => strategy.id) ?? [],
|
||||
};
|
||||
|
||||
const updatedStrategies = change.payload
|
||||
const updatedStrategies: IFeatureStrategy[] = change.payload
|
||||
.map(({ id }) => {
|
||||
return environmentStrategies.find((s) => s.id === id);
|
||||
})
|
||||
.filter(Boolean);
|
||||
.filter((strategy): strategy is IFeatureStrategy => Boolean(strategy));
|
||||
|
||||
const data = {
|
||||
strategyIds: updatedStrategies.map((strategy) => strategy!.id),
|
||||
strategyIds: updatedStrategies.map((strategy) => strategy.id),
|
||||
};
|
||||
|
||||
return (
|
||||
@ -105,7 +106,7 @@ export const EnvironmentStrategyExecutionOrder = ({
|
||||
</StyledChangeHeader>
|
||||
<StyledStrategyExecutionWrapper>
|
||||
{updatedStrategies.map((strategy, index) => (
|
||||
<StyledStrategyContainer>
|
||||
<StyledStrategyContainer key={strategy.id}>
|
||||
{`${index + 1}: `}
|
||||
{formatStrategyName(strategy?.name || '')}
|
||||
{strategy?.title && ` - ${strategy.title}`}
|
||||
|
@ -7,6 +7,7 @@ import type {
|
||||
} from '../../../changeRequest.types';
|
||||
import { SegmentChangeDetails } from './SegmentChangeDetails.tsx';
|
||||
import { ConflictWarning } from './ConflictWarning.tsx';
|
||||
import { useSegment } from 'hooks/api/getters/useSegment/useSegment.ts';
|
||||
|
||||
interface ISegmentChangeProps {
|
||||
segmentChange: ISegmentChange;
|
||||
@ -20,61 +21,65 @@ export const SegmentChange: FC<ISegmentChangeProps> = ({
|
||||
onNavigate,
|
||||
actions,
|
||||
changeRequestState,
|
||||
}) => (
|
||||
<Card
|
||||
elevation={0}
|
||||
sx={(theme) => ({
|
||||
marginTop: theme.spacing(2),
|
||||
marginBottom: theme.spacing(2),
|
||||
overflow: 'hidden',
|
||||
})}
|
||||
>
|
||||
<Box
|
||||
}) => {
|
||||
const { segment } = useSegment(segmentChange.payload.id);
|
||||
|
||||
return (
|
||||
<Card
|
||||
elevation={0}
|
||||
sx={(theme) => ({
|
||||
backgroundColor: theme.palette.neutral.light,
|
||||
borderRadius: (theme) =>
|
||||
`${theme.shape.borderRadiusLarge}px ${theme.shape.borderRadiusLarge}px 0 0`,
|
||||
border: '1px solid',
|
||||
borderColor: (theme) =>
|
||||
segmentChange.conflict
|
||||
? theme.palette.warning.border
|
||||
: theme.palette.divider,
|
||||
borderBottom: 'none',
|
||||
marginTop: theme.spacing(2),
|
||||
marginBottom: theme.spacing(2),
|
||||
overflow: 'hidden',
|
||||
})}
|
||||
>
|
||||
<ConflictWarning conflict={segmentChange.conflict} />
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
pt: 2,
|
||||
pb: 2,
|
||||
px: 3,
|
||||
}}
|
||||
sx={(theme) => ({
|
||||
backgroundColor: theme.palette.neutral.light,
|
||||
borderRadius: `${theme.shape.borderRadiusLarge}px ${theme.shape.borderRadiusLarge}px 0 0`,
|
||||
border: '1px solid',
|
||||
borderColor: segmentChange.conflict
|
||||
? theme.palette.warning.border
|
||||
: theme.palette.divider,
|
||||
borderBottom: 'none',
|
||||
overflow: 'hidden',
|
||||
})}
|
||||
>
|
||||
<Typography>Segment name: </Typography>
|
||||
|
||||
<Link
|
||||
component={RouterLink}
|
||||
to={`/segments/edit/${segmentChange.payload.id}`}
|
||||
color='primary'
|
||||
underline='hover'
|
||||
<ConflictWarning conflict={segmentChange.conflict} />
|
||||
<Box
|
||||
sx={{
|
||||
marginLeft: 1,
|
||||
'& :hover': {
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
display: 'flex',
|
||||
pt: 2,
|
||||
pb: 2,
|
||||
px: 3,
|
||||
}}
|
||||
onClick={onNavigate}
|
||||
>
|
||||
<strong>{segmentChange.payload.name}</strong>
|
||||
</Link>
|
||||
<Typography>Segment name:</Typography>
|
||||
|
||||
<Link
|
||||
component={RouterLink}
|
||||
to={`/segments/edit/${segmentChange.payload.id}`}
|
||||
color='primary'
|
||||
underline='hover'
|
||||
sx={{
|
||||
marginLeft: 1,
|
||||
'& :hover': {
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
}}
|
||||
onClick={onNavigate}
|
||||
>
|
||||
<strong>
|
||||
{segmentChange.payload.name || segment?.name}
|
||||
</strong>
|
||||
</Link>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
<SegmentChangeDetails
|
||||
change={segmentChange}
|
||||
actions={actions}
|
||||
changeRequestState={changeRequestState}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
<SegmentChangeDetails
|
||||
change={segmentChange}
|
||||
actions={actions}
|
||||
changeRequestState={changeRequestState}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
@ -9,13 +9,14 @@ import {
|
||||
formatStrategyName,
|
||||
GetFeatureStrategyIcon,
|
||||
} from 'utils/strategyNames';
|
||||
import EventDiff from 'component/events/EventDiff/EventDiff';
|
||||
import EventDiff, { NewEventDiff } from 'component/events/EventDiff/EventDiff';
|
||||
import omit from 'lodash.omit';
|
||||
import { TooltipLink } from 'component/common/TooltipLink/TooltipLink';
|
||||
import { Typography, styled } from '@mui/material';
|
||||
import type { IFeatureStrategy } from 'interfaces/strategy';
|
||||
import { textTruncated } from 'themes/themeStyles';
|
||||
import { NameWithChangeInfo } from '../NameWithChangeInfo/NameWithChangeInfo.tsx';
|
||||
import { useUiFlag } from 'hooks/useUiFlag.ts';
|
||||
|
||||
const StyledCodeSection = styled('div')(({ theme }) => ({
|
||||
overflowX: 'auto',
|
||||
@ -47,12 +48,22 @@ export const StrategyDiff: FC<{
|
||||
| IChangeRequestDeleteStrategy;
|
||||
currentStrategy?: IFeatureStrategy;
|
||||
}> = ({ change, currentStrategy }) => {
|
||||
const useNewDiff = useUiFlag('improvedJsonDiff');
|
||||
const changeRequestStrategy =
|
||||
change.action === 'deleteStrategy' ? undefined : change.payload;
|
||||
|
||||
const sortedCurrentStrategy = sortSegments(currentStrategy);
|
||||
const sortedChangeRequestStrategy = sortSegments(changeRequestStrategy);
|
||||
|
||||
if (useNewDiff) {
|
||||
return (
|
||||
<NewEventDiff
|
||||
entry={{
|
||||
preData: omit(sortedCurrentStrategy, 'sortOrder'),
|
||||
data: omit(sortedChangeRequestStrategy, 'snapshot'),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<StyledCodeSection>
|
||||
<EventDiff
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { styled } from '@mui/material';
|
||||
import { styled, type TypographyProps } from '@mui/material';
|
||||
import { Box, Card, Paper, Typography } from '@mui/material';
|
||||
import { UserAvatar } from 'component/common/UserAvatar/UserAvatar';
|
||||
|
||||
@ -18,12 +18,14 @@ export const StyledInnerContainer = styled(Box)(({ theme }) => ({
|
||||
alignItems: 'center',
|
||||
}));
|
||||
|
||||
export const StyledHeader = styled(Typography)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginRight: theme.spacing(1),
|
||||
fontSize: theme.fontSizes.mainHeader,
|
||||
}));
|
||||
export const StyledHeader = styled(Typography)<TypographyProps>(
|
||||
({ theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginRight: theme.spacing(1),
|
||||
fontSize: theme.fontSizes.mainHeader,
|
||||
}),
|
||||
);
|
||||
|
||||
export const StyledCard = styled(Card)(({ theme }) => ({
|
||||
padding: theme.spacing(0.75, 1.5),
|
||||
|
@ -26,7 +26,7 @@ export const ChangeRequestHeader: FC<{ changeRequest: ChangeRequestType }> = ({
|
||||
title={title}
|
||||
setTitle={setTitle}
|
||||
>
|
||||
<StyledHeader variant='h1' sx={{ mr: 1.5 }}>
|
||||
<StyledHeader variant='h1' component='h2' sx={{ mr: 1.5 }}>
|
||||
{title}
|
||||
</StyledHeader>
|
||||
</ChangeRequestTitle>
|
||||
|
@ -70,7 +70,7 @@ test('Add single archive feature change to change request', async () => {
|
||||
|
||||
expect(screen.getByText('Archive feature flag')).toBeInTheDocument();
|
||||
await screen.findByText(
|
||||
'Archiving features with dependencies will also remove those dependencies.',
|
||||
'Archiving flags with dependencies will also remove those dependencies.',
|
||||
);
|
||||
const button = await screen.findByText('Add change to draft');
|
||||
|
||||
@ -100,7 +100,7 @@ test('Add multiple archive feature changes to change request', async () => {
|
||||
|
||||
await screen.findByText('Archive feature flags');
|
||||
await screen.findByText(
|
||||
'Archiving features with dependencies will also remove those dependencies.',
|
||||
'Archiving flags with dependencies will also remove those dependencies.',
|
||||
);
|
||||
const button = await screen.findByText('Add to change request');
|
||||
|
||||
@ -163,7 +163,7 @@ test('Show error message when multiple parents of orphaned children are archived
|
||||
);
|
||||
expect(
|
||||
screen.queryByText(
|
||||
'Archiving features with dependencies will also remove those dependencies.',
|
||||
'Archiving flags with dependencies will also remove those dependencies.',
|
||||
),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
@ -189,7 +189,7 @@ test('Show error message when 1 parent of orphaned children is archived', async
|
||||
);
|
||||
expect(
|
||||
screen.queryByText(
|
||||
'Archiving features with dependencies will also remove those dependencies.',
|
||||
'Archiving flags with dependencies will also remove those dependencies.',
|
||||
),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
@ -26,7 +26,7 @@ interface IFeatureArchiveDialogProps {
|
||||
const RemovedDependenciesAlert = () => {
|
||||
return (
|
||||
<Alert severity='warning' sx={{ m: (theme) => theme.spacing(2, 0) }}>
|
||||
Archiving features with dependencies will also remove those
|
||||
Archiving flags with dependencies will also remove those
|
||||
dependencies.
|
||||
</Alert>
|
||||
);
|
||||
|
@ -4,6 +4,7 @@ import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashCon
|
||||
import { ConstraintsList } from 'component/common/ConstraintsList/ConstraintsList';
|
||||
import { ConstraintAccordionView } from 'component/common/NewConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView';
|
||||
import { constraintId } from 'constants/constraintId';
|
||||
import { objectId } from 'utils/objectId';
|
||||
|
||||
export interface IViewableConstraintsListProps {
|
||||
constraints: IConstraint[];
|
||||
@ -29,7 +30,7 @@ export const ViewableConstraintsList = ({
|
||||
<ConstraintsList>
|
||||
{constraints.map((constraint) => (
|
||||
<ConstraintAccordionView
|
||||
key={constraint[constraintId]}
|
||||
key={constraint[constraintId] || objectId(constraint)}
|
||||
constraint={constraint}
|
||||
/>
|
||||
))}
|
||||
|
@ -78,7 +78,7 @@ const ButtonIcon = styled('span')(({ theme }) => ({
|
||||
marginInlineEnd: theme.spacing(0.5),
|
||||
}));
|
||||
|
||||
const NewEventDiff: FC<IEventDiffProps> = ({ entry, excludeKeys }) => {
|
||||
export const NewEventDiff: FC<IEventDiffProps> = ({ entry, excludeKeys }) => {
|
||||
const changeType = entry.preData && entry.data ? 'edit' : 'replacement';
|
||||
const showExpandButton = changeType === 'edit';
|
||||
const [full, setFull] = useState(false);
|
||||
@ -220,4 +220,7 @@ const EventDiff: FC<IEventDiffProps> = (props) => {
|
||||
return <OldEventDiff {...props} />;
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated remove the default export with flag improvedJsonDiff. Switch imports in files that use this to the named import instead.
|
||||
*/
|
||||
export default EventDiff;
|
||||
|
Loading…
Reference in New Issue
Block a user