mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-15 01:16:22 +02:00
chore: Playground Strategy Lists (#9510)
Continue the implementation of Playground strategy lists. This PR also adjusts some existing strategy container and list items to accomodate more use cases (such as this). The playground strategy execution component is still the old design. After (playground results):  After (env strategy list):  Before (env strategy list): 
This commit is contained in:
parent
4ddb8fe7d8
commit
732b7f342a
@ -4,7 +4,6 @@ import DragIndicator from '@mui/icons-material/DragIndicator';
|
||||
import { Box, IconButton, Typography, styled } from '@mui/material';
|
||||
import type { IFeatureStrategy } from 'interfaces/strategy';
|
||||
import { formatStrategyName } from 'utils/strategyNames';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import type { PlaygroundStrategySchema } from 'openapi';
|
||||
import { Badge } from '../Badge/Badge';
|
||||
import { Link } from 'react-router-dom';
|
||||
@ -16,16 +15,20 @@ type StrategyItemContainerProps = {
|
||||
onDragStart?: DragEventHandler<HTMLButtonElement>;
|
||||
onDragEnd?: DragEventHandler<HTMLButtonElement>;
|
||||
headerItemsRight?: ReactNode;
|
||||
headerItemsLeft?: ReactNode;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
const DragIcon = styled(IconButton)({
|
||||
const inlinePadding = 3;
|
||||
|
||||
const DragIcon = styled(IconButton)(({ theme }) => ({
|
||||
marginLeft: theme.spacing(-inlinePadding),
|
||||
padding: 0,
|
||||
cursor: 'inherit',
|
||||
transition: 'color 0.2s ease-in-out',
|
||||
});
|
||||
}));
|
||||
|
||||
const StyledHeaderContainer = styled('hgroup')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
@ -38,9 +41,14 @@ const StyledHeaderContainer = styled('hgroup')(({ theme }) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const StyledContainer = styled('article')({
|
||||
const StyledContainer = styled('article')(({ theme }) => ({
|
||||
background: 'inherit',
|
||||
});
|
||||
padding: theme.spacing(inlinePadding),
|
||||
paddingTop: theme.spacing(0.5),
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
rowGap: theme.spacing(0.5),
|
||||
}));
|
||||
|
||||
const StyledTruncator = styled(Truncator)(({ theme }) => ({
|
||||
fontSize: theme.typography.body1.fontSize,
|
||||
@ -49,102 +57,101 @@ const StyledTruncator = styled(Truncator)(({ theme }) => ({
|
||||
}));
|
||||
|
||||
const StyledHeader = styled('div', {
|
||||
shouldForwardProp: (prop) => prop !== 'draggable' && prop !== 'disabled',
|
||||
})<{ draggable: boolean; disabled: boolean }>(
|
||||
({ theme, draggable, disabled }) => ({
|
||||
padding: theme.spacing(0.5, 2),
|
||||
display: 'flex',
|
||||
gap: theme.spacing(1),
|
||||
alignItems: 'center',
|
||||
paddingLeft: draggable ? theme.spacing(1) : theme.spacing(2),
|
||||
color: disabled
|
||||
? theme.palette.text.secondary
|
||||
: theme.palette.text.primary,
|
||||
}),
|
||||
);
|
||||
shouldForwardProp: (prop) => prop !== 'disabled',
|
||||
})<{ disabled: boolean }>(({ theme, disabled }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
color: disabled ? theme.palette.text.secondary : theme.palette.text.primary,
|
||||
}));
|
||||
|
||||
const StyledHeaderInner = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: theme.spacing(1),
|
||||
}));
|
||||
|
||||
export const StrategyItemContainer: FC<StrategyItemContainerProps> = ({
|
||||
strategy,
|
||||
onDragStart,
|
||||
onDragEnd,
|
||||
headerItemsRight,
|
||||
headerItemsLeft,
|
||||
strategyHeaderLevel = 3,
|
||||
children,
|
||||
style = {},
|
||||
className,
|
||||
}) => {
|
||||
const StrategyHeaderLink: React.FC<{ children?: React.ReactNode }> =
|
||||
'links' in strategy // todo: revisit this when we get to playground, related to flag `flagOverviewRedesign`
|
||||
'links' in strategy
|
||||
? ({ children }) => <Link to={strategy.links.edit}>{children}</Link>
|
||||
: ({ children }) => <> {children} </>;
|
||||
|
||||
return (
|
||||
<Box sx={{ position: 'relative' }}>
|
||||
<StyledContainer style={style}>
|
||||
<StyledHeader
|
||||
draggable={Boolean(onDragStart)}
|
||||
disabled={Boolean(strategy?.disabled)}
|
||||
>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(onDragStart)}
|
||||
show={() => (
|
||||
<DragIcon
|
||||
draggable
|
||||
disableRipple
|
||||
size='small'
|
||||
onDragStart={onDragStart}
|
||||
onDragEnd={onDragEnd}
|
||||
sx={{ cursor: 'move' }}
|
||||
>
|
||||
<DragIndicator
|
||||
titleAccess='Drag to reorder'
|
||||
cursor='grab'
|
||||
sx={{ color: 'action.active' }}
|
||||
/>
|
||||
</DragIcon>
|
||||
)}
|
||||
/>
|
||||
<StrategyHeaderLink>
|
||||
<StyledHeaderContainer>
|
||||
{strategy.title ? (
|
||||
<>
|
||||
<p className='strategy-name'>
|
||||
<StyledContainer style={style} className={className}>
|
||||
<StyledHeader disabled={Boolean(strategy?.disabled)}>
|
||||
{onDragStart ? (
|
||||
<DragIcon
|
||||
draggable
|
||||
disableRipple
|
||||
size='small'
|
||||
onDragStart={onDragStart}
|
||||
onDragEnd={onDragEnd}
|
||||
sx={{ cursor: 'move' }}
|
||||
>
|
||||
<DragIndicator
|
||||
titleAccess='Drag to reorder'
|
||||
cursor='grab'
|
||||
sx={{ color: 'action.active' }}
|
||||
/>
|
||||
</DragIcon>
|
||||
) : null}
|
||||
<StyledHeaderInner>
|
||||
<StrategyHeaderLink>
|
||||
<StyledHeaderContainer>
|
||||
{strategy.title ? (
|
||||
<>
|
||||
<p className='strategy-name'>
|
||||
{formatStrategyName(
|
||||
String(strategy.name),
|
||||
)}
|
||||
:
|
||||
</p>
|
||||
<StyledTruncator
|
||||
component={`h${strategyHeaderLevel}`}
|
||||
>
|
||||
{strategy.title}
|
||||
</StyledTruncator>
|
||||
</>
|
||||
) : (
|
||||
<Typography
|
||||
className='strategy-name'
|
||||
component={`h${strategyHeaderLevel}`}
|
||||
>
|
||||
{formatStrategyName(
|
||||
String(strategy.name),
|
||||
)}
|
||||
:
|
||||
</p>
|
||||
<StyledTruncator
|
||||
component={`h${strategyHeaderLevel}`}
|
||||
>
|
||||
{strategy.title}
|
||||
</StyledTruncator>
|
||||
</>
|
||||
) : (
|
||||
<Typography
|
||||
className='strategy-name'
|
||||
component={`h${strategyHeaderLevel}`}
|
||||
>
|
||||
{formatStrategyName(String(strategy.name))}
|
||||
</Typography>
|
||||
)}
|
||||
</StyledHeaderContainer>
|
||||
</StrategyHeaderLink>
|
||||
</Typography>
|
||||
)}
|
||||
</StyledHeaderContainer>
|
||||
</StrategyHeaderLink>
|
||||
|
||||
{strategy.disabled ? (
|
||||
<Badge color='disabled'>Disabled</Badge>
|
||||
) : null}
|
||||
{strategy.disabled ? (
|
||||
<Badge color='disabled'>Disabled</Badge>
|
||||
) : null}
|
||||
{headerItemsLeft}
|
||||
</StyledHeaderInner>
|
||||
<Box
|
||||
sx={{
|
||||
marginLeft: 'auto',
|
||||
display: 'flex',
|
||||
minHeight: (theme) => theme.spacing(6),
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
{headerItemsRight}
|
||||
</Box>
|
||||
</StyledHeader>
|
||||
<Box sx={{ p: 2, pt: 0 }}>{children}</Box>
|
||||
<Box>{children}</Box>
|
||||
</StyledContainer>
|
||||
</Box>
|
||||
);
|
||||
|
@ -30,9 +30,7 @@ interface IEnvironmentAccordionBodyProps {
|
||||
}
|
||||
|
||||
const StyledAccordionBodyInnerContainer = styled('div')(({ theme }) => ({
|
||||
[theme.breakpoints.down(400)]: {
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
}));
|
||||
|
||||
export const StyledContentList = styled('ol')(({ theme }) => ({
|
||||
@ -44,6 +42,9 @@ export const StyledContentList = styled('ol')(({ theme }) => ({
|
||||
paddingBlock: theme.spacing(2.5),
|
||||
position: 'relative',
|
||||
},
|
||||
'& > li + li': {
|
||||
borderTop: `1px solid ${theme.palette.divider}`,
|
||||
},
|
||||
'&:not(li > &) > li:first-of-type': {
|
||||
// select first list elements in lists that are not directly nested
|
||||
// within other lists.
|
||||
@ -58,7 +59,6 @@ export const StyledContentList = styled('ol')(({ theme }) => ({
|
||||
export const StyledListItem = styled('li', {
|
||||
shouldForwardProp: (prop) => prop !== 'type',
|
||||
})<{ type?: 'release plan' | 'strategy' }>(({ theme, type }) => ({
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
background:
|
||||
type === 'release plan'
|
||||
? theme.palette.background.elevation2
|
||||
@ -290,34 +290,29 @@ export const EnvironmentAccordionBody = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<StyledAccordionBodyInnerContainer>
|
||||
<StyledContentList>
|
||||
{releasePlans.length > 0 ? (
|
||||
<>
|
||||
{releasePlans.map((plan) => (
|
||||
<StyledListItem
|
||||
type='release plan'
|
||||
key={plan.id}
|
||||
>
|
||||
<ReleasePlan
|
||||
plan={plan}
|
||||
environmentIsDisabled={isDisabled}
|
||||
/>
|
||||
</StyledListItem>
|
||||
))}
|
||||
{strategies.length > 0 ? (
|
||||
<li>
|
||||
<StrategySeparator />
|
||||
<Strategies />
|
||||
</li>
|
||||
) : null}
|
||||
</>
|
||||
) : strategies.length > 0 ? (
|
||||
<Strategies />
|
||||
) : null}
|
||||
</StyledContentList>
|
||||
</StyledAccordionBodyInnerContainer>
|
||||
</div>
|
||||
<StyledAccordionBodyInnerContainer>
|
||||
<StyledContentList>
|
||||
{releasePlans.length > 0 ? (
|
||||
<>
|
||||
{releasePlans.map((plan) => (
|
||||
<StyledListItem type='release plan' key={plan.id}>
|
||||
<ReleasePlan
|
||||
plan={plan}
|
||||
environmentIsDisabled={isDisabled}
|
||||
/>
|
||||
</StyledListItem>
|
||||
))}
|
||||
{strategies.length > 0 ? (
|
||||
<li>
|
||||
<StrategySeparator />
|
||||
<Strategies />
|
||||
</li>
|
||||
) : null}
|
||||
</>
|
||||
) : strategies.length > 0 ? (
|
||||
<Strategies />
|
||||
) : null}
|
||||
</StyledContentList>
|
||||
</StyledAccordionBodyInnerContainer>
|
||||
);
|
||||
};
|
||||
|
@ -18,22 +18,17 @@ const FeatureResultPopoverWrapper = styled('div')(({ theme }) => ({
|
||||
color: theme.palette.divider,
|
||||
}));
|
||||
|
||||
export const FeatureResultInfoPopoverCell = ({
|
||||
const LegacyFeatureResultInfoPopoverCell = ({
|
||||
feature,
|
||||
input,
|
||||
}: FeatureResultInfoPopoverCellProps) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const ref = useRef(null);
|
||||
const useNewStrategyDesign = useUiFlag('flagOverviewRedesign');
|
||||
|
||||
const togglePopover = () => {
|
||||
setOpen(!open);
|
||||
};
|
||||
|
||||
if (!feature) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<FeatureResultPopoverWrapper>
|
||||
<IconButton onClick={togglePopover}>
|
||||
@ -47,7 +42,7 @@ export const FeatureResultInfoPopoverCell = ({
|
||||
sx: (theme) => ({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
padding: theme.spacing(useNewStrategyDesign ? 4 : 6),
|
||||
padding: theme.spacing(6),
|
||||
width: 728,
|
||||
maxWidth: '100%',
|
||||
height: 'auto',
|
||||
@ -65,32 +60,96 @@ export const FeatureResultInfoPopoverCell = ({
|
||||
horizontal: 'left',
|
||||
}}
|
||||
>
|
||||
{useNewStrategyDesign ? (
|
||||
<>
|
||||
<FeatureDetails
|
||||
feature={feature}
|
||||
input={input}
|
||||
onClose={() => setOpen(false)}
|
||||
/>
|
||||
<PlaygroundResultFeatureStrategyList
|
||||
feature={feature}
|
||||
input={input}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<LegacyFeatureDetails
|
||||
feature={feature}
|
||||
input={input}
|
||||
onClose={() => setOpen(false)}
|
||||
/>
|
||||
<LegacyPlaygroundResultFeatureStrategyList
|
||||
feature={feature}
|
||||
input={input}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<LegacyFeatureDetails
|
||||
feature={feature}
|
||||
input={input}
|
||||
onClose={() => setOpen(false)}
|
||||
/>
|
||||
<LegacyPlaygroundResultFeatureStrategyList
|
||||
feature={feature}
|
||||
input={input}
|
||||
/>
|
||||
</Popover>
|
||||
</FeatureResultPopoverWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const DetailsPadding = styled('div')(({ theme }) => ({
|
||||
paddingInline: `var(--popover-inline-padding, ${theme.spacing(4)})`,
|
||||
paddingTop: theme.spacing(2.5),
|
||||
}));
|
||||
|
||||
export const NewFeatureResultInfoPopoverCell = ({
|
||||
feature,
|
||||
input,
|
||||
}: FeatureResultInfoPopoverCellProps) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const ref = useRef(null);
|
||||
|
||||
const togglePopover = () => {
|
||||
setOpen(!open);
|
||||
};
|
||||
|
||||
return (
|
||||
<FeatureResultPopoverWrapper>
|
||||
<IconButton onClick={togglePopover}>
|
||||
<InfoOutlined ref={ref} />
|
||||
</IconButton>
|
||||
<Popover
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
anchorEl={ref.current}
|
||||
PaperProps={{
|
||||
sx: (theme) => ({
|
||||
'--popover-inline-padding': theme.spacing(4),
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: 728,
|
||||
maxWidth: '100%',
|
||||
height: 'auto',
|
||||
gap: theme.spacing(3),
|
||||
overflowY: 'auto',
|
||||
backgroundColor: theme.palette.background.elevation1,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
}),
|
||||
}}
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'center',
|
||||
horizontal: 'left',
|
||||
}}
|
||||
>
|
||||
<DetailsPadding>
|
||||
<FeatureDetails
|
||||
feature={feature}
|
||||
input={input}
|
||||
onClose={() => setOpen(false)}
|
||||
/>
|
||||
</DetailsPadding>
|
||||
<PlaygroundResultFeatureStrategyList
|
||||
feature={feature}
|
||||
input={input}
|
||||
/>
|
||||
</Popover>
|
||||
</FeatureResultPopoverWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export const FeatureResultInfoPopoverCell = (
|
||||
props: FeatureResultInfoPopoverCellProps,
|
||||
) => {
|
||||
const useNewStrategyDesign = useUiFlag('flagOverviewRedesign');
|
||||
|
||||
if (!props.feature) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return useNewStrategyDesign ? (
|
||||
<NewFeatureResultInfoPopoverCell {...props} />
|
||||
) : (
|
||||
<LegacyFeatureResultInfoPopoverCell {...props} />
|
||||
);
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {
|
||||
PlaygroundResultStrategyLists,
|
||||
WrappedPlaygroundResultStrategyList,
|
||||
} from './StrategyList/playgroundResultStrategyLists';
|
||||
} from './StrategyList/LegacyPlaygroundResultStrategyLists';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import type { PlaygroundFeatureSchema, PlaygroundRequestSchema } from 'openapi';
|
||||
import { Alert } from '@mui/material';
|
||||
|
@ -24,7 +24,7 @@ const testCases = [
|
||||
hasUnsatisfiedDependency: true,
|
||||
} as PlaygroundFeatureSchema,
|
||||
expectedText:
|
||||
'If environment was enabled and parent dependencies were satisfied, then this feature flag would be TRUE with strategies evaluated like so:',
|
||||
'If the environment was enabled and parent dependencies were satisfied, then this feature flag would be TRUE with strategies evaluated like this:',
|
||||
},
|
||||
{
|
||||
name: 'Environment enabled and parent dependency not satisfied',
|
||||
@ -44,7 +44,7 @@ const testCases = [
|
||||
hasUnsatisfiedDependency: true,
|
||||
} as PlaygroundFeatureSchema,
|
||||
expectedText:
|
||||
'If parent dependencies were satisfied, then this feature flag would be TRUE with strategies evaluated like so:',
|
||||
'If parent dependencies were satisfied, then this feature flag would be TRUE with strategies evaluated like this:',
|
||||
},
|
||||
{
|
||||
name: 'Environment not enabled and parent dependency satisfied',
|
||||
@ -64,7 +64,7 @@ const testCases = [
|
||||
hasUnsatisfiedDependency: false,
|
||||
} as PlaygroundFeatureSchema,
|
||||
expectedText:
|
||||
'If environment was enabled, then this feature flag would be TRUE with strategies evaluated like so:',
|
||||
'If the environment was enabled, then this feature flag would be TRUE with strategies evaluated like this:',
|
||||
},
|
||||
{
|
||||
name: 'Has disabled strategies and is enabled in environment',
|
||||
|
@ -50,7 +50,7 @@ export const PlaygroundResultFeatureStrategyList = ({
|
||||
<PlaygroundResultStrategyLists
|
||||
strategies={enabledStrategies || []}
|
||||
input={input}
|
||||
titlePrefix={showDisabledStrategies ? 'Enabled' : ''}
|
||||
titlePrefix={showDisabledStrategies ? 'Enabled' : undefined}
|
||||
/>
|
||||
{showDisabledStrategies ? (
|
||||
<PlaygroundResultStrategyLists
|
||||
|
@ -0,0 +1,152 @@
|
||||
import { Fragment } from 'react';
|
||||
import { Alert, Box, styled, Typography } from '@mui/material';
|
||||
import type {
|
||||
PlaygroundStrategySchema,
|
||||
PlaygroundRequestSchema,
|
||||
PlaygroundFeatureSchema,
|
||||
} from 'openapi';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { FeatureStrategyItem } from './StrategyItem/LegacyFeatureStrategyItem';
|
||||
import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator';
|
||||
|
||||
const StyledAlertWrapper = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
padding: `0, 4px`,
|
||||
flexDirection: 'column',
|
||||
borderRadius: theme.shape.borderRadiusMedium,
|
||||
border: `1px solid ${theme.palette.warning.border}`,
|
||||
}));
|
||||
|
||||
const StyledListWrapper = styled('div')(({ theme }) => ({
|
||||
padding: theme.spacing(1, 0.5),
|
||||
}));
|
||||
|
||||
const StyledAlert = styled(Alert)(({ theme }) => ({
|
||||
border: '0!important',
|
||||
borderBottomLeftRadius: 0,
|
||||
borderBottomRightRadius: 0,
|
||||
borderBottom: `1px solid ${theme.palette.warning.border}!important`,
|
||||
}));
|
||||
|
||||
interface PlaygroundResultStrategyListProps {
|
||||
strategies: PlaygroundStrategySchema[];
|
||||
input?: PlaygroundRequestSchema;
|
||||
titlePrefix?: string;
|
||||
infoText?: string;
|
||||
}
|
||||
|
||||
const StyledSubtitle = styled(Typography)(({ theme }) => ({
|
||||
margin: theme.spacing(2, 1, 2, 0),
|
||||
color: 'text.secondary',
|
||||
}));
|
||||
|
||||
export const PlaygroundResultStrategyLists = ({
|
||||
strategies,
|
||||
input,
|
||||
titlePrefix,
|
||||
infoText,
|
||||
}: PlaygroundResultStrategyListProps) => (
|
||||
<ConditionallyRender
|
||||
condition={strategies.length > 0}
|
||||
show={
|
||||
<>
|
||||
<StyledSubtitle variant={'subtitle1'}>{`${
|
||||
titlePrefix
|
||||
? titlePrefix.concat(' strategies')
|
||||
: 'Strategies'
|
||||
} (${strategies?.length})`}</StyledSubtitle>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(infoText)}
|
||||
show={
|
||||
<StyledSubtitle variant={'subtitle2'}>
|
||||
{infoText}
|
||||
</StyledSubtitle>
|
||||
}
|
||||
/>
|
||||
<Box sx={{ width: '100%' }}>
|
||||
{strategies?.map((strategy, index) => (
|
||||
<Fragment key={strategy.id}>
|
||||
<ConditionallyRender
|
||||
condition={index > 0}
|
||||
show={<StrategySeparator text='OR' />}
|
||||
/>
|
||||
<FeatureStrategyItem
|
||||
key={strategy.id}
|
||||
strategy={strategy}
|
||||
index={index}
|
||||
input={input}
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
</Box>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
interface IWrappedPlaygroundResultStrategyListProps {
|
||||
feature: PlaygroundFeatureSchema;
|
||||
input?: PlaygroundRequestSchema;
|
||||
}
|
||||
|
||||
const resolveHintText = (feature: PlaygroundFeatureSchema) => {
|
||||
if (
|
||||
feature.hasUnsatisfiedDependency &&
|
||||
!feature.isEnabledInCurrentEnvironment
|
||||
) {
|
||||
return 'If the environment was enabled and parent dependencies were satisfied';
|
||||
}
|
||||
if (feature.hasUnsatisfiedDependency) {
|
||||
return 'If parent dependencies were satisfied';
|
||||
}
|
||||
if (!feature.isEnabledInCurrentEnvironment) {
|
||||
return 'If the environment was enabled';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
export const WrappedPlaygroundResultStrategyList = ({
|
||||
feature,
|
||||
input,
|
||||
}: IWrappedPlaygroundResultStrategyListProps) => {
|
||||
const enabledStrategies = feature.strategies?.data?.filter(
|
||||
(strategy) => !strategy.disabled,
|
||||
);
|
||||
const disabledStrategies = feature.strategies?.data?.filter(
|
||||
(strategy) => strategy.disabled,
|
||||
);
|
||||
|
||||
const showDisabledStrategies = disabledStrategies?.length > 0;
|
||||
|
||||
return (
|
||||
<StyledAlertWrapper sx={{ pb: 1, mt: 2 }}>
|
||||
<StyledAlert severity={'info'} color={'warning'}>
|
||||
{resolveHintText(feature)}, then this feature flag would be{' '}
|
||||
{feature.strategies?.result ? 'TRUE' : 'FALSE'} with strategies
|
||||
evaluated like this:{' '}
|
||||
</StyledAlert>
|
||||
<StyledListWrapper sx={{ p: 2.5 }}>
|
||||
<PlaygroundResultStrategyLists
|
||||
strategies={enabledStrategies || []}
|
||||
input={input}
|
||||
titlePrefix={showDisabledStrategies ? 'Enabled' : ''}
|
||||
/>
|
||||
</StyledListWrapper>
|
||||
<ConditionallyRender
|
||||
condition={showDisabledStrategies}
|
||||
show={
|
||||
<StyledListWrapper sx={{ p: 2.5 }}>
|
||||
<PlaygroundResultStrategyLists
|
||||
strategies={disabledStrategies}
|
||||
input={input}
|
||||
titlePrefix={'Disabled'}
|
||||
infoText={
|
||||
'Disabled strategies are not evaluated for the overall result.'
|
||||
}
|
||||
/>
|
||||
</StyledListWrapper>
|
||||
}
|
||||
/>
|
||||
</StyledAlertWrapper>
|
||||
);
|
||||
};
|
@ -8,18 +8,18 @@ import { StrategyExecution } from './StrategyExecution/StrategyExecution';
|
||||
import { objectId } from 'utils/objectId';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { DisabledStrategyExecution } from './StrategyExecution/DisabledStrategyExecution';
|
||||
import { StrategyItemContainer } from 'component/common/StrategyItemContainer/LegacyStrategyItemContainer';
|
||||
import { StrategyItemContainer } from 'component/common/StrategyItemContainer/StrategyItemContainer';
|
||||
|
||||
interface IFeatureStrategyItemProps {
|
||||
strategy: PlaygroundStrategySchema;
|
||||
index: number;
|
||||
input?: PlaygroundRequestSchema;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const FeatureStrategyItem = ({
|
||||
strategy,
|
||||
input,
|
||||
index,
|
||||
className,
|
||||
}: IFeatureStrategyItemProps) => {
|
||||
const { result } = strategy;
|
||||
const theme = useTheme();
|
||||
@ -33,22 +33,19 @@ export const FeatureStrategyItem = ({
|
||||
|
||||
return (
|
||||
<StrategyItemContainer
|
||||
style={{
|
||||
borderColor:
|
||||
result.enabled && result.evaluationStatus === 'complete'
|
||||
? theme.palette.success.main
|
||||
: 'none',
|
||||
}}
|
||||
strategy={{ ...strategy, id: `${objectId(strategy)}` }}
|
||||
orderNumber={index + 1}
|
||||
actions={
|
||||
strategyHeaderLevel={4}
|
||||
className={className}
|
||||
headerItemsLeft={
|
||||
<PlaygroundResultChip
|
||||
tabindex={-1}
|
||||
showIcon={false}
|
||||
enabled={result.enabled}
|
||||
label={label}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{/* todo: use new strategy execution components */}
|
||||
<ConditionallyRender
|
||||
condition={Boolean(strategy.disabled)}
|
||||
show={
|
||||
|
@ -0,0 +1,70 @@
|
||||
import { useTheme } from '@mui/material';
|
||||
import { PlaygroundResultChip } from '../../../../PlaygroundResultChip/PlaygroundResultChip';
|
||||
import type {
|
||||
PlaygroundStrategySchema,
|
||||
PlaygroundRequestSchema,
|
||||
} from 'openapi';
|
||||
import { StrategyExecution } from './StrategyExecution/StrategyExecution';
|
||||
import { objectId } from 'utils/objectId';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { DisabledStrategyExecution } from './StrategyExecution/DisabledStrategyExecution';
|
||||
import { StrategyItemContainer } from 'component/common/StrategyItemContainer/LegacyStrategyItemContainer';
|
||||
|
||||
interface IFeatureStrategyItemProps {
|
||||
strategy: PlaygroundStrategySchema;
|
||||
index: number;
|
||||
input?: PlaygroundRequestSchema;
|
||||
}
|
||||
|
||||
export const FeatureStrategyItem = ({
|
||||
strategy,
|
||||
input,
|
||||
index,
|
||||
}: IFeatureStrategyItemProps) => {
|
||||
const { result } = strategy;
|
||||
const theme = useTheme();
|
||||
const label =
|
||||
result.evaluationStatus === 'incomplete' ||
|
||||
result.evaluationStatus === 'unevaluated'
|
||||
? 'Unevaluated'
|
||||
: result.enabled
|
||||
? 'True'
|
||||
: 'False';
|
||||
|
||||
return (
|
||||
<StrategyItemContainer
|
||||
style={{
|
||||
borderColor:
|
||||
result.enabled && result.evaluationStatus === 'complete'
|
||||
? theme.palette.success.main
|
||||
: 'none',
|
||||
}}
|
||||
strategy={{ ...strategy, id: `${objectId(strategy)}` }}
|
||||
orderNumber={index + 1}
|
||||
actions={
|
||||
<PlaygroundResultChip
|
||||
showIcon={false}
|
||||
enabled={result.enabled}
|
||||
label={label}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(strategy.disabled)}
|
||||
show={
|
||||
<DisabledStrategyExecution
|
||||
strategyResult={strategy}
|
||||
input={input}
|
||||
/>
|
||||
}
|
||||
elseShow={
|
||||
<StrategyExecution
|
||||
strategyResult={strategy}
|
||||
input={input}
|
||||
percentageFill={theme.palette.background.elevation2}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</StrategyItemContainer>
|
||||
);
|
||||
};
|
@ -1,13 +1,16 @@
|
||||
import { Fragment } from 'react';
|
||||
import { Alert, Box, styled, Typography } from '@mui/material';
|
||||
import { Alert, styled } from '@mui/material';
|
||||
import type {
|
||||
PlaygroundStrategySchema,
|
||||
PlaygroundRequestSchema,
|
||||
PlaygroundFeatureSchema,
|
||||
} from 'openapi';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import {
|
||||
StyledContentList,
|
||||
StyledListItem,
|
||||
} from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody';
|
||||
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
|
||||
import { FeatureStrategyItem } from './StrategyItem/FeatureStrategyItem';
|
||||
import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator';
|
||||
|
||||
const StyledAlertWrapper = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
@ -31,13 +34,28 @@ const StyledAlert = styled(Alert)(({ theme }) => ({
|
||||
interface PlaygroundResultStrategyListProps {
|
||||
strategies: PlaygroundStrategySchema[];
|
||||
input?: PlaygroundRequestSchema;
|
||||
titlePrefix?: string;
|
||||
titlePrefix?: 'Enabled' | 'Disabled';
|
||||
infoText?: string;
|
||||
}
|
||||
const StyledHeaderGroup = styled('hgroup')(({ theme }) => ({
|
||||
paddingInline: `var(--popover-inline-padding, ${theme.spacing(4)})`,
|
||||
paddingBottom: theme.spacing(2),
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
}));
|
||||
|
||||
const StyledSubtitle = styled(Typography)(({ theme }) => ({
|
||||
margin: theme.spacing(2, 1, 2, 0),
|
||||
color: 'text.secondary',
|
||||
const StyledListTitle = styled('h4')(({ theme }) => ({
|
||||
fontWeight: 'normal',
|
||||
fontSize: theme.typography.body1.fontSize,
|
||||
margin: 0,
|
||||
}));
|
||||
|
||||
const StyledListTitleDescription = styled('p')(({ theme }) => ({
|
||||
fontWeight: 'bold',
|
||||
fontSize: theme.typography.body2.fontSize,
|
||||
}));
|
||||
|
||||
const StyledFeatureStrategyItem = styled(FeatureStrategyItem)(({ theme }) => ({
|
||||
paddingInline: `var(--popover-inline-padding, ${theme.spacing(4)})`,
|
||||
}));
|
||||
|
||||
export const PlaygroundResultStrategyLists = ({
|
||||
@ -45,44 +63,39 @@ export const PlaygroundResultStrategyLists = ({
|
||||
input,
|
||||
titlePrefix,
|
||||
infoText,
|
||||
}: PlaygroundResultStrategyListProps) => (
|
||||
<ConditionallyRender
|
||||
condition={strategies.length > 0}
|
||||
show={
|
||||
<>
|
||||
<StyledSubtitle variant={'subtitle1'}>{`${
|
||||
}: PlaygroundResultStrategyListProps) => {
|
||||
if (strategies.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<StyledHeaderGroup>
|
||||
<StyledListTitle>{`${
|
||||
titlePrefix
|
||||
? titlePrefix.concat(' strategies')
|
||||
: 'Strategies'
|
||||
} (${strategies?.length})`}</StyledSubtitle>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(infoText)}
|
||||
show={
|
||||
<StyledSubtitle variant={'subtitle2'}>
|
||||
{infoText}
|
||||
</StyledSubtitle>
|
||||
}
|
||||
/>
|
||||
<Box sx={{ width: '100%' }}>
|
||||
{strategies?.map((strategy, index) => (
|
||||
<Fragment key={strategy.id}>
|
||||
<ConditionallyRender
|
||||
condition={index > 0}
|
||||
show={<StrategySeparator text='OR' />}
|
||||
/>
|
||||
<FeatureStrategyItem
|
||||
key={strategy.id}
|
||||
strategy={strategy}
|
||||
index={index}
|
||||
input={input}
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
</Box>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
} (${strategies?.length})`}</StyledListTitle>
|
||||
{infoText ? (
|
||||
<StyledListTitleDescription>
|
||||
{infoText}
|
||||
</StyledListTitleDescription>
|
||||
) : null}
|
||||
</StyledHeaderGroup>
|
||||
<StyledContentList>
|
||||
{strategies?.map((strategy, index) => (
|
||||
<StyledListItem key={strategy.id}>
|
||||
{index > 0 ? <StrategySeparator /> : ''}
|
||||
<StyledFeatureStrategyItem
|
||||
strategy={strategy}
|
||||
input={input}
|
||||
/>
|
||||
</StyledListItem>
|
||||
))}
|
||||
</StyledContentList>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface IWrappedPlaygroundResultStrategyListProps {
|
||||
feature: PlaygroundFeatureSchema;
|
||||
@ -94,13 +107,13 @@ const resolveHintText = (feature: PlaygroundFeatureSchema) => {
|
||||
feature.hasUnsatisfiedDependency &&
|
||||
!feature.isEnabledInCurrentEnvironment
|
||||
) {
|
||||
return 'If environment was enabled and parent dependencies were satisfied';
|
||||
return 'If the environment was enabled and parent dependencies were satisfied';
|
||||
}
|
||||
if (feature.hasUnsatisfiedDependency) {
|
||||
return 'If parent dependencies were satisfied';
|
||||
}
|
||||
if (!feature.isEnabledInCurrentEnvironment) {
|
||||
return 'If environment was enabled';
|
||||
return 'If the environment was enabled';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
@ -123,19 +136,19 @@ export const WrappedPlaygroundResultStrategyList = ({
|
||||
<StyledAlert severity={'info'} color={'warning'}>
|
||||
{resolveHintText(feature)}, then this feature flag would be{' '}
|
||||
{feature.strategies?.result ? 'TRUE' : 'FALSE'} with strategies
|
||||
evaluated like so:{' '}
|
||||
evaluated like this:{' '}
|
||||
</StyledAlert>
|
||||
<StyledListWrapper sx={{ p: 2.5 }}>
|
||||
<StyledListWrapper>
|
||||
<PlaygroundResultStrategyLists
|
||||
strategies={enabledStrategies || []}
|
||||
input={input}
|
||||
titlePrefix={showDisabledStrategies ? 'Enabled' : ''}
|
||||
titlePrefix={showDisabledStrategies ? 'Enabled' : undefined}
|
||||
/>
|
||||
</StyledListWrapper>
|
||||
<ConditionallyRender
|
||||
condition={showDisabledStrategies}
|
||||
show={
|
||||
<StyledListWrapper sx={{ p: 2.5 }}>
|
||||
<StyledListWrapper>
|
||||
<PlaygroundResultStrategyLists
|
||||
strategies={disabledStrategies}
|
||||
input={input}
|
||||
|
Loading…
Reference in New Issue
Block a user