From 920b55005143c9312353a36624c5818fdcd188ad Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Fri, 9 May 2025 19:44:28 +0200 Subject: [PATCH] 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). --- .../EditableConstraint.tsx | 149 ++++++++++-------- 1 file changed, 79 insertions(+), 70 deletions(-) diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint.tsx index ae8df6be85..6dd89606e0 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint.tsx @@ -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; +}> = ({ localConstraint, updateConstraint, validator, addValuesButtonRef }) => { + if (isDateConstraint(localConstraint)) { + return ( + + updateConstraint({ + type: 'set value', + payload: value, + }) + } + value={localConstraint.value} + validator={validator} + /> + ); + } + if (isSemVerConstraint(localConstraint)) { + return ( + { + 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 ( + { + updateConstraint({ + type: 'set value', + payload: newValue, + }); + }} + removeValue={() => updateConstraint({ type: 'clear values' })} + currentValue={localConstraint.value} + helpText={'Add a single number'} + inputType={'number'} + /> + ); + } + + return ( + { + updateConstraint({ + type: 'add value(s)', + payload: newValues, + }); + }} + /> + ); +}; + type Props = { constraint: IConstraint; onDelete: () => void; @@ -175,75 +248,6 @@ export const EditableConstraint: FC = ({ updateConstraint({ type: 'set operator', payload: operator }); }; - const TopRowInput = () => { - if (isDateConstraint(localConstraint)) { - return ( - - updateConstraint({ - type: 'set value', - payload: value, - }) - } - value={localConstraint.value} - validator={validator} - /> - ); - } - if (isSemVerConstraint(localConstraint)) { - return ( - { - 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 ( - { - updateConstraint({ - type: 'set value', - payload: newValue, - }); - }} - removeValue={() => - updateConstraint({ type: 'clear values' }) - } - currentValue={localConstraint.value} - helpText={'Add a single number'} - inputType={'number'} - /> - ); - } - - return ( - { - updateConstraint({ - type: 'add value(s)', - payload: newValues, - }); - }} - /> - ); - }; - return ( @@ -336,7 +340,12 @@ export const EditableConstraint: FC = ({ deleteButtonRef.current } > - +