1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-07-31 13:47:02 +02:00

Propagate to use IConstraintWithId everywhere until editor stops complaining.

This now passes checks to be "valid", but it doesn't do anything in and of itself. We still need to actually provide an id for these things to matter.

I'm wondering whether it'd be better to make id mandatory on the regular constraints and add it on every incoming constraint.
This commit is contained in:
Thomas Heartman 2025-07-18 13:06:59 +02:00
parent 4c358c88df
commit 6ff20cc7ff
No known key found for this signature in database
GPG Key ID: BD1F880DAED1EE78
12 changed files with 49 additions and 47 deletions

View File

@ -4,7 +4,7 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { formatUnknownError } from 'utils/formatUnknownError';
import useToast from 'hooks/useToast';
import type { IFeatureStrategy } from 'interfaces/strategy';
import type { IEditableStrategy, IFeatureStrategy } from 'interfaces/strategy';
import { UPDATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions';
import type { ISegment } from 'interfaces/segment';
import { useFormErrors } from 'hooks/useFormErrors';
@ -63,7 +63,7 @@ export const EditChange = ({
const constraintsWithId = addIdSymbolToConstraints(change.payload);
const [strategy, setStrategy] = useState<Partial<IFeatureStrategy>>({
const [strategy, setStrategy] = useState<Partial<IEditableStrategy>>({
...change.payload,
constraints: constraintsWithId,
});

View File

@ -7,12 +7,12 @@ import {
type Theme,
styled,
} from '@mui/material';
import type { IConstraint } from 'interfaces/strategy';
import { ConstraintAccordionViewBody } from './ConstraintAccordionViewBody/ConstraintAccordionViewBody.tsx';
import { ConstraintAccordionViewHeader } from './ConstraintAccordionViewHeader/ConstraintAccordionViewHeader.tsx';
import type { IConstraintWithId } from 'interfaces/strategy.ts';
interface IConstraintAccordionViewProps {
constraint: IConstraint;
constraint: IConstraintWithId;
onUse?: () => void;
sx?: SxProps<Theme>;
disabled?: boolean;

View File

@ -1,21 +1,20 @@
import type React from 'react';
import { useEffect, useImperativeHandle } from 'react';
import { useImperativeHandle } from 'react';
import { forwardRef } from 'react';
import { styled } from '@mui/material';
import type { IConstraint } from 'interfaces/strategy';
import type { IConstraint, IConstraintWithId } from 'interfaces/strategy';
import produce from 'immer';
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
import { ConstraintsList } from 'component/common/ConstraintsList/ConstraintsList';
import { EditableConstraint } from 'component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/EditableConstraint';
import { createEmptyConstraint } from '../../../../utils/createEmptyConstraint.ts';
import { constraintId } from 'constants/constraintId.ts';
import { v4 as uuidv4 } from 'uuid';
export interface IEditableConstraintsListRef {
addConstraint?: (contextName: string) => void;
}
export interface IEditableConstraintsListProps {
constraints: IConstraint[];
constraints: IConstraintWithId[];
setConstraints: React.Dispatch<React.SetStateAction<IConstraint[]>>;
}
@ -40,17 +39,6 @@ export const EditableConstraintsList = forwardRef<
},
}));
useEffect(() => {
if (!constraints.every((constraint) => constraintId in constraint)) {
setConstraints(
constraints.map((constraint) => ({
[constraintId]: uuidv4(),
...constraint,
})),
);
}
}, [constraints, setConstraints]);
const onDelete = (index: number) => {
setConstraints(
produce((draft) => {
@ -79,6 +67,7 @@ export const EditableConstraintsList = forwardRef<
return (
<StyledContainer>
IN THE constraints list. mapping: {JSON.stringify(constraints)}
<ConstraintsList>
{constraints.map((constraint, index) => (
<EditableConstraint

View File

@ -2,7 +2,7 @@ import type React from 'react';
import { forwardRef, useImperativeHandle, type RefObject } from 'react';
import { Box, Button, styled, Typography } from '@mui/material';
import Add from '@mui/icons-material/Add';
import type { IConstraint } from 'interfaces/strategy';
import type { IConstraint, IConstraintWithId } from 'interfaces/strategy';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
@ -15,8 +15,8 @@ import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashCon
import { createEmptyConstraint } from 'utils/createEmptyConstraint.ts';
interface IConstraintAccordionListProps {
constraints: IConstraint[];
setConstraints?: React.Dispatch<React.SetStateAction<IConstraint[]>>;
constraints: IConstraintWithId[];
setConstraints?: React.Dispatch<React.SetStateAction<IConstraintWithId[]>>;
showCreateButton?: boolean;
}
@ -57,7 +57,7 @@ interface IConstraintAccordionListItemState {
const useConstraintAccordionList = (
setConstraints:
| React.Dispatch<React.SetStateAction<IConstraint[]>>
| React.Dispatch<React.SetStateAction<IConstraintWithId[]>>
| undefined,
ref: React.RefObject<IConstraintAccordionListRef>,
) => {
@ -88,7 +88,7 @@ const useConstraintAccordionList = (
export const FeatureStrategyConstraintAccordionList = forwardRef<
IConstraintAccordionListRef | undefined,
IConstraintAccordionListProps
>(({ constraints, setConstraints, showCreateButton }, ref) => {
>(({ constraints, setConstraints }, ref) => {
const { onAdd, context } = useConstraintAccordionList(
setConstraints,
ref as RefObject<IConstraintAccordionListRef>,
@ -101,8 +101,9 @@ export const FeatureStrategyConstraintAccordionList = forwardRef<
return (
<StyledContainer>
constraints are {JSON.stringify(constraints)}
<ConditionallyRender
condition={Boolean(showCreateButton && onAdd)}
condition={Boolean(true)}
show={
<div>
<StyledHelpIconBox>
@ -128,13 +129,14 @@ export const FeatureStrategyConstraintAccordionList = forwardRef<
}
/>
</StyledHelpIconBox>
{setConstraints ? (
<fieldset disabled>
<EditableConstraintsList
ref={ref}
setConstraints={setConstraints}
setConstraints={() => {}}
constraints={constraints}
/>
) : null}
</fieldset>
<Box
sx={(theme) => ({
marginTop: theme.spacing(2),
@ -156,7 +158,7 @@ export const FeatureStrategyConstraintAccordionList = forwardRef<
variant='outlined'
color='primary'
data-testid='ADD_CONSTRAINT_BUTTON'
disabled={Boolean(limitReached)}
disabled={Boolean(limitReached || !onAdd)}
>
Add constraint
</Button>

View File

@ -1,4 +1,4 @@
import type { IConstraint, IFeatureStrategy } from 'interfaces/strategy';
import type { IConstraintWithId, IEditableStrategy } from 'interfaces/strategy';
import type React from 'react';
import { useEffect } from 'react';
import {
@ -11,9 +11,9 @@ import { FeatureStrategyConstraintAccordionList } from './FeatureStrategyConstra
interface IFeatureStrategyConstraintsProps {
projectId: string;
environmentId: string;
strategy: Partial<IFeatureStrategy>;
strategy: Partial<IEditableStrategy>;
setStrategy: React.Dispatch<
React.SetStateAction<Partial<IFeatureStrategy>>
React.SetStateAction<Partial<IEditableStrategy>>
>;
}
@ -53,7 +53,9 @@ export const FeatureStrategyConstraints = ({
const constraints = strategy.constraints || [];
const setConstraints = (value: React.SetStateAction<IConstraint[]>) => {
const setConstraints = (
value: React.SetStateAction<IConstraintWithId[]>,
) => {
setStrategy((prev) => {
return {
...prev,

View File

@ -5,11 +5,11 @@ import {
areConstraintsEqual,
getConstraintKey,
} from './useRecentlyUsedConstraints.ts';
import type { IConstraint } from 'interfaces/strategy';
import type { IConstraintWithId } from 'interfaces/strategy.ts';
type IRecentlyUsedConstraintsProps = {
setConstraints?: React.Dispatch<React.SetStateAction<IConstraint[]>>;
constraints?: IConstraint[];
setConstraints?: React.Dispatch<React.SetStateAction<IConstraintWithId[]>>;
constraints?: IConstraintWithId[];
};
const StyledContainer = styled('div')(({ theme }) => ({

View File

@ -1,5 +1,5 @@
import { useLocalStorageState } from 'hooks/useLocalStorageState';
import type { IConstraint } from 'interfaces/strategy';
import type { IConstraint, IConstraintWithId } from 'interfaces/strategy';
const hashString = (str: string): number => {
let hash = 0;
@ -41,14 +41,14 @@ export const areConstraintsEqual = (
};
export const useRecentlyUsedConstraints = (
initialItems: IConstraint[] = [],
initialItems: IConstraintWithId[] = [],
) => {
const [items, setItems] = useLocalStorageState<IConstraint[]>(
const [items, setItems] = useLocalStorageState<IConstraintWithId[]>(
'recently-used-constraints',
initialItems,
);
const addItem = (newItem: IConstraint | IConstraint[]) => {
const addItem = (newItem: IConstraintWithId | IConstraintWithId[]) => {
setItems((prevItems) => {
const itemsToAdd = Array.isArray(newItem) ? newItem : [newItem];

View File

@ -13,7 +13,7 @@ import {
Link,
} from '@mui/material';
import type {
IFeatureStrategy,
IEditableStrategy,
IFeatureStrategyParameters,
IStrategyParameter,
} from 'interfaces/strategy';
@ -57,9 +57,9 @@ interface IFeatureStrategyFormProps {
onCancel?: () => void;
loading: boolean;
isChangeRequest: boolean;
strategy: Partial<IFeatureStrategy>;
strategy: Partial<IEditableStrategy>;
setStrategy: React.Dispatch<
React.SetStateAction<Partial<IFeatureStrategy>>
React.SetStateAction<Partial<IEditableStrategy>>
>;
segments: ISegment[];
setSegments: React.Dispatch<React.SetStateAction<ISegment[]>>;

View File

@ -1,4 +1,8 @@
import type { IFeatureStrategy, IStrategy } from 'interfaces/strategy';
import type {
IEditableStrategy,
IFeatureStrategy,
IStrategy,
} from 'interfaces/strategy';
import DefaultStrategy from 'component/feature/StrategyTypes/DefaultStrategy/DefaultStrategy';
import FlexibleStrategy from 'component/feature/StrategyTypes/FlexibleStrategy/FlexibleStrategy';
import GeneralStrategy from 'component/feature/StrategyTypes/GeneralStrategy/GeneralStrategy';
@ -12,7 +16,7 @@ interface IFeatureStrategyTypeProps {
strategy: Partial<IFeatureStrategy>;
strategyDefinition: IStrategy;
setStrategy: React.Dispatch<
React.SetStateAction<Partial<IFeatureStrategy>>
React.SetStateAction<Partial<IEditableStrategy>>
>;
validateParameter: (name: string, value: string) => boolean;
errors: IFormErrors;

View File

@ -8,7 +8,7 @@ import { UPDATE_FEATURE_ENVIRONMENT_VARIANTS } from '../../providers/AccessProvi
import { v4 as uuidv4 } from 'uuid';
import { WeightType } from '../../../constants/variantTypes.ts';
import { Box, styled, Typography, useTheme, Alert } from '@mui/material';
import type { IFeatureStrategy } from 'interfaces/strategy';
import type { IEditableStrategy, IFeatureStrategy } from 'interfaces/strategy';
import { VariantsSplitPreview } from 'component/common/VariantsSplitPreview/VariantsSplitPreview';
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
import { StrategyVariantsUpgradeAlert } from 'component/common/StrategyVariantsUpgradeAlert/StrategyVariantsUpgradeAlert';
@ -30,7 +30,7 @@ const StyledHelpIconBox = styled(Box)(({ theme }) => ({
export const NewStrategyVariants: FC<{
setStrategy: React.Dispatch<
React.SetStateAction<Partial<IFeatureStrategy>>
React.SetStateAction<Partial<IEditableStrategy>>
>;
strategy: Partial<IFeatureStrategy>;
projectId: string;

View File

@ -3,6 +3,7 @@ import { formatApiPath } from 'utils/formatPath';
import handleErrorResponses from '../httpErrorResponseHandler.js';
import type { ChangeRequestType } from 'component/changeRequest/changeRequest.types';
// we get constraints here
export const useChangeRequest = (projectId: string, id: string) => {
const { data, error, mutate } = useSWR<ChangeRequestType>(
formatApiPath(`api/admin/projects/${projectId}/change-requests/${id}`),

View File

@ -68,6 +68,10 @@ export interface IConstraint {
[constraintId]?: string;
}
export interface IEditableStrategy extends IFeatureStrategy {
constraints: IConstraintWithId[];
}
export interface IConstraintWithId extends IConstraint {
[constraintId]: string;
}