1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-11-10 01:19:53 +01:00

1-3687/input mode separation (#9882)

This commit is contained in:
Thomas Heartman 2025-05-02 12:30:38 +02:00 committed by GitHub
parent 1b9c0e5000
commit 3d84001273
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 109 additions and 64 deletions

View File

@ -64,6 +64,13 @@ const resolveLegalValues = (
}; };
}; };
/**
* @deprecated; remove with `addEditStrategy` flag. Need an input? Prefer using specific input components.
*
* For the case of `ProjectActionsFilterItem.tsx`: it already excludes legal
* values and date operators. This leaves only free text and single value
* text/numeric operators. Alternatively, consider rewriting this component to only handle those cases.
*/
export const ResolveInput = ({ export const ResolveInput = ({
input, input,
contextDefinition, contextDefinition,

View File

@ -68,7 +68,7 @@ export const useConstraintInput = ({
contextDefinition, contextDefinition,
localConstraint, localConstraint,
}: IUseConstraintInputProps): IUseConstraintOutput => { }: IUseConstraintInputProps): IUseConstraintOutput => {
const [input, setInput] = useState<Input>(IN_OPERATORS_LEGAL_VALUES); const [input, setInput] = useState<Input>(IN_OPERATORS_FREETEXT);
const [validator, setValidator] = useState<Validator>( const [validator, setValidator] = useState<Validator>(
STRING_ARRAY_VALIDATOR, STRING_ARRAY_VALIDATOR,
); );

View File

@ -25,12 +25,13 @@ import { ReactComponent as CaseSensitiveIcon } from 'assets/icons/case-sensitive
import { ReactComponent as CaseInsensitiveIcon } from 'assets/icons/case-insensitive.svg'; import { ReactComponent as CaseInsensitiveIcon } from 'assets/icons/case-insensitive.svg';
import { ScreenReaderOnly } from 'component/common/ScreenReaderOnly/ScreenReaderOnly'; import { ScreenReaderOnly } from 'component/common/ScreenReaderOnly/ScreenReaderOnly';
import { AddValuesWidget } from './AddValuesWidget'; import { AddValuesWidget } from './AddValuesWidget';
import { ResolveInput } from 'component/common/NewConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/ResolveInput/ResolveInput';
import { ReactComponent as EqualsIcon } from 'assets/icons/constraint-equals.svg'; import { ReactComponent as EqualsIcon } from 'assets/icons/constraint-equals.svg';
import { ReactComponent as NotEqualsIcon } from 'assets/icons/constraint-not-equals.svg'; import { ReactComponent as NotEqualsIcon } from 'assets/icons/constraint-not-equals.svg';
import { AddSingleValueWidget } from './AddSingleValueWidget'; import { AddSingleValueWidget } from './AddSingleValueWidget';
import { ConstraintDateInput } from './ConstraintDateInput'; import { ConstraintDateInput } from './ConstraintDateInput';
import { LegalValuesSelector } from './LegalValuesSelector';
import { resolveLegalValues } from './resolve-legal-values';
const Container = styled('article')(({ theme }) => ({ const Container = styled('article')(({ theme }) => ({
'--padding': theme.spacing(2), '--padding': theme.spacing(2),
@ -68,7 +69,7 @@ const ConstraintDetails = styled('div')(({ theme }) => ({
height: 'min-content', height: 'min-content',
})); }));
const InputContainer = styled('div')(({ theme }) => ({ const LegalValuesContainer = styled('div')(({ theme }) => ({
padding: 'var(--padding)', padding: 'var(--padding)',
borderTop: `1px dashed ${theme.palette.divider}`, borderTop: `1px dashed ${theme.palette.divider}`,
})); }));
@ -145,15 +146,23 @@ const StyledCaseSensitiveIcon = styled(CaseSensitiveIcon)(({ theme }) => ({
fill: 'currentcolor', fill: 'currentcolor',
})); }));
const OPERATORS_WITH_ADD_VALUES_WIDGET = [ const getInputType = (input: Input) => {
'IN_OPERATORS_FREETEXT', switch (input) {
'STRING_OPERATORS_FREETEXT', case 'IN_OPERATORS_LEGAL_VALUES':
]; case 'STRING_OPERATORS_LEGAL_VALUES':
case 'NUM_OPERATORS_LEGAL_VALUES':
const SINGLE_VALUE_OPERATORS = [ case 'SEMVER_OPERATORS_LEGAL_VALUES':
'NUM_OPERATORS_SINGLE_VALUE', return 'legal values';
'SEMVER_OPERATORS_SINGLE_VALUE', case 'DATE_OPERATORS_SINGLE_VALUE':
]; return 'date input';
case 'NUM_OPERATORS_SINGLE_VALUE':
case 'SEMVER_OPERATORS_SINGLE_VALUE':
return 'single text value';
case 'IN_OPERATORS_FREETEXT':
case 'STRING_OPERATORS_FREETEXT':
return 'free text';
}
};
type Props = { type Props = {
constraint: IConstraint; constraint: IConstraint;
@ -206,11 +215,7 @@ export const EditableConstraint: FC<Props> = ({
useState(false); useState(false);
const deleteButtonRef = useRef<HTMLButtonElement>(null); const deleteButtonRef = useRef<HTMLButtonElement>(null);
const addValuesButtonRef = useRef<HTMLButtonElement>(null); const addValuesButtonRef = useRef<HTMLButtonElement>(null);
const showSingleValueButton = SINGLE_VALUE_OPERATORS.includes(input); const inputType = getInputType(input);
const showAddValuesButton =
OPERATORS_WITH_ADD_VALUES_WIDGET.includes(input);
const showDateInput = input.includes('DATE');
const showInputField = input.includes('LEGAL_VALUES');
/* We need a special case to handle the currentTime context field. Since /* We need a special case to handle the currentTime context field. Since
this field will be the only one to allow DATE_BEFORE and DATE_AFTER operators this field will be the only one to allow DATE_BEFORE and DATE_AFTER operators
@ -267,6 +272,46 @@ export const EditableConstraint: FC<Props> = ({
} }
}; };
const TopRowInput = () => {
switch (inputType) {
case 'date input':
return (
<ConstraintDateInput
setValue={setValue}
value={localConstraint.value}
error={error}
setError={setError}
/>
);
case 'single text value':
return (
<AddSingleValueWidget
onAddValue={(newValue) => {
setValue(newValue);
}}
removeValue={() => setValue('')}
currentValue={localConstraint.value}
/>
);
case 'free text':
return (
<AddValuesWidget
ref={addValuesButtonRef}
onAddValues={(newValues) => {
// todo (`addEditStrategy`): move deduplication logic higher up in the context handling
const combinedValues = new Set([
...(localConstraint.values || []),
...newValues,
]);
setValuesWithRecord(Array.from(combinedValues));
}}
/>
);
default:
return null;
}
};
return ( return (
<Container> <Container>
<TopRow> <TopRow>
@ -339,39 +384,8 @@ export const EditableConstraint: FC<Props> = ({
deleteButtonRef.current deleteButtonRef.current
} }
> >
{showAddValuesButton ? ( <TopRowInput />
<AddValuesWidget
ref={addValuesButtonRef}
onAddValues={(newValues) => {
// todo (`addEditStrategy`): move deduplication logic higher up in the context handling
const combinedValues = new Set([
...(localConstraint.values || []),
...newValues,
]);
setValuesWithRecord(
Array.from(combinedValues),
);
}}
/>
) : null}
</ValueList> </ValueList>
{showSingleValueButton ? (
<AddSingleValueWidget
onAddValue={(newValue) => {
setValue(newValue);
}}
removeValue={() => setValue('')}
currentValue={localConstraint.value}
/>
) : null}
{showDateInput ? (
<ConstraintDateInput
setValue={setValue}
value={localConstraint.value}
error={error}
setError={setError}
/>
) : null}
</ConstraintDetails> </ConstraintDetails>
<ButtonPlaceholder /> <ButtonPlaceholder />
<HtmlTooltip title='Delete constraint' arrow> <HtmlTooltip title='Delete constraint' arrow>
@ -386,22 +400,19 @@ export const EditableConstraint: FC<Props> = ({
</StyledIconButton> </StyledIconButton>
</HtmlTooltip> </HtmlTooltip>
</TopRow> </TopRow>
{showInputField ? ( {inputType === 'legal values' ? (
<InputContainer> <LegalValuesContainer>
<ResolveInput <LegalValuesSelector
setValues={setValues} data={resolveLegalValues(
constraintValues,
contextDefinition.legalValues,
)}
constraintValues={constraintValues}
values={localConstraint.values || []}
setValuesWithRecord={setValuesWithRecord} setValuesWithRecord={setValuesWithRecord}
setValue={setValue} setValues={setValues}
setError={setError} />{' '}
localConstraint={localConstraint} </LegalValuesContainer>
constraintValues={constraint?.values || []}
constraintValue={constraint?.value || ''}
input={input}
error={error}
contextDefinition={contextDefinition}
removeValue={removeValue}
/>
</InputContainer>
) : null} ) : null}
</Container> </Container>
); );

View File

@ -0,0 +1,27 @@
import type {
IUnleashContextDefinition,
ILegalValue,
} from 'interfaces/context';
import type { IConstraint } from 'interfaces/strategy';
export const resolveLegalValues = (
values: IConstraint['values'] = [],
legalValues: IUnleashContextDefinition['legalValues'] = [],
): { legalValues: ILegalValue[]; deletedLegalValues: ILegalValue[] } => {
if (legalValues.length === 0) {
return {
legalValues: [],
deletedLegalValues: [],
};
}
const existingLegalValues = new Set(legalValues.map(({ value }) => value));
const deletedLegalValues = values
.filter((value) => !existingLegalValues.has(value))
.map((v) => ({ value: v, description: '' }));
return {
legalValues,
deletedLegalValues,
};
};