From 1deee10317005e188a3a13cd8766a1ff3d2c0a0e Mon Sep 17 00:00:00 2001 From: Fredrik Strand Oseberg Date: Wed, 17 Jan 2024 13:20:39 +0100 Subject: [PATCH] Feat/disabled strategies (#5930) This PR makes disabled strategies more prominent in the UI: Skjermbilde 2024-01-17 kl 11 26 11 --- .../ConstraintAccordion/ConstraintIcon.tsx | 2 + .../ConstraintOperator/ConstraintOperator.tsx | 4 +- .../ConstraintOperator/ConstraintOperator.tsx | 4 +- .../PercentageCircle/PercentageCircle.tsx | 8 ++- .../common/SegmentItem/SegmentItem.tsx | 14 ++++-- .../StrategyExecution/StrategyExecution.tsx | 49 ++++++++++++++----- .../StrategyItem/StrategyItem.tsx | 15 ++++-- .../FeatureOverviewSegment.tsx | 4 +- .../__snapshots__/TagTypeList.test.tsx.snap | 2 +- frontend/src/themes/theme.ts | 2 +- 10 files changed, 75 insertions(+), 29 deletions(-) diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintIcon.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintIcon.tsx index 4434f838c2..cf006aacb2 100644 --- a/frontend/src/component/common/ConstraintAccordion/ConstraintIcon.tsx +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintIcon.tsx @@ -12,6 +12,7 @@ export const ConstraintIcon: VFC = ({ disabled, }) => ( ({ backgroundColor: disabled ? theme.palette.neutral.border @@ -24,6 +25,7 @@ export const ConstraintIcon: VFC = ({ })} > ({ fill: theme.palette.common.white, display: 'block', diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintOperator/ConstraintOperator.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintOperator/ConstraintOperator.tsx index 9481ed1d27..aa28d10e22 100644 --- a/frontend/src/component/common/ConstraintAccordion/ConstraintOperator/ConstraintOperator.tsx +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintOperator/ConstraintOperator.tsx @@ -15,7 +15,7 @@ const StyledContainer = styled('div')(({ theme }) => ({ lineHeight: 1.25, })); -const StyledName = styled('div', { +const StyledName = styled('p', { shouldForwardProp: (prop) => prop !== 'disabled', })<{ disabled: boolean }>(({ theme, disabled }) => ({ fontSize: theme.fontSizes.smallBody, @@ -23,7 +23,7 @@ const StyledName = styled('div', { color: disabled ? theme.palette.text.secondary : theme.palette.text.primary, })); -const StyledText = styled('div', { +const StyledText = styled('p', { shouldForwardProp: (prop) => prop !== 'disabled', })<{ disabled: boolean }>(({ theme, disabled }) => ({ fontSize: theme.fontSizes.smallerBody, diff --git a/frontend/src/component/common/NewConstraintAccordion/ConstraintOperator/ConstraintOperator.tsx b/frontend/src/component/common/NewConstraintAccordion/ConstraintOperator/ConstraintOperator.tsx index 9481ed1d27..aa28d10e22 100644 --- a/frontend/src/component/common/NewConstraintAccordion/ConstraintOperator/ConstraintOperator.tsx +++ b/frontend/src/component/common/NewConstraintAccordion/ConstraintOperator/ConstraintOperator.tsx @@ -15,7 +15,7 @@ const StyledContainer = styled('div')(({ theme }) => ({ lineHeight: 1.25, })); -const StyledName = styled('div', { +const StyledName = styled('p', { shouldForwardProp: (prop) => prop !== 'disabled', })<{ disabled: boolean }>(({ theme, disabled }) => ({ fontSize: theme.fontSizes.smallBody, @@ -23,7 +23,7 @@ const StyledName = styled('div', { color: disabled ? theme.palette.text.secondary : theme.palette.text.primary, })); -const StyledText = styled('div', { +const StyledText = styled('p', { shouldForwardProp: (prop) => prop !== 'disabled', })<{ disabled: boolean }>(({ theme, disabled }) => ({ fontSize: theme.fontSizes.smallerBody, diff --git a/frontend/src/component/common/PercentageCircle/PercentageCircle.tsx b/frontend/src/component/common/PercentageCircle/PercentageCircle.tsx index d06ebaaab8..175d42e860 100644 --- a/frontend/src/component/common/PercentageCircle/PercentageCircle.tsx +++ b/frontend/src/component/common/PercentageCircle/PercentageCircle.tsx @@ -4,11 +4,13 @@ import { CSSProperties } from 'react'; interface IPercentageCircleProps { percentage: number; size?: `${number}rem`; + disabled?: boolean | null; } const PercentageCircle = ({ percentage, size = '4rem', + disabled = false, }: IPercentageCircleProps) => { const theme = useTheme(); @@ -27,6 +29,10 @@ const PercentageCircle = ({ const r = 100 / (2 * Math.PI); const d = 2 * r; + const color = disabled + ? theme.palette.neutral.border + : theme.palette.primary.light; + return ( A circle progress bar with {percentage}% completion. @@ -35,7 +41,7 @@ const PercentageCircle = ({ cx={r} cy={r} fill='none' - stroke={theme.palette.primary.light} + stroke={color} strokeWidth={d} strokeDasharray={`${percentage} 100`} /> diff --git a/frontend/src/component/common/SegmentItem/SegmentItem.tsx b/frontend/src/component/common/SegmentItem/SegmentItem.tsx index 061ee6dacf..8db242b286 100644 --- a/frontend/src/component/common/SegmentItem/SegmentItem.tsx +++ b/frontend/src/component/common/SegmentItem/SegmentItem.tsx @@ -16,15 +16,16 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit interface ISegmentItemProps { segment: Partial; isExpanded?: boolean; - disabled?: boolean; + disabled?: boolean | null; constraintList?: JSX.Element; headerContent?: JSX.Element; } -const StyledAccordion = styled(Accordion)(({ theme }) => ({ +const StyledAccordion = styled(Accordion, { + shouldForwardProp: (prop) => prop !== 'isDisabled', +})<{ isDisabled: boolean | null }>(({ theme, isDisabled }) => ({ border: `1px solid ${theme.palette.divider}`, borderRadius: theme.shape.borderRadiusMedium, - backgroundColor: theme.palette.background.paper, boxShadow: 'none', margin: 0, transition: 'all 0.1s ease', @@ -32,6 +33,9 @@ const StyledAccordion = styled(Accordion)(({ theme }) => ({ opacity: '0 !important', }, '&.Mui-expanded': { backgroundColor: theme.palette.neutral.light }, + backgroundColor: isDisabled + ? theme.palette.envAccordion.disabled + : theme.palette.background.paper, })); const StyledAccordionSummary = styled(AccordionSummary)(({ theme }) => ({ @@ -52,7 +56,7 @@ const StyledLink = styled(Link)(({ theme }) => ({ })); const StyledText = styled('span', { shouldForwardProp: (prop) => prop !== 'disabled', -})<{ disabled: boolean }>(({ theme, disabled }) => ({ +})<{ disabled: boolean | null }>(({ theme, disabled }) => ({ color: disabled ? theme.palette.text.secondary : 'inherit', })); @@ -66,7 +70,7 @@ export const SegmentItem: VFC = ({ const [isOpen, setIsOpen] = useState(isExpanded || false); return ( - + ({ diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution.tsx index 4f22681bf1..80571b75a5 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution.tsx @@ -6,7 +6,6 @@ import { StrategySeparator } from 'component/common/StrategySeparator/StrategySe import { ConstraintItem } from './ConstraintItem/ConstraintItem'; import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies'; import { useSegments } from 'hooks/api/getters/useSegments/useSegments'; -import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import { FeatureOverviewSegment } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewSegment/FeatureOverviewSegment'; import { ConstraintAccordionList } from 'component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList'; import { @@ -23,6 +22,25 @@ interface IStrategyExecutionProps { strategy: IFeatureStrategyPayload | CreateFeatureStrategySchema; } +const StyledContainer = styled(Box, { + shouldForwardProp: (prop) => prop !== 'disabled', +})<{ disabled?: boolean | null }>(({ theme, disabled }) => ({ + '& p, & span, & h1, & h2, & h3, & h4, & h5, & h6': { + color: disabled ? theme.palette.neutral.main : 'inherit', + }, + '.constraint-icon-container': { + backgroundColor: disabled + ? theme.palette.neutral.border + : theme.palette.primary.light, + borderRadius: '50%', + }, + '.constraint-icon': { + fill: disabled + ? theme.palette.neutral.light + : theme.palette.common.white, + }, +})); + const NoItems: VFC = () => ( This strategy does not have constraints or parameters. @@ -44,7 +62,6 @@ export const StrategyExecution: VFC = ({ }) => { const { parameters, constraints = [] } = strategy; const { strategies } = useStrategies(); - const { uiConfig } = useUiConfig(); const { segments } = useSegments(); const strategySegments = segments?.filter((segment) => { return strategy.segments?.includes(segment.id); @@ -63,6 +80,8 @@ export const StrategyExecution: VFC = ({ case 'Rollout': { const percentage = parseParameterNumber(parameters[key]); + const badgeType = strategy.disabled ? 'neutral' : 'success'; + return ( = ({
- {percentage}% of - your base{' '} - {constraints.length > 0 - ? 'who match constraints' - : ''}{' '} - is included. + {percentage}%{' '} + of your base{' '} + + {constraints.length > 0 + ? 'who match constraints' + : ''}{' '} + is included. +
); @@ -109,7 +131,7 @@ export const StrategyExecution: VFC = ({ return null; } }); - }, [parameters, definition, constraints]); + }, [parameters, definition, constraints, strategy.disabled]); const customStrategyList = useMemo(() => { if (!parameters || !definition?.editable) return null; @@ -252,7 +274,10 @@ export const StrategyExecution: VFC = ({ const listItems = [ strategySegments && strategySegments.length > 0 && ( - + ), constraints.length > 0 && ( = ({ 0} show={ - <> + {listItems.map((item, index) => ( // biome-ignore lint/suspicious/noArrayIndexKey: @@ -287,7 +312,7 @@ export const StrategyExecution: VFC = ({ {item} ))} - + } elseShow={} /> diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx index c51f710f33..57b75ff687 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx @@ -13,7 +13,7 @@ import { CopyStrategyIconMenu } from './CopyStrategyIconMenu/CopyStrategyIconMen import { StrategyItemContainer } from 'component/common/StrategyItemContainer/StrategyItemContainer'; import MenuStrategyRemove from './MenuStrategyRemove/MenuStrategyRemove'; import SplitPreviewSlider from 'component/feature/StrategyTypes/SplitPreviewSlider/SplitPreviewSlider'; - +import { Box } from '@mui/material'; interface IStrategyItemProps { environmentId: string; strategy: IFeatureStrategy; @@ -87,9 +87,16 @@ export const StrategyItem: FC = ({ } > - {strategy.variants ? ( - - ) : null} + + {strategy.variants && + strategy.variants.length > 0 && + (strategy.disabled ? ( + + + + ) : ( + + ))} ); }; diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSegment/FeatureOverviewSegment.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSegment/FeatureOverviewSegment.tsx index c4dc02fa83..0d77104d13 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSegment/FeatureOverviewSegment.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewSegment/FeatureOverviewSegment.tsx @@ -6,10 +6,12 @@ import { ISegment } from 'interfaces/segment'; interface IFeatureOverviewSegmentProps { segments?: ISegment[]; + disabled?: boolean | null; } export const FeatureOverviewSegment = ({ segments, + disabled = false, }: IFeatureOverviewSegmentProps) => { if (!segments || segments.length === 0) { return null; @@ -23,7 +25,7 @@ export const FeatureOverviewSegment = ({ condition={index > 0} show={} /> - + ))} diff --git a/frontend/src/component/tags/TagTypeList/__tests__/__snapshots__/TagTypeList.test.tsx.snap b/frontend/src/component/tags/TagTypeList/__tests__/__snapshots__/TagTypeList.test.tsx.snap index 1ea15d7bf7..926f64b128 100644 --- a/frontend/src/component/tags/TagTypeList/__tests__/__snapshots__/TagTypeList.test.tsx.snap +++ b/frontend/src/component/tags/TagTypeList/__tests__/__snapshots__/TagTypeList.test.tsx.snap @@ -35,7 +35,7 @@ exports[`renders an empty list correctly 1`] = ` className="css-non55o" >