Skip to content

Commit 19a6b74

Browse files
authored
Merge pull request #494 from mashmatrix/support-slds-2-convert-comboboxes-to-buttons
Convert `combobox`es to `input`s for SLDS2
2 parents 1e219f8 + 42543c5 commit 19a6b74

File tree

2 files changed

+67
-14
lines changed

2 files changed

+67
-14
lines changed

src/scripts/Lookup.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,16 +152,18 @@ const LookupSelectedState: FC<LookupSelectedStateProps> = ({
152152
size='small'
153153
/>
154154
)}
155-
<div
155+
<input
156+
type='text'
157+
readOnly
158+
disabled={disabled}
159+
value={selected.label}
156160
role='combobox'
157161
tabIndex={disabled ? -1 : 0}
158162
className='slds-input_faux slds-combobox__input slds-combobox__input-value'
159163
aria-controls={listboxId}
160164
aria-haspopup='listbox'
161165
aria-expanded='false'
162-
>
163-
<span className='slds-truncate'>{selected.label}</span>
164-
</div>
166+
/>
165167
<Button
166168
type='icon'
167169
icon='close'

src/scripts/Picklist.tsx

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { FormElement, FormElementProps } from './FormElement';
1717
import { Icon } from './Icon';
1818
import { AutoAlign, RectangleAlignment } from './AutoAlign';
1919
import { DropdownMenuProps } from './DropdownMenu';
20-
import { isElInChildren } from './util';
20+
import { registerStyle, isElInChildren } from './util';
2121
import { ComponentSettingsContext } from './ComponentSettings';
2222
import { useControlledValue, useEventCallback, useMergeRefs } from './hooks';
2323
import { createFC } from './common';
@@ -67,7 +67,7 @@ function collectOptionValues(children: unknown): PicklistValue[] {
6767
function findSelectedItemLabel(
6868
children: unknown,
6969
selectedValue: PicklistValue
70-
): React.ReactNode | null {
70+
): string | number | null {
7171
return (
7272
React.Children.map(children, (child) => {
7373
if (!React.isValidElement(child)) {
@@ -107,21 +107,56 @@ function findSelectedItemLabel(
107107
typeof label === 'string' ||
108108
typeof label === 'number' ||
109109
React.isValidElement(label)
110-
? label
110+
? extractTextContent(label)
111111
: undefined;
112112
const childrenValue =
113113
typeof itemChildren === 'string' ||
114114
typeof itemChildren === 'number' ||
115115
React.isValidElement(itemChildren) ||
116116
Array.isArray(itemChildren)
117-
? itemChildren
117+
? extractTextContent(itemChildren)
118118
: undefined;
119119

120120
return labelValue || childrenValue;
121121
}).find((result) => result !== null) ?? null
122122
);
123123
}
124124

125+
/**
126+
* Extract text content from React node recursively
127+
*/
128+
function extractTextContent(node: unknown): string | number | null {
129+
if (node == null) {
130+
return null;
131+
}
132+
133+
if (typeof node === 'string' || typeof node === 'number') {
134+
return node;
135+
}
136+
137+
if (typeof node === 'boolean') {
138+
return String(node);
139+
}
140+
141+
if (Array.isArray(node)) {
142+
return node
143+
.map(extractTextContent)
144+
.filter((result) => result !== null)
145+
.join('');
146+
}
147+
148+
if (
149+
React.isValidElement(node) &&
150+
node.props &&
151+
typeof node.props === 'object' &&
152+
'children' in node.props
153+
) {
154+
return extractTextContent(node.props.children);
155+
}
156+
157+
return null;
158+
}
159+
125160
/**
126161
*
127162
*/
@@ -150,6 +185,17 @@ const PicklistContext = createContext<{
150185
optionIdPrefix: '',
151186
});
152187

188+
/**
189+
*
190+
*/
191+
function useInitComponentStyle() {
192+
useEffect(() => {
193+
registerStyle('picklist', [
194+
['.react-picklist-input:not(:disabled)', '{ cursor: pointer; }'],
195+
]);
196+
}, []);
197+
}
198+
153199
/**
154200
*
155201
*/
@@ -174,7 +220,7 @@ export type PicklistProps<MultiSelect extends boolean | undefined> = {
174220
tooltip?: ReactNode;
175221
tooltipIcon?: string;
176222
elementRef?: Ref<HTMLDivElement>;
177-
inputRef?: Ref<HTMLDivElement>;
223+
inputRef?: Ref<HTMLInputElement>;
178224
dropdownRef?: Ref<HTMLDivElement>;
179225
onValueChange?: Bivariant<
180226
(
@@ -227,6 +273,8 @@ export const Picklist: (<MultiSelect extends boolean | undefined>(
227273
...rprops
228274
} = props;
229275

276+
useInitComponentStyle();
277+
230278
const fallbackId = useId();
231279
const id = id_ ?? fallbackId;
232280
const listboxId = `${id}-listbox`;
@@ -338,7 +386,7 @@ export const Picklist: (<MultiSelect extends boolean | undefined>(
338386

339387
const elRef = useRef<HTMLDivElement | null>(null);
340388
const elementRef = useMergeRefs([elRef, elementRef_]);
341-
const comboboxElRef = useRef<HTMLDivElement | null>(null);
389+
const comboboxElRef = useRef<HTMLInputElement | null>(null);
342390
const inputRef = useMergeRefs([comboboxElRef, inputRef_]);
343391
const dropdownElRef = useRef<HTMLDivElement | null>(null);
344392
const dropdownRef = useMergeRefs([dropdownElRef, dropdownRef_]);
@@ -522,6 +570,7 @@ export const Picklist: (<MultiSelect extends boolean | undefined>(
522570
}
523571
);
524572
const inputClassNames = classnames(
573+
'react-picklist-input',
525574
'slds-input_faux',
526575
'slds-combobox__input',
527576
{
@@ -572,7 +621,8 @@ export const Picklist: (<MultiSelect extends boolean | undefined>(
572621
className='slds-combobox__form-element slds-input-has-icon slds-input-has-icon_right'
573622
role='none'
574623
>
575-
<div
624+
<input
625+
type='text'
576626
ref={inputRef}
577627
role='combobox'
578628
tabIndex={disabled ? -1 : 0}
@@ -588,9 +638,10 @@ export const Picklist: (<MultiSelect extends boolean | undefined>(
588638
onKeyDown={onKeyDown}
589639
onBlur={onBlur}
590640
{...rprops}
591-
>
592-
<span className='slds-truncate'>{selectedItemLabel}</span>
593-
</div>
641+
value={selectedItemLabel}
642+
readOnly
643+
disabled={disabled}
644+
/>
594645
<Icon
595646
containerClassName='slds-input__icon slds-input__icon_right'
596647
category='utility'

0 commit comments

Comments
 (0)