Skip to content

Commit 906c378

Browse files
(Picklist): handle deeply nested children
1 parent b1edc8a commit 906c378

File tree

1 file changed

+92
-28
lines changed

1 file changed

+92
-28
lines changed

src/scripts/Picklist.tsx

Lines changed: 92 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,95 @@ import { useControlledValue, useEventCallback, useMergeRefs } from './hooks';
2121
import { createFC } from './common';
2222
import { Bivariant } from './typeUtils';
2323

24+
/**
25+
* Recursively collect option values from PicklistItem components
26+
*/
27+
function collectOptionValues(children: unknown): PicklistValue[] {
28+
return React.Children.map(children, (child) => {
29+
if (!React.isValidElement(child)) {
30+
return [];
31+
}
32+
33+
const props = child.props;
34+
const isPropsObject = typeof props === 'object' && props !== null;
35+
36+
if (!isPropsObject) {
37+
return [];
38+
}
39+
40+
// Recursively check children for nested PicklistItems
41+
if (child.type !== PicklistItem) {
42+
return !('children' in props) ? [] : collectOptionValues(props.children);
43+
}
44+
45+
// Check if this is specifically a PicklistItem component
46+
if (
47+
!('value' in props) ||
48+
(typeof props.value !== 'string' && typeof props.value !== 'number')
49+
) {
50+
return [];
51+
}
52+
53+
return [props.value];
54+
}).flat();
55+
}
56+
57+
/**
58+
* Recursively find selected item label from PicklistItem components
59+
*/
60+
function findSelectedItemLabel(
61+
children: unknown,
62+
selectedValue: PicklistValue
63+
): React.ReactNode | null {
64+
return (
65+
React.Children.map(children, (child) => {
66+
if (!React.isValidElement(child)) {
67+
return null;
68+
}
69+
70+
const props = child.props;
71+
const isPropsObject = typeof props === 'object' && props !== null;
72+
73+
if (!isPropsObject) {
74+
return null;
75+
}
76+
77+
// Recursively check children for nested PicklistItems
78+
if (child.type !== PicklistItem) {
79+
return !('children' in props)
80+
? null
81+
: findSelectedItemLabel(props.children, selectedValue);
82+
}
83+
84+
// Check if this is specifically a PicklistItem component
85+
if (!('value' in props) || props.value !== selectedValue) {
86+
return null;
87+
}
88+
89+
// Safely access label and children properties with proper type checking
90+
const label = 'label' in props ? props.label : undefined;
91+
const itemChildren = 'children' in props ? props.children : undefined;
92+
93+
// Simple type check for React.ReactNode values
94+
const labelValue =
95+
typeof label === 'string' ||
96+
typeof label === 'number' ||
97+
React.isValidElement(label)
98+
? label
99+
: undefined;
100+
const childrenValue =
101+
typeof itemChildren === 'string' ||
102+
typeof itemChildren === 'number' ||
103+
React.isValidElement(itemChildren) ||
104+
Array.isArray(itemChildren)
105+
? itemChildren
106+
: undefined;
107+
108+
return labelValue || childrenValue;
109+
}).find((result) => result !== null) ?? null
110+
);
111+
}
112+
24113
/**
25114
*
26115
*/
@@ -161,26 +250,9 @@ export const Picklist: (<MultiSelect extends boolean | undefined>(
161250

162251
const { getActiveElement } = useContext(ComponentSettingsContext);
163252

164-
// Get option values from children
253+
// Get option values from children (recursively)
165254
const getOptionValues = useCallback(() => {
166-
const optionValues: PicklistValue[] = [];
167-
React.Children.forEach(children, (child) => {
168-
if (!React.isValidElement(child)) {
169-
return;
170-
}
171-
172-
const props: unknown = child.props;
173-
const isPropsObject = typeof props === 'object' && props !== null;
174-
175-
if (
176-
isPropsObject &&
177-
'value' in props &&
178-
(typeof props.value === 'string' || typeof props.value === 'number')
179-
) {
180-
optionValues.push(props.value);
181-
}
182-
});
183-
return optionValues;
255+
return collectOptionValues(children);
184256
}, [children]);
185257

186258
// Get next option value for keyboard navigation
@@ -414,15 +486,7 @@ export const Picklist: (<MultiSelect extends boolean | undefined>(
414486
// one item
415487
if (values.length === 1) {
416488
const selectedValue = values[0];
417-
let selected = null;
418-
React.Children.forEach(children, (item) => {
419-
if (React.isValidElement(item)) {
420-
const { label, value, children } = item.props as PicklistItemProps;
421-
if (value === selectedValue) {
422-
selected = label || children;
423-
}
424-
}
425-
});
489+
const selected = findSelectedItemLabel(children, selectedValue);
426490
return selected || selectedValue;
427491
}
428492

0 commit comments

Comments
 (0)