Skip to content

Commit

Permalink
feat: strategy variant UI spike (#4246)
Browse files Browse the repository at this point in the history
  • Loading branch information
kwasniew committed Jul 14, 2023
1 parent e8ea79c commit 99d63cf
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 33 deletions.
Expand Up @@ -242,6 +242,7 @@ export const createStrategyPayload = (
title: strategy.title,
constraints: strategy.constraints ?? [],
parameters: strategy.parameters ?? {},
variants: strategy.variants ?? [],
segments: segments.map(segment => segment.id),
disabled: strategy.disabled ?? false,
});
Expand Down
Expand Up @@ -31,6 +31,7 @@ import { usePendingChangeRequests } from 'hooks/api/getters/usePendingChangeRequ
import { useHasProjectEnvironmentAccess } from 'hooks/useHasAccess';
import { FeatureStrategyTitle } from './FeatureStrategyTitle/FeatureStrategyTitle';
import { FeatureStrategyEnabledDisabled } from './FeatureStrategyEnabledDisabled/FeatureStrategyEnabledDisabled';
import { StrategyVariants } from 'component/feature/StrategyTypes/StrategyVariants';

interface IFeatureStrategyFormProps {
feature: IFeatureToggle;
Expand Down Expand Up @@ -246,6 +247,16 @@ export const FeatureStrategyForm = ({
hasAccess={access}
/>
<StyledHr />
<ConditionallyRender
condition={Boolean(uiConfig?.flags?.strategyVariant)}
show={
<StrategyVariants
strategy={strategy}
setStrategy={setStrategy}
/>
}
/>
<StyledHr />
<FeatureStrategyEnabledDisabled
enabled={!strategy?.disabled}
onToggleEnabled={() =>
Expand Down
Expand Up @@ -390,7 +390,6 @@ export const EnvironmentVariantsModal = ({
)
)
}
projectId={projectId}
apiPayload={apiPayload}
/>
))}
Expand Down
Expand Up @@ -150,7 +150,6 @@ interface IVariantFormProps {
variants: IFeatureVariantEdit[];
updateVariant: (updatedVariant: IFeatureVariantEdit) => void;
removeVariant: (variantId: string) => void;
projectId: string;
apiPayload: {
patch: Operation[];
error?: string;
Expand Down
Expand Up @@ -17,8 +17,6 @@ import Loader from '../../../common/Loader/Loader';
import { useEffect, useMemo } from 'react';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { useLocation } from 'react-router';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';

interface IFlexibleStrategyProps {
parameters: IFeatureStrategyParameters;
Expand All @@ -35,7 +33,6 @@ const FlexibleStrategy = ({
const projectId = useRequiredPathParam('projectId');
const { defaultStickiness, loading } = useDefaultProjectSettings(projectId);
const { pathname } = useLocation();
const { uiConfig } = useUiConfig();

const isDefaultStrategyEdit = pathname.includes('default-strategy');
const onUpdate = (field: string) => (newValue: string) => {
Expand Down Expand Up @@ -126,34 +123,6 @@ const FlexibleStrategy = ({
onChange={e => onUpdate('groupId')(e.target.value)}
data-testid={FLEXIBLE_STRATEGY_GROUP_ID}
/>
<ConditionallyRender
condition={Boolean(uiConfig?.flags?.strategyVariant)}
show={
<>
<br />
<Typography
variant="subtitle2"
style={{
marginBottom: '1rem',
display: 'flex',
gap: '1ch',
}}
component="h2"
>
Variant
</Typography>
<Input
label="variant"
id="variant-input"
value={parseParameterString(parameters.variant)}
disabled={!editable}
onChange={e =>
onUpdate('variant')(e.target.value)
}
/>
</>
}
/>
</div>
</div>
);
Expand Down
125 changes: 125 additions & 0 deletions frontend/src/component/feature/StrategyTypes/StrategyVariants.tsx
@@ -0,0 +1,125 @@
import { VariantForm } from '../FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsModal/VariantForm/VariantForm';
import { updateWeightEdit } from '../../common/util';
import React, { FC, useEffect, useState } from 'react';
import { IFeatureVariantEdit } from '../FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsModal/EnvironmentVariantsModal';
import PermissionButton from '../../common/PermissionButton/PermissionButton';
import { UPDATE_FEATURE_ENVIRONMENT_VARIANTS } from '../../providers/AccessProvider/permissions';
import { v4 as uuidv4 } from 'uuid';
import { WeightType } from '../../../constants/variantTypes';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { useDefaultProjectSettings } from 'hooks/useDefaultProjectSettings';
import { styled } from '@mui/material';
import { useRequiredQueryParam } from 'hooks/useRequiredQueryParam';
import { IFeatureStrategy } from 'interfaces/strategy';

const StyledVariantForms = styled('div')({
display: 'flex',
flexDirection: 'column',
});

export const StrategyVariants: FC<{
setStrategy: React.Dispatch<
React.SetStateAction<Partial<IFeatureStrategy>>
>;
strategy: Partial<IFeatureStrategy>;
}> = ({ strategy, setStrategy }) => {
const projectId = useRequiredPathParam('projectId');
const environment = useRequiredQueryParam('environmentId');
const [variantsEdit, setVariantsEdit] = useState<IFeatureVariantEdit[]>([]);
const [newVariant, setNewVariant] = useState<string>();
const { defaultStickiness, loading } = useDefaultProjectSettings(projectId);

useEffect(() => {
setVariantsEdit(
(strategy.variants || []).map(variant => ({
...variant,
new: false,
isValid: true,
id: uuidv4(),
overrides: [],
}))
);
}, []);

useEffect(() => {
setStrategy(prev => ({
...prev,
variants: variantsEdit.map(variant => ({
name: variant.name,
weight: variant.weight,
stickiness: variant.stickiness,
payload: variant.payload,
weightType: variant.weightType,
})),
}));
}, [JSON.stringify(variantsEdit)]);

const updateVariant = (updatedVariant: IFeatureVariantEdit, id: string) => {
setVariantsEdit(prevVariants =>
updateWeightEdit(
prevVariants.map(prevVariant =>
prevVariant.id === id ? updatedVariant : prevVariant
),
1000
)
);
};

const addVariant = () => {
const id = uuidv4();
setVariantsEdit(variantsEdit => [
...variantsEdit,
{
name: '',
weightType: WeightType.VARIABLE,
weight: 0,
overrides: [],
stickiness:
variantsEdit?.length > 0
? variantsEdit[0].stickiness
: defaultStickiness,
new: true,
isValid: false,
id,
},
]);
setNewVariant(id);
};

return (
<>
<StyledVariantForms>
{variantsEdit.map(variant => (
<VariantForm
key={variant.id}
variant={variant}
variants={variantsEdit}
updateVariant={updatedVariant =>
updateVariant(updatedVariant, variant.id)
}
removeVariant={() =>
setVariantsEdit(variantsEdit =>
updateWeightEdit(
variantsEdit.filter(
v => v.id !== variant.id
),
1000
)
)
}
apiPayload={{ patch: [] }}
/>
))}
</StyledVariantForms>
<PermissionButton
onClick={addVariant}
variant="outlined"
permission={UPDATE_FEATURE_ENVIRONMENT_VARIANTS}
projectId={projectId}
environmentId={environment}
>
Add variant
</PermissionButton>
</>
);
};
8 changes: 8 additions & 0 deletions frontend/src/interfaces/featureToggle.ts
Expand Up @@ -57,6 +57,14 @@ export interface IFeatureVariant {
payload?: IPayload;
}

export interface IFeatureStrategyVariant {
name: string;
stickiness: string;
weight: number;
weightType: string;
payload?: IPayload;
}

export interface IOverride {
contextName: string;
values: string[];
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/interfaces/strategy.ts
@@ -1,4 +1,5 @@
import { Operator } from 'constants/operators';
import { IFeatureStrategyVariant } from './featureToggle';

export interface IFeatureStrategy {
id: string;
Expand All @@ -7,6 +8,7 @@ export interface IFeatureStrategy {
title?: string;
constraints: IConstraint[];
parameters: IFeatureStrategyParameters;
variants?: IFeatureStrategyVariant[];
featureName?: string;
projectId?: string;
environment?: string;
Expand All @@ -24,6 +26,7 @@ export interface IFeatureStrategyPayload {
title?: string;
constraints: IConstraint[];
parameters: IFeatureStrategyParameters;
variants?: IFeatureStrategyVariant[];
segments?: number[];
disabled?: boolean;
}
Expand Down

0 comments on commit 99d63cf

Please sign in to comment.