1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-10-09 11:14:29 +02:00
unleash.unleash/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/AddValuesPopover.tsx
Thomas Heartman cb4beb71ac
Chore(1 3895)/small input tweaks (#10316)
Doesn't clear the value from the constraint input value popover if you
close it and then re-open. In other words, if you accidentally click
out, you don't lose your progress. Instead, the popover will open again,
with the value you had when you closed it highlighted (so that it's easy
to type over if you want to):

<img width="452" alt="image"
src="https://github.com/user-attachments/assets/d86aa00e-4956-40a8-8fea-e75be5d5425b"
/>

The reason I'm changing this now is because I noticed that the error
wasn't cleared correctly when the popover was closed. If we do it this
way instead, then that makes sense, because you can still see the value.
This is also how the single-value popover has worked forever.

From some quick testing, the single value popover still works as
expected:
<img width="562" alt="image"
src="https://github.com/user-attachments/assets/9041a922-b055-4310-ab60-93ad219981a4"
/>


As a side note: I'm adding a comment to anyone coming after as to why
focus handling on escape doesn't work correctly on the single value
button. I was about to go down a rabbit hole on that before I read my
own comment on the previous PR. So I thought I'd put that here too.
2025-07-04 14:57:52 +02:00

155 lines
4.7 KiB
TypeScript

import {
Button,
ClickAwayListener,
type InputBaseComponentProps,
Popover,
styled,
TextField,
} from '@mui/material';
import { ScreenReaderOnly } from 'component/common/ScreenReaderOnly/ScreenReaderOnly';
import { type FC, useId, useRef, useState } from 'react';
const StyledPopover = styled(Popover)(({ theme }) => ({
'& .MuiPaper-root': {
borderRadius: theme.shape.borderRadiusLarge,
border: `1px solid ${theme.palette.divider}`,
padding: theme.spacing(2),
width: '250px',
},
'&.MuiPopover-root': {
pointerEvents: 'none',
},
'& .MuiPopover-paper': {
pointerEvents: 'all',
},
}));
const StyledTextField = styled(TextField)(({ theme }) => ({
flexGrow: 1,
}));
const InputRow = styled('div')(({ theme }) => ({
display: 'flex',
gap: theme.spacing(1),
alignItems: 'start',
width: '100%',
}));
export type OnAddActions = {
setError: (error: string) => void;
clearInput: () => void;
};
type AddValuesProps = {
onAdd: (newValue: string, actions: OnAddActions) => void;
initialValue?: string;
open: boolean;
anchorEl: HTMLElement | null;
onClose: () => void;
helpText?: string;
inputProps?: InputBaseComponentProps;
};
const HelpText = styled('p')(({ theme }) => ({
color: theme.palette.text.secondary,
fontSize: theme.typography.caption.fontSize,
}));
const AddButton = styled(Button)(({ theme }) => ({
minWidth: theme.spacing(4),
}));
export const AddValuesPopover: FC<AddValuesProps> = ({
initialValue,
onAdd,
anchorEl,
open,
onClose,
helpText,
inputProps,
}) => {
const [inputValue, setInputValue] = useState(initialValue || '');
const [error, setError] = useState('');
const inputRef = useRef<HTMLInputElement>(null);
const inputId = useId();
const helpTextId = useId();
return (
<StyledPopover
open={open}
onTransitionEnter={() => {
inputRef?.current?.select();
}}
disableScrollLock
anchorEl={anchorEl}
onClose={onClose}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}
>
<ClickAwayListener onClickAway={onClose}>
<form
onSubmit={(e) => {
e.stopPropagation();
e.preventDefault();
if (!inputValue?.trim()) {
setError('Value cannot be empty or whitespace');
return;
} else {
onAdd(inputValue, {
setError,
clearInput: () => setInputValue(''),
});
}
}}
>
<InputRow>
<ScreenReaderOnly>
<label htmlFor={inputId}>Constraint Value</label>
</ScreenReaderOnly>
<StyledTextField
id={inputId}
placeholder='Enter value'
value={inputValue}
onChange={(e) => {
setInputValue(e.target.value);
setError('');
}}
size='small'
variant='standard'
fullWidth
inputRef={inputRef}
autoFocus
error={!!error}
helperText={error}
aria-describedby={helpTextId}
inputProps={{
...inputProps,
}}
data-testid='CONSTRAINT_VALUES_INPUT'
/>
<AddButton
variant='text'
type='submit'
size='small'
color='primary'
disabled={!inputValue?.trim()}
data-testid='CONSTRAINT_VALUES_ADD_BUTTON'
>
Add
</AddButton>
</InputRow>
<HelpText id={helpTextId}>{helpText}</HelpText>
</form>
</ClickAwayListener>
</StyledPopover>
);
};