From 732b7f342a72e45d66ba732100a4f06960f34e1c Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Thu, 13 Mar 2025 12:01:44 +0100 Subject: [PATCH] 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): ![image](https://github.com/user-attachments/assets/f32505ba-f040-4491-a298-6e8bf606536d) After (env strategy list): ![image](https://github.com/user-attachments/assets/b39174c7-3ee2-4fb4-aa7c-b51134c740b8) Before (env strategy list): ![image](https://github.com/user-attachments/assets/a0a045e5-3623-44ef-96fa-8ba2f5be6b98) --- .../StrategyItemContainer.tsx | 149 +++++++++-------- .../EnvironmentAccordionBody.tsx | 61 ++++--- .../FeatureResultInfoPopoverCell.tsx | 123 ++++++++++---- ...acyPlaygroundResultFeatureStrategyList.tsx | 2 +- ...aygroundResultFeatureStrategyList.test.tsx | 6 +- .../PlaygroundResultsFeatureStrategyList.tsx | 2 +- .../LegacyPlaygroundResultStrategyLists.tsx | 152 ++++++++++++++++++ .../StrategyItem/FeatureStrategyItem.tsx | 19 +-- .../LegacyFeatureStrategyItem.tsx | 70 ++++++++ .../playgroundResultStrategyLists.tsx | 109 +++++++------ 10 files changed, 493 insertions(+), 200 deletions(-) create mode 100644 frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/LegacyPlaygroundResultStrategyLists.tsx create mode 100644 frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/StrategyList/StrategyItem/LegacyFeatureStrategyItem.tsx diff --git a/frontend/src/component/common/StrategyItemContainer/StrategyItemContainer.tsx b/frontend/src/component/common/StrategyItemContainer/StrategyItemContainer.tsx index 7be8459bf5..d1018c2d03 100644 --- a/frontend/src/component/common/StrategyItemContainer/StrategyItemContainer.tsx +++ b/frontend/src/component/common/StrategyItemContainer/StrategyItemContainer.tsx @@ -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; onDragEnd?: DragEventHandler; 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 = ({ 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 }) => {children} : ({ children }) => <> {children} ; return ( - - - ( - - - - )} - /> - - - {strategy.title ? ( - <> -

+ + + {onDragStart ? ( + + + + ) : null} + + + + {strategy.title ? ( + <> +

+ {formatStrategyName( + String(strategy.name), + )} + : +

+ + {strategy.title} + + + ) : ( + {formatStrategyName( String(strategy.name), )} - : -

- - {strategy.title} - - - ) : ( - - {formatStrategyName(String(strategy.name))} - - )} -
-
+ + )} + + - {strategy.disabled ? ( - Disabled - ) : null} + {strategy.disabled ? ( + Disabled + ) : null} + {headerItemsLeft} + theme.spacing(6), alignItems: 'center', }} > {headerItemsRight}
- {children} + {children}
); diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.tsx index abb24d5b36..c09eb9e79e 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.tsx @@ -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 ( -
- - - {releasePlans.length > 0 ? ( - <> - {releasePlans.map((plan) => ( - - - - ))} - {strategies.length > 0 ? ( -
  • - - -
  • - ) : null} - - ) : strategies.length > 0 ? ( - - ) : null} -
    -
    -
    + + + {releasePlans.length > 0 ? ( + <> + {releasePlans.map((plan) => ( + + + + ))} + {strategies.length > 0 ? ( +
  • + + +
  • + ) : null} + + ) : strategies.length > 0 ? ( + + ) : null} +
    +
    ); }; diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureResultInfoPopoverCell.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureResultInfoPopoverCell.tsx index dc5b418e9f..fe8e82c7a9 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureResultInfoPopoverCell.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureResultInfoPopoverCell.tsx @@ -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 ( @@ -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 ? ( - <> - setOpen(false)} - /> - - - ) : ( - <> - setOpen(false)} - /> - - - )} + setOpen(false)} + /> + ); }; + +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 ( + + + + + 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', + }} + > + + setOpen(false)} + /> + + + + + ); +}; + +export const FeatureResultInfoPopoverCell = ( + props: FeatureResultInfoPopoverCellProps, +) => { + const useNewStrategyDesign = useUiFlag('flagOverviewRedesign'); + + if (!props.feature) { + return null; + } + + return useNewStrategyDesign ? ( + + ) : ( + + ); +}; diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/LegacyPlaygroundResultFeatureStrategyList.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/LegacyPlaygroundResultFeatureStrategyList.tsx index 396e7881ad..451856d8ef 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/LegacyPlaygroundResultFeatureStrategyList.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/LegacyPlaygroundResultFeatureStrategyList.tsx @@ -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'; 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 b64be1ee17..0d4b27944d 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 @@ -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', diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/PlaygroundResultsFeatureStrategyList.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/PlaygroundResultsFeatureStrategyList.tsx index 2a3e1b25b3..8a88c62863 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/PlaygroundResultsFeatureStrategyList.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureStrategyList/PlaygroundResultsFeatureStrategyList.tsx @@ -50,7 +50,7 @@ export const PlaygroundResultFeatureStrategyList = ({ {showDisabledStrategies ? ( ({ + 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) => ( + 0} + show={ + <> + {`${ + titlePrefix + ? titlePrefix.concat(' strategies') + : 'Strategies' + } (${strategies?.length})`} + + {infoText} + + } + /> + + {strategies?.map((strategy, index) => ( + + 0} + show={} + /> + + + ))} + + + } + /> +); + +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 ( + + + {resolveHintText(feature)}, then this feature flag would be{' '} + {feature.strategies?.result ? 'TRUE' : 'FALSE'} with strategies + evaluated like this:{' '} + + + + + + + + } + /> + + ); +}; 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 2587097548..39dd3be10d 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 @@ -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 ( } > + {/* todo: use new strategy execution components */} { + const { result } = strategy; + const theme = useTheme(); + const label = + result.evaluationStatus === 'incomplete' || + result.evaluationStatus === 'unevaluated' + ? 'Unevaluated' + : result.enabled + ? 'True' + : 'False'; + + return ( + + } + > + + } + elseShow={ + + } + /> + + ); +}; 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 82d6faa32e..d56d8af0ab 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 @@ -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) => ( - 0} - show={ - <> - {`${ +}: PlaygroundResultStrategyListProps) => { + if (strategies.length === 0) { + return null; + } + + return ( +
    + + {`${ titlePrefix ? titlePrefix.concat(' strategies') : 'Strategies' - } (${strategies?.length})`} - - {infoText} - - } - /> - - {strategies?.map((strategy, index) => ( - - 0} - show={} - /> - - - ))} - - - } - /> -); + } (${strategies?.length})`} + {infoText ? ( + + {infoText} + + ) : null} + + + {strategies?.map((strategy, index) => ( + + {index > 0 ? : ''} + + + ))} + +
    + ); +}; 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 = ({ {resolveHintText(feature)}, then this feature flag would be{' '} {feature.strategies?.result ? 'TRUE' : 'FALSE'} with strategies - evaluated like so:{' '} + evaluated like this:{' '} - + +