mirror of
https://github.com/Unleash/unleash.git
synced 2025-09-28 17:55:15 +02:00
* refactor: update mui packages * refactor: run mui codemods * refactor: format files after codemods * refactor: fix broken types * refactor: clean up theme * refactor: fix broken tests * refactor: replace @mui/styles with tss-react * refactor: move breakpoints into classes for tss * refactor: fix crash on missing feature description * refactor: remove void classNames * refactor: adjust styles to new defaults * refactor: remove broken rollout slider e2e test * refactor: fix duplicate e2e testid * refactor: update makeStyles after rebase * refactor: add missing snapshot after rebase * refactor: fix TableCellSortable focus styles * refactor: use 1.4 as the default line-height * refactor: hide webkit search field icons * refactor: fix select box label * refactor: make AutocompleteBox smaller * refactor: make heading smaller * refactor: fix toast close icon color * refactor: update snapshots * refactor: add missing test event awaits * refactor: fix default button line-height
224 lines
8.2 KiB
TypeScript
224 lines
8.2 KiB
TypeScript
import Input from 'component/common/Input/Input';
|
|
import { TextField, Button, Switch, Typography } from '@mui/material';
|
|
import { useStyles } from './ContextForm.styles';
|
|
import React, { useState, useEffect } from 'react';
|
|
import { Add } from '@mui/icons-material';
|
|
import { ILegalValue } from 'interfaces/context';
|
|
import { ContextFormChip } from 'component/context/ContectFormChip/ContextFormChip';
|
|
import { ContextFormChipList } from 'component/context/ContectFormChip/ContextFormChipList';
|
|
|
|
interface IContextForm {
|
|
contextName: string;
|
|
contextDesc: string;
|
|
legalValues: ILegalValue[];
|
|
stickiness: boolean;
|
|
setContextName: React.Dispatch<React.SetStateAction<string>>;
|
|
setContextDesc: React.Dispatch<React.SetStateAction<string>>;
|
|
setStickiness: React.Dispatch<React.SetStateAction<boolean>>;
|
|
setLegalValues: React.Dispatch<React.SetStateAction<ILegalValue[]>>;
|
|
handleSubmit: (e: any) => void;
|
|
onCancel: () => void;
|
|
errors: { [key: string]: string };
|
|
mode: 'Create' | 'Edit';
|
|
clearErrors: (key?: string) => void;
|
|
validateContext?: () => void;
|
|
setErrors: React.Dispatch<React.SetStateAction<Object>>;
|
|
}
|
|
|
|
const ENTER = 'Enter';
|
|
|
|
export const ContextForm: React.FC<IContextForm> = ({
|
|
children,
|
|
handleSubmit,
|
|
onCancel,
|
|
contextName,
|
|
contextDesc,
|
|
legalValues,
|
|
stickiness,
|
|
setContextName,
|
|
setContextDesc,
|
|
setLegalValues,
|
|
setStickiness,
|
|
errors,
|
|
mode,
|
|
validateContext,
|
|
setErrors,
|
|
clearErrors,
|
|
}) => {
|
|
const { classes: styles } = useStyles();
|
|
const [value, setValue] = useState('');
|
|
const [valueDesc, setValueDesc] = useState('');
|
|
const [valueFocused, setValueFocused] = useState(false);
|
|
|
|
const isMissingValue = valueDesc.trim() && !value.trim();
|
|
|
|
const isDuplicateValue = legalValues.some(legalValue => {
|
|
return legalValue.value.trim() === value.trim();
|
|
});
|
|
|
|
useEffect(() => {
|
|
setErrors(prev => ({
|
|
...prev,
|
|
tag: isMissingValue
|
|
? 'Value cannot be empty'
|
|
: isDuplicateValue
|
|
? 'Duplicate value'
|
|
: undefined,
|
|
}));
|
|
}, [setErrors, isMissingValue, isDuplicateValue]);
|
|
|
|
const onSubmit = (event: React.SyntheticEvent) => {
|
|
event.preventDefault();
|
|
handleSubmit(event);
|
|
};
|
|
|
|
const onKeyDown = (event: React.KeyboardEvent) => {
|
|
if (event.key === ENTER) {
|
|
event.preventDefault();
|
|
if (valueFocused) {
|
|
addLegalValue();
|
|
} else {
|
|
handleSubmit(event);
|
|
}
|
|
}
|
|
};
|
|
|
|
const sortLegalValues = (a: ILegalValue, b: ILegalValue) => {
|
|
return a.value.toLowerCase().localeCompare(b.value.toLowerCase());
|
|
};
|
|
|
|
const addLegalValue = () => {
|
|
const next: ILegalValue = {
|
|
value: value.trim(),
|
|
description: valueDesc.trim(),
|
|
};
|
|
if (next.value && !isDuplicateValue) {
|
|
setValue('');
|
|
setValueDesc('');
|
|
setLegalValues(prev => [...prev, next].sort(sortLegalValues));
|
|
}
|
|
};
|
|
|
|
const removeLegalValue = (value: ILegalValue) => {
|
|
setLegalValues(prev => prev.filter(p => p.value !== value.value));
|
|
};
|
|
|
|
return (
|
|
<form onSubmit={onSubmit} className={styles.form}>
|
|
<div className={styles.container}>
|
|
<p className={styles.inputDescription}>
|
|
What is your context name?
|
|
</p>
|
|
<Input
|
|
className={styles.input}
|
|
label="Context name"
|
|
value={contextName}
|
|
disabled={mode === 'Edit'}
|
|
onChange={e => setContextName(e.target.value.trim())}
|
|
error={Boolean(errors.name)}
|
|
errorText={errors.name}
|
|
onFocus={() => clearErrors('name')}
|
|
onBlur={validateContext}
|
|
autoFocus
|
|
/>
|
|
<p className={styles.inputDescription}>
|
|
What is this context for?
|
|
</p>
|
|
<TextField
|
|
className={styles.input}
|
|
label="Context description (optional)"
|
|
variant="outlined"
|
|
multiline
|
|
maxRows={4}
|
|
value={contextDesc}
|
|
size="small"
|
|
onChange={e => setContextDesc(e.target.value)}
|
|
/>
|
|
<p className={styles.inputDescription}>
|
|
Which values do you want to allow?
|
|
</p>
|
|
<div className={styles.tagContainer}>
|
|
<TextField
|
|
label="Legal value (optional)"
|
|
name="value"
|
|
className={styles.tagInput}
|
|
value={value}
|
|
error={Boolean(errors.tag)}
|
|
helperText={errors.tag}
|
|
variant="outlined"
|
|
size="small"
|
|
onChange={e => setValue(e.target.value)}
|
|
onKeyPress={e => onKeyDown(e)}
|
|
onBlur={() => setValueFocused(false)}
|
|
onFocus={() => setValueFocused(true)}
|
|
inputProps={{ maxLength: 100 }}
|
|
/>
|
|
<TextField
|
|
label="Value description (optional)"
|
|
className={styles.tagInput}
|
|
value={valueDesc}
|
|
variant="outlined"
|
|
size="small"
|
|
onChange={e => setValueDesc(e.target.value)}
|
|
onKeyPress={e => onKeyDown(e)}
|
|
onBlur={() => setValueFocused(false)}
|
|
onFocus={() => setValueFocused(true)}
|
|
inputProps={{ maxLength: 100 }}
|
|
/>
|
|
<Button
|
|
className={styles.tagButton}
|
|
startIcon={<Add />}
|
|
onClick={addLegalValue}
|
|
variant="outlined"
|
|
color="primary"
|
|
disabled={!value.trim() || isDuplicateValue}
|
|
>
|
|
Add
|
|
</Button>
|
|
</div>
|
|
<ContextFormChipList>
|
|
{legalValues.map(legalValue => {
|
|
return (
|
|
<ContextFormChip
|
|
key={legalValue.value}
|
|
label={legalValue.value}
|
|
description={legalValue.description}
|
|
onRemove={() => removeLegalValue(legalValue)}
|
|
/>
|
|
);
|
|
})}
|
|
</ContextFormChipList>
|
|
<p className={styles.inputHeader}>Custom stickiness</p>
|
|
<p>
|
|
By enabling stickiness on this context field you can use it
|
|
together with the flexible-rollout strategy. This will
|
|
guarantee a consistent behavior for specific values of this
|
|
context field. PS! Not all client SDK's support this feature
|
|
yet!{' '}
|
|
<a
|
|
href="https://docs.getunleash.io/advanced/stickiness"
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
>
|
|
Read more
|
|
</a>
|
|
</p>
|
|
<div className={styles.switchContainer}>
|
|
<Switch
|
|
checked={stickiness}
|
|
value={stickiness}
|
|
onChange={() => setStickiness(!stickiness)}
|
|
/>
|
|
<Typography>{stickiness ? 'On' : 'Off'}</Typography>
|
|
</div>
|
|
</div>
|
|
<div className={styles.buttonContainer}>
|
|
{children}
|
|
<Button onClick={onCancel} className={styles.cancelButton}>
|
|
Cancel
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
);
|
|
};
|