mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-09 01:17:06 +02:00
chore(1-3422): playground strategies list (#9504)
Initial rough work on adapting the playground strategies to the new designs. This PR primarily splits components into Legacy files and adds new replacements. There are *some* updates (including spacing and text color), but nothing juicy yet. However, I wanted to get this in now, before this PR grows even bigger.
This commit is contained in:
parent
9547fd962f
commit
a064672635
@ -1,7 +1,8 @@
|
|||||||
import { screen } from '@testing-library/react';
|
import { screen } from '@testing-library/react';
|
||||||
import { render } from 'utils/testRenderer';
|
import { render } from 'utils/testRenderer';
|
||||||
import { FeatureDetails } from './FeatureDetails';
|
import { FeatureDetails as LegacyFeatureDetails } from './LegacyFeatureDetails';
|
||||||
import type { PlaygroundFeatureSchema, PlaygroundRequestSchema } from 'openapi';
|
import type { PlaygroundFeatureSchema, PlaygroundRequestSchema } from 'openapi';
|
||||||
|
import { FeatureDetails } from './FeatureDetails';
|
||||||
|
|
||||||
const testCases = [
|
const testCases = [
|
||||||
{
|
{
|
||||||
@ -10,7 +11,7 @@ const testCases = [
|
|||||||
hasUnsatisfiedDependency: true,
|
hasUnsatisfiedDependency: true,
|
||||||
isEnabledInCurrentEnvironment: false,
|
isEnabledInCurrentEnvironment: false,
|
||||||
} as PlaygroundFeatureSchema,
|
} as PlaygroundFeatureSchema,
|
||||||
expectedText1: 'This feature flag is False in development because',
|
expectedText1: /This feature flag is False in development because/,
|
||||||
expectedText2:
|
expectedText2:
|
||||||
'parent dependency is not satisfied and the environment is disabled',
|
'parent dependency is not satisfied and the environment is disabled',
|
||||||
},
|
},
|
||||||
@ -20,7 +21,7 @@ const testCases = [
|
|||||||
hasUnsatisfiedDependency: true,
|
hasUnsatisfiedDependency: true,
|
||||||
isEnabledInCurrentEnvironment: true,
|
isEnabledInCurrentEnvironment: true,
|
||||||
} as PlaygroundFeatureSchema,
|
} as PlaygroundFeatureSchema,
|
||||||
expectedText1: 'This feature flag is False in development because',
|
expectedText1: /This feature flag is False in development because/,
|
||||||
expectedText2: 'parent dependency is not satisfied',
|
expectedText2: 'parent dependency is not satisfied',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -29,7 +30,7 @@ const testCases = [
|
|||||||
hasUnsatisfiedDependency: false,
|
hasUnsatisfiedDependency: false,
|
||||||
isEnabledInCurrentEnvironment: false,
|
isEnabledInCurrentEnvironment: false,
|
||||||
} as PlaygroundFeatureSchema,
|
} as PlaygroundFeatureSchema,
|
||||||
expectedText1: 'This feature flag is False in development because',
|
expectedText1: /This feature flag is False in development because/,
|
||||||
expectedText2: 'the environment is disabled',
|
expectedText2: 'the environment is disabled',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -37,7 +38,7 @@ const testCases = [
|
|||||||
feature: {
|
feature: {
|
||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
} as PlaygroundFeatureSchema,
|
} as PlaygroundFeatureSchema,
|
||||||
expectedText1: 'This feature flag is True in development because',
|
expectedText1: /This feature flag is True in development because/,
|
||||||
expectedText2: 'at least one strategy is True',
|
expectedText2: 'at least one strategy is True',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -48,7 +49,7 @@ const testCases = [
|
|||||||
data: [{ name: 'custom' }],
|
data: [{ name: 'custom' }],
|
||||||
},
|
},
|
||||||
} as PlaygroundFeatureSchema,
|
} as PlaygroundFeatureSchema,
|
||||||
expectedText1: 'This feature flag is Unknown in development because',
|
expectedText1: /This feature flag is Unknown in development because/,
|
||||||
expectedText2: 'no strategies could be fully evaluated',
|
expectedText2: 'no strategies could be fully evaluated',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -59,7 +60,7 @@ const testCases = [
|
|||||||
data: [{ name: 'custom' }, { name: 'default' }],
|
data: [{ name: 'custom' }, { name: 'default' }],
|
||||||
},
|
},
|
||||||
} as PlaygroundFeatureSchema,
|
} as PlaygroundFeatureSchema,
|
||||||
expectedText1: 'This feature flag is Unknown in development because',
|
expectedText1: /This feature flag is Unknown in development because/,
|
||||||
expectedText2: 'not all strategies could be fully evaluated',
|
expectedText2: 'not all strategies could be fully evaluated',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -70,12 +71,29 @@ const testCases = [
|
|||||||
data: [{ name: 'default' }],
|
data: [{ name: 'default' }],
|
||||||
},
|
},
|
||||||
} as PlaygroundFeatureSchema,
|
} as PlaygroundFeatureSchema,
|
||||||
expectedText1: 'This feature flag is False in development because',
|
expectedText1: /This feature flag is False in development because/,
|
||||||
expectedText2:
|
expectedText2:
|
||||||
'all strategies are either False or could not be fully evaluated',
|
'all strategies are either False or could not be fully evaluated',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
testCases.forEach(({ name, feature, expectedText1, expectedText2 }) => {
|
||||||
|
test(`${name} (legacy)`, async () => {
|
||||||
|
render(
|
||||||
|
<LegacyFeatureDetails
|
||||||
|
feature={feature}
|
||||||
|
input={
|
||||||
|
{ environment: 'development' } as PlaygroundRequestSchema
|
||||||
|
}
|
||||||
|
onClose={() => {}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
await screen.findByText(expectedText1);
|
||||||
|
await screen.findByText(expectedText2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
testCases.forEach(({ name, feature, expectedText1, expectedText2 }) => {
|
testCases.forEach(({ name, feature, expectedText1, expectedText2 }) => {
|
||||||
test(name, async () => {
|
test(name, async () => {
|
||||||
render(
|
render(
|
||||||
|
@ -1,45 +1,32 @@
|
|||||||
import type { PlaygroundFeatureSchema, PlaygroundRequestSchema } from 'openapi';
|
import type { PlaygroundFeatureSchema, PlaygroundRequestSchema } from 'openapi';
|
||||||
import { Alert, IconButton, Typography, useTheme, styled } from '@mui/material';
|
import { Alert, Typography, useTheme, styled, IconButton } from '@mui/material';
|
||||||
import { PlaygroundResultChip } from '../../PlaygroundResultChip/PlaygroundResultChip';
|
import { PlaygroundResultChip } from '../../PlaygroundResultChip/PlaygroundResultChip';
|
||||||
import CloseOutlined from '@mui/icons-material/CloseOutlined';
|
import CloseOutlined from '@mui/icons-material/CloseOutlined';
|
||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
|
||||||
import {
|
import {
|
||||||
checkForEmptyValues,
|
checkForEmptyValues,
|
||||||
hasCustomStrategies,
|
hasCustomStrategies,
|
||||||
hasOnlyCustomStrategies,
|
hasOnlyCustomStrategies,
|
||||||
} from './helpers';
|
} from './helpers';
|
||||||
|
|
||||||
const StyledDivWrapper = styled('div')({
|
const HeaderRow = styled('div')({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
});
|
});
|
||||||
|
|
||||||
const StyledDivTitleRow = styled('div')(({ theme }) => ({
|
const HeaderGroup = styled('hgroup')(({ theme }) => ({
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: theme.spacing(1.5),
|
gap: theme.spacing(1.5),
|
||||||
marginTop: theme.spacing(1.5),
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledDivAlertRow = styled('div')(({ theme }) => ({
|
const StyledTypographyName = styled('h3')(({ theme }) => ({
|
||||||
margin: theme.spacing(1, 0),
|
fontWeight: 'bold',
|
||||||
|
fontSize: theme.typography.subtitle1.fontSize,
|
||||||
|
margin: 0,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledDivDescriptionRow = styled('div')(({ theme }) => ({
|
|
||||||
margin: theme.spacing(1, 0.5),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledTypographyName = styled(Typography)(({ theme }) => ({
|
|
||||||
fontWeight: 600,
|
|
||||||
padding: theme.spacing(0.5),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledIconButton = styled(IconButton)({
|
|
||||||
textAlign: 'right',
|
|
||||||
});
|
|
||||||
|
|
||||||
interface PlaygroundFeatureResultDetailsProps {
|
interface PlaygroundFeatureResultDetailsProps {
|
||||||
feature: PlaygroundFeatureSchema;
|
feature: PlaygroundFeatureSchema;
|
||||||
input?: PlaygroundRequestSchema;
|
input?: PlaygroundRequestSchema;
|
||||||
@ -57,7 +44,7 @@ export const FeatureDetails = ({
|
|||||||
return [
|
return [
|
||||||
`This feature flag is True in ${input?.environment} because `,
|
`This feature flag is True in ${input?.environment} because `,
|
||||||
'at least one strategy is True',
|
'at least one strategy is True',
|
||||||
theme.palette.success.main,
|
theme.palette.success.contrastText,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -67,7 +54,7 @@ export const FeatureDetails = ({
|
|||||||
return [
|
return [
|
||||||
`This feature flag is False in ${input?.environment} because `,
|
`This feature flag is False in ${input?.environment} because `,
|
||||||
'parent dependency is not satisfied and the environment is disabled',
|
'parent dependency is not satisfied and the environment is disabled',
|
||||||
theme.palette.error.main,
|
theme.palette.error.contrastText,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,35 +62,35 @@ export const FeatureDetails = ({
|
|||||||
return [
|
return [
|
||||||
`This feature flag is False in ${input?.environment} because `,
|
`This feature flag is False in ${input?.environment} because `,
|
||||||
'the environment is disabled',
|
'the environment is disabled',
|
||||||
theme.palette.error.main,
|
theme.palette.error.contrastText,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (hasOnlyCustomStrategies(feature))
|
if (hasOnlyCustomStrategies(feature))
|
||||||
return [
|
return [
|
||||||
`This feature flag is Unknown in ${input?.environment} because `,
|
`This feature flag is Unknown in ${input?.environment} because `,
|
||||||
'no strategies could be fully evaluated',
|
'no strategies could be fully evaluated',
|
||||||
theme.palette.warning.main,
|
theme.palette.warning.contrastText,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (hasCustomStrategies(feature))
|
if (hasCustomStrategies(feature))
|
||||||
return [
|
return [
|
||||||
`This feature flag is Unknown in ${input?.environment} because `,
|
`This feature flag is Unknown in ${input?.environment} because `,
|
||||||
'not all strategies could be fully evaluated',
|
'not all strategies could be fully evaluated',
|
||||||
theme.palette.warning.main,
|
theme.palette.warning.contrastText,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (feature.hasUnsatisfiedDependency) {
|
if (feature.hasUnsatisfiedDependency) {
|
||||||
return [
|
return [
|
||||||
`This feature flag is False in ${input?.environment} because `,
|
`This feature flag is False in ${input?.environment} because `,
|
||||||
'parent dependency is not satisfied',
|
'parent dependency is not satisfied',
|
||||||
theme.palette.error.main,
|
theme.palette.error.contrastText,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
`This feature flag is False in ${input?.environment} because `,
|
`This feature flag is False in ${input?.environment} because `,
|
||||||
'all strategies are either False or could not be fully evaluated',
|
'all strategies are either False or could not be fully evaluated',
|
||||||
theme.palette.error.main,
|
theme.palette.error.contrastText,
|
||||||
];
|
];
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@ -124,61 +111,43 @@ export const FeatureDetails = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StyledDivWrapper>
|
<HeaderRow>
|
||||||
<StyledDivTitleRow>
|
<HeaderGroup>
|
||||||
<StyledTypographyName variant={'subtitle1'}>
|
<StyledTypographyName>{feature.name}</StyledTypographyName>
|
||||||
{feature.name}
|
<p>
|
||||||
</StyledTypographyName>
|
{feature?.strategies?.result !== 'unknown' ? (
|
||||||
<ConditionallyRender
|
|
||||||
condition={feature?.strategies?.result !== 'unknown'}
|
|
||||||
show={() => (
|
|
||||||
<PlaygroundResultChip
|
<PlaygroundResultChip
|
||||||
|
tabindex={-1}
|
||||||
enabled={feature.isEnabled}
|
enabled={feature.isEnabled}
|
||||||
label={feature.isEnabled ? 'True' : 'False'}
|
label={feature.isEnabled ? 'True' : 'False'}
|
||||||
/>
|
/>
|
||||||
)}
|
) : (
|
||||||
elseShow={() => (
|
|
||||||
<PlaygroundResultChip
|
<PlaygroundResultChip
|
||||||
|
tabindex={-1}
|
||||||
enabled='unknown'
|
enabled='unknown'
|
||||||
label={'Unknown'}
|
label={'Unknown'}
|
||||||
showIcon={false}
|
showIcon={false}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
</p>
|
||||||
</StyledDivTitleRow>
|
</HeaderGroup>
|
||||||
<StyledIconButton onClick={onCloseClick}>
|
<IconButton aria-label='Close' onClick={onCloseClick}>
|
||||||
<CloseOutlined />
|
<CloseOutlined />
|
||||||
</StyledIconButton>
|
</IconButton>
|
||||||
</StyledDivWrapper>
|
</HeaderRow>
|
||||||
<StyledDivDescriptionRow>
|
<p>
|
||||||
<Typography variant='body1' component='span'>
|
{description}
|
||||||
{description}
|
<Typography color={color} component='span'>
|
||||||
</Typography>
|
|
||||||
<Typography variant='subtitle1' color={color} component='span'>
|
|
||||||
{reason}
|
{reason}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant='body1' component='span'>
|
.
|
||||||
.
|
</p>
|
||||||
</Typography>
|
{noValueTxt ? <Alert color={'info'}>{noValueTxt}</Alert> : null}
|
||||||
</StyledDivDescriptionRow>
|
{customStrategiesTxt ? (
|
||||||
<ConditionallyRender
|
<Alert severity='warning' color='info'>
|
||||||
condition={Boolean(noValueTxt)}
|
{customStrategiesTxt}
|
||||||
show={
|
</Alert>
|
||||||
<StyledDivAlertRow>
|
) : null}
|
||||||
<Alert color={'info'}>{noValueTxt}</Alert>
|
|
||||||
</StyledDivAlertRow>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={Boolean(customStrategiesTxt)}
|
|
||||||
show={
|
|
||||||
<StyledDivAlertRow>
|
|
||||||
<Alert severity='warning' color='info'>
|
|
||||||
{customStrategiesTxt}
|
|
||||||
</Alert>
|
|
||||||
</StyledDivAlertRow>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,184 @@
|
|||||||
|
import type { PlaygroundFeatureSchema, PlaygroundRequestSchema } from 'openapi';
|
||||||
|
import { Alert, IconButton, Typography, useTheme, styled } from '@mui/material';
|
||||||
|
import { PlaygroundResultChip } from '../../PlaygroundResultChip/PlaygroundResultChip';
|
||||||
|
import CloseOutlined from '@mui/icons-material/CloseOutlined';
|
||||||
|
import type React from 'react';
|
||||||
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
|
import {
|
||||||
|
checkForEmptyValues,
|
||||||
|
hasCustomStrategies,
|
||||||
|
hasOnlyCustomStrategies,
|
||||||
|
} from './helpers';
|
||||||
|
|
||||||
|
const StyledDivWrapper = styled('div')({
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
width: '100%',
|
||||||
|
});
|
||||||
|
|
||||||
|
const StyledDivTitleRow = styled('div')(({ theme }) => ({
|
||||||
|
display: 'inline-flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: theme.spacing(1.5),
|
||||||
|
marginTop: theme.spacing(1.5),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledDivAlertRow = styled('div')(({ theme }) => ({
|
||||||
|
margin: theme.spacing(1, 0),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledDivDescriptionRow = styled('div')(({ theme }) => ({
|
||||||
|
margin: theme.spacing(1, 0.5),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledTypographyName = styled(Typography)(({ theme }) => ({
|
||||||
|
fontWeight: 600,
|
||||||
|
padding: theme.spacing(0.5),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledIconButton = styled(IconButton)({
|
||||||
|
textAlign: 'right',
|
||||||
|
});
|
||||||
|
|
||||||
|
interface PlaygroundFeatureResultDetailsProps {
|
||||||
|
feature: PlaygroundFeatureSchema;
|
||||||
|
input?: PlaygroundRequestSchema;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
export const FeatureDetails = ({
|
||||||
|
feature,
|
||||||
|
input,
|
||||||
|
onClose,
|
||||||
|
}: PlaygroundFeatureResultDetailsProps) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
const [description, reason, color] = (() => {
|
||||||
|
if (feature.isEnabled)
|
||||||
|
return [
|
||||||
|
`This feature flag is True in ${input?.environment} because `,
|
||||||
|
'at least one strategy is True',
|
||||||
|
theme.palette.success.main,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (
|
||||||
|
feature.hasUnsatisfiedDependency &&
|
||||||
|
!feature.isEnabledInCurrentEnvironment
|
||||||
|
) {
|
||||||
|
return [
|
||||||
|
`This feature flag is False in ${input?.environment} because `,
|
||||||
|
'parent dependency is not satisfied and the environment is disabled',
|
||||||
|
theme.palette.error.main,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!feature.isEnabledInCurrentEnvironment)
|
||||||
|
return [
|
||||||
|
`This feature flag is False in ${input?.environment} because `,
|
||||||
|
'the environment is disabled',
|
||||||
|
theme.palette.error.main,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (hasOnlyCustomStrategies(feature))
|
||||||
|
return [
|
||||||
|
`This feature flag is Unknown in ${input?.environment} because `,
|
||||||
|
'no strategies could be fully evaluated',
|
||||||
|
theme.palette.warning.main,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (hasCustomStrategies(feature))
|
||||||
|
return [
|
||||||
|
`This feature flag is Unknown in ${input?.environment} because `,
|
||||||
|
'not all strategies could be fully evaluated',
|
||||||
|
theme.palette.warning.main,
|
||||||
|
];
|
||||||
|
|
||||||
|
if (feature.hasUnsatisfiedDependency) {
|
||||||
|
return [
|
||||||
|
`This feature flag is False in ${input?.environment} because `,
|
||||||
|
'parent dependency is not satisfied',
|
||||||
|
theme.palette.error.main,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
`This feature flag is False in ${input?.environment} because `,
|
||||||
|
'all strategies are either False or could not be fully evaluated',
|
||||||
|
theme.palette.error.main,
|
||||||
|
];
|
||||||
|
})();
|
||||||
|
|
||||||
|
const noValueTxt = checkForEmptyValues(input?.context)
|
||||||
|
? 'You did not provide a value for your context field in step 2 of the configuration'
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const customStrategiesTxt = hasCustomStrategies(feature)
|
||||||
|
? `This feature uses custom strategies. Custom strategies can't be evaluated, so they will be marked accordingly.`
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const onCloseClick =
|
||||||
|
onClose &&
|
||||||
|
((event: React.SyntheticEvent) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
onClose();
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<StyledDivWrapper>
|
||||||
|
<StyledDivTitleRow>
|
||||||
|
<StyledTypographyName variant={'subtitle1'}>
|
||||||
|
{feature.name}
|
||||||
|
</StyledTypographyName>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={feature?.strategies?.result !== 'unknown'}
|
||||||
|
show={() => (
|
||||||
|
<PlaygroundResultChip
|
||||||
|
enabled={feature.isEnabled}
|
||||||
|
label={feature.isEnabled ? 'True' : 'False'}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
elseShow={() => (
|
||||||
|
<PlaygroundResultChip
|
||||||
|
enabled='unknown'
|
||||||
|
label={'Unknown'}
|
||||||
|
showIcon={false}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</StyledDivTitleRow>
|
||||||
|
<StyledIconButton onClick={onCloseClick}>
|
||||||
|
<CloseOutlined />
|
||||||
|
</StyledIconButton>
|
||||||
|
</StyledDivWrapper>
|
||||||
|
<StyledDivDescriptionRow>
|
||||||
|
<Typography variant='body1' component='span'>
|
||||||
|
{description}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant='subtitle1' color={color} component='span'>
|
||||||
|
{reason}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant='body1' component='span'>
|
||||||
|
.
|
||||||
|
</Typography>
|
||||||
|
</StyledDivDescriptionRow>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={Boolean(noValueTxt)}
|
||||||
|
show={
|
||||||
|
<StyledDivAlertRow>
|
||||||
|
<Alert color={'info'}>{noValueTxt}</Alert>
|
||||||
|
</StyledDivAlertRow>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={Boolean(customStrategiesTxt)}
|
||||||
|
show={
|
||||||
|
<StyledDivAlertRow>
|
||||||
|
<Alert severity='warning' color='info'>
|
||||||
|
{customStrategiesTxt}
|
||||||
|
</Alert>
|
||||||
|
</StyledDivAlertRow>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -2,8 +2,11 @@ import { useRef, useState } from 'react';
|
|||||||
import type { PlaygroundFeatureSchema, PlaygroundRequestSchema } from 'openapi';
|
import type { PlaygroundFeatureSchema, PlaygroundRequestSchema } from 'openapi';
|
||||||
import { IconButton, Popover, styled } from '@mui/material';
|
import { IconButton, Popover, styled } from '@mui/material';
|
||||||
import InfoOutlined from '@mui/icons-material/InfoOutlined';
|
import InfoOutlined from '@mui/icons-material/InfoOutlined';
|
||||||
|
import { FeatureDetails as LegacyFeatureDetails } from './FeatureDetails/LegacyFeatureDetails';
|
||||||
|
import { PlaygroundResultFeatureStrategyList as LegacyPlaygroundResultFeatureStrategyList } from './FeatureStrategyList/LegacyPlaygroundResultFeatureStrategyList';
|
||||||
|
import { useUiFlag } from 'hooks/useUiFlag';
|
||||||
import { FeatureDetails } from './FeatureDetails/FeatureDetails';
|
import { FeatureDetails } from './FeatureDetails/FeatureDetails';
|
||||||
import { PlaygroundResultFeatureStrategyList } from './FeatureStrategyList/PlaygroundResultFeatureStrategyList';
|
import { PlaygroundResultFeatureStrategyList } from './FeatureStrategyList/PlaygroundResultsFeatureStrategyList';
|
||||||
|
|
||||||
interface FeatureResultInfoPopoverCellProps {
|
interface FeatureResultInfoPopoverCellProps {
|
||||||
feature: PlaygroundFeatureSchema;
|
feature: PlaygroundFeatureSchema;
|
||||||
@ -21,6 +24,7 @@ export const FeatureResultInfoPopoverCell = ({
|
|||||||
}: FeatureResultInfoPopoverCellProps) => {
|
}: FeatureResultInfoPopoverCellProps) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
|
const useNewStrategyDesign = useUiFlag('flagOverviewRedesign');
|
||||||
|
|
||||||
const togglePopover = () => {
|
const togglePopover = () => {
|
||||||
setOpen(!open);
|
setOpen(!open);
|
||||||
@ -43,7 +47,7 @@ export const FeatureResultInfoPopoverCell = ({
|
|||||||
sx: (theme) => ({
|
sx: (theme) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
padding: theme.spacing(6),
|
padding: theme.spacing(useNewStrategyDesign ? 4 : 6),
|
||||||
width: 728,
|
width: 728,
|
||||||
maxWidth: '100%',
|
maxWidth: '100%',
|
||||||
height: 'auto',
|
height: 'auto',
|
||||||
@ -61,15 +65,31 @@ export const FeatureResultInfoPopoverCell = ({
|
|||||||
horizontal: 'left',
|
horizontal: 'left',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FeatureDetails
|
{useNewStrategyDesign ? (
|
||||||
feature={feature}
|
<>
|
||||||
input={input}
|
<FeatureDetails
|
||||||
onClose={() => setOpen(false)}
|
feature={feature}
|
||||||
/>
|
input={input}
|
||||||
<PlaygroundResultFeatureStrategyList
|
onClose={() => setOpen(false)}
|
||||||
feature={feature}
|
/>
|
||||||
input={input}
|
<PlaygroundResultFeatureStrategyList
|
||||||
/>
|
feature={feature}
|
||||||
|
input={input}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<LegacyFeatureDetails
|
||||||
|
feature={feature}
|
||||||
|
input={input}
|
||||||
|
onClose={() => setOpen(false)}
|
||||||
|
/>
|
||||||
|
<LegacyPlaygroundResultFeatureStrategyList
|
||||||
|
feature={feature}
|
||||||
|
input={input}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Popover>
|
</Popover>
|
||||||
</FeatureResultPopoverWrapper>
|
</FeatureResultPopoverWrapper>
|
||||||
);
|
);
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { screen } from '@testing-library/react';
|
import { screen } from '@testing-library/react';
|
||||||
import { render } from 'utils/testRenderer';
|
import { render } from 'utils/testRenderer';
|
||||||
import type { PlaygroundFeatureSchema, PlaygroundRequestSchema } from 'openapi';
|
import type { PlaygroundFeatureSchema, PlaygroundRequestSchema } from 'openapi';
|
||||||
import { PlaygroundResultFeatureStrategyList } from './PlaygroundResultFeatureStrategyList';
|
import { PlaygroundResultFeatureStrategyList as LegacyPlaygroundResultFeatureStrategyList } from './LegacyPlaygroundResultFeatureStrategyList';
|
||||||
import { vi } from 'vitest';
|
import { vi } from 'vitest';
|
||||||
|
import { PlaygroundResultFeatureStrategyList } from './PlaygroundResultsFeatureStrategyList';
|
||||||
|
|
||||||
const testCases = [
|
const testCases = [
|
||||||
{
|
{
|
||||||
@ -135,6 +136,21 @@ afterAll(() => {
|
|||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testCases.forEach(({ name, feature, expectedText }) => {
|
||||||
|
test(`${name} (legacy)`, async () => {
|
||||||
|
render(
|
||||||
|
<LegacyPlaygroundResultFeatureStrategyList
|
||||||
|
feature={feature}
|
||||||
|
input={
|
||||||
|
{ environment: 'development' } as PlaygroundRequestSchema
|
||||||
|
}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
await screen.findByText(expectedText);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
testCases.forEach(({ name, feature, expectedText }) => {
|
testCases.forEach(({ name, feature, expectedText }) => {
|
||||||
test(name, async () => {
|
test(name, async () => {
|
||||||
render(
|
render(
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
import {
|
||||||
|
PlaygroundResultStrategyLists,
|
||||||
|
WrappedPlaygroundResultStrategyList,
|
||||||
|
} from './StrategyList/playgroundResultStrategyLists';
|
||||||
|
import type { PlaygroundFeatureSchema, PlaygroundRequestSchema } from 'openapi';
|
||||||
|
import { Alert } from '@mui/material';
|
||||||
|
|
||||||
|
interface PlaygroundResultFeatureStrategyListProps {
|
||||||
|
feature: PlaygroundFeatureSchema;
|
||||||
|
input?: PlaygroundRequestSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PlaygroundResultFeatureStrategyList = ({
|
||||||
|
feature,
|
||||||
|
input,
|
||||||
|
}: PlaygroundResultFeatureStrategyListProps) => {
|
||||||
|
const enabledStrategies = feature.strategies?.data?.filter(
|
||||||
|
(strategy) => !strategy.disabled,
|
||||||
|
);
|
||||||
|
const disabledStrategies = feature.strategies?.data?.filter(
|
||||||
|
(strategy) => strategy.disabled,
|
||||||
|
);
|
||||||
|
|
||||||
|
const showDisabledStrategies = disabledStrategies?.length > 0;
|
||||||
|
|
||||||
|
if ((feature?.strategies?.data.length ?? 0) === 0) {
|
||||||
|
return (
|
||||||
|
<Alert severity='warning' sx={{ mt: 2 }}>
|
||||||
|
There are no strategies added to this feature flag in the
|
||||||
|
selected environment.
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(feature.hasUnsatisfiedDependency ||
|
||||||
|
!feature.isEnabledInCurrentEnvironment) &&
|
||||||
|
Boolean(feature?.strategies?.data)
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<WrappedPlaygroundResultStrategyList
|
||||||
|
feature={feature}
|
||||||
|
input={input}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PlaygroundResultStrategyLists
|
||||||
|
strategies={enabledStrategies || []}
|
||||||
|
input={input}
|
||||||
|
titlePrefix={showDisabledStrategies ? 'Enabled' : ''}
|
||||||
|
/>
|
||||||
|
{showDisabledStrategies ? (
|
||||||
|
<PlaygroundResultStrategyLists
|
||||||
|
strategies={disabledStrategies}
|
||||||
|
input={input}
|
||||||
|
titlePrefix={'Disabled'}
|
||||||
|
infoText={
|
||||||
|
'Disabled strategies are not evaluated for the overall result.'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -11,12 +11,14 @@ interface IResultChipProps {
|
|||||||
label: string;
|
label: string;
|
||||||
// Result icon - defaults to true
|
// Result icon - defaults to true
|
||||||
showIcon?: boolean;
|
showIcon?: boolean;
|
||||||
|
tabindex?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PlaygroundResultChip: VFC<IResultChipProps> = ({
|
export const PlaygroundResultChip: VFC<IResultChipProps> = ({
|
||||||
enabled,
|
enabled,
|
||||||
label,
|
label,
|
||||||
showIcon = true,
|
showIcon = true,
|
||||||
|
tabindex,
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const icon = (
|
const icon = (
|
||||||
@ -28,12 +30,14 @@ export const PlaygroundResultChip: VFC<IResultChipProps> = ({
|
|||||||
condition={typeof enabled === 'boolean' && Boolean(enabled)}
|
condition={typeof enabled === 'boolean' && Boolean(enabled)}
|
||||||
show={
|
show={
|
||||||
<FeatureEnabledIcon
|
<FeatureEnabledIcon
|
||||||
|
aria-hidden
|
||||||
color={theme.palette.success.main}
|
color={theme.palette.success.main}
|
||||||
strokeWidth='0.25'
|
strokeWidth='0.25'
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
elseShow={
|
elseShow={
|
||||||
<FeatureDisabledIcon
|
<FeatureDisabledIcon
|
||||||
|
aria-hidden
|
||||||
color={theme.palette.error.main}
|
color={theme.palette.error.main}
|
||||||
strokeWidth='0.25'
|
strokeWidth='0.25'
|
||||||
/>
|
/>
|
||||||
@ -56,6 +60,7 @@ export const PlaygroundResultChip: VFC<IResultChipProps> = ({
|
|||||||
condition={typeof enabled === 'boolean' && Boolean(enabled)}
|
condition={typeof enabled === 'boolean' && Boolean(enabled)}
|
||||||
show={
|
show={
|
||||||
<Badge
|
<Badge
|
||||||
|
tabIndex={tabindex}
|
||||||
color='success'
|
color='success'
|
||||||
icon={showIcon ? icon : undefined}
|
icon={showIcon ? icon : undefined}
|
||||||
>
|
>
|
||||||
@ -63,7 +68,11 @@ export const PlaygroundResultChip: VFC<IResultChipProps> = ({
|
|||||||
</Badge>
|
</Badge>
|
||||||
}
|
}
|
||||||
elseShow={
|
elseShow={
|
||||||
<Badge color='error' icon={showIcon ? icon : undefined}>
|
<Badge
|
||||||
|
color='error'
|
||||||
|
icon={showIcon ? icon : undefined}
|
||||||
|
tabIndex={tabindex}
|
||||||
|
>
|
||||||
{label}
|
{label}
|
||||||
</Badge>
|
</Badge>
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user