From 42fbc27148cc9c2b20a434bb04b6e0af16119e57 Mon Sep 17 00:00:00 2001 From: andreas-unleash <104830839+andreas-unleash@users.noreply.github.com> Date: Tue, 2 Aug 2022 11:57:23 +0300 Subject: [PATCH] bug fixes, improvements and env handling --- .../ConstraintAccordionView.tsx | 9 +- .../ConstraintAccordionViewHeader.tsx | 8 +- .../ConstraintAccordionViewHeaderInfo.tsx | 24 ++- .../playground/Playground/Playground.tsx | 1 + .../FeatureResultInfoPopoverCell.tsx | 117 +++------------ .../PlaygroundResultFeatureDetails.styles.ts | 4 +- .../PlaygroundResultFeatureDetails.tsx | 37 ++++- .../PlaygroundResultFeatureDetails/helpers.ts | 8 + .../PlaygroundResultConstraintExecution.tsx | 33 ++--- ...ygroundResultFeatureStrategyItem.styles.ts | 1 + .../PlaygroundResultFeatureStrategyItem.tsx | 75 ++++++---- ...PlaygroundResultSegmentExecution.styles.ts | 0 .../PlaygroundResultSegmentExecution.tsx | 20 ++- ...laygroundResultStrategyExecution.styles.ts | 2 +- .../PlaygroundResultStrategyExecution.tsx | 35 ++++- .../PlaygroundResultStrategyList.tsx | 88 +++++++++++ .../PlaygroundResultChip.tsx | 6 +- .../PlaygroundResultsTable.tsx | 140 ++++++++++-------- .../actions/usePlayground/playground.model.ts | 6 +- 19 files changed, 361 insertions(+), 253 deletions(-) create mode 100644 frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureDetails/helpers.ts rename frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/{ => PlaygroundResultFeatureStrategyItem}/PlaygroundResultConstraintExecution/PlaygroundResultConstraintExecution.tsx (59%) rename frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/{ => PlaygroundResultFeatureStrategyItem}/PlaygroundResultSegmentExecution/PlaygroundResultSegmentExecution.styles.ts (100%) rename frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/{ => PlaygroundResultFeatureStrategyItem}/PlaygroundResultSegmentExecution/PlaygroundResultSegmentExecution.tsx (84%) rename frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/{ => PlaygroundResultFeatureStrategyItem}/PlaygroundResultStrategyExecution/PlaygroundResultStrategyExecution.styles.ts (94%) rename frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/{ => PlaygroundResultFeatureStrategyItem}/PlaygroundResultStrategyExecution/PlaygroundResultStrategyExecution.tsx (62%) create mode 100644 frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultStrategyList/PlaygroundResultStrategyList.tsx diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView.tsx index 1377a2b2a0..22db144fd0 100644 --- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView.tsx +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView.tsx @@ -18,14 +18,14 @@ import { import { useStyles } from '../ConstraintAccordion.styles'; import { PlaygroundFeatureStrategyConstraintResult, - SdkContextSchema, + PlaygroundRequestSchema, } from '../../../../hooks/api/actions/usePlayground/playground.model'; interface IConstraintAccordionViewProps { constraint: IConstraint | PlaygroundFeatureStrategyConstraintResult; onDelete?: () => void; onEdit?: () => void; - playgroundContext?: SdkContextSchema; + playgroundInput?: PlaygroundRequestSchema; maxLength?: number; sx?: SxProps; } @@ -36,7 +36,7 @@ export const ConstraintAccordionView = ({ onDelete, sx = undefined, maxLength, - playgroundContext, + playgroundInput, }: IConstraintAccordionViewProps) => { const { classes: styles } = useStyles(); const [expandable, setExpandable] = useState(true); @@ -46,7 +46,6 @@ export const ConstraintAccordionView = ({ [...semVerOperators, ...numOperators, ...dateOperators], constraint.operator ); - const handleClick = () => { if (expandable) { setExpanded(!expanded); @@ -79,7 +78,7 @@ export const ConstraintAccordionView = ({ allowExpand={setExpandable} expanded={expanded} maxLength={maxLength ?? 112} - playgroundContext={playgroundContext} + playgroundInput={playgroundInput} /> diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeader.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeader.tsx index 9a4b0bbb58..f3833c3e1f 100644 --- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeader.tsx +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeader.tsx @@ -5,7 +5,7 @@ import { ConstraintAccordionHeaderActions } from '../../ConstraintAccordionHeade import { useStyles } from 'component/common/ConstraintAccordion/ConstraintAccordion.styles'; import { PlaygroundFeatureStrategyConstraintResult, - SdkContextSchema, + PlaygroundRequestSchema, } from '../../../../../hooks/api/actions/usePlayground/playground.model'; interface IConstraintAccordionViewHeaderProps { @@ -15,7 +15,7 @@ interface IConstraintAccordionViewHeaderProps { singleValue: boolean; expanded: boolean; allowExpand: (shouldExpand: boolean) => void; - playgroundContext?: SdkContextSchema; + playgroundInput?: PlaygroundRequestSchema; maxLength?: number; } @@ -27,7 +27,7 @@ export const ConstraintAccordionViewHeader = ({ allowExpand, expanded, maxLength, - playgroundContext, + playgroundInput, }: IConstraintAccordionViewHeaderProps) => { const { classes: styles } = useStyles(); @@ -41,7 +41,7 @@ export const ConstraintAccordionViewHeader = ({ expanded={expanded} result={'result' in constraint ? constraint.result : undefined} maxLength={maxLength} - playgroundContext={playgroundContext} + playgroundInput={playgroundInput} /> ({ display: '-webkit-box', @@ -43,7 +43,7 @@ interface ConstraintAccordionViewHeaderMetaInfoProps { allowExpand: (shouldExpand: boolean) => void; result?: boolean; maxLength?: number; - playgroundContext?: SdkContextSchema; + playgroundInput?: PlaygroundRequestSchema; } export const ConstraintAccordionViewHeaderInfo = ({ @@ -52,12 +52,17 @@ export const ConstraintAccordionViewHeaderInfo = ({ allowExpand, expanded, result, + playgroundInput, maxLength = 112, - playgroundContext, }: ConstraintAccordionViewHeaderMetaInfoProps) => { const { classes: styles } = useStyles(); const theme = useTheme(); + const isPlayground = Boolean(playgroundInput); + const constrainExistsInContext = + isPlayground && + Boolean(playgroundInput?.context[constraint.contextName]); + return (
@@ -65,24 +70,17 @@ export const ConstraintAccordionViewHeaderInfo = ({ {constraint.contextName} - {playgroundContext![ + {playgroundInput?.context[ constraint.contextName ] || 'no value'} diff --git a/frontend/src/component/playground/Playground/Playground.tsx b/frontend/src/component/playground/Playground/Playground.tsx index 50c8bb72bd..b01bee5e10 100644 --- a/frontend/src/component/playground/Playground/Playground.tsx +++ b/frontend/src/component/playground/Playground/Playground.tsx @@ -203,6 +203,7 @@ export const Playground: VFC<{}> = () => { } elseShow={} diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureResultInfoPopoverCell.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureResultInfoPopoverCell.tsx index 31e5b79f26..b91c120876 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureResultInfoPopoverCell.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureResultInfoPopoverCell.tsx @@ -1,17 +1,21 @@ import { PlaygroundFeatureSchema, - PlaygroundFeatureStrategyResult, + PlaygroundRequestSchema, } from 'hooks/api/actions/usePlayground/playground.model'; -import { IconButton, Popover, styled, Typography } from '@mui/material'; +import { IconButton, Popover, styled } from '@mui/material'; import { InfoOutlined } from '@mui/icons-material'; import React, { useRef, useState } from 'react'; import { ConditionallyRender } from '../../../../common/ConditionallyRender/ConditionallyRender'; -import { PlaygroundResultFeatureStrategyItem } from './PlaygroundResultFeatureStrategyItem/PlaygroundResultFeatureStrategyItem'; import { useStyles } from './FeatureResultInfoPopoverCell.styles'; import { PlaygroundResultFeatureDetails } from './PlaygroundResultFeatureDetails/PlaygroundResultFeatureDetails'; +import { + PlaygroundResultStrategyList, + WrappedPlaygroundResultStrategyList, +} from './PlaygroundResultStrategyList/PlaygroundResultStrategyList'; interface FeatureResultInfoPopoverCellProps { - feature?: PlaygroundFeatureSchema; + feature: PlaygroundFeatureSchema; + input?: PlaygroundRequestSchema; } const FeatureResultPopoverWrapper = styled('div')(({ theme }) => ({ @@ -21,6 +25,7 @@ const FeatureResultPopoverWrapper = styled('div')(({ theme }) => ({ export const FeatureResultInfoPopoverCell = ({ feature, + input, }: FeatureResultInfoPopoverCellProps) => { const [open, setOpen] = useState(false); const { classes: styles } = useStyles(); @@ -30,77 +35,6 @@ export const FeatureResultInfoPopoverCell = ({ setOpen(!open); }; - const dummyPlaygroundFeatureTrue: PlaygroundFeatureSchema = { - name: feature?.name as any, - projectId: 'default', - isEnabled: true, - } as any; - - const dummyPlaygroundFeatureFalse: PlaygroundFeatureSchema = { - name: feature?.name as any, - projectId: 'default', - isEnabled: false, - } as any; - - const strategies: PlaygroundFeatureStrategyResult[] = [ - { - name: 'default', - id: 'strategy-id', - parameters: {}, - result: feature?.isEnabled as any, - constraints: [ - { - result: true, - contextName: 'appName', - operator: 'IN', - caseInsensitive: false, - inverted: false, - values: ['MyApp', 'MyOtherApp', 'Unleash'], - }, - ], - segments: [ - { - result: feature?.isEnabled as any, - id: 5, - name: 'my-segment', - constraints: [ - { - result: feature?.isEnabled as any, - contextName: 'environment', - operator: 'IN', - caseInsensitive: false, - inverted: false, - values: ['development'], - }, - ], - }, - ], - }, - { - name: 'flexibleRollout', - id: 'strategy-id', - parameters: {}, - result: false, - segments: [ - { - result: false, - id: 6, - name: 'my-segment', - constraints: [ - { - result: false, - contextName: 'appName', - operator: 'IN', - caseInsensitive: false, - inverted: false, - values: ['MyApp2'], - }, - ], - }, - ], - }, - ]; - if (!feature) { return null; } @@ -125,29 +59,24 @@ export const FeatureResultInfoPopoverCell = ({ classes={{ paper: styles.popoverPaper }} > setOpen(false)} /> 0} + condition={!feature.isEnabledInCurrentEnvironment} show={ - <> - {`Strategies (${strategies.length})`} - {strategies.map((strategy, index) => ( - - ))} - + + } + elseShow={ + } /> diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureDetails/PlaygroundResultFeatureDetails.styles.ts b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureDetails/PlaygroundResultFeatureDetails.styles.ts index 11c247d82e..c1dbc23cf6 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureDetails/PlaygroundResultFeatureDetails.styles.ts +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureDetails/PlaygroundResultFeatureDetails.styles.ts @@ -9,9 +9,11 @@ export const useStyles = makeStyles()(theme => ({ titleRow: { display: 'inline-flex', alignItems: 'flex-start', - justifyItems: 'center', + justifyContent: 'center', gap: '12px', + marginTop: '12px', }, + alertRow: {}, descriptionRow: { marginBottom: theme.spacing(2), }, diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureDetails/PlaygroundResultFeatureDetails.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureDetails/PlaygroundResultFeatureDetails.tsx index c923bc9b5b..95a0abe411 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureDetails/PlaygroundResultFeatureDetails.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureDetails/PlaygroundResultFeatureDetails.tsx @@ -1,30 +1,45 @@ -import { PlaygroundFeatureSchema } from '../../../../../../hooks/api/actions/usePlayground/playground.model'; -import { IconButton, Typography, useTheme } from '@mui/material'; +import { + PlaygroundFeatureSchema, + PlaygroundRequestSchema, +} from '../../../../../../hooks/api/actions/usePlayground/playground.model'; +import { Alert, IconButton, Typography, useTheme } from '@mui/material'; import { PlaygroundResultChip } from '../../PlaygroundResultChip/PlaygroundResultChip'; import { useStyles } from './PlaygroundResultFeatureDetails.styles'; import { CloseOutlined } from '@mui/icons-material'; import React from 'react'; +import { ConditionallyRender } from '../../../../../common/ConditionallyRender/ConditionallyRender'; +import { checkForEmptyValues } from './helpers'; interface PlaygroundFeatureResultDetailsProps { feature: PlaygroundFeatureSchema; + input?: PlaygroundRequestSchema; onClose: () => void; } export const PlaygroundResultFeatureDetails = ({ feature, + input, onClose, }: PlaygroundFeatureResultDetailsProps) => { const { classes: styles } = useStyles(); const theme = useTheme(); - const description = feature.isEnabled - ? 'This feature toggle is True in production because ' - : 'This feature toggle is False in production because '; - const reason = feature.isEnabled + const description = Boolean(feature.isEnabled) + ? `This feature toggle is True in ${input?.environment} because ` + : `This feature toggle is False in ${input?.environment} because `; + + const reason = Boolean(feature.isEnabled) ? 'at least one strategy is True' + : feature?.isEnabledInCurrentEnvironment + ? 'the environment is disabled' : 'all strategies are False'; - const color = feature.isEnabled + + const color = Boolean(feature.isEnabled) ? theme.palette.success.main : theme.palette.error.main; + const noValueTxt = checkForEmptyValues(input?.context) + ? 'You did not provide a value for your context filed in step 2 of the configuration' + : undefined; + const onCloseClick = onClose && ((event: React.SyntheticEvent) => { @@ -55,6 +70,14 @@ export const PlaygroundResultFeatureDetails = ({ {reason}
+ + {noValueTxt} + + } + /> ); }; diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureDetails/helpers.ts b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureDetails/helpers.ts new file mode 100644 index 0000000000..cbb3f5ad39 --- /dev/null +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureDetails/helpers.ts @@ -0,0 +1,8 @@ +export function checkForEmptyValues(object?: Object): boolean { + if (object === undefined) { + return true; + } + return Object.values(object).every(v => + v && typeof v === 'object' ? checkForEmptyValues(v) : v === null + ); +} diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultConstraintExecution/PlaygroundResultConstraintExecution.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultConstraintExecution/PlaygroundResultConstraintExecution.tsx similarity index 59% rename from frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultConstraintExecution/PlaygroundResultConstraintExecution.tsx rename to frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultConstraintExecution/PlaygroundResultConstraintExecution.tsx index 12ef40d479..8f407466cf 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultConstraintExecution/PlaygroundResultConstraintExecution.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultConstraintExecution/PlaygroundResultConstraintExecution.tsx @@ -1,16 +1,18 @@ import { PlaygroundFeatureStrategyConstraintResult, - SdkContextSchema, + PlaygroundRequestSchema, } from 'hooks/api/actions/usePlayground/playground.model'; import React, { Fragment } from 'react'; -import { objectId } from '../../../../../../utils/objectId'; -import { ConditionallyRender } from '../../../../../common/ConditionallyRender/ConditionallyRender'; -import { StrategySeparator } from '../../../../../common/StrategySeparator/StrategySeparator'; -import { ConstraintAccordionView } from '../../../../../common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView'; +import { objectId } from '../../../../../../../utils/objectId'; +import { ConditionallyRender } from '../../../../../../common/ConditionallyRender/ConditionallyRender'; +import { StrategySeparator } from '../../../../../../common/StrategySeparator/StrategySeparator'; +import { ConstraintAccordionView } from '../../../../../../common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView'; import { styled } from '@mui/material'; interface PlaygroundResultConstraintExecutionProps { constraints?: PlaygroundFeatureStrategyConstraintResult[]; + compact: boolean; + input?: PlaygroundRequestSchema; } export const PlaygroundResultConstraintExecutionWrapper = styled('div')( @@ -23,18 +25,9 @@ export const PlaygroundResultConstraintExecutionWrapper = styled('div')( export const PlaygroundResultConstraintExecution = ({ constraints, + compact, + input, }: PlaygroundResultConstraintExecutionProps) => { - // const context = usePlaygroundContext(); - const contextFalse: SdkContextSchema = { - appName: 'MyApp', - environment: '', - }; - - const contextTrue: SdkContextSchema = { - appName: 'MyApp', - environment: 'development', - }; - if (!constraints) return null; return ( @@ -47,12 +40,8 @@ export const PlaygroundResultConstraintExecution = ({ /> ({ [theme.breakpoints.down(400)]: { padding: '0.5rem', }, + width: '100%', paddingBottom: '1rem', borderRadius: theme.shape.borderRadiusMedium, background: theme.palette.background.default, diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultFeatureStrategyItem.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultFeatureStrategyItem.tsx index 98a1113c65..f4cbddcac9 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultFeatureStrategyItem.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultFeatureStrategyItem.tsx @@ -1,4 +1,4 @@ -import { Box, useTheme } from '@mui/material'; +import { Box, styled, Typography, useTheme } from '@mui/material'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator'; import { @@ -7,18 +7,33 @@ import { } from 'utils/strategyNames'; import StringTruncator from 'component/common/StringTruncator/StringTruncator'; import { PlaygroundResultChip } from '../../PlaygroundResultChip/PlaygroundResultChip'; -import { PlaygroundFeatureStrategyResult } from 'hooks/api/actions/usePlayground/playground.model'; -import { PlaygroundResultStrategyExecution } from '../PlaygroundResultStrategyExecution/PlaygroundResultStrategyExecution'; +import { + PlaygroundFeatureStrategyResult, + PlaygroundRequestSchema, +} from 'hooks/api/actions/usePlayground/playground.model'; +import { PlaygroundResultStrategyExecution } from './PlaygroundResultStrategyExecution/PlaygroundResultStrategyExecution'; import { useStyles } from './PlaygroundResultFeatureStrategyItem.styles'; interface IPlaygroundResultFeatureStrategyItemProps { strategy: PlaygroundFeatureStrategyResult; index: number; + input?: PlaygroundRequestSchema; + compact: boolean; } +const StyledItemWrapper = styled('div')(({ theme }) => ({ + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + marginTop: '4px', + gap: '4px', +})); + export const PlaygroundResultFeatureStrategyItem = ({ strategy, + input, index, + compact, }: IPlaygroundResultFeatureStrategyItemProps) => { const { result, name } = strategy; const { classes: styles } = useStyles(); @@ -31,34 +46,44 @@ export const PlaygroundResultFeatureStrategyItem = ({ : `1px solid ${theme.palette.divider}`; return ( - + 0} show={} /> - -
-
- - + {index + 1} + +
+
+ + +
+
- -
-
- -
- +
+ +
+ + ); }; diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultSegmentExecution/PlaygroundResultSegmentExecution.styles.ts b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultSegmentExecution/PlaygroundResultSegmentExecution.styles.ts similarity index 100% rename from frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultSegmentExecution/PlaygroundResultSegmentExecution.styles.ts rename to frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultSegmentExecution/PlaygroundResultSegmentExecution.styles.ts diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultSegmentExecution/PlaygroundResultSegmentExecution.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultSegmentExecution/PlaygroundResultSegmentExecution.tsx similarity index 84% rename from frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultSegmentExecution/PlaygroundResultSegmentExecution.tsx rename to frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultSegmentExecution/PlaygroundResultSegmentExecution.tsx index 46ba3cf8ad..2d87a61985 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultSegmentExecution/PlaygroundResultSegmentExecution.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultSegmentExecution/PlaygroundResultSegmentExecution.tsx @@ -1,14 +1,19 @@ -import { PlaygroundFeatureStrategySegmentResult } from '../../../../../../hooks/api/actions/usePlayground/playground.model'; +import { + PlaygroundFeatureStrategySegmentResult, + PlaygroundRequestSchema, +} from '../../../../../../../hooks/api/actions/usePlayground/playground.model'; import { PlaygroundResultConstraintExecution } from '../PlaygroundResultConstraintExecution/PlaygroundResultConstraintExecution'; import { CancelOutlined, DonutLarge } from '@mui/icons-material'; import { Link } from 'react-router-dom'; -import { StrategySeparator } from '../../../../../common/StrategySeparator/StrategySeparator'; +import { StrategySeparator } from '../../../../../../common/StrategySeparator/StrategySeparator'; import { useStyles } from './PlaygroundResultSegmentExecution.styles'; import { styled, Typography } from '@mui/material'; -import { ConditionallyRender } from '../../../../../common/ConditionallyRender/ConditionallyRender'; +import { ConditionallyRender } from '../../../../../../common/ConditionallyRender/ConditionallyRender'; interface PlaygroundResultSegmentExecutionProps { segments?: PlaygroundFeatureStrategySegmentResult[]; + input?: PlaygroundRequestSchema; + hasConstraints: boolean; } const SegmentExecutionLinkWrapper = styled('div')(({ theme }) => ({ @@ -55,8 +60,11 @@ const SegmentResultTextWrapper = styled('div')(({ theme }) => ({ export const PlaygroundResultSegmentExecution = ({ segments, + input, + hasConstraints, }: PlaygroundResultSegmentExecutionProps) => { const { classes: styles } = useStyles(); + if (!segments) return null; return ( <> @@ -93,10 +101,14 @@ export const PlaygroundResultSegmentExecution = ({ } /> diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultStrategyExecution/PlaygroundResultStrategyExecution.styles.ts b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultStrategyExecution/PlaygroundResultStrategyExecution.styles.ts similarity index 94% rename from frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultStrategyExecution/PlaygroundResultStrategyExecution.styles.ts rename to frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultStrategyExecution/PlaygroundResultStrategyExecution.styles.ts index e2d49389e1..9db6fc4961 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultStrategyExecution/PlaygroundResultStrategyExecution.styles.ts +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultStrategyExecution/PlaygroundResultStrategyExecution.styles.ts @@ -10,7 +10,7 @@ export const useStyles = makeStyles()(theme => ({ color: theme.palette.grey[700], }, summary: { - width: '100%', + width: 'auto', padding: theme.spacing(2, 3), borderRadius: theme.shape.borderRadius, border: `1px solid ${theme.palette.divider}`, diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultStrategyExecution/PlaygroundResultStrategyExecution.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultStrategyExecution/PlaygroundResultStrategyExecution.tsx similarity index 62% rename from frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultStrategyExecution/PlaygroundResultStrategyExecution.tsx rename to frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultStrategyExecution/PlaygroundResultStrategyExecution.tsx index 385ca77c4f..b18ad5f57f 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultStrategyExecution/PlaygroundResultStrategyExecution.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultStrategyExecution/PlaygroundResultStrategyExecution.tsx @@ -1,9 +1,12 @@ -import { ConditionallyRender } from '../../../../../common/ConditionallyRender/ConditionallyRender'; -import { StrategySeparator } from '../../../../../common/StrategySeparator/StrategySeparator'; -import { Box, Chip } from '@mui/material'; +import { ConditionallyRender } from '../../../../../../common/ConditionallyRender/ConditionallyRender'; +import { StrategySeparator } from '../../../../../../common/StrategySeparator/StrategySeparator'; +import { Box, Chip, styled } from '@mui/material'; import { useStyles } from './PlaygroundResultStrategyExecution.styles'; -import { PlaygroundFeatureStrategyResult } from '../../../../../../hooks/api/actions/usePlayground/playground.model'; -import useUiConfig from '../../../../../../hooks/api/getters/useUiConfig/useUiConfig'; +import { + PlaygroundFeatureStrategyResult, + PlaygroundRequestSchema, +} from '../../../../../../../hooks/api/actions/usePlayground/playground.model'; +import useUiConfig from '../../../../../../../hooks/api/getters/useUiConfig/useUiConfig'; import React from 'react'; import { PlaygroundResultConstraintExecution } from '../PlaygroundResultConstraintExecution/PlaygroundResultConstraintExecution'; import { PlaygroundResultSegmentExecution } from '../PlaygroundResultSegmentExecution/PlaygroundResultSegmentExecution'; @@ -11,24 +14,38 @@ import { PlaygroundResultSegmentExecution } from '../PlaygroundResultSegmentExec interface PlaygroundResultStrategyExecutionProps { strategyResult: PlaygroundFeatureStrategyResult; percentageFill?: string; + input?: PlaygroundRequestSchema; } +const StyledStrategyExecutionWrapper = styled('div')(({ theme }) => ({ + padding: theme.spacing(1), +})); + export const PlaygroundResultStrategyExecution = ({ strategyResult, + input, }: PlaygroundResultStrategyExecutionProps) => { const { name, constraints, segments } = strategyResult; const { uiConfig } = useUiConfig(); const { classes: styles } = useStyles(); + const hasConstraints = Boolean(constraints && constraints.length > 0); + return ( - <> + 0) } - show={} + show={ + + } /> 0)} @@ -36,6 +53,8 @@ export const PlaygroundResultStrategyExecution = ({ <> @@ -56,6 +75,6 @@ export const PlaygroundResultStrategyExecution = ({ } /> - + ); }; diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultStrategyList/PlaygroundResultStrategyList.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultStrategyList/PlaygroundResultStrategyList.tsx new file mode 100644 index 0000000000..29e3867dad --- /dev/null +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultStrategyList/PlaygroundResultStrategyList.tsx @@ -0,0 +1,88 @@ +import { + PlaygroundFeatureSchema, + PlaygroundFeatureStrategyResult, + PlaygroundRequestSchema, +} from '../../../../../../hooks/api/actions/usePlayground/playground.model'; +import { ConditionallyRender } from '../../../../../common/ConditionallyRender/ConditionallyRender'; +import { Alert, styled, Typography } from '@mui/material'; +import { PlaygroundResultFeatureStrategyItem } from '../PlaygroundResultFeatureStrategyItem/PlaygroundResultFeatureStrategyItem'; + +interface PlaygroundResultStrategyListProps { + strategies: PlaygroundFeatureStrategyResult[]; + input?: PlaygroundRequestSchema; + compact?: boolean; +} + +export const PlaygroundResultStrategyList = ({ + strategies, + input, + compact = false, +}: PlaygroundResultStrategyListProps) => { + return ( + 0} + show={ + <> + {`Strategies (${strategies.length})`} + {strategies.map((strategy, index) => ( + + ))} + + } + /> + ); +}; + +const StyledAlertWrapper = styled('div')(({ theme }) => ({ + width: '100%', + display: 'flex', + flexDirection: 'column', + borderRadius: theme.shape.borderRadiusMedium, + border: `1px solid ${theme.palette.info.main}`, +})); + +const StyledListWrapper = styled('div')(({ theme }) => ({ + padding: theme.spacing(1, 0.5), +})); + +const StyledAlert = styled(Alert)(({ theme }) => ({ + borderBottomLeftRadius: 0, + borderBottomRightRadius: 0, +})); + +interface WrappedPlaygroundResultStrategyListProps + extends PlaygroundResultStrategyListProps { + feature: PlaygroundFeatureSchema; +} + +export const WrappedPlaygroundResultStrategyList = ({ + strategies, + feature, + input, +}: WrappedPlaygroundResultStrategyListProps) => { + return ( + + + If environment would be enabled then this feature would be{' '} + {feature.isEnabled ? 'TRUE' : 'FALSE'} and the strategies would + evaluate like this:{' '} + + + + + + ); +}; diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/PlaygroundResultChip/PlaygroundResultChip.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/PlaygroundResultChip/PlaygroundResultChip.tsx index b5294110eb..d91b4ae9c6 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/PlaygroundResultChip/PlaygroundResultChip.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/PlaygroundResultChip/PlaygroundResultChip.tsx @@ -6,7 +6,7 @@ import { ReactComponent as FeatureEnabledIcon } from '../../../../../assets/icon import { ReactComponent as FeatureDisabledIcon } from '../../../../../assets/icons/isenabled-false.svg'; interface IResultChipProps { - enabled: boolean; + enabled: boolean | 'unevaluated'; // Result icon - defaults to true showIcon?: boolean; label?: string; @@ -53,7 +53,7 @@ export const PlaygroundResultChip = ({ const theme = useTheme(); const icon = ( { const [searchParams, setSearchParams] = useSearchParams(); @@ -49,6 +54,75 @@ export const PlaygroundResultsTable = ({ const isExtraSmallScreen = useMediaQuery(theme.breakpoints.down('sm')); const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); + const COLUMNS = useMemo(() => { + return [ + { + Header: 'Name', + accessor: 'name', + searchable: true, + minWidth: 160, + Cell: ({ value, row: { original } }: any) => ( + + ), + }, + { + Header: 'Project ID', + accessor: 'projectId', + sortType: 'alphanumeric', + filterName: 'projectId', + searchable: true, + maxWidth: 170, + Cell: ({ value }: any) => ( + + ), + }, + { + Header: 'Variant', + id: 'variant', + accessor: 'variant.name', + sortType: 'alphanumeric', + filterName: 'variant', + searchable: true, + width: 200, + Cell: ({ + value, + row: { + original: { variant, feature, variants, isEnabled }, + }, + }: any) => ( + + ), + }, + { + Header: 'isEnabled', + accessor: 'isEnabled', + filterName: 'isEnabled', + filterParsing: (value: boolean) => (value ? 'true' : 'false'), + Cell: ({ value }: any) => , + sortType: 'boolean', + sortInverted: true, + }, + { + Header: '', + id: 'info', + Cell: ({ row }: any) => ( + + ), + }, + ]; + }, [input]); + const { data: searchedData, getSearchText, @@ -236,67 +310,3 @@ export const PlaygroundResultsTable = ({ ); }; - -const COLUMNS = [ - { - Header: 'Name', - accessor: 'name', - searchable: true, - minWidth: 160, - Cell: ({ value, row: { original } }: any) => ( - - ), - }, - { - Header: 'Project ID', - accessor: 'projectId', - sortType: 'alphanumeric', - filterName: 'projectId', - searchable: true, - maxWidth: 170, - Cell: ({ value }: any) => ( - - ), - }, - { - Header: 'Variant', - id: 'variant', - accessor: 'variant.name', - sortType: 'alphanumeric', - filterName: 'variant', - searchable: true, - width: 200, - Cell: ({ - value, - row: { - original: { variant, feature, variants, isEnabled }, - }, - }: any) => ( - - ), - }, - { - Header: 'isEnabled', - accessor: 'isEnabled', - filterName: 'isEnabled', - filterParsing: (value: boolean) => (value ? 'true' : 'false'), - Cell: ({ value }: any) => , - sortType: 'boolean', - sortInverted: true, - }, - { - Header: '', - id: 'info', - Cell: ({ row }: any) => ( - - ), - }, -]; diff --git a/frontend/src/hooks/api/actions/usePlayground/playground.model.ts b/frontend/src/hooks/api/actions/usePlayground/playground.model.ts index eff504cda2..b4cedbaffb 100644 --- a/frontend/src/hooks/api/actions/usePlayground/playground.model.ts +++ b/frontend/src/hooks/api/actions/usePlayground/playground.model.ts @@ -65,13 +65,17 @@ export interface PlaygroundFeatureSchema { * @type {boolean} * @memberof PlaygroundFeatureSchema */ - isEnabled: boolean; + isEnabled: boolean | 'unevaluated'; + + isEnabledInCurrentEnvironment: boolean; /** * * @type {PlaygroundFeatureSchemaVariant} * @memberof PlaygroundFeatureSchema */ variant: PlaygroundFeatureSchemaVariant | null; + + strategies: PlaygroundFeatureStrategyResult[]; } export interface PlaygroundResponseSchema { /**