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

fix: avoid focus loss when using popover (and don't close multi-value popover after adding a value) (#9951)

Fixes an issue where you would lose keyboard focus when interacting with
one of the input fields in the new editable constraint.

The fix was spinning out the inputs into their own separate component.
This prevents them from being re-rendered every time (or something idk)
which allows us to keep focus.

It also stops the popover for multi-value constraint operators from
closing after you've entered a value; allowing for faster entry of more
values (as was intended and as was how it functioned previously).
This commit is contained in:
Thomas Heartman 2025-05-09 19:44:28 +02:00 committed by GitHub
parent fbc58ca1fc
commit 920b550051
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -20,11 +20,14 @@ import { LegalValuesSelector } from './LegalValuesSelector';
import { useEditableConstraint } from './useEditableConstraint/useEditableConstraint';
import type { IConstraint } from 'interfaces/strategy';
import {
type EditableConstraint as EditableConstraintType,
isDateConstraint,
isMultiValueConstraint,
isNumberConstraint,
isSemVerConstraint,
} from './useEditableConstraint/editable-constraint-type';
import type { ConstraintUpdateAction } from './useEditableConstraint/constraint-reducer';
import type { ConstraintValidationResult } from './useEditableConstraint/constraint-validator';
const Container = styled('article')(({ theme }) => ({
'--padding': theme.spacing(2),
@ -139,6 +142,76 @@ const StyledCaseSensitiveIcon = styled(CaseSensitiveIcon)(({ theme }) => ({
fill: 'currentcolor',
}));
const TopRowInput: FC<{
localConstraint: EditableConstraintType;
updateConstraint: (action: ConstraintUpdateAction) => void;
validator: (value: string) => ConstraintValidationResult;
addValuesButtonRef: React.RefObject<HTMLButtonElement>;
}> = ({ localConstraint, updateConstraint, validator, addValuesButtonRef }) => {
if (isDateConstraint(localConstraint)) {
return (
<ConstraintDateInput
setValue={(value: string) =>
updateConstraint({
type: 'set value',
payload: value,
})
}
value={localConstraint.value}
validator={validator}
/>
);
}
if (isSemVerConstraint(localConstraint)) {
return (
<AddSingleValueWidget
validator={validator}
onAddValue={(newValue) => {
updateConstraint({
type: 'set value',
payload: newValue,
});
}}
removeValue={() => updateConstraint({ type: 'clear values' })}
currentValue={localConstraint.value}
helpText={'A semver value should be of the format X.Y.Z'}
inputType={'text'}
/>
);
}
if (isNumberConstraint(localConstraint)) {
return (
<AddSingleValueWidget
validator={validator}
onAddValue={(newValue) => {
updateConstraint({
type: 'set value',
payload: newValue,
});
}}
removeValue={() => updateConstraint({ type: 'clear values' })}
currentValue={localConstraint.value}
helpText={'Add a single number'}
inputType={'number'}
/>
);
}
return (
<AddValuesWidget
validator={validator}
helpText='Maximum 100 char length per value'
ref={addValuesButtonRef}
onAddValues={(newValues) => {
updateConstraint({
type: 'add value(s)',
payload: newValues,
});
}}
/>
);
};
type Props = {
constraint: IConstraint;
onDelete: () => void;
@ -175,75 +248,6 @@ export const EditableConstraint: FC<Props> = ({
updateConstraint({ type: 'set operator', payload: operator });
};
const TopRowInput = () => {
if (isDateConstraint(localConstraint)) {
return (
<ConstraintDateInput
setValue={(value: string) =>
updateConstraint({
type: 'set value',
payload: value,
})
}
value={localConstraint.value}
validator={validator}
/>
);
}
if (isSemVerConstraint(localConstraint)) {
return (
<AddSingleValueWidget
validator={validator}
onAddValue={(newValue) => {
updateConstraint({
type: 'set value',
payload: newValue,
});
}}
removeValue={() =>
updateConstraint({ type: 'clear values' })
}
currentValue={localConstraint.value}
helpText={'A semver value should be of the format X.Y.Z'}
inputType={'text'}
/>
);
}
if (isNumberConstraint(localConstraint)) {
return (
<AddSingleValueWidget
validator={validator}
onAddValue={(newValue) => {
updateConstraint({
type: 'set value',
payload: newValue,
});
}}
removeValue={() =>
updateConstraint({ type: 'clear values' })
}
currentValue={localConstraint.value}
helpText={'Add a single number'}
inputType={'number'}
/>
);
}
return (
<AddValuesWidget
validator={validator}
helpText='Maximum 100 char length per value'
ref={addValuesButtonRef}
onAddValues={(newValues) => {
updateConstraint({
type: 'add value(s)',
payload: newValues,
});
}}
/>
);
};
return (
<Container>
<TopRow>
@ -336,7 +340,12 @@ export const EditableConstraint: FC<Props> = ({
deleteButtonRef.current
}
>
<TopRowInput />
<TopRowInput
localConstraint={localConstraint}
updateConstraint={updateConstraint}
validator={validator}
addValuesButtonRef={addValuesButtonRef}
/>
</ValueList>
</ConstraintDetails>
<ButtonPlaceholder />