diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/PlaygroundResultFeatureStrategyList.test.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/PlaygroundResultFeatureStrategyList.test.tsx
index d181431a1f..3f63cbd41e 100644
--- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/PlaygroundResultFeatureStrategyList.test.tsx
+++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/PlaygroundResultFeatureStrategyList.test.tsx
@@ -3,6 +3,7 @@ import { render } from 'utils/testRenderer';
import React from 'react';
import { PlaygroundFeatureSchema, PlaygroundRequestSchema } from 'openapi';
import { PlaygroundResultFeatureStrategyList } from './PlaygroundResultFeatureStrategyList';
+import { vi } from 'vitest';
const testCases = [
{
@@ -62,8 +63,72 @@ const testCases = [
expectedText:
'If environment was enabled, then this feature toggle would be TRUE with strategies evaluated like so:',
},
+ {
+ name: 'Has disabled strategies and is enabled in environment',
+ feature: {
+ strategies: {
+ result: true,
+ data: [
+ {
+ name: 'default',
+ parameters: {},
+ result: { enabled: true, evaluationStatus: 'complete' },
+ },
+ {
+ name: 'default',
+ parameters: {},
+ disabled: true,
+ result: {
+ enabled: 'unknown',
+ evaluationStatus: 'unevaluated',
+ },
+ },
+ ],
+ },
+ isEnabledInCurrentEnvironment: true,
+ hasUnsatisfiedDependency: false,
+ } as PlaygroundFeatureSchema,
+ expectedText:
+ 'Disabled strategies are not evaluated for the overall result.',
+ },
+ {
+ name: 'Has disabled strategies and is disabled in environment',
+ feature: {
+ strategies: {
+ result: true,
+ data: [
+ {
+ name: 'default',
+ parameters: {},
+ result: { enabled: true, evaluationStatus: 'complete' },
+ },
+ {
+ name: 'default',
+ parameters: {},
+ disabled: true,
+ result: {
+ enabled: 'unknown',
+ evaluationStatus: 'unevaluated',
+ },
+ },
+ ],
+ },
+ isEnabledInCurrentEnvironment: false,
+ hasUnsatisfiedDependency: false,
+ } as PlaygroundFeatureSchema,
+ expectedText:
+ 'Disabled strategies are not evaluated for the overall result.',
+ },
];
+vi.mock('../../../../../../hooks/useUiFlag', () => ({
+ useUiFlag: vi.fn().mockImplementation(() => true),
+}));
+
+afterAll(() => {
+ vi.clearAllMocks();
+});
+
testCases.forEach(({ name, feature, expectedText }) => {
test(name, async () => {
render(
diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/PlaygroundResultFeatureStrategyList.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/PlaygroundResultFeatureStrategyList.tsx
index c15efe19a9..2adbcdb49d 100644
--- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/PlaygroundResultFeatureStrategyList.tsx
+++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/PlaygroundResultFeatureStrategyList.tsx
@@ -5,6 +5,7 @@ import {
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { PlaygroundFeatureSchema, PlaygroundRequestSchema } from 'openapi';
import { Alert } from '@mui/material';
+import { useUiFlag } from '../../../../../../hooks/useUiFlag';
interface PlaygroundResultFeatureStrategyListProps {
feature: PlaygroundFeatureSchema;
@@ -15,6 +16,17 @@ export const PlaygroundResultFeatureStrategyList = ({
feature,
input,
}: PlaygroundResultFeatureStrategyListProps) => {
+ const playgroundImprovements = useUiFlag('playgroundImprovements');
+ const enabledStrategies = feature.strategies?.data?.filter(
+ (strategy) => !strategy.disabled,
+ );
+ const disabledStrategies = feature.strategies?.data?.filter(
+ (strategy) => strategy.disabled,
+ );
+
+ const showDisabledStrategies =
+ playgroundImprovements && disabledStrategies?.length > 0;
+
return (
<>
}
elseShow={
-
+ <>
+
+
+ }
+ />
+ >
}
/>
>
diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/FeatureStrategyItem.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/FeatureStrategyItem.tsx
index 9c6a9f87e4..63d07dd1f2 100644
--- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/FeatureStrategyItem.tsx
+++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/FeatureStrategyItem.tsx
@@ -4,6 +4,8 @@ import { PlaygroundStrategySchema, PlaygroundRequestSchema } from 'openapi';
import { StrategyExecution } from './StrategyExecution/StrategyExecution';
import { StrategyItemContainer } from 'component/common/StrategyItemContainer/StrategyItemContainer';
import { objectId } from 'utils/objectId';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
+import { DisabledStrategyExecution } from './StrategyExecution/DisabledStrategyExecution';
interface IFeatureStrategyItemProps {
strategy: PlaygroundStrategySchema;
@@ -19,7 +21,8 @@ export const FeatureStrategyItem = ({
const { result } = strategy;
const theme = useTheme();
const label =
- result.evaluationStatus === 'incomplete'
+ result.evaluationStatus === 'incomplete' ||
+ result.evaluationStatus === 'unevaluated'
? 'Unevaluated'
: result.enabled
? 'True'
@@ -28,9 +31,10 @@ export const FeatureStrategyItem = ({
return (
}
>
-
+ }
+ elseShow={
+
+ }
/>
);
diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/ConstraintExecution/ConstraintExecutionWithoutResults.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/ConstraintExecution/ConstraintExecutionWithoutResults.tsx
new file mode 100644
index 0000000000..bcfbc342c0
--- /dev/null
+++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/ConstraintExecution/ConstraintExecutionWithoutResults.tsx
@@ -0,0 +1,43 @@
+import { Fragment, VFC } from 'react';
+import {
+ PlaygroundConstraintSchema,
+ PlaygroundRequestSchema,
+ PlaygroundStrategySchemaResultAnyOfEvaluationStatus,
+} from 'openapi';
+import { objectId } from 'utils/objectId';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
+import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
+import { styled } from '@mui/material';
+import { ConstraintAccordionView } from 'component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView';
+import { ConstraintError } from './ConstraintError/ConstraintError';
+import { ConstraintOk } from './ConstraintOk/ConstraintOk';
+
+interface IConstraintExecutionWithoutResultsProps {
+ constraints?: PlaygroundConstraintSchema[];
+}
+
+export const ConstraintExecutionWrapper = styled('div')(() => ({
+ width: '100%',
+ display: 'flex',
+ flexDirection: 'column',
+}));
+
+export const ConstraintExecutionWithoutResults: VFC<
+ IConstraintExecutionWithoutResultsProps
+> = ({ constraints }) => {
+ if (!constraints) return null;
+
+ return (
+
+ {constraints?.map((constraint, index) => (
+
+ 0}
+ show={}
+ />
+
+
+ ))}
+
+ );
+};
diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/DisabledStrategyExecution.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/DisabledStrategyExecution.tsx
new file mode 100644
index 0000000000..f195bd8f0f
--- /dev/null
+++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/DisabledStrategyExecution.tsx
@@ -0,0 +1,85 @@
+import { Fragment, VFC } from 'react';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
+import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
+import { styled } from '@mui/material';
+import { PlaygroundRequestSchema, PlaygroundStrategySchema } from 'openapi';
+import { ConstraintExecution } from './ConstraintExecution/ConstraintExecution';
+import { SegmentExecution } from './SegmentExecution/SegmentExecution';
+import { PlaygroundResultStrategyExecutionParameters } from './StrategyExecutionParameters/StrategyExecutionParameters';
+import { CustomStrategyParams } from './CustomStrategyParams/CustomStrategyParams';
+import { formattedStrategyNames } from 'utils/strategyNames';
+import { StyledBoxSummary } from './StrategyExecution.styles';
+import { Badge } from 'component/common/Badge/Badge';
+import { ConstraintExecutionWithoutResults } from './ConstraintExecution/ConstraintExecutionWithoutResults';
+import { SegmentExecutionWithoutResult } from './SegmentExecution/SegmentExecutionWithoutResult';
+
+interface IDisabledStrategyExecutionProps {
+ strategyResult: PlaygroundStrategySchema;
+ percentageFill?: string;
+ input?: PlaygroundRequestSchema;
+}
+
+const StyledStrategyExecutionWrapper = styled('div')(({ theme }) => ({
+ padding: theme.spacing(0),
+}));
+
+export const DisabledStrategyExecution: VFC =
+ ({ strategyResult, input, percentageFill }) => {
+ const { name, constraints, segments, parameters } = strategyResult;
+
+ const hasSegments = Boolean(segments && segments.length > 0);
+ const hasConstraints = Boolean(constraints && constraints?.length > 0);
+ const hasExecutionParameters =
+ name !== 'default' &&
+ Object.keys(formattedStrategyNames).includes(name);
+ const hasCustomStrategyParameters =
+ Object.keys(parameters).length > 0 &&
+ strategyResult.result.evaluationStatus === 'incomplete'; // Use of custom strategy can be more explicit from the API
+
+ if (!parameters) {
+ return null;
+ }
+
+ const items = [
+ hasSegments && (
+
+ ),
+ hasConstraints && (
+
+ ),
+ hasExecutionParameters && (
+
+ ),
+ hasCustomStrategyParameters && (
+
+ ),
+ name === 'default' && (
+
+ The standard strategy is ON{' '}
+ for all users.
+
+ ),
+ ].filter(Boolean);
+
+ return (
+
+ {items.map((item, index) => (
+ // biome-ignore lint/suspicious/noArrayIndexKey:
+
+ 0}
+ show={}
+ />
+ {item}
+
+ ))}
+
+ );
+ };
diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/SegmentExecution/SegmentExecutionWithoutResult.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/SegmentExecution/SegmentExecutionWithoutResult.tsx
new file mode 100644
index 0000000000..33e40ac84c
--- /dev/null
+++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/SegmentExecution/SegmentExecutionWithoutResult.tsx
@@ -0,0 +1,47 @@
+import { Fragment, VFC } from 'react';
+import { PlaygroundSegmentSchema, PlaygroundRequestSchema } from 'openapi';
+import { ConstraintExecution } from '../ConstraintExecution/ConstraintExecution';
+import { CancelOutlined } from '@mui/icons-material';
+import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
+import { styled, Typography } from '@mui/material';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
+import { SegmentItem } from 'component/common/SegmentItem/SegmentItem';
+import { ConstraintExecutionWithoutResults } from '../ConstraintExecution/ConstraintExecutionWithoutResults';
+
+interface ISegmentExecutionWithoutResultProps {
+ segments?: PlaygroundSegmentSchema[];
+}
+
+export const SegmentExecutionWithoutResult: VFC<
+ ISegmentExecutionWithoutResultProps
+> = ({ segments }) => {
+ if (!segments) return null;
+
+ return (
+ <>
+ {segments.map((segment, index) => (
+
+
+ }
+ isExpanded
+ />
+ = 0 &&
+ segments.length > 1 &&
+ // Don't add if it's the last segment item
+ index !== segments.length - 1
+ }
+ show={}
+ />
+
+ ))}
+ >
+ );
+};
diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/StrategyExecution.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/StrategyExecution.tsx
index 78d542ae84..7a0214f369 100644
--- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/StrategyExecution.tsx
+++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/StrategyExecution/StrategyExecution.tsx
@@ -10,6 +10,8 @@ import { CustomStrategyParams } from './CustomStrategyParams/CustomStrategyParam
import { formattedStrategyNames } from 'utils/strategyNames';
import { StyledBoxSummary } from './StrategyExecution.styles';
import { Badge } from 'component/common/Badge/Badge';
+import { ConstraintExecutionWithoutResults } from './ConstraintExecution/ConstraintExecutionWithoutResults';
+import { SegmentExecutionWithoutResult } from './SegmentExecution/SegmentExecutionWithoutResult';
interface IStrategyExecutionProps {
strategyResult: PlaygroundStrategySchema;
diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/playgroundResultStrategyLists.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/playgroundResultStrategyLists.tsx
index 308a7d8beb..aa08a59f93 100644
--- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/playgroundResultStrategyLists.tsx
+++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/playgroundResultStrategyLists.tsx
@@ -8,6 +8,7 @@ import {
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { FeatureStrategyItem } from './StrategyItem/FeatureStrategyItem';
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
+import { useUiFlag } from '../../../../../../../hooks/useUiFlag';
const StyledAlertWrapper = styled('div')(({ theme }) => ({
display: 'flex',
@@ -31,20 +32,38 @@ const StyledAlert = styled(Alert)(({ theme }) => ({
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) => (
0}
show={
<>
- {`Strategies (${strategies?.length})`}
+ {`${
+ titlePrefix
+ ? titlePrefix.concat(' strategies')
+ : 'Strategies'
+ } (${strategies?.length})`}
+
+ {infoText}
+
+ }
+ />
{strategies?.map((strategy, index) => (
@@ -91,6 +110,17 @@ export const WrappedPlaygroundResultStrategyList = ({
feature,
input,
}: IWrappedPlaygroundResultStrategyListProps) => {
+ const playgroundImprovements = useUiFlag('playgroundImprovements');
+ const enabledStrategies = feature.strategies?.data?.filter(
+ (strategy) => !strategy.disabled,
+ );
+ const disabledStrategies = feature.strategies?.data?.filter(
+ (strategy) => strategy.disabled,
+ );
+
+ const showDisabledStrategies =
+ playgroundImprovements && disabledStrategies?.length > 0;
+
return (
@@ -100,10 +130,26 @@ export const WrappedPlaygroundResultStrategyList = ({
+
+
+
+ }
+ />
);
};