diff --git a/.changeset/metal-pumpkins-appear.md b/.changeset/metal-pumpkins-appear.md
new file mode 100644
index 000000000..44d66ed02
--- /dev/null
+++ b/.changeset/metal-pumpkins-appear.md
@@ -0,0 +1,5 @@
+---
+"@cube-dev/ui-kit": patch
+---
+
+Full items prop support in FilterPicker.
diff --git a/src/components/actions/Button/Button.tsx b/src/components/actions/Button/Button.tsx
index fc889854c..30c9bea84 100644
--- a/src/components/actions/Button/Button.tsx
+++ b/src/components/actions/Button/Button.tsx
@@ -124,7 +124,7 @@ export const DEFAULT_BUTTON_STYLES = {
},
ButtonIcon: {
- width: 'max-content',
+ width: 'min 1fs',
},
'& [data-element="ButtonIcon"]:first-child:not(:last-child)': {
@@ -722,8 +722,7 @@ export const Button = forwardRef(function Button(
)
) : null}
- {((hasIcons && children) || (!!icon && !!rightIcon)) &&
- typeof children === 'string' ? (
+ {hasIcons && typeof children === 'string' ? (
{children}
) : (
children
diff --git a/src/components/fields/FilterListBox/FilterListBox.stories.tsx b/src/components/fields/FilterListBox/FilterListBox.stories.tsx
index 46da8a864..81b82e779 100644
--- a/src/components/fields/FilterListBox/FilterListBox.stories.tsx
+++ b/src/components/fields/FilterListBox/FilterListBox.stories.tsx
@@ -1187,12 +1187,12 @@ EscapeKeyHandling.parameters = {
};
export const VirtualizedList: StoryFn> = (args) => {
- const [selectedKeys, setSelectedKeys] = useState([]);
+ const [selectedKeys, setSelectedKeys] = useState(['item-2']);
// Generate a large list of items with varying content to test virtualization
// Mix items with and without descriptions to test dynamic sizing
const items = Array.from({ length: 100 }, (_, i) => ({
- id: `item-${i}`,
+ id: `item-${i + 1}`,
name: `Item ${i + 1}${i % 7 === 0 ? ' - This is a longer item name to test dynamic sizing' : ''}`,
description:
i % 3 === 0
diff --git a/src/components/fields/FilterPicker/FilterPicker.stories.tsx b/src/components/fields/FilterPicker/FilterPicker.stories.tsx
index b30e8eee3..aaa0196d1 100644
--- a/src/components/fields/FilterPicker/FilterPicker.stories.tsx
+++ b/src/components/fields/FilterPicker/FilterPicker.stories.tsx
@@ -1526,12 +1526,12 @@ export const VirtualizedList: Story = {
await userEvent.click(trigger);
},
render: (args) => {
- const [selectedKeys, setSelectedKeys] = useState([]);
+ const [selectedKeys, setSelectedKeys] = useState(['item-2']);
// Generate a large list of items with varying content to trigger virtualization
// Mix items with and without descriptions to test dynamic sizing
const items = Array.from({ length: 100 }, (_, i) => ({
- id: `item-${i}`,
+ id: `item-${i + 1}`,
name: `Item ${i + 1}${i % 7 === 0 ? ' - This is a longer item name to test dynamic sizing' : ''}`,
description:
i % 3 === 0
@@ -1584,15 +1584,15 @@ export const VirtualizedList: Story = {
export const WithSelectAll: Story = {
render: (args) => (
-
- {permissions.map((permission) => (
+
+ {(permission: any) => (
{permission.label}
- ))}
+ )}
),
args: {
@@ -1603,8 +1603,7 @@ export const WithSelectAll: Story = {
showSelectAll: true,
selectAllLabel: 'All Permissions',
defaultSelectedKeys: ['read'],
- type: 'outline',
- size: 'medium',
+ width: '30x',
},
parameters: {
docs: {
diff --git a/src/components/fields/FilterPicker/FilterPicker.test.tsx b/src/components/fields/FilterPicker/FilterPicker.test.tsx
index 5918b2a72..38b987228 100644
--- a/src/components/fields/FilterPicker/FilterPicker.test.tsx
+++ b/src/components/fields/FilterPicker/FilterPicker.test.tsx
@@ -843,4 +843,75 @@ describe('', () => {
expect(options[1]).toHaveTextContent('Apple');
});
});
+
+ describe('Items prop functionality', () => {
+ const itemsWithLabels = [
+ { key: 'apple', label: 'Red Apple' },
+ { key: 'banana', label: 'Yellow Banana' },
+ { key: 'cherry', label: 'Sweet Cherry' },
+ ];
+
+ it('should display labels correctly when using items prop with label property', async () => {
+ const { getByRole } = renderWithRoot(
+
+ {(item) => (
+ {item.label}
+ )}
+ ,
+ );
+
+ // Wait for component to render
+ await act(async () => {
+ await new Promise((resolve) => setTimeout(resolve, 50));
+ });
+
+ const trigger = getByRole('button');
+
+ // Should display the label, not the key
+ expect(trigger).toHaveTextContent('Red Apple');
+ expect(trigger).not.toHaveTextContent('apple');
+ });
+
+ it('should display correct labels in multiple selection mode with items prop', async () => {
+ const renderSummary = jest.fn(
+ ({ selectedLabels }) => `Selected: ${selectedLabels.join(', ')}`,
+ );
+
+ const { getByRole } = renderWithRoot(
+
+ {(item) => (
+ {item.label}
+ )}
+ ,
+ );
+
+ // Wait for component to render
+ await act(async () => {
+ await new Promise((resolve) => setTimeout(resolve, 50));
+ });
+
+ // Check that renderSummary was called with correct labels
+ expect(renderSummary).toHaveBeenCalledWith({
+ selectedLabels: ['Red Apple', 'Sweet Cherry'],
+ selectedKeys: ['apple', 'cherry'],
+ selectionMode: 'multiple',
+ });
+
+ const trigger = getByRole('button');
+ expect(trigger).toHaveTextContent('Selected: Red Apple, Sweet Cherry');
+ });
+ });
});
diff --git a/src/components/fields/FilterPicker/FilterPicker.tsx b/src/components/fields/FilterPicker/FilterPicker.tsx
index d8c9f2aa0..9004bfa6f 100644
--- a/src/components/fields/FilterPicker/FilterPicker.tsx
+++ b/src/components/fields/FilterPicker/FilterPicker.tsx
@@ -14,7 +14,7 @@ import {
useState,
} from 'react';
import { FocusScope, Key, useKeyboard } from 'react-aria';
-import { Section as BaseSection, Item } from 'react-stately';
+import { Section as BaseSection, Item, ListState } from 'react-stately';
import { useWarn } from '../../../_internal/hooks/use-warn';
import { DirectionIcon } from '../../../icons';
@@ -44,6 +44,15 @@ import { ListBox } from '../ListBox';
import type { FieldBaseProps } from '../../../shared';
+// Define interface for items that can have keys
+interface ItemWithKey {
+ key?: string | number;
+ id?: string | number;
+ textValue?: string;
+ children?: ItemWithKey[];
+ [key: string]: unknown;
+}
+
export interface CubeFilterPickerProps
extends Omit, 'size'>,
BasePropsWithoutChildren,
@@ -95,16 +104,16 @@ export interface CubeFilterPickerProps
*/
renderSummary?:
| ((args: {
- selectedLabels: string[];
- selectedKeys: 'all' | (string | number)[];
+ selectedLabels?: string[];
+ selectedKeys?: 'all' | (string | number)[];
selectedLabel?: string;
selectedKey?: string | number | null;
- selectionMode: 'single' | 'multiple';
+ selectionMode?: 'single' | 'multiple';
}) => ReactNode)
| false;
/** Ref to access internal ListBox state (from FilterListBox) */
- listStateRef?: MutableRefObject;
+ listStateRef?: MutableRefObject>;
/** Additional modifiers for styling the FilterPicker */
mods?: Record;
}
@@ -130,7 +139,7 @@ export const FilterPicker = forwardRef(function FilterPicker(
props = useFormProps(props);
props = useFieldProps(props, {
valuePropsMapper: ({ value, onChange }) => {
- const fieldProps: any = {};
+ const fieldProps: Record = {};
if (props.selectionMode === 'multiple') {
fieldProps.selectedKeys = value || [];
@@ -138,7 +147,7 @@ export const FilterPicker = forwardRef(function FilterPicker(
fieldProps.selectedKey = value ?? null;
}
- fieldProps.onSelectionChange = (key: any) => {
+ fieldProps.onSelectionChange = (key: Key | null | 'all' | Key[]) => {
if (props.selectionMode === 'multiple') {
// Handle "all" selection and array selections
if (key === 'all') {
@@ -225,6 +234,8 @@ export const FilterPicker = forwardRef(function FilterPicker(
// Track popover open/close and capture children order for session
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const cachedChildrenOrder = useRef(null);
+ // Cache for sorted items array when using `items` prop
+ const cachedItemsOrder = useRef(null);
const triggerRef = useRef(null);
const isControlledSingle = selectedKey !== undefined;
@@ -239,7 +250,7 @@ export const FilterPicker = forwardRef(function FilterPicker(
// Utility: remove React's ".$" / "." prefixes from element keys so that we
// can compare them with user-provided keys.
- const normalizeKeyValue = (key: any): string => {
+ const normalizeKeyValue = (key: Key): string => {
if (key == null) return '';
const str = String(key);
return str.startsWith('.$')
@@ -258,24 +269,25 @@ export const FilterPicker = forwardRef(function FilterPicker(
// ---------------------------------------------------------------------------
const findReactKey = useCallback(
- (lookup: any): any => {
+ (lookup: Key): Key => {
if (lookup == null) return lookup;
const normalizedLookup = normalizeKeyValue(lookup);
- let foundKey: any = lookup;
+ let foundKey: Key = lookup;
const traverse = (nodes: ReactNode): void => {
- Children.forEach(nodes, (child: any) => {
+ Children.forEach(nodes, (child: ReactNode) => {
if (!child || typeof child !== 'object') return;
+ const element = child as ReactElement;
- if (child.key != null) {
- if (normalizeKeyValue(child.key) === normalizedLookup) {
- foundKey = child.key;
+ if (element.key != null) {
+ if (normalizeKeyValue(element.key) === normalizedLookup) {
+ foundKey = element.key;
}
}
- if (child.props?.children) {
- traverse(child.props.children);
+ if (element.props?.children) {
+ traverse(element.props.children);
}
});
};
@@ -289,23 +301,23 @@ export const FilterPicker = forwardRef(function FilterPicker(
const mappedSelectedKey = useMemo(() => {
if (selectionMode !== 'single') return null;
- return findReactKey(effectiveSelectedKey);
+ return effectiveSelectedKey ? findReactKey(effectiveSelectedKey) : null;
}, [selectionMode, effectiveSelectedKey, findReactKey]);
const mappedSelectedKeys = useMemo(() => {
- if (selectionMode !== 'multiple') return undefined as any;
+ if (selectionMode !== 'multiple') return undefined;
if (effectiveSelectedKeys === 'all') return 'all' as const;
if (Array.isArray(effectiveSelectedKeys)) {
- return (effectiveSelectedKeys as any[]).map((k) => findReactKey(k));
+ return (effectiveSelectedKeys as Key[]).map((k) => findReactKey(k));
}
- return effectiveSelectedKeys as any;
+ return effectiveSelectedKeys;
}, [selectionMode, effectiveSelectedKeys, findReactKey]);
// Given an iterable of keys (array or Set) toggle membership for duplicates
- const processSelectionArray = (iterable: Iterable): string[] => {
+ const processSelectionArray = (iterable: Iterable): string[] => {
const resultSet = new Set();
for (const key of iterable) {
const nKey = String(key);
@@ -320,33 +332,74 @@ export const FilterPicker = forwardRef(function FilterPicker(
// Helper to get selected item labels for display
const getSelectedLabels = () => {
- if (!children) return [];
-
// Handle "all" selection - return all available labels
if (selectionMode === 'multiple' && effectiveSelectedKeys === 'all') {
const allLabels: string[] = [];
- const extractAllLabels = (nodes: ReactNode): void => {
- Children.forEach(nodes, (child: any) => {
- if (!child || typeof child !== 'object') return;
+ // Extract from items prop if available
+ if (items) {
+ const extractFromItems = (itemsArray: unknown[]): void => {
+ itemsArray.forEach((item) => {
+ if (item && typeof item === 'object') {
+ const itemObj = item as ItemWithKey;
+ if (Array.isArray(itemObj.children)) {
+ // Section-like object
+ extractFromItems(itemObj.children);
+ } else {
+ // Regular item - extract label
+ const label =
+ itemObj.textValue ||
+ (itemObj as any).label ||
+ (typeof (itemObj as any).children === 'string'
+ ? (itemObj as any).children
+ : '') ||
+ String(
+ (itemObj as any).children ||
+ itemObj.key ||
+ itemObj.id ||
+ item,
+ );
+ allLabels.push(label);
+ }
+ }
+ });
+ };
- if (child.type === Item) {
- const label =
- child.props.textValue ||
- (typeof child.props.children === 'string'
- ? child.props.children
- : '') ||
- String(child.props.children || '');
- allLabels.push(label);
- }
+ const itemsArray = Array.isArray(items)
+ ? items
+ : Array.from(items as Iterable);
+ extractFromItems(itemsArray);
+ return allLabels;
+ }
- if (child.props?.children) {
- extractAllLabels(child.props.children);
- }
- });
- };
+ // Extract from children if available
+ if (children) {
+ const extractAllLabels = (nodes: ReactNode): void => {
+ if (!nodes) return;
+ Children.forEach(nodes, (child: ReactNode) => {
+ if (!child || typeof child !== 'object') return;
+ const element = child as ReactElement;
+
+ if (element.type === Item) {
+ const label =
+ element.props.textValue ||
+ (typeof element.props.children === 'string'
+ ? element.props.children
+ : '') ||
+ String(element.props.children || '');
+ allLabels.push(label);
+ }
+
+ if (element.props?.children) {
+ extractAllLabels(element.props.children);
+ }
+ });
+ };
+
+ extractAllLabels(children as ReactNode);
+ return allLabels;
+ }
- extractAllLabels(children as ReactNode);
return allLabels;
}
@@ -359,59 +412,77 @@ export const FilterPicker = forwardRef(function FilterPicker(
);
const labels: string[] = [];
+ const processedKeys = new Set();
- const extractLabels = (nodes: ReactNode): void => {
- Children.forEach(nodes, (child: any) => {
- if (!child || typeof child !== 'object') return;
-
- if (child.type === Item) {
- if (selectedSet.has(normalizeKeyValue(child.key))) {
- const label =
- child.props.textValue ||
- (typeof child.props.children === 'string'
- ? child.props.children
- : '') ||
- String(child.props.children || '');
- labels.push(label);
+ // Extract from items prop if available
+ if (items) {
+ const extractFromItems = (itemsArray: unknown[]): void => {
+ itemsArray.forEach((item) => {
+ if (item && typeof item === 'object') {
+ const itemObj = item as ItemWithKey;
+ if (Array.isArray(itemObj.children)) {
+ // Section-like object
+ extractFromItems(itemObj.children);
+ } else {
+ // Regular item - check if selected
+ const itemKey = itemObj.key || itemObj.id;
+ if (
+ itemKey != null &&
+ selectedSet.has(normalizeKeyValue(itemKey))
+ ) {
+ const label =
+ itemObj.textValue ||
+ (itemObj as any).label ||
+ (typeof (itemObj as any).children === 'string'
+ ? (itemObj as any).children
+ : '') ||
+ String((itemObj as any).children || itemKey);
+ labels.push(label);
+ processedKeys.add(normalizeKeyValue(itemKey));
+ }
+ }
}
- }
-
- if (child.props?.children) {
- extractLabels(child.props.children);
- }
- });
- };
+ });
+ };
- const processedKeys = new Set();
+ const itemsArray = Array.isArray(items)
+ ? items
+ : Array.from(items as Iterable);
+ extractFromItems(itemsArray);
+ }
- // Modified extractLabels to track which keys we've processed
- const extractLabelsWithTracking = (nodes: ReactNode): void => {
- Children.forEach(nodes, (child: any) => {
- if (!child || typeof child !== 'object') return;
-
- if (child.type === Item) {
- const childKey = String(child.key);
- if (selectedSet.has(childKey)) {
- const label =
- child.props.textValue ||
- (typeof child.props.children === 'string'
- ? child.props.children
- : '') ||
- String(child.props.children || '');
- labels.push(label);
- processedKeys.add(childKey);
+ // Extract from children if available (for mixed mode or fallback)
+ if (children) {
+ const extractLabelsWithTracking = (nodes: ReactNode): void => {
+ if (!nodes) return;
+ Children.forEach(nodes, (child: ReactNode) => {
+ if (!child || typeof child !== 'object') return;
+ const element = child as ReactElement;
+
+ if (element.type === Item) {
+ const childKey = String(element.key);
+ if (selectedSet.has(normalizeKeyValue(childKey))) {
+ const label =
+ element.props.textValue ||
+ (typeof element.props.children === 'string'
+ ? element.props.children
+ : '') ||
+ String(element.props.children || '');
+ labels.push(label);
+ processedKeys.add(normalizeKeyValue(childKey));
+ }
}
- }
- if (child.props?.children) {
- extractLabelsWithTracking(child.props.children);
- }
- });
- };
+ if (element.props?.children) {
+ extractLabelsWithTracking(element.props.children);
+ }
+ });
+ };
- extractLabelsWithTracking(children as ReactNode);
+ extractLabelsWithTracking(children as ReactNode);
+ }
- // Handle custom values that don't have corresponding children
+ // Handle custom values that don't have corresponding items/children
const selectedKeysArr =
selectionMode === 'multiple' && effectiveSelectedKeys !== 'all'
? (effectiveSelectedKeys || []).map(String)
@@ -468,7 +539,8 @@ export const FilterPicker = forwardRef(function FilterPicker(
// Function to sort children with selected items on top
const getSortedChildren = useCallback(() => {
- if (!children) return children;
+ // If children is not provided or is a render function, return it as-is
+ if (!children || typeof children === 'function') return children;
// When the popover is **closed**, reuse the cached order if we have it to
// avoid unnecessary reflows. If we don't have a cache yet (first open),
@@ -519,7 +591,7 @@ export const FilterPicker = forwardRef(function FilterPicker(
}
// Helper function to check if an item is selected
- const isItemSelected = (child: any): boolean => {
+ const isItemSelected = (child: ReactElement): boolean => {
return (
child?.key != null && selectedSet.has(normalizeKeyValue(child.key))
);
@@ -527,45 +599,50 @@ export const FilterPicker = forwardRef(function FilterPicker(
// Helper function to sort children array
const sortChildrenArray = (childrenArray: ReactNode[]): ReactNode[] => {
- const cloneWithNormalizedKey = (item: any) =>
+ const cloneWithNormalizedKey = (item: ReactElement) =>
cloneElement(item, {
- key: normalizeKeyValue(item.key),
+ key: item.key ? normalizeKeyValue(item.key) : undefined,
});
const selected: ReactNode[] = [];
const unselected: ReactNode[] = [];
- childrenArray.forEach((child: any) => {
+ childrenArray.forEach((child: ReactNode) => {
if (!child || typeof child !== 'object') {
unselected.push(child);
return;
}
+ const element = child as ReactElement;
+
// Handle sections - sort items within each section
if (
- child.type === BaseSection ||
- child.type?.displayName === 'Section'
+ element.type === BaseSection ||
+ (element.type as any)?.displayName === 'Section'
) {
- const sectionChildren = Array.isArray(child.props.children)
- ? child.props.children
- : [child.props.children];
+ const sectionChildren = Array.isArray(element.props.children)
+ ? element.props.children
+ : [element.props.children];
const selectedItems: ReactNode[] = [];
const unselectedItems: ReactNode[] = [];
- sectionChildren.forEach((sectionChild: any) => {
- if (
- sectionChild &&
- typeof sectionChild === 'object' &&
- (sectionChild.type === Item ||
- sectionChild.type?.displayName === 'Item')
- ) {
- const clonedItem = cloneWithNormalizedKey(sectionChild);
-
- if (isItemSelected(sectionChild)) {
- selectedItems.push(clonedItem);
+ sectionChildren.forEach((sectionChild: ReactNode) => {
+ if (sectionChild && typeof sectionChild === 'object') {
+ const sectionElement = sectionChild as ReactElement;
+ if (
+ sectionElement.type === Item ||
+ (sectionElement.type as any)?.displayName === 'Item'
+ ) {
+ const clonedItem = cloneWithNormalizedKey(sectionElement);
+
+ if (isItemSelected(sectionElement)) {
+ selectedItems.push(clonedItem);
+ } else {
+ unselectedItems.push(clonedItem);
+ }
} else {
- unselectedItems.push(clonedItem);
+ unselectedItems.push(sectionChild);
}
} else {
unselectedItems.push(sectionChild);
@@ -574,17 +651,17 @@ export const FilterPicker = forwardRef(function FilterPicker(
// Create new section with sorted children, preserving React element properly
unselected.push(
- cloneElement(child, {
- ...child.props,
+ cloneElement(element, {
+ ...element.props,
children: [...selectedItems, ...unselectedItems],
}),
);
}
// Handle non-section elements (items, dividers, etc.)
else {
- const clonedItem = cloneWithNormalizedKey(child);
+ const clonedItem = cloneWithNormalizedKey(element);
- if (isItemSelected(child)) {
+ if (isItemSelected(element)) {
selected.push(clonedItem);
} else {
unselected.push(clonedItem);
@@ -614,8 +691,92 @@ export const FilterPicker = forwardRef(function FilterPicker(
isPopoverOpen,
]);
+ // Compute sorted items array when using `items` prop
+ const getSortedItems = useCallback(() => {
+ if (!items) return items;
+
+ // Reuse cached order when popover is closed to avoid needless re-renders
+ if (!isPopoverOpen && cachedItemsOrder.current) {
+ return cachedItemsOrder.current;
+ }
+
+ const selectedSet = new Set();
+
+ const addSelected = (key: Key) => {
+ if (key != null) selectedSet.add(String(key));
+ };
+
+ if (selectionMode === 'multiple') {
+ if (selectionsWhenClosed.current.multiple === 'all') {
+ // Do not sort when all selected – keep original order
+ return items;
+ }
+ (selectionsWhenClosed.current.multiple as string[]).forEach(addSelected);
+ } else {
+ if (selectionsWhenClosed.current.single != null) {
+ addSelected(selectionsWhenClosed.current.single);
+ }
+ }
+
+ if (selectedSet.size === 0) {
+ return items;
+ }
+
+ // Helpers to extract key from item object
+ const getItemKey = (obj: unknown): string | undefined => {
+ if (obj == null || typeof obj !== 'object') return undefined;
+
+ const item = obj as ItemWithKey;
+ if (item.key != null) return String(item.key);
+ if (item.id != null) return String(item.id);
+ return undefined;
+ };
+
+ const sortArray = (arr: unknown[]): unknown[] => {
+ const selectedArr: unknown[] = [];
+ const unselectedArr: unknown[] = [];
+
+ arr.forEach((obj) => {
+ const item = obj as ItemWithKey;
+ if (obj && Array.isArray(item.children)) {
+ // Section-like object – keep order, but sort its children
+ const sortedChildren = sortArray(item.children);
+ unselectedArr.push({ ...item, children: sortedChildren });
+ } else {
+ const key = getItemKey(obj);
+ if (key && selectedSet.has(key)) {
+ selectedArr.push(obj);
+ } else {
+ unselectedArr.push(obj);
+ }
+ }
+ });
+
+ return [...selectedArr, ...unselectedArr];
+ };
+
+ const itemsArray = Array.isArray(items)
+ ? items
+ : Array.from(items as Iterable);
+ const sorted = sortArray(itemsArray) as T[];
+
+ if (isPopoverOpen || !cachedItemsOrder.current) {
+ cachedItemsOrder.current = sorted;
+ }
+
+ return sorted;
+ }, [
+ items,
+ selectionMode,
+ isPopoverOpen,
+ selectionsWhenClosed.current.multiple,
+ selectionsWhenClosed.current.single,
+ ]);
+
+ const finalItems = getSortedItems();
+
// FilterListBox handles custom values internally when allowsCustomValue={true}
- // We only provide the sorted original children
+ // We provide sorted children (if any) and sorted items
const finalChildren = getSortedChildren();
const renderTriggerContent = () => {
@@ -626,14 +787,14 @@ export const FilterPicker = forwardRef(function FilterPicker(
selectedLabel: selectedLabels[0],
selectedKey: effectiveSelectedKey ?? null,
selectedLabels,
- selectedKeys: effectiveSelectedKeys as any,
+ selectedKeys: effectiveSelectedKeys,
selectionMode: 'single',
});
}
return renderSummary({
selectedLabels,
- selectedKeys: effectiveSelectedKeys as any,
+ selectedKeys: effectiveSelectedKeys,
selectionMode: 'multiple',
});
} else if (hasSelection && renderSummary === false) {
@@ -749,7 +910,7 @@ export const FilterPicker = forwardRef(function FilterPicker(
(
// Update internal state if uncontrolled
if (selectionMode === 'single') {
if (!isControlledSingle) {
- setInternalSelectedKey(selection as any);
+ setInternalSelectedKey(selection as Key | null);
}
} else {
if (!isControlledMultiple) {
- let normalized: any = selection;
+ let normalized: 'all' | Key[] = selection as
+ | 'all'
+ | Key[];
if (selection === 'all') {
normalized = 'all';
@@ -810,16 +973,19 @@ export const FilterPicker = forwardRef(function FilterPicker(
typeof selection === 'object' &&
(selection as any) instanceof Set
) {
- normalized = processSelectionArray(selection as any);
+ normalized = processSelectionArray(
+ selection as Set,
+ );
}
- setInternalSelectedKeys(normalized as any);
+ setInternalSelectedKeys(normalized);
}
}
// Update latest selection ref synchronously
if (selectionMode === 'single') {
- latestSelectionRef.current.single = selection as any;
+ latestSelectionRef.current.single =
+ selection != null ? String(selection) : null;
} else {
if (selection === 'all') {
latestSelectionRef.current.multiple = 'all';
@@ -833,21 +999,30 @@ export const FilterPicker = forwardRef(function FilterPicker(
(selection as any) instanceof Set
) {
latestSelectionRef.current.multiple = Array.from(
- new Set(processSelectionArray(selection as any)),
+ new Set(processSelectionArray(selection as Set)),
);
} else {
- latestSelectionRef.current.multiple = selection as any;
+ latestSelectionRef.current.multiple =
+ selection === 'all'
+ ? 'all'
+ : Array.isArray(selection)
+ ? selection.map(String)
+ : [];
}
}
- onSelectionChange?.(selection as any);
+ onSelectionChange?.(selection);
if (selectionMode === 'single') {
close();
}
}}
>
- {finalChildren as CollectionChildren}
+ {
+ (children
+ ? (finalChildren as CollectionChildren)
+ : undefined) as CollectionChildren
+ }