mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
fix(FE-Storyless): update SelectInput value handling to use undefined instead of null for better compatibility
This commit is contained in:
@@ -9,6 +9,8 @@ import Select, {
|
||||
InputActionMeta,
|
||||
MultiValue,
|
||||
SingleValue,
|
||||
components as ReactSelectComponents,
|
||||
ControlProps,
|
||||
} from 'react-select';
|
||||
import CreatableSelect from 'react-select/creatable';
|
||||
import makeAnimated from 'react-select/animated';
|
||||
@@ -64,6 +66,33 @@ interface SelectInputProps<T = OptionType> extends SelectInputBaseProps<T> {
|
||||
|
||||
const animatedComponents = makeAnimated();
|
||||
|
||||
const CustomControl = <
|
||||
Option,
|
||||
IsMulti extends boolean,
|
||||
Group extends GroupBase<Option>,
|
||||
>(
|
||||
props: ControlProps<Option, IsMulti, Group>
|
||||
) => {
|
||||
const { children } = props;
|
||||
|
||||
const customProps = props.selectProps as unknown as {
|
||||
shouldShowAdornment?: boolean;
|
||||
startAdornment?: ReactNode;
|
||||
};
|
||||
|
||||
const shouldShowAdornment = customProps.shouldShowAdornment ?? false;
|
||||
const startAdornment = customProps.startAdornment;
|
||||
|
||||
return (
|
||||
<ReactSelectComponents.Control {...props}>
|
||||
<div className='flex-1 px-4! py-1.5 gap-1 flex items-center'>
|
||||
{shouldShowAdornment && startAdornment}
|
||||
{children}
|
||||
</div>
|
||||
</ReactSelectComponents.Control>
|
||||
);
|
||||
};
|
||||
|
||||
const SelectInput = <T extends OptionType>(props: SelectInputProps<T>) => {
|
||||
const {
|
||||
label,
|
||||
@@ -94,10 +123,18 @@ const SelectInput = <T extends OptionType>(props: SelectInputProps<T>) => {
|
||||
const [internalInputValue, setInternalInputValue] = useState('');
|
||||
const [debouncedInputValue] = useDebounce(internalInputValue, delay);
|
||||
|
||||
const shouldShowAdornment = startAdornment && !internalInputValue;
|
||||
|
||||
const components = useMemo(() => {
|
||||
const base = isAnimated ? animatedComponents : {};
|
||||
return { ...base, IndicatorSeparator: () => null };
|
||||
}, [isAnimated]);
|
||||
const customComponents = { ...base, IndicatorSeparator: () => null };
|
||||
|
||||
if (startAdornment) {
|
||||
customComponents.Control = CustomControl;
|
||||
}
|
||||
|
||||
return customComponents;
|
||||
}, [isAnimated, startAdornment]);
|
||||
|
||||
const internalInputChangeHandler = (val: string, meta: InputActionMeta) => {
|
||||
if (meta.action === 'input-change') setInternalInputValue(val);
|
||||
@@ -150,12 +187,13 @@ const SelectInput = <T extends OptionType>(props: SelectInputProps<T>) => {
|
||||
|
||||
<SelectComponent<T, boolean, GroupBase<T>>
|
||||
instanceId='select'
|
||||
value={value ?? (isMulti ? [] : null)}
|
||||
value={value ?? (isMulti ? [] : undefined)}
|
||||
onChange={onChange ? handleChange : undefined}
|
||||
options={options}
|
||||
menuIsOpen={openMenu}
|
||||
inputValue={internalInputValue}
|
||||
onInputChange={internalInputChangeHandler}
|
||||
onMenuClose={() => setInternalInputValue('')}
|
||||
isMulti={isMulti}
|
||||
isDisabled={isDisabled}
|
||||
isLoading={isLoading}
|
||||
@@ -165,17 +203,19 @@ const SelectInput = <T extends OptionType>(props: SelectInputProps<T>) => {
|
||||
placeholder={placeholder}
|
||||
className={cn('w-full', className?.select)}
|
||||
classNames={{
|
||||
control: ({ isFocused, isDisabled }) =>
|
||||
cn(
|
||||
'w-full min-h-12! rounded border bg-white transition-shadow cursor-pointer!',
|
||||
{
|
||||
'border-red-500! ring-2 ring-red-200': isError,
|
||||
'border-indigo-500 ring-2 ring-indigo-200': isFocused,
|
||||
'border-gray-300': !isError && !isFocused,
|
||||
'bg-gray-100 text-gray-400 cursor-not-allowed': isDisabled,
|
||||
}
|
||||
),
|
||||
valueContainer: () => cn('flex-1 px-4! py-2! gap-1'),
|
||||
...(!startAdornment && {
|
||||
control: ({ isFocused, isDisabled }) =>
|
||||
cn(
|
||||
'w-full min-h-12! rounded border bg-white transition-shadow cursor-pointer!',
|
||||
{
|
||||
'border-red-500! ring-2 ring-red-200': isError,
|
||||
'border-indigo-500 ring-2 ring-indigo-200': isFocused,
|
||||
'border-gray-300': !isError && !isFocused,
|
||||
'bg-gray-100 text-gray-400 cursor-not-allowed': isDisabled,
|
||||
}
|
||||
),
|
||||
valueContainer: () => cn('flex-1 px-4! py-2! gap-1'),
|
||||
}),
|
||||
placeholder: () =>
|
||||
cn({ 'text-gray-400': !isError, 'text-red-300!': isError }),
|
||||
singleValue: () =>
|
||||
@@ -212,29 +252,11 @@ const SelectInput = <T extends OptionType>(props: SelectInputProps<T>) => {
|
||||
components={{
|
||||
...components,
|
||||
...(optionComponent ? { Option: optionComponent } : {}),
|
||||
...(startAdornment ? {
|
||||
Control: ({ children, innerRef, innerProps, menuIsOpen, isFocused, isDisabled }) => (
|
||||
<div
|
||||
ref={innerRef}
|
||||
{...innerProps}
|
||||
className={cn(
|
||||
'w-full min-h-12! rounded-lg! border bg-white transition-shadow cursor-pointer! flex items-center',
|
||||
{
|
||||
'border-red-500! ring-2 ring-red-200': isError,
|
||||
'border-indigo-500 ring-2 ring-indigo-200': isFocused,
|
||||
'border-gray-300': !isError && !isFocused,
|
||||
'bg-gray-100 text-gray-400 cursor-not-allowed': isDisabled,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<div className='flex-1 px-4! gap-1 flex items-center'>
|
||||
{startAdornment}
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
} : {}),
|
||||
}}
|
||||
{...(startAdornment && {
|
||||
shouldShowAdornment,
|
||||
startAdornment,
|
||||
})}
|
||||
menuPortalTarget={
|
||||
typeof document !== 'undefined' ? document.body : undefined
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user