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