1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-06-23 01:16:27 +02:00

fmt and lint

This commit is contained in:
andreas-unleash 2022-08-04 14:15:57 +03:00
parent 2299eb9305
commit 2bd239f481
26 changed files with 930 additions and 390 deletions

View File

@ -17,17 +17,11 @@ import {
semVerOperators,
} from 'constants/operators';
import { useStyles } from '../ConstraintAccordion.styles';
import {
PlaygroundConstraintSchema,
PlaygroundRequestSchema,
} from '../../../../hooks/api/actions/usePlayground/playground.model';
interface IConstraintAccordionViewProps {
constraint: IConstraint | PlaygroundConstraintSchema;
constraint: IConstraint;
onDelete?: () => void;
onEdit?: () => void;
playgroundInput?: PlaygroundRequestSchema;
maxLength?: number;
sx?: SxProps<Theme>;
}
@ -36,8 +30,6 @@ export const ConstraintAccordionView = ({
onEdit,
onDelete,
sx = undefined,
maxLength,
playgroundInput,
}: IConstraintAccordionViewProps) => {
const { classes: styles } = useStyles();
const [expandable, setExpandable] = useState(true);
@ -53,11 +45,6 @@ export const ConstraintAccordionView = ({
setExpanded(!expanded);
}
};
const backgroundColor = Boolean(playgroundInput)
? !Boolean((constraint as PlaygroundConstraintSchema).result)
? theme.palette.neutral.light
: 'inherit'
: 'inherit';
return (
<Accordion
@ -75,7 +62,6 @@ export const ConstraintAccordionView = ({
'&:hover': {
cursor: expandable ? 'pointer' : 'default!important',
},
backgroundColor: backgroundColor,
}}
>
<ConstraintAccordionViewHeader
@ -85,8 +71,6 @@ export const ConstraintAccordionView = ({
singleValue={singleValue}
allowExpand={setExpandable}
expanded={expanded}
maxLength={maxLength ?? 112}
playgroundInput={playgroundInput}
/>
</AccordionSummary>

View File

@ -3,20 +3,14 @@ import { IConstraint } from 'interfaces/strategy';
import { ConstraintAccordionViewHeaderInfo } from './ConstraintAccordionViewHeaderInfo/ConstraintAccordionViewHeaderInfo';
import { ConstraintAccordionHeaderActions } from '../../ConstraintAccordionHeaderActions/ConstraintAccordionHeaderActions';
import { useStyles } from 'component/common/ConstraintAccordion/ConstraintAccordion.styles';
import {
PlaygroundConstraintSchema,
PlaygroundRequestSchema,
} from 'hooks/api/actions/usePlayground/playground.model';
interface IConstraintAccordionViewHeaderProps {
constraint: IConstraint | PlaygroundConstraintSchema;
constraint: IConstraint;
onDelete?: () => void;
onEdit?: () => void;
singleValue: boolean;
expanded: boolean;
allowExpand: (shouldExpand: boolean) => void;
playgroundInput?: PlaygroundRequestSchema;
maxLength?: number;
}
export const ConstraintAccordionViewHeader = ({
@ -26,8 +20,6 @@ export const ConstraintAccordionViewHeader = ({
singleValue,
allowExpand,
expanded,
maxLength,
playgroundInput,
}: IConstraintAccordionViewHeaderProps) => {
const { classes: styles } = useStyles();
@ -39,9 +31,6 @@ export const ConstraintAccordionViewHeader = ({
singleValue={singleValue}
allowExpand={allowExpand}
expanded={expanded}
result={'result' in constraint ? constraint.result : undefined}
maxLength={maxLength}
playgroundInput={playgroundInput}
/>
<ConstraintAccordionHeaderActions
onEdit={onEdit}

View File

@ -1,13 +1,11 @@
import { styled, Tooltip, Typography, useTheme } from '@mui/material';
import { styled, Tooltip } from '@mui/material';
import { ConstraintViewHeaderOperator } from '../ConstraintViewHeaderOperator/ConstraintViewHeaderOperator';
import { ConditionallyRender } from '../../../../ConditionallyRender/ConditionallyRender';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { ConstraintAccordionViewHeaderSingleValue } from '../ContraintAccordionViewHeaderSingleValue/ConstraintAccordionViewHeaderSingleValue';
import { ConstraintAccordionViewHeaderMultipleValues } from '../ContraintAccordionViewHeaderMultipleValues/ConstraintAccordionViewHeaderMultipleValues';
import React from 'react';
import { IConstraint } from '../../../../../../interfaces/strategy';
import { useStyles } from '../../../ConstraintAccordion.styles';
import { CancelOutlined } from '@mui/icons-material';
import { PlaygroundRequestSchema } from '../../../../../../hooks/api/actions/usePlayground/playground.model';
const StyledHeaderText = styled('span')(({ theme }) => ({
display: '-webkit-box',
@ -41,9 +39,7 @@ interface ConstraintAccordionViewHeaderMetaInfoProps {
singleValue: boolean;
expanded: boolean;
allowExpand: (shouldExpand: boolean) => void;
result?: boolean;
maxLength?: number;
playgroundInput?: PlaygroundRequestSchema;
}
export const ConstraintAccordionViewHeaderInfo = ({
@ -51,17 +47,9 @@ export const ConstraintAccordionViewHeaderInfo = ({
singleValue,
allowExpand,
expanded,
result,
playgroundInput,
maxLength = 112,
}: ConstraintAccordionViewHeaderMetaInfoProps) => {
const { classes: styles } = useStyles();
const theme = useTheme();
const isPlayground = Boolean(playgroundInput);
const constrainExistsInContext =
isPlayground &&
Boolean(playgroundInput?.context[constraint.contextName]);
return (
<StyledHeaderWrapper>
@ -69,23 +57,6 @@ export const ConstraintAccordionViewHeaderInfo = ({
<Tooltip title={constraint.contextName} arrow>
<StyledHeaderText>
{constraint.contextName}
<ConditionallyRender
condition={isPlayground}
show={
<Typography
variant={'body1'}
color={
constrainExistsInContext
? theme.palette.neutral.dark
: theme.palette.error.main
}
>
{playgroundInput?.context[
constraint.contextName
] || 'no value'}
</Typography>
}
/>
</StyledHeaderText>
</Tooltip>
<ConstraintViewHeaderOperator constraint={constraint} />
@ -107,10 +78,6 @@ export const ConstraintAccordionViewHeaderInfo = ({
}
/>
</div>
<ConditionallyRender
condition={result !== undefined && !Boolean(result)}
show={<CancelOutlined color="error" sx={{ mt: 1 }} />}
/>
</StyledHeaderWrapper>
);
};

View File

@ -27,7 +27,9 @@ export const FeatureResultInfoPopoverCell = ({
const { classes: styles } = useStyles();
const ref = useRef(null);
const togglePopover = (event: React.SyntheticEvent) => {
console.log(feature)
const togglePopover = () => {
setOpen(!open);
};

View File

@ -13,7 +13,9 @@ export const useStyles = makeStyles()(theme => ({
gap: '12px',
marginTop: '12px',
},
alertRow: {},
alertRow: {
margin: theme.spacing(1,0),
},
descriptionRow: {
marginBottom: theme.spacing(2),
},

View File

@ -24,25 +24,19 @@ export const PlaygroundResultFeatureDetails = ({
const theme = useTheme();
const description =
feature.isEnabled === 'unevaluated'
? `This feature toggle is Unevaluated in ${input?.environment} because `
: feature.isEnabled
feature.isEnabled
? `This feature toggle is True in ${input?.environment} because `
: `This feature toggle is False in ${input?.environment} because `;
const reason =
feature.isEnabled === 'unevaluated'
? 'custom strategies are not evaluated yet'
feature.isEnabled
? 'at least one strategy is True'
: !feature.isEnabledInCurrentEnvironment
? 'the environment is disabled'
: feature.isEnabled
? 'at least one strategy is True'
: 'all strategies are False';
const color =
feature.isEnabled === 'unevaluated'
? theme.palette.warning.main
: feature.isEnabled
feature.isEnabled
? theme.palette.success.main
: theme.palette.error.main;
@ -67,10 +61,7 @@ export const PlaygroundResultFeatureDetails = ({
<span>
<PlaygroundResultChip
enabled={feature.isEnabled}
label={
feature.isEnabled === 'unevaluated'
? '?'
: Boolean(feature.isEnabled)
label={feature.isEnabled
? 'True'
: 'False'
}

View File

@ -20,17 +20,17 @@ export const PlaygroundResultFeatureStrategyList = ({
}: PlaygroundResultFeatureStrategyListProps) => {
return (
<ConditionallyRender
condition={!feature.isEnabledInCurrentEnvironment}
condition={!feature.isEnabledInCurrentEnvironment && Boolean(feature?.strategies?.data)}
show={
<WrappedPlaygroundResultStrategyList
strategies={feature?.strategies}
strategies={feature?.strategies?.data!}
feature={feature}
input={input}
/>
}
elseShow={
<PlaygroundResultStrategyLists
strategies={feature?.strategies}
strategies={feature?.strategies?.data!}
input={input}
/>
}

View File

@ -25,8 +25,8 @@ const StyledItemWrapper = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
marginTop: '4px',
gap: '8px',
margin: theme.spacing(0.5),
gap: theme.spacing(1),
}));
export const PlaygroundResultFeatureStrategyItem = ({
@ -63,8 +63,8 @@ export const PlaygroundResultFeatureStrategyItem = ({
condition={index > 0}
show={<StrategySeparator text="OR" />}
/>
<StyledItemWrapper>
<Typography variant={'subtitle1'} color={'text.secondary'}>
<StyledItemWrapper sx={{mr: 2}}>
<Typography variant={'subtitle1'} color={'text.secondary'} sx={{ml: 1}}>
{index + 1}
</Typography>
<Box className={styles.innerContainer} sx={{ border }}>

View File

@ -0,0 +1,158 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
constraintIconContainer: {
backgroundColor: theme.palette.primary.main,
borderRadius: '50%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
marginRight: theme.spacing(1),
[theme.breakpoints.down(650)]: {
marginBottom: '1rem',
marginRight: 0,
},
},
constraintIcon: {
fill: '#fff',
},
accordion: {
border: `1px solid ${theme.palette.grey[400]}`,
borderRadius: '8px',
backgroundColor: '#fff',
boxShadow: 'none',
margin: 0,
},
accordionRoot: {
'&:before': {
opacity: '0 !important',
},
},
accordionEdit: {
backgroundColor: '#F6F6FA',
},
headerMetaInfo: {
display: 'flex',
alignItems: 'stretch',
[theme.breakpoints.down(710)]: { flexDirection: 'column' },
},
headerContainer: {
display: 'flex',
alignItems: 'center',
width: '100%',
[theme.breakpoints.down(710)]: {
flexDirection: 'column',
alignItems: 'center',
position: 'relative',
},
},
headerValuesContainerWrapper: {
display: 'flex',
alignItems: 'stretch',
margin: 'auto 0',
},
headerValuesContainer: {
display: 'flex',
justifyContent: 'stretch',
margin: 'auto 0',
flexDirection: 'column',
},
headerValues: {
fontSize: theme.fontSizes.smallBody,
},
headerValuesExpand: {
fontSize: theme.fontSizes.smallBody,
marginTop: '4px',
color: theme.palette.primary.dark,
[theme.breakpoints.down(710)]: {
textAlign: 'center',
},
},
headerConstraintContainer: {
minWidth: '220px',
position: 'relative',
paddingRight: '1rem',
[theme.breakpoints.between(1101, 1365)]: {
minWidth: '152px',
paddingRight: '0.5rem',
},
},
editingBadge: {
borderRadius: theme.shape.borderRadiusExtraLarge,
padding: '0.25rem 0.5rem',
backgroundColor: '#635DC5',
color: '#fff',
marginLeft: 'auto',
fontSize: '0.9rem',
[theme.breakpoints.down(650)]: {
position: 'absolute',
right: 0,
top: '-10px',
},
},
headerText: {
maxWidth: '400px',
fontSize: theme.fontSizes.smallBody,
[theme.breakpoints.down('xl')]: {
display: 'none',
},
},
selectContainer: {
display: 'flex',
alignItems: 'center',
[theme.breakpoints.down(770)]: {
flexDirection: 'column',
},
},
bottomSelect: {
[theme.breakpoints.down(770)]: {
marginTop: '1rem',
},
display: 'inline-flex',
},
headerSelect: {
marginRight: '1rem',
width: '200px',
[theme.breakpoints.between(1101, 1365)]: {
width: '170px',
marginRight: '8px',
},
},
chip: {
margin: '0 0.5rem 0.5rem 0',
},
chipValue: {
whiteSpace: 'pre',
},
headerActions: {
marginLeft: 'auto',
whiteSpace: 'nowrap',
[theme.breakpoints.down(710)]: {
display: 'none',
},
},
accordionDetails: {
borderTop: `1px dashed ${theme.palette.grey[300]}`,
display: 'flex',
flexDirection: 'column',
},
valuesContainer: {
padding: '1rem 0rem',
maxHeight: '400px',
overflowY: 'auto',
},
summary: {
border: 'none',
padding: theme.spacing(0.5, 3),
'&:hover .valuesExpandLabel': {
textDecoration: 'underline',
},
},
settingsIcon: {
height: '32.5px',
width: '32.5px',
marginRight: '0.5rem',
fill: theme.palette.inactiveIcon,
},
form: { padding: 0, margin: 0, width: '100%' },
}));

View File

@ -0,0 +1,41 @@
import {ConstraintIcon} from 'component/common/ConstraintAccordion/ConstraintIcon';
import {
PlaygroundConstraintAccordionViewHeaderInfo
} from './PlaygroundConstraintAccordionViewHeaderInfo/PlaygroundConstraintAccordionViewHeaderInfo';
import {useStyles} from 'component/common/ConstraintAccordion/ConstraintAccordion.styles';
import {PlaygroundConstraintSchema, PlaygroundRequestSchema,} from 'hooks/api/actions/usePlayground/playground.model';
interface PlaygroundConstraintAccordionViewHeaderProps {
constraint: PlaygroundConstraintSchema;
singleValue: boolean;
expanded: boolean;
allowExpand: (shouldExpand: boolean) => void;
playgroundInput?: PlaygroundRequestSchema;
maxLength?: number;
}
export const PlaygroundConstraintAccordionViewHeader = ({
constraint,
singleValue,
allowExpand,
expanded,
maxLength,
playgroundInput,
}: PlaygroundConstraintAccordionViewHeaderProps) => {
const { classes: styles } = useStyles();
return (
<div className={styles.headerContainer}>
<ConstraintIcon />
<PlaygroundConstraintAccordionViewHeaderInfo
constraint={constraint}
singleValue={singleValue}
allowExpand={allowExpand}
expanded={expanded}
result={constraint.result}
maxLength={maxLength}
playgroundInput={playgroundInput}
/>
</div>
);
};

View File

@ -0,0 +1,110 @@
import { styled, Tooltip, Typography, useTheme } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { PlaygroundConstraintAccordionViewHeaderSingleValue } from '../PlaygroundContraintAccordionViewHeaderSingleValue/PlaygroundConstraintAccordionViewHeaderSingleValue';
import { PLaygroundConstraintAccordionViewHeaderMultipleValues } from '../PlaygroundContraintAccordionViewHeaderMultipleValues/PLaygroundConstraintAccordionViewHeaderMultipleValues';
import React from 'react';
import { useStyles } from '../../PlaygroundConstraintAccordion.styles';
import { CancelOutlined } from '@mui/icons-material';
import {PlaygroundConstraintSchema, PlaygroundRequestSchema} from 'hooks/api/actions/usePlayground/playground.model';
import {
ConstraintViewHeaderOperator
} from "component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintViewHeaderOperator/ConstraintViewHeaderOperator";
const StyledHeaderText = styled('span')(({ theme }) => ({
display: '-webkit-box',
WebkitLineClamp: 3,
WebkitBoxOrient: 'vertical',
overflow: 'hidden',
maxWidth: '100px',
minWidth: '100px',
marginRight: '10px',
marginTop: 'auto',
marginBottom: 'auto',
wordBreak: 'break-word',
fontSize: theme.fontSizes.smallBody,
[theme.breakpoints.down(710)]: {
textAlign: 'center',
padding: theme.spacing(1, 0),
marginRight: 'inherit',
maxWidth: 'inherit',
},
}));
const StyledHeaderWrapper = styled('div')(({ theme }) => ({
display: 'flex',
width: '100%',
justifyContent: 'space-between',
borderRadius: theme.spacing(1),
}));
interface PlaygroundConstraintAccordionViewHeaderInfoProps {
constraint: PlaygroundConstraintSchema;
singleValue: boolean;
expanded: boolean;
allowExpand: (shouldExpand: boolean) => void;
result?: boolean;
maxLength?: number;
playgroundInput?: PlaygroundRequestSchema;
}
export const PlaygroundConstraintAccordionViewHeaderInfo = ({
constraint,
singleValue,
allowExpand,
expanded,
result,
playgroundInput,
maxLength = 112,
}: PlaygroundConstraintAccordionViewHeaderInfoProps) => {
const { classes: styles } = useStyles();
const theme = useTheme();
const constraintExistsInContext =
Boolean(playgroundInput?.context[constraint.contextName]);
return (
<StyledHeaderWrapper>
<div className={styles.headerMetaInfo}>
<Tooltip title={constraint.contextName} arrow>
<StyledHeaderText>
{constraint.contextName}
<Typography
variant={'body1'}
color={
constraintExistsInContext
? theme.palette.neutral.dark
: theme.palette.error.main
}
>
{playgroundInput?.context[
constraint.contextName
] || 'no value'}
</Typography>
</StyledHeaderText>
</Tooltip>
<ConstraintViewHeaderOperator constraint={constraint} />
<ConditionallyRender
condition={singleValue}
show={
<PlaygroundConstraintAccordionViewHeaderSingleValue
constraint={constraint}
allowExpand={allowExpand}
/>
}
elseShow={
<PLaygroundConstraintAccordionViewHeaderMultipleValues
constraint={constraint}
expanded={expanded}
allowExpand={allowExpand}
maxLength={maxLength}
/>
}
/>
</div>
<ConditionallyRender
condition={result !== undefined && !Boolean(result)}
show={<CancelOutlined color="error" sx={{ mt: 1 }} />}
/>
</StyledHeaderWrapper>
);
};

View File

@ -0,0 +1,85 @@
import {ConditionallyRender} from 'component/common/ConditionallyRender/ConditionallyRender';
import {styled, Typography} from '@mui/material';
import React, {useEffect, useMemo, useState} from 'react';
import classnames from 'classnames';
import {useStyles} from '../../PlaygroundConstraintAccordion.styles';
import {PlaygroundConstraintSchema} from 'hooks/api/actions/usePlayground/playground.model';
const StyledValuesSpan = styled('span')(({ theme }) => ({
display: '-webkit-box',
WebkitLineClamp: 2,
WebkitBoxOrient: 'vertical',
overflow: 'hidden',
wordBreak: 'break-word',
fontSize: theme.fontSizes.smallBody,
margin: 'auto 0',
[theme.breakpoints.down(710)]: {
margin: theme.spacing(1, 0),
textAlign: 'center',
},
}));
interface ConstraintSingleValueProps {
constraint: PlaygroundConstraintSchema;
expanded: boolean;
maxLength: number;
allowExpand: (shouldExpand: boolean) => void;
}
export const PLaygroundConstraintAccordionViewHeaderMultipleValues = ({
constraint,
expanded,
allowExpand,
maxLength,
}: ConstraintSingleValueProps) => {
const { classes: styles } = useStyles();
const [expandable, setExpandable] = useState(false);
const text = useMemo(() => {
return constraint?.values?.map(value => value).join(', ');
}, [constraint]);
useEffect(() => {
if (text) {
allowExpand((text?.length ?? 0) > maxLength);
setExpandable((text?.length ?? 0) > maxLength);
}
}, [text, maxLength, allowExpand, setExpandable]);
return (
<div className={styles.headerValuesContainerWrapper}>
<div className={styles.headerValuesContainer}>
<ConditionallyRender
condition={!Boolean(constraint.result)}
show={
<Typography
variant={'body2'}
color={'error'}
noWrap={true}
sx={{ mr: 1 }}
>
does not match any values{' '}
</Typography>
}
/>
<StyledValuesSpan>{text}</StyledValuesSpan>
<ConditionallyRender
condition={expandable}
show={
<p
className={classnames(
styles.headerValuesExpand,
'valuesExpandLabel'
)}
>
{!expanded
? `View all (${constraint?.values?.length})`
: 'View less'}
</p>
}
/>
</div>
</div>
);
};

View File

@ -0,0 +1,47 @@
import React, {useEffect} from 'react';
import {Chip, styled, Typography} from '@mui/material';
import {formatConstraintValue} from 'utils/formatConstraintValue';
import {useStyles} from '../../PlaygroundConstraintAccordion.styles';
import {useLocationSettings} from 'hooks/useLocationSettings';
import {PlaygroundConstraintSchema} from 'hooks/api/actions/usePlayground/playground.model';
import {ConditionallyRender} from 'component/common/ConditionallyRender/ConditionallyRender';
const StyledSingleValueChip = styled(Chip)(({ theme }) => ({
margin: 'auto 0',
[theme.breakpoints.down(710)]: {
margin: theme.spacing(1, 0),
},
}));
interface ConstraintSingleValueProps {
constraint: PlaygroundConstraintSchema;
allowExpand: (shouldExpand: boolean) => void;
}
export const PlaygroundConstraintAccordionViewHeaderSingleValue = ({
constraint,
allowExpand,
}: ConstraintSingleValueProps) => {
const { locationSettings } = useLocationSettings();
const { classes: styles } = useStyles();
useEffect(() => {
allowExpand(false);
}, [allowExpand]);
return (
<div className={styles.headerValuesContainerWrapper}>
<ConditionallyRender
condition={!Boolean(constraint.result)}
show={
<Typography variant={'body1'} color={'error'}>
does not match any values{' '}
</Typography>
}
/>
<StyledSingleValueChip
label={formatConstraintValue(constraint, locationSettings)}
/>
</div>
);
};

View File

@ -0,0 +1,93 @@
import { useState } from 'react';
import {
Accordion,
AccordionSummary,
AccordionDetails,
SxProps,
Theme,
useTheme,
} from '@mui/material';
import { PlaygroundConstraintAccordionViewHeader } from './PlaygroundConstraintAccordionViewHeader/PlaygroundConstraintAccordionViewHeader';
import { oneOf } from 'utils/oneOf';
import {
dateOperators,
numOperators,
semVerOperators,
} from 'constants/operators';
import { useStyles } from './PlaygroundConstraintAccordion.styles';
import {
PlaygroundConstraintSchema,
PlaygroundRequestSchema,
} from 'hooks/api/actions/usePlayground/playground.model';
import {
ConstraintAccordionViewBody
} from "component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewBody/ConstraintAccordionViewBody";
interface IConstraintAccordionViewProps {
constraint: PlaygroundConstraintSchema;
playgroundInput?: PlaygroundRequestSchema;
maxLength?: number;
sx?: SxProps<Theme>;
}
export const PlaygroundResultConstraintAccordionView = ({
constraint,
sx = undefined,
maxLength,
playgroundInput,
}: IConstraintAccordionViewProps) => {
const { classes: styles } = useStyles();
const [expandable, setExpandable] = useState(true);
const [expanded, setExpanded] = useState(false);
const theme = useTheme();
const singleValue = oneOf(
[...semVerOperators, ...numOperators, ...dateOperators],
constraint.operator
);
const handleClick = () => {
if (expandable) {
setExpanded(!expanded);
}
};
const backgroundColor = Boolean(playgroundInput)
? !Boolean((constraint as PlaygroundConstraintSchema).result)
? theme.palette.neutral.light
: 'inherit'
: 'inherit';
return (
<Accordion
className={styles.accordion}
classes={{ root: styles.accordionRoot }}
expanded={expanded}
sx={sx}
>
<AccordionSummary
classes={{ root: styles.summary }}
expandIcon={null}
onClick={handleClick}
sx={{
cursor: expandable ? 'pointer' : 'default!important',
'&:hover': {
cursor: expandable ? 'pointer' : 'default!important',
},
backgroundColor: backgroundColor,
}}
>
<PlaygroundConstraintAccordionViewHeader
constraint={constraint}
singleValue={singleValue}
allowExpand={setExpandable}
expanded={expanded}
maxLength={maxLength ?? 112}
playgroundInput={playgroundInput}
/>
</AccordionSummary>
<AccordionDetails className={styles.accordionDetails}>
<ConstraintAccordionViewBody constraint={constraint} />
</AccordionDetails>
</Accordion>
);
};

View File

@ -3,11 +3,13 @@ import {
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 { styled } from '@mui/material';
import {
PlaygroundResultConstraintAccordionView
} from "./PlaygroundResultConstraintAccordion/PlaygroundResultConstraintAccordionView/PlaygroundResultConstraintAccordionView";
interface PlaygroundResultConstraintExecutionProps {
constraints?: PlaygroundConstraintSchema[];
@ -16,7 +18,7 @@ interface PlaygroundResultConstraintExecutionProps {
}
export const PlaygroundResultConstraintExecutionWrapper = styled('div')(
({ theme }) => ({
() => ({
width: '100%',
display: 'flex',
flexDirection: 'column',
@ -38,7 +40,7 @@ export const PlaygroundResultConstraintExecution = ({
condition={index > 0}
show={<StrategySeparator text="AND" />}
/>
<ConstraintAccordionView
<PlaygroundResultConstraintAccordionView
constraint={constraint}
playgroundInput={input}
maxLength={compact ? 25 : 50}

View File

@ -1,14 +1,14 @@
import {
PlaygroundSegmentSchema,
PlaygroundRequestSchema,
} from '../../../../../../../../../hooks/api/actions/usePlayground/playground.model';
} 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?: PlaygroundSegmentSchema[];
@ -43,7 +43,7 @@ const SegmentExecutionWrapper = styled('div')(({ theme }) => ({
marginTop: theme.spacing(2),
},
background: theme.palette.neutral.light,
marginBottom: '8px',
marginBottom: theme.spacing(1),
}));
const SegmentExecutionConstraintWrapper = styled('div')(({ theme }) => ({
@ -55,7 +55,7 @@ const SegmentResultTextWrapper = styled('div')(({ theme }) => ({
display: 'inline-flex',
justifyContent: 'center',
marginRight: '12px',
gap: '8px',
gap: theme.spacing(1),
}));
export const PlaygroundResultSegmentExecution = ({

View File

@ -1,24 +1,17 @@
import { ConditionallyRender } from '../../../../../../../../common/ConditionallyRender/ConditionallyRender';
import { StrategySeparator } from '../../../../../../../../common/StrategySeparator/StrategySeparator';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
import { Box, Chip, styled } from '@mui/material';
import { useStyles } from './PlaygroundResultStrategyExecution.styles';
import {
PlaygroundRequestSchema,
PlaygroundStrategySchema,
} from '../../../../../../../../../hooks/api/actions/usePlayground/playground.model';
import useUiConfig from '../../../../../../../../../hooks/api/getters/useUiConfig/useUiConfig';
import React, { Fragment } from 'react';
import { PlaygroundResultConstraintExecution } from '../PlaygroundResultConstraintExecution/PlaygroundResultConstraintExecution';
import { PlaygroundResultSegmentExecution } from '../PlaygroundResultSegmentExecution/PlaygroundResultSegmentExecution';
import {
parseParameterNumber,
parseParameterString,
parseParameterStrings,
} from '../../../../../../../../../utils/parseParameter';
import PercentageCircle from '../../../../../../../../common/PercentageCircle/PercentageCircle';
import StringTruncator from '../../../../../../../../common/StringTruncator/StringTruncator';
import { useStrategies } from '../../../../../../../../../hooks/api/getters/useStrategies/useStrategies';
import { PlaygroundConstraintItem } from '../PlaygroundConstraintItem/PlaygroundConstraintItem';
} 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';
import { PlaygroundResultStrategyExecutionParameters } from './PlaygroundResultStrategyExecutionParameters/PlaygroundResultStrategyExecutionParameters';
import { PlaygroundResultStrategyExecutionCustomStrategyParams } from './PlaygroundResultStrategyExecutionCustomStrategyParams./PlaygroundResultStrategyExecutionCustomStrategyParams';
interface PlaygroundResultStrategyExecutionProps {
strategyResult: PlaygroundStrategySchema;
@ -31,7 +24,7 @@ const StyledStrategyExecutionWrapper = styled('div')(({ theme }) => ({
}));
const StyledParamWrapper = styled('div')(({ theme }) => ({
padding: theme.spacing(2, 1),
padding: theme.spacing(0, 0),
}));
export const PlaygroundResultStrategyExecution = ({
@ -39,7 +32,7 @@ export const PlaygroundResultStrategyExecution = ({
input,
}: PlaygroundResultStrategyExecutionProps) => {
const { name, constraints, segments, parameters } = strategyResult;
const { strategies } = useStrategies();
const { uiConfig } = useUiConfig();
const { classes: styles } = useStyles();
@ -49,267 +42,6 @@ export const PlaygroundResultStrategyExecution = ({
return null;
}
const definition = strategies.find(strategyDefinition => {
return strategyDefinition.name === strategyResult.name;
});
const renderParameters = () => {
return Object.keys(parameters).map(key => {
switch (key) {
case 'rollout':
case 'Rollout':
const percentage = parseParameterNumber(parameters[key]);
return (
<Box
className={styles.summary}
key={key}
sx={{ display: 'flex', alignItems: 'center' }}
>
<Box sx={{ mr: '1rem' }}>
<PercentageCircle
percentage={percentage}
size="2rem"
/>
</Box>
<div>
<Chip
color="success"
variant="outlined"
size="small"
label={`${percentage}%`}
/>{' '}
of your base{' '}
{constraints.length > 0
? 'who match constraints'
: ''}{' '}
is included.
</div>
</Box>
);
case 'userIds':
case 'UserIds':
const users = parseParameterStrings(parameters[key]);
return (
<PlaygroundConstraintItem
key={key}
value={users}
text="user"
input={
Boolean(input?.context?.[key])
? input?.context?.[key]
: 'no value'
}
showReason={
Boolean(input?.context?.[key])
? !users.includes(input?.context?.[key])
: undefined
}
/>
);
case 'hostNames':
case 'HostNames':
const hosts = parseParameterStrings(parameters[key]);
console.log(input?.context);
console.log(key);
console.log(input?.context?.[key]);
console.log(hosts.includes(input?.context?.[key]));
return (
<PlaygroundConstraintItem
key={key}
value={hosts}
text={'host'}
input={
Boolean(input?.context?.[key])
? input?.context?.[key]
: 'no value'
}
showReason={
Boolean(input?.context?.[key])
? !hosts.includes(input?.context?.[key])
: undefined
}
/>
);
case 'IPs':
const IPs = parseParameterStrings(parameters[key]);
return (
<PlaygroundConstraintItem
key={key}
value={IPs}
text={'IP'}
input={
Boolean(input?.context?.[key])
? input?.context?.[key]
: 'no value'
}
showReason={
Boolean(input?.context?.[key])
? !IPs.includes(input?.context?.[key])
: undefined
}
/>
);
case 'stickiness':
case 'groupId':
return null;
default:
return null;
}
});
};
const renderCustomStrategyParameters = () => {
if (!definition?.editable) return null;
return definition?.parameters.map((param: any, index: number) => {
const notLastItem = index !== definition?.parameters?.length - 1;
switch (param?.type) {
case 'list':
const values = parseParameterStrings(
strategyResult?.parameters[param.name]
);
return (
<Fragment key={param?.name}>
<PlaygroundConstraintItem
value={values}
text={param.name}
/>
<ConditionallyRender
condition={notLastItem}
show={<StrategySeparator text="AND" />}
/>
</Fragment>
);
case 'percentage':
return (
<Fragment key={param?.name}>
<div>
<Chip
size="small"
variant="outlined"
color="success"
label={`${
strategyResult?.parameters[param.name]
}%`}
/>{' '}
of your base{' '}
{constraints?.length > 0
? 'who match constraints'
: ''}{' '}
is included.
</div>
<PercentageCircle
percentage={parseParameterNumber(
strategyResult.parameters[param.name]
)}
/>
<ConditionallyRender
condition={notLastItem}
show={<StrategySeparator text="AND" />}
/>
</Fragment>
);
case 'boolean':
return (
<Fragment key={param.name}>
<p key={param.name}>
<StringTruncator
maxLength={15}
maxWidth="150"
text={param.name}
/>{' '}
{strategyResult.parameters[param.name]}
</p>
<ConditionallyRender
condition={
typeof strategyResult.parameters[
param.name
] !== 'undefined'
}
show={
<ConditionallyRender
condition={notLastItem}
show={<StrategySeparator text="AND" />}
/>
}
/>
</Fragment>
);
case 'string':
const value = parseParameterString(
strategyResult.parameters[param.name]
);
return (
<ConditionallyRender
condition={
typeof strategyResult.parameters[param.name] !==
'undefined'
}
key={param.name}
show={
<>
<p className={styles.valueContainer}>
<StringTruncator
maxWidth="150"
maxLength={15}
text={param.name}
/>
<span className={styles.valueSeparator}>
is set to
</span>
<StringTruncator
maxWidth="300"
text={value}
maxLength={50}
/>
</p>
<ConditionallyRender
condition={notLastItem}
show={<StrategySeparator text="AND" />}
/>
</>
}
/>
);
case 'number':
const number = parseParameterNumber(
strategyResult.parameters[param.name]
);
return (
<ConditionallyRender
condition={number !== undefined}
key={param.name}
show={
<>
<p className={styles.valueContainer}>
<StringTruncator
maxLength={15}
maxWidth="150"
text={param.name}
/>
<span className={styles.valueSeparator}>
is set to
</span>
<StringTruncator
maxWidth="300"
text={String(number)}
maxLength={50}
/>
</p>
<ConditionallyRender
condition={notLastItem}
show={<StrategySeparator text="AND" />}
/>
</>
}
/>
);
case 'default':
return null;
}
return null;
});
};
return (
<StyledStrategyExecutionWrapper>
<ConditionallyRender
@ -359,8 +91,18 @@ export const PlaygroundResultStrategyExecution = ({
}
/>
<StyledParamWrapper>
{renderParameters()}
{renderCustomStrategyParameters()}
<PlaygroundResultStrategyExecutionParameters
parameters={parameters}
constraints={constraints}
input={input}
/>
<StyledParamWrapper sx={{ pt: 2}}>
<PlaygroundResultStrategyExecutionCustomStrategyParams
strategyName={strategyResult.name}
parameters={parameters}
constraints={constraints}
/>
</StyledParamWrapper>
</StyledParamWrapper>
</StyledStrategyExecutionWrapper>
);

View File

@ -0,0 +1,181 @@
import {
parseParameterNumber,
parseParameterString,
parseParameterStrings,
} from 'utils/parseParameter';
import React, { Fragment } from 'react';
import { PlaygroundConstraintItem } from '../PlaygroundConstraintItem/PlaygroundConstraintItem';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
import { Chip } from '@mui/material';
import PercentageCircle from 'component/common/PercentageCircle/PercentageCircle';
import StringTruncator from 'component/common/StringTruncator/StringTruncator';
import { IStrategy } from 'interfaces/strategy';
import { PlaygroundConstraintSchema } from 'hooks/api/actions/usePlayground/playground.model';
import { useStyles } from '../PlaygroundResultStrategyExecution.styles';
import { useStrategies } from '../../../../../../../../../../hooks/api/getters/useStrategies/useStrategies';
interface PlaygroundResultStrategyExecutionCustomStrategyProps {
parameters: { [key: string]: string };
strategyName: string;
constraints: PlaygroundConstraintSchema[];
}
export const PlaygroundResultStrategyExecutionCustomStrategyParams = ({
strategyName,
constraints,
parameters,
}: PlaygroundResultStrategyExecutionCustomStrategyProps) => {
const { classes: styles } = useStyles();
const { strategies } = useStrategies();
const definition = strategies.find(strategyDefinition => {
return strategyDefinition.name === strategyName;
});
const renderCustomStrategyParameters = () => {
if (!definition?.editable) return null;
return definition?.parameters.map((param: any, index: number) => {
const notLastItem = index !== definition?.parameters?.length - 1;
switch (param?.type) {
case 'list':
const values = parseParameterStrings(
parameters[param.name]
);
return (
<Fragment key={param?.name}>
<PlaygroundConstraintItem
value={values}
text={param.name}
/>
<ConditionallyRender
condition={notLastItem}
show={<StrategySeparator text="AND" />}
/>
</Fragment>
);
case 'percentage':
return (
<Fragment key={param?.name}>
<div>
<Chip
size="small"
variant="outlined"
color="success"
label={`${parameters[param.name]}%`}
/>{' '}
of your base{' '}
{constraints?.length > 0
? 'who match constraints'
: ''}{' '}
is included.
</div>
<PercentageCircle
percentage={parseParameterNumber(
parameters[param.name]
)}
/>
<ConditionallyRender
condition={notLastItem}
show={<StrategySeparator text="AND" />}
/>
</Fragment>
);
case 'boolean':
return (
<Fragment key={param.name}>
<p key={param.name}>
<StringTruncator
maxLength={15}
maxWidth="150"
text={param.name}
/>{' '}
{parameters[param.name]}
</p>
<ConditionallyRender
condition={
typeof parameters[param.name] !==
'undefined'
}
show={
<ConditionallyRender
condition={notLastItem}
show={<StrategySeparator text="AND" />}
/>
}
/>
</Fragment>
);
case 'string':
const value = parseParameterString(parameters[param.name]);
return (
<ConditionallyRender
condition={
typeof parameters[param.name] !== 'undefined'
}
key={param.name}
show={
<>
<p className={styles.valueContainer}>
<StringTruncator
maxWidth="150"
maxLength={15}
text={param.name}
/>
<span className={styles.valueSeparator}>
is set to
</span>
<StringTruncator
maxWidth="300"
text={value}
maxLength={50}
/>
</p>
<ConditionallyRender
condition={notLastItem}
show={<StrategySeparator text="AND" />}
/>
</>
}
/>
);
case 'number':
const number = parseParameterNumber(parameters[param.name]);
return (
<ConditionallyRender
condition={number !== undefined}
key={param.name}
show={
<>
<p className={styles.valueContainer}>
<StringTruncator
maxLength={15}
maxWidth="150"
text={param.name}
/>
<span className={styles.valueSeparator}>
is set to
</span>
<StringTruncator
maxWidth="300"
text={String(number)}
maxLength={50}
/>
</p>
<ConditionallyRender
condition={notLastItem}
show={<StrategySeparator text="AND" />}
/>
</>
}
/>
);
case 'default':
return null;
}
return null;
});
};
return <>{renderCustomStrategyParameters()}</>;
};

View File

@ -0,0 +1,130 @@
import {
parseParameterNumber,
parseParameterStrings,
} from 'utils/parseParameter';
import { Box, Chip } from '@mui/material';
import PercentageCircle from 'component/common/PercentageCircle/PercentageCircle';
import { PlaygroundConstraintItem } from '../PlaygroundConstraintItem/PlaygroundConstraintItem';
import React from 'react';
import { useStyles } from '../PlaygroundResultStrategyExecution.styles';
import {
PlaygroundConstraintSchema,
PlaygroundRequestSchema,
} from 'hooks/api/actions/usePlayground/playground.model';
import {getMappedParam} from "../helepers";
export interface PlaygroundResultStrategyExecutionParametersProps {
parameters: { [key: string]: string };
constraints: PlaygroundConstraintSchema[];
input?: PlaygroundRequestSchema;
}
export const PlaygroundResultStrategyExecutionParameters = ({
parameters,
constraints,
input,
}: PlaygroundResultStrategyExecutionParametersProps) => {
const { classes: styles } = useStyles();
const renderParameters = () => {
return Object.keys(parameters).map(key => {
switch (key) {
case 'rollout':
case 'Rollout':
const percentage = parseParameterNumber(parameters[key]);
return (
<Box
className={styles.summary}
key={key}
sx={{ display: 'flex', alignItems: 'center' }}
>
<Box sx={{ mr: '1rem' }}>
<PercentageCircle
percentage={percentage}
size="2rem"
/>
</Box>
<div>
<Chip
color="success"
variant="outlined"
size="small"
label={`${percentage}%`}
/>{' '}
of your base{' '}
{constraints.length > 0
? 'who match constraints'
: ''}{' '}
is included.
</div>
</Box>
);
case 'userIds':
case 'UserIds':
const users = parseParameterStrings(parameters[key]);
return (
<PlaygroundConstraintItem
key={key}
value={users}
text="user"
input={
Boolean(input?.context?.[getMappedParam(key)])
? input?.context?.[getMappedParam(key)]
: 'no value'
}
showReason={
Boolean(input?.context?.[getMappedParam(key)])
? !users.includes(input?.context?.[getMappedParam(key)])
: undefined
}
/>
);
case 'hostNames':
case 'HostNames':
const hosts = parseParameterStrings(parameters[key]);
return (
<PlaygroundConstraintItem
key={key}
value={hosts}
text={'host'}
input={
Boolean(input?.context?.[getMappedParam(key)])
? input?.context?.[getMappedParam(key)]
: 'no value'
}
showReason={
Boolean(input?.context?.[getMappedParam(key)])
? !hosts.includes(input?.context?.[getMappedParam(key)])
: undefined
}
/>
);
case 'IPs':
const IPs = parseParameterStrings(parameters[key]);
return (
<PlaygroundConstraintItem
key={key}
value={IPs}
text={'IP'}
input={
Boolean(input?.context?.[getMappedParam(key)])
? input?.context?.[getMappedParam(key)]
: 'no value'
}
showReason={
Boolean(input?.context?.[getMappedParam(key)])
? !IPs.includes(input?.context?.[getMappedParam(key)])
: undefined
}
/>
);
case 'stickiness':
case 'groupId':
return null;
default:
return null;
}
});
};
return <>{renderParameters()}</>;
};

View File

@ -0,0 +1,14 @@
export const getMappedParam = (key: string) => {
switch (key) {
case 'userIds':
case 'UserIds':
return 'userId';
case 'hostNames':
case 'HostNames':
return 'hostname';
case 'IPs':
return 'remoteAddress'
default:
return key;
}
}

View File

@ -8,8 +8,8 @@ import { Alert, styled, Typography } from '@mui/material';
import { PlaygroundResultFeatureStrategyItem } from './PlaygroundResultFeatureStrategyItem/PlaygroundResultFeatureStrategyItem';
const StyledAlertWrapper = styled('div')(({ theme }) => ({
width: '100%',
display: 'flex',
padding: `0, 4px`,
flexDirection: 'column',
borderRadius: theme.shape.borderRadiusMedium,
border: `1px solid ${theme.palette.info.border}`,
@ -70,10 +70,10 @@ export const WrappedPlaygroundResultStrategyList = ({
input,
}: WrappedPlaygroundResultStrategyListProps) => {
return (
<StyledAlertWrapper>
<StyledAlertWrapper sx={{pb: 1}}>
<StyledAlert severity={'info'} color={'info'}>
If environment would be enabled then this feature would be{' '}
{feature.isEnabled ? 'TRUE' : 'FALSE'} and the strategies would
{feature.strategies.result ? 'TRUE' : 'FALSE'} and the strategies would
evaluate like this:{' '}
</StyledAlert>
<StyledListWrapper>

View File

@ -3,7 +3,7 @@ import { Box, styled } from '@mui/material';
import { PlaygroundResultChip } from '../PlaygroundResultChip/PlaygroundResultChip';
interface IFeatureStatusCellProps {
enabled: boolean | 'unevaluated';
enabled: boolean;
}
const StyledCellBox = styled(Box)(({ theme }) => ({
@ -22,10 +22,7 @@ export const FeatureStatusCell = ({ enabled }: IFeatureStatusCellProps) => {
<StyledChipWrapper data-loading>
<PlaygroundResultChip
enabled={enabled}
label={
enabled === 'unevaluated'
? '?'
: Boolean(enabled)
label={enabled
? 'True'
: 'False'
}

View File

@ -15,7 +15,7 @@ export interface PlaygroundConstraintSchema {
*/
operator: Operator;
/**
* Whether the operator should be case sensitive or not. Defaults to `false` (being case sensitive).
* Whether the operator should be case-sensitive or not. Defaults to `false` (being case-sensitive).
* @type {boolean}
* @memberof PlaygroundConstraintSchema
*/
@ -64,7 +64,7 @@ export interface PlaygroundFeatureSchema {
* @type {Array<PlaygroundStrategySchema>}
* @memberof PlaygroundFeatureSchema
*/
strategies: Array<PlaygroundStrategySchema>;
strategies: PlaygroundStrategyResultSchema;
/**
* Whether the feature is active and would be evaluated in the provided environment in a normal SDK context.
* @type {boolean}
@ -76,7 +76,7 @@ export interface PlaygroundFeatureSchema {
* @type {boolean | 'unevaluated'}
* @memberof PlaygroundFeatureSchema
*/
isEnabled: boolean | 'unevaluated';
isEnabled: boolean;
/**
*
* @type {PlaygroundFeatureSchemaVariant}
@ -200,6 +200,11 @@ export interface PlaygroundSegmentSchema {
constraints: Array<PlaygroundConstraintSchema>;
}
export interface PlaygroundStrategyResultSchema {
result: boolean | "unknown";
data?: Array<PlaygroundStrategySchema>
}
export interface PlaygroundStrategySchema {
/**
* The strategy's name.