mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-22 19:07:54 +01:00
fix: hide warnings that you'll overwrite changes on CRs that are already applied (#6214)
The current approach uses adds an extra parameter to the components and passes it through from the parent components. It's never a lot of levels, so it feels alright, but it's feels like a bit of a code smell. I wonder if it would make sense to use a context for each change request? 🤔 Supersedes: https://github.com/Unleash/unleash/pull/6181
This commit is contained in:
parent
64a6af2858
commit
a8fa1ae347
@ -34,6 +34,7 @@ export const ChangeRequest: VFC<IChangeRequestProps> = ({
|
||||
key={segmentChange.payload.id}
|
||||
segmentChange={segmentChange}
|
||||
onNavigate={onNavigate}
|
||||
changeRequestState={changeRequest.state}
|
||||
actions={
|
||||
<ChangeActions
|
||||
changeRequest={changeRequest}
|
||||
|
@ -0,0 +1,73 @@
|
||||
import {
|
||||
ChangeRequestState,
|
||||
IChangeRequestPatchVariant,
|
||||
IChangeRequestUpdateSegment,
|
||||
IChangeRequestUpdateStrategy,
|
||||
} from 'component/changeRequest/changeRequest.types';
|
||||
import { useChangeRequestPlausibleContext } from 'component/changeRequest/ChangeRequestContext';
|
||||
import { useUiFlag } from 'hooks/useUiFlag';
|
||||
import { IFeatureVariant } from 'interfaces/featureToggle';
|
||||
import { ISegment } from 'interfaces/segment';
|
||||
import { IFeatureStrategy } from 'interfaces/strategy';
|
||||
import { useEffect } from 'react';
|
||||
import { OverwriteWarning } from './OverwriteWarning';
|
||||
import {
|
||||
getEnvVariantChangesThatWouldBeOverwritten,
|
||||
getSegmentChangesThatWouldBeOverwritten,
|
||||
getStrategyChangesThatWouldBeOverwritten,
|
||||
} from './strategy-change-diff-calculation';
|
||||
|
||||
type ChangeData =
|
||||
| {
|
||||
changeType: 'environment variant configuration';
|
||||
current?: IFeatureVariant[];
|
||||
change: IChangeRequestPatchVariant;
|
||||
}
|
||||
| {
|
||||
changeType: 'segment';
|
||||
current?: ISegment;
|
||||
change: IChangeRequestUpdateSegment;
|
||||
}
|
||||
| {
|
||||
changeType: 'strategy';
|
||||
current?: IFeatureStrategy;
|
||||
change: IChangeRequestUpdateStrategy;
|
||||
};
|
||||
|
||||
export const ChangeOverwriteWarning: React.FC<{
|
||||
data: ChangeData;
|
||||
changeRequestState: ChangeRequestState;
|
||||
}> = ({ data, changeRequestState }) => {
|
||||
const checkForChanges = useUiFlag('changeRequestConflictHandling');
|
||||
if (!checkForChanges) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const getChangesThatWouldBeOverwritten = () => {
|
||||
switch (data.changeType) {
|
||||
case 'segment':
|
||||
return getSegmentChangesThatWouldBeOverwritten(
|
||||
data.current,
|
||||
data.change,
|
||||
);
|
||||
case 'strategy':
|
||||
return getStrategyChangesThatWouldBeOverwritten(
|
||||
data.current,
|
||||
data.change,
|
||||
);
|
||||
case 'environment variant configuration':
|
||||
return getEnvVariantChangesThatWouldBeOverwritten(
|
||||
data.current,
|
||||
data.change,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<OverwriteWarning
|
||||
changeRequestState={changeRequestState}
|
||||
changeType={data.changeType}
|
||||
changesThatWouldBeOverwritten={getChangesThatWouldBeOverwritten()}
|
||||
/>
|
||||
);
|
||||
};
|
@ -0,0 +1,27 @@
|
||||
import { render } from 'utils/testRenderer';
|
||||
import { screen } from '@testing-library/react';
|
||||
import { OverwriteWarning } from './OverwriteWarning';
|
||||
import { ChangeRequestState } from 'component/changeRequest/changeRequest.types';
|
||||
|
||||
test.each([
|
||||
['Draft', true],
|
||||
['Approved', true],
|
||||
['In review', true],
|
||||
['Applied', false],
|
||||
['Scheduled', true],
|
||||
['Cancelled', false],
|
||||
['Rejected', false],
|
||||
])('Shows warnings for CRs in the "%s" state: %s', (status, showWarning) => {
|
||||
render(
|
||||
<OverwriteWarning
|
||||
changesThatWouldBeOverwritten={[
|
||||
{ property: 'some-prop', oldValue: 'old', newValue: 'new' },
|
||||
]}
|
||||
changeType={'strategy'}
|
||||
changeRequestState={status as ChangeRequestState}
|
||||
/>,
|
||||
);
|
||||
|
||||
const hasRendered = screen.queryByText('Heads up!') !== null;
|
||||
expect(hasRendered).toBe(showWarning);
|
||||
});
|
@ -1,21 +1,6 @@
|
||||
import { Box, styled } from '@mui/material';
|
||||
import { useChangeRequestPlausibleContext } from 'component/changeRequest/ChangeRequestContext';
|
||||
import {
|
||||
IChangeRequestPatchVariant,
|
||||
IChangeRequestUpdateSegment,
|
||||
IChangeRequestUpdateStrategy,
|
||||
} from 'component/changeRequest/changeRequest.types';
|
||||
import { useUiFlag } from 'hooks/useUiFlag';
|
||||
import { ISegment } from 'interfaces/segment';
|
||||
import { IFeatureStrategy } from 'interfaces/strategy';
|
||||
import {
|
||||
ChangesThatWouldBeOverwritten,
|
||||
getEnvVariantChangesThatWouldBeOverwritten,
|
||||
getStrategyChangesThatWouldBeOverwritten,
|
||||
getSegmentChangesThatWouldBeOverwritten,
|
||||
} from './strategy-change-diff-calculation';
|
||||
import { useEffect } from 'react';
|
||||
import { IFeatureVariant } from 'interfaces/featureToggle';
|
||||
import { ChangeRequestState } from 'component/changeRequest/changeRequest.types';
|
||||
import { ChangesThatWouldBeOverwritten } from './strategy-change-diff-calculation';
|
||||
|
||||
const ChangesToOverwriteContainer = styled(Box)(({ theme }) => ({
|
||||
color: theme.palette.warning.dark,
|
||||
@ -137,10 +122,19 @@ const DetailsTable: React.FC<{
|
||||
);
|
||||
};
|
||||
|
||||
const OverwriteWarning: React.FC<{
|
||||
export const OverwriteWarning: React.FC<{
|
||||
changeType: 'segment' | 'strategy' | 'environment variant configuration';
|
||||
changesThatWouldBeOverwritten: ChangesThatWouldBeOverwritten;
|
||||
}> = ({ changeType, changesThatWouldBeOverwritten }) => {
|
||||
changesThatWouldBeOverwritten: ChangesThatWouldBeOverwritten | null;
|
||||
changeRequestState: ChangeRequestState;
|
||||
}> = ({ changeType, changesThatWouldBeOverwritten, changeRequestState }) => {
|
||||
const changeRequestIsClosed = ['Applied', 'Cancelled', 'Rejected'].includes(
|
||||
changeRequestState,
|
||||
);
|
||||
|
||||
if (!changesThatWouldBeOverwritten || changeRequestIsClosed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ChangesToOverwriteContainer>
|
||||
<p>
|
||||
@ -159,74 +153,3 @@ const OverwriteWarning: React.FC<{
|
||||
</ChangesToOverwriteContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export const EnvVariantChangesToOverwrite: React.FC<{
|
||||
currentVariants?: IFeatureVariant[];
|
||||
change: IChangeRequestPatchVariant;
|
||||
}> = ({ change, currentVariants }) => {
|
||||
const checkForChanges = useUiFlag('changeRequestConflictHandling');
|
||||
const changesThatWouldBeOverwritten = checkForChanges
|
||||
? getEnvVariantChangesThatWouldBeOverwritten(currentVariants, change)
|
||||
: null;
|
||||
|
||||
if (!changesThatWouldBeOverwritten) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<OverwriteWarning
|
||||
changeType='environment variant configuration'
|
||||
changesThatWouldBeOverwritten={changesThatWouldBeOverwritten}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const SegmentChangesToOverwrite: React.FC<{
|
||||
currentSegment?: ISegment;
|
||||
change: IChangeRequestUpdateSegment;
|
||||
}> = ({ change, currentSegment }) => {
|
||||
const checkForChanges = useUiFlag('changeRequestConflictHandling');
|
||||
const changesThatWouldBeOverwritten = checkForChanges
|
||||
? getSegmentChangesThatWouldBeOverwritten(currentSegment, change)
|
||||
: null;
|
||||
|
||||
if (!changesThatWouldBeOverwritten) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<OverwriteWarning
|
||||
changeType='segment'
|
||||
changesThatWouldBeOverwritten={changesThatWouldBeOverwritten}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const StrategyChangesToOverwrite: React.FC<{
|
||||
currentStrategy?: IFeatureStrategy;
|
||||
change: IChangeRequestUpdateStrategy;
|
||||
}> = ({ change, currentStrategy }) => {
|
||||
const checkForChanges = useUiFlag('changeRequestConflictHandling');
|
||||
const changesThatWouldBeOverwritten = checkForChanges
|
||||
? getStrategyChangesThatWouldBeOverwritten(currentStrategy, change)
|
||||
: null;
|
||||
const { registerWillOverwriteStrategyChanges } =
|
||||
useChangeRequestPlausibleContext();
|
||||
|
||||
useEffect(() => {
|
||||
if (changesThatWouldBeOverwritten) {
|
||||
registerWillOverwriteStrategyChanges();
|
||||
}
|
||||
}, [changesThatWouldBeOverwritten]);
|
||||
|
||||
if (!changesThatWouldBeOverwritten) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<OverwriteWarning
|
||||
changeType='strategy'
|
||||
changesThatWouldBeOverwritten={changesThatWouldBeOverwritten}
|
||||
/>
|
||||
);
|
||||
};
|
@ -170,12 +170,14 @@ export const FeatureChange: FC<{
|
||||
featureName={feature.name}
|
||||
environmentName={changeRequest.environment}
|
||||
projectId={changeRequest.project}
|
||||
changeRequestState={changeRequest.state}
|
||||
/>
|
||||
) : null}
|
||||
{change.action === 'patchVariant' && (
|
||||
<VariantPatch
|
||||
feature={feature.name}
|
||||
project={changeRequest.project}
|
||||
changeRequestState={changeRequest.state}
|
||||
environment={changeRequest.environment}
|
||||
change={change}
|
||||
actions={actions}
|
||||
|
@ -1,7 +1,10 @@
|
||||
import { FC, ReactNode } from 'react';
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
import { Box, Card, Typography, Link } from '@mui/material';
|
||||
import { ISegmentChange } from '../../../changeRequest.types';
|
||||
import {
|
||||
ChangeRequestState,
|
||||
ISegmentChange,
|
||||
} from '../../../changeRequest.types';
|
||||
import { SegmentChangeDetails } from './SegmentChangeDetails';
|
||||
import { ConflictWarning } from './ConflictWarning';
|
||||
|
||||
@ -9,12 +12,14 @@ interface ISegmentChangeProps {
|
||||
segmentChange: ISegmentChange;
|
||||
onNavigate?: () => void;
|
||||
actions: ReactNode;
|
||||
changeRequestState: ChangeRequestState;
|
||||
}
|
||||
|
||||
export const SegmentChange: FC<ISegmentChangeProps> = ({
|
||||
segmentChange,
|
||||
onNavigate,
|
||||
actions,
|
||||
changeRequestState,
|
||||
}) => (
|
||||
<Card
|
||||
elevation={0}
|
||||
@ -66,6 +71,10 @@ export const SegmentChange: FC<ISegmentChangeProps> = ({
|
||||
</Link>
|
||||
</Box>
|
||||
</Box>
|
||||
<SegmentChangeDetails change={segmentChange} actions={actions} />
|
||||
<SegmentChangeDetails
|
||||
change={segmentChange}
|
||||
actions={actions}
|
||||
changeRequestState={changeRequestState}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
|
@ -1,13 +1,14 @@
|
||||
import { VFC, FC, ReactNode } from 'react';
|
||||
import { Box, styled, Typography } from '@mui/material';
|
||||
import {
|
||||
ChangeRequestState,
|
||||
IChangeRequestDeleteSegment,
|
||||
IChangeRequestUpdateSegment,
|
||||
} from 'component/changeRequest/changeRequest.types';
|
||||
import { useSegment } from 'hooks/api/getters/useSegment/useSegment';
|
||||
import { SegmentDiff, SegmentTooltipLink } from '../../SegmentTooltipLink';
|
||||
import { ConstraintAccordionList } from 'component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList';
|
||||
import { SegmentChangesToOverwrite } from './StrategyChangeOverwriteWarning';
|
||||
import { ChangeOverwriteWarning } from './ChangeOverwriteWarning/ChangeOverwriteWarning';
|
||||
|
||||
const ChangeItemCreateEditWrapper = styled(Box)(({ theme }) => ({
|
||||
display: 'grid',
|
||||
@ -51,7 +52,8 @@ const SegmentContainer = styled(Box, {
|
||||
export const SegmentChangeDetails: VFC<{
|
||||
actions?: ReactNode;
|
||||
change: IChangeRequestUpdateSegment | IChangeRequestDeleteSegment;
|
||||
}> = ({ actions, change }) => {
|
||||
changeRequestState: ChangeRequestState;
|
||||
}> = ({ actions, change, changeRequestState }) => {
|
||||
const { segment: currentSegment } = useSegment(change.payload.id);
|
||||
|
||||
return (
|
||||
@ -78,9 +80,13 @@ export const SegmentChangeDetails: VFC<{
|
||||
)}
|
||||
{change.action === 'updateSegment' && (
|
||||
<>
|
||||
<SegmentChangesToOverwrite
|
||||
currentSegment={currentSegment}
|
||||
change={change}
|
||||
<ChangeOverwriteWarning
|
||||
data={{
|
||||
current: currentSegment,
|
||||
change,
|
||||
changeType: 'segment',
|
||||
}}
|
||||
changeRequestState={changeRequestState}
|
||||
/>
|
||||
<ChangeItemCreateEditWrapper>
|
||||
<ChangeItemInfo>
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
} from '../../StrategyTooltipLink/StrategyTooltipLink';
|
||||
import { StrategyExecution } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution';
|
||||
import {
|
||||
ChangeRequestState,
|
||||
IChangeRequestAddStrategy,
|
||||
IChangeRequestDeleteStrategy,
|
||||
IChangeRequestUpdateStrategy,
|
||||
@ -17,7 +18,7 @@ import { Badge } from 'component/common/Badge/Badge';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { flexRow } from 'themes/themeStyles';
|
||||
import { EnvironmentVariantsTable } from 'component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/EnvironmentVariantsTable/EnvironmentVariantsTable';
|
||||
import { StrategyChangesToOverwrite } from './StrategyChangeOverwriteWarning';
|
||||
import { ChangeOverwriteWarning } from './ChangeOverwriteWarning/ChangeOverwriteWarning';
|
||||
|
||||
export const ChangeItemWrapper = styled(Box)({
|
||||
display: 'flex',
|
||||
@ -120,7 +121,15 @@ export const StrategyChange: VFC<{
|
||||
environmentName: string;
|
||||
featureName: string;
|
||||
projectId: string;
|
||||
}> = ({ actions, change, featureName, environmentName, projectId }) => {
|
||||
changeRequestState: ChangeRequestState;
|
||||
}> = ({
|
||||
actions,
|
||||
change,
|
||||
featureName,
|
||||
environmentName,
|
||||
projectId,
|
||||
changeRequestState,
|
||||
}) => {
|
||||
const currentStrategy = useCurrentStrategy(
|
||||
change,
|
||||
projectId,
|
||||
@ -224,9 +233,13 @@ export const StrategyChange: VFC<{
|
||||
)}
|
||||
{change.action === 'updateStrategy' && (
|
||||
<>
|
||||
<StrategyChangesToOverwrite
|
||||
currentStrategy={currentStrategy}
|
||||
change={change}
|
||||
<ChangeOverwriteWarning
|
||||
data={{
|
||||
current: currentStrategy,
|
||||
change,
|
||||
changeType: 'strategy',
|
||||
}}
|
||||
changeRequestState={changeRequestState}
|
||||
/>
|
||||
<ChangeItemCreateEditDeleteWrapper>
|
||||
<ChangeItemInfo>
|
||||
|
@ -1,12 +1,15 @@
|
||||
import { Box, styled } from '@mui/material';
|
||||
import { IChangeRequestPatchVariant } from 'component/changeRequest/changeRequest.types';
|
||||
import {
|
||||
ChangeRequestState,
|
||||
IChangeRequestPatchVariant,
|
||||
} from 'component/changeRequest/changeRequest.types';
|
||||
import { Badge } from 'component/common/Badge/Badge';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { TooltipLink } from 'component/common/TooltipLink/TooltipLink';
|
||||
import { EnvironmentVariantsTable } from 'component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/EnvironmentVariantsTable/EnvironmentVariantsTable';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import { ReactNode } from 'react';
|
||||
import { EnvVariantChangesToOverwrite } from '../StrategyChangeOverwriteWarning';
|
||||
import { ChangeOverwriteWarning } from '../ChangeOverwriteWarning/ChangeOverwriteWarning';
|
||||
import { VariantDiff } from './VariantDiff';
|
||||
|
||||
const ChangeItemInfo = styled(Box)({
|
||||
@ -37,6 +40,7 @@ interface IVariantPatchProps {
|
||||
environment: string;
|
||||
change: IChangeRequestPatchVariant;
|
||||
actions?: ReactNode;
|
||||
changeRequestState: ChangeRequestState;
|
||||
}
|
||||
|
||||
export const VariantPatch = ({
|
||||
@ -45,6 +49,7 @@ export const VariantPatch = ({
|
||||
environment,
|
||||
change,
|
||||
actions,
|
||||
changeRequestState,
|
||||
}: IVariantPatchProps) => {
|
||||
const { feature: featureData } = useFeature(project, feature);
|
||||
|
||||
@ -54,9 +59,13 @@ export const VariantPatch = ({
|
||||
|
||||
return (
|
||||
<ChangeItemInfo>
|
||||
<EnvVariantChangesToOverwrite
|
||||
currentVariants={preData}
|
||||
change={change}
|
||||
<ChangeOverwriteWarning
|
||||
data={{
|
||||
current: preData,
|
||||
change,
|
||||
changeType: 'environment variant configuration',
|
||||
}}
|
||||
changeRequestState={changeRequestState}
|
||||
/>
|
||||
<StyledChangeHeader>
|
||||
<TooltipLink
|
||||
|
Loading…
Reference in New Issue
Block a user