diff --git a/lib/components/MultiSelectDropdown/MultiSelectDropdown.stories.tsx b/lib/components/MultiSelectDropdown/MultiSelectDropdown.stories.tsx
index dc0d6dd0..e5343fbb 100644
--- a/lib/components/MultiSelectDropdown/MultiSelectDropdown.stories.tsx
+++ b/lib/components/MultiSelectDropdown/MultiSelectDropdown.stories.tsx
@@ -34,8 +34,6 @@ export const MultiSelectDropdown: Story = {
value: 'prod',
},
],
- onBlur: () => console.log('blur'),
- onChange: (args) => console.log('change', args),
},
render: (args) => (
diff --git a/lib/components/MultiSelectDropdown/MultiSelectDropdown.tsx b/lib/components/MultiSelectDropdown/MultiSelectDropdown.tsx
index 97c43430..f7ea21d0 100644
--- a/lib/components/MultiSelectDropdown/MultiSelectDropdown.tsx
+++ b/lib/components/MultiSelectDropdown/MultiSelectDropdown.tsx
@@ -10,7 +10,17 @@ export const MultiSelectDropdown: FC
= forwardRef<
MultiSelectDropdownProps
>(
(
- { options, multiselect, value, onChange, onBlur, name, ...delegated },
+ {
+ options,
+ multiselect,
+ value,
+ onChange,
+ onBlur,
+ name,
+ isLoading,
+ noOptionsText,
+ ...delegated
+ },
ref,
) => (
= forwardRef<
onChange={onChange}
onBlur={onBlur}
name={name}
+ isLoading={isLoading}
+ noOptionsText={noOptionsText}
>
),
);
+
+MultiSelectDropdown.displayName = 'MultiSelectDropdown';
diff --git a/lib/components/MultiSelectDropdown/MultiSelectDropdown.types.ts b/lib/components/MultiSelectDropdown/MultiSelectDropdown.types.ts
index 0f3cbb3a..f8b2da97 100644
--- a/lib/components/MultiSelectDropdown/MultiSelectDropdown.types.ts
+++ b/lib/components/MultiSelectDropdown/MultiSelectDropdown.types.ts
@@ -8,7 +8,7 @@ import { multiSelectDropdownVariants } from './MultiSelectDropdown.variants';
export type MultiSelectDropdownOption = {
id: string | number;
label: string;
- tagLabel: string;
+ tagLabel?: string;
tagColor?: TagProps['color'];
value?: string;
};
@@ -17,6 +17,11 @@ type OnChangeFn = (params: {
target: { value: MultiSelectDropdownOption[]; name: string };
}) => void;
+type OnBlurFn = (event: {
+ target: HTMLInputElement | null;
+ type?: string;
+}) => void;
+
export interface MultiSelectDropdownProps
extends
VariantProps,
@@ -33,5 +38,7 @@ export interface MultiSelectDropdownProps
multiselect?: boolean;
value?: MultiSelectDropdownOption[];
onChange?: OnChangeFn;
- onBlur?: VoidFunction;
+ onBlur?: OnBlurFn;
+ isLoading?: boolean;
+ noOptionsText?: string;
}
diff --git a/lib/components/MultiSelectDropdown/components/Item/Item.tsx b/lib/components/MultiSelectDropdown/components/Item/Item.tsx
index d9d2e2d1..8557cabf 100644
--- a/lib/components/MultiSelectDropdown/components/Item/Item.tsx
+++ b/lib/components/MultiSelectDropdown/components/Item/Item.tsx
@@ -8,25 +8,32 @@ import { ItemProps } from './Item.types';
import { wrapperVariants } from './Item.variants';
import { Tag, Typography } from '@/components';
-export const Item: FC = ({ option, theme, isSelected }) => {
+export const Item: FC = ({
+ option,
+ theme,
+ isSelected,
+ className,
+}) => {
const { onSelectOption } = useMultiSelectDropdown();
return (
onSelectOption(option)}
>
{option.label}
-
+ {option.tagLabel && (
+
+ )}
);
};
diff --git a/lib/components/MultiSelectDropdown/components/Item/Item.types.ts b/lib/components/MultiSelectDropdown/components/Item/Item.types.ts
index efc5d574..b7f33be9 100644
--- a/lib/components/MultiSelectDropdown/components/Item/Item.types.ts
+++ b/lib/components/MultiSelectDropdown/components/Item/Item.types.ts
@@ -6,4 +6,5 @@ export type ItemProps = {
option: MultiSelectDropdownOption;
theme?: Theme;
isSelected?: boolean;
+ className?: string;
};
diff --git a/lib/components/MultiSelectDropdown/components/List/List.tsx b/lib/components/MultiSelectDropdown/components/List/List.tsx
index 5da46e72..99d74e41 100644
--- a/lib/components/MultiSelectDropdown/components/List/List.tsx
+++ b/lib/components/MultiSelectDropdown/components/List/List.tsx
@@ -1,20 +1,28 @@
import { FC } from 'react';
import { cn } from '@/utils';
+import { Typography } from '@/components';
import { Item } from '../Item/Item';
import { useMultiSelectDropdown } from '../../contexts';
import { ListProps } from './List.types';
import { wrapperVariants } from './List.variants';
-import { Typography } from '@/components';
export const List: FC = ({ theme }) => {
- const { options, selectedOptions } = useMultiSelectDropdown();
+ const { options, selectedOptions, isLoading, noOptionsText } =
+ useMultiSelectDropdown();
return (
- {options.length > 0 ? (
+ {isLoading ? (
+
+ ) : options.length > 0 ? (
options.map((option) => (
- = ({ theme }) => {
/>
))
) : (
-
- No options
-
+
-
+
+ {noOptionsText ?? 'No options'}
+
+
)}
);
diff --git a/lib/components/MultiSelectDropdown/components/Wrapper/Wrapper.tsx b/lib/components/MultiSelectDropdown/components/Wrapper/Wrapper.tsx
index 67ad4e91..80021b97 100644
--- a/lib/components/MultiSelectDropdown/components/Wrapper/Wrapper.tsx
+++ b/lib/components/MultiSelectDropdown/components/Wrapper/Wrapper.tsx
@@ -1,7 +1,8 @@
import { FC, forwardRef, useId, useImperativeHandle } from 'react';
import { ChevronUp } from 'react-feather';
-import { Tag } from '@/components/Tag/Tag';
+import { Tag } from '@/components';
+import Loader from '@/assets/icons/loader.svg';
import { cn } from '@/utils';
import { useMultiSelectDropdown as useMultiSelectDropdownContext } from '../../contexts';
@@ -25,8 +26,14 @@ export const Wrapper: FC = forwardRef<
ref,
) => {
const id = useId();
- const { selectedOptions, isOpen, onOpen, onRemoveOption, inputRef } =
- useMultiSelectDropdownContext();
+ const {
+ selectedOptions,
+ isOpen,
+ onOpen,
+ onRemoveOption,
+ inputRef,
+ isLoading,
+ } = useMultiSelectDropdownContext();
const { wrapperRef, handleOpen } = useMultiSelectDropdown();
useImperativeHandle(ref, () => inputRef!.current!, [inputRef]);
@@ -72,7 +79,7 @@ export const Wrapper: FC = forwardRef<
= forwardRef<
)}
-
+ {isLoading ? (
+
+ ) : (
+
+ )}
= forwardRef<
);
},
);
+
+Wrapper.displayName = 'MultiSelectDropdownWrapper';
diff --git a/lib/components/MultiSelectDropdown/contexts/MultiSelectDropdown.context.ts b/lib/components/MultiSelectDropdown/contexts/MultiSelectDropdown.context.ts
index f5cd65d4..13cd77c0 100644
--- a/lib/components/MultiSelectDropdown/contexts/MultiSelectDropdown.context.ts
+++ b/lib/components/MultiSelectDropdown/contexts/MultiSelectDropdown.context.ts
@@ -16,6 +16,8 @@ const initialState: State = {
onOpen() {
throw new Error('Function not implemented.');
},
+ isLoading: false,
+ noOptionsText: undefined,
};
export const MultiSelectDropdownContext = createContext(initialState);
diff --git a/lib/components/MultiSelectDropdown/contexts/MultiSelectDropdown.provider.tsx b/lib/components/MultiSelectDropdown/contexts/MultiSelectDropdown.provider.tsx
index 9b267b74..6534f85f 100644
--- a/lib/components/MultiSelectDropdown/contexts/MultiSelectDropdown.provider.tsx
+++ b/lib/components/MultiSelectDropdown/contexts/MultiSelectDropdown.provider.tsx
@@ -23,6 +23,8 @@ export const MultiSelectDropdownProvider: FC<
onChange,
onBlur,
name,
+ isLoading,
+ noOptionsText,
}) => {
const inputRef = useRef>(null);
const [isOpen, setIsOpen] = useToggle(false);
@@ -33,7 +35,24 @@ export const MultiSelectDropdownProvider: FC<
>([]);
const isControlled = value !== undefined;
- // Sync value prop to selected options
+ // Sync defaultOptions to options state (for uncontrolled mode)
+ useEffect(() => {
+ if (!isControlled) {
+ const selectedIdsSet = new Set(
+ selectedOptions.map((option) => option.id),
+ );
+ setOptions(
+ multiselect
+ ? defaultOptions.filter((option) => !selectedIdsSet.has(option.id))
+ : defaultOptions.map((option) => ({
+ ...option,
+ isSelected: selectedIdsSet.has(option.id),
+ })),
+ );
+ }
+ }, [defaultOptions, multiselect, isControlled, selectedOptions]);
+
+ // Sync value prop to selected options (for controlled mode)
useEffect(() => {
if (isControlled) {
const selected = value || [];
@@ -83,11 +102,14 @@ export const MultiSelectDropdownProvider: FC<
setIsOpen(value);
// Call onBlur when closing the dropdown
- if (wasOpen && value === false && onBlur) {
- onBlur();
+ if (wasOpen && value === false && onBlur && inputRef.current) {
+ onBlur({
+ target: inputRef.current,
+ type: 'blur',
+ });
}
},
- [isOpen, setIsOpen, onBlur],
+ [isOpen, setIsOpen, onBlur, inputRef],
);
const handleSelectOption = useCallback(
@@ -179,6 +201,8 @@ export const MultiSelectDropdownProvider: FC<
onSelectOption: handleSelectOption,
onRemoveOption: handleRemoveOption,
onOpen: handleOpen,
+ isLoading,
+ noOptionsText,
}}
>
{children}
diff --git a/lib/components/MultiSelectDropdown/contexts/MultiSelectDropdown.types.ts b/lib/components/MultiSelectDropdown/contexts/MultiSelectDropdown.types.ts
index 8f91cd85..099e8eba 100644
--- a/lib/components/MultiSelectDropdown/contexts/MultiSelectDropdown.types.ts
+++ b/lib/components/MultiSelectDropdown/contexts/MultiSelectDropdown.types.ts
@@ -10,6 +10,8 @@ export type State = {
onSelectOption: (option: MultiSelectDropdownOption) => void;
onRemoveOption: (option: MultiSelectDropdownOption) => void;
onOpen: (value?: boolean) => void;
+ isLoading?: boolean;
+ noOptionsText?: string;
};
export type MultiSelectDropdownProviderProps = PropsWithChildren & {
@@ -19,6 +21,8 @@ export type MultiSelectDropdownProviderProps = PropsWithChildren & {
onChange?: (params: {
target: { value: MultiSelectDropdownOption[]; name: string };
}) => void;
- onBlur?: VoidFunction;
+ onBlur?: (event: { target: HTMLInputElement | null; type?: string }) => void;
name?: string;
+ isLoading?: boolean;
+ noOptionsText?: string;
};