Skip to content

Commit a410d13

Browse files
committed
enhance Search widget to accept an object as default value
1 parent 252b869 commit a410d13

2 files changed

Lines changed: 124 additions & 16 deletions

File tree

apps/kitchen-sink/src/ensemble/screens/widgets.yaml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,6 +1291,60 @@ View:
12911291
onRefresh:
12921292
executeCode: |
12931293
console.log('refreshed')
1294+
- Row:
1295+
children:
1296+
- Search:
1297+
id: searchDefaultObject
1298+
value: # assuming this data is coming from server
1299+
id: 105
1300+
firstName: Emma
1301+
lastName: Wilson
1302+
styles:
1303+
width: 320px
1304+
height: 40px
1305+
borderRadius: 12
1306+
borderWidth: 2
1307+
borderStyle: solid
1308+
borderColor: "#B8BED6"
1309+
placeholder: Search
1310+
item-template:
1311+
data: |-
1312+
${findUsers.body.users.map(user => ({id: user.id, firstName: user.firstName, lastName: user.lastName}))}
1313+
name: user
1314+
template:
1315+
SearchText:
1316+
inputs:
1317+
user: ${user}
1318+
selectedLabel:
1319+
SearchText:
1320+
inputs:
1321+
user: ${value.user}
1322+
searchKey: id
1323+
onSearch:
1324+
invokeAPI:
1325+
name: findUsers
1326+
inputs:
1327+
search: ${search}
1328+
onSelect:
1329+
executeCode: |
1330+
console.log(value)
1331+
onClear:
1332+
executeCode: |
1333+
console.log("Search cleared")
1334+
- Text:
1335+
text: ${JSON.stringify(searchDefaultObject.value)}
1336+
- Text:
1337+
text: ${JSON.stringify(findUsers.body.users.map((user) => user.id))}
1338+
1339+
SearchText:
1340+
inputs:
1341+
- user
1342+
body:
1343+
Text:
1344+
text: ${user.firstName} ${user.lastName}
1345+
styles:
1346+
color: red
1347+
12941348
API:
12951349
getDummyProducts:
12961350
method: GET

packages/runtime/src/widgets/Search.tsx

Lines changed: 70 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { Icon } from "./Icon";
2626
const widgetName = "Search";
2727

2828
export type SearchProps = {
29-
value?: string;
29+
value?: string | { [key: string]: unknown };
3030
placeholder?: string;
3131
searchKey?: string;
3232
selectedLabel?: { [key: string]: unknown };
@@ -58,6 +58,7 @@ export const Search: React.FC<SearchProps> = ({
5858
}) => {
5959
const [searchValue, setSearchValue] = useState<string | null>(null);
6060
const [value, setValue] = useState<unknown>();
61+
const [selectedObject, setSelectedObject] = useState<unknown>(null);
6162

6263
const { namedData } = useTemplateData({
6364
data: itemTemplate?.data,
@@ -143,8 +144,14 @@ export const Search: React.FC<SearchProps> = ({
143144
(option) => extractValue(option) === selectedValue,
144145
);
145146

147+
const fullSelectedObject = get(selectedOption, [
148+
itemTemplate.name,
149+
]) as unknown;
150+
// store full object separately only for selectedLabel rendering
151+
setSelectedObject(fullSelectedObject);
152+
146153
onSelectAction?.callback({
147-
value: get(selectedOption, [itemTemplate.name]) as unknown,
154+
value: fullSelectedObject,
148155
});
149156
}
150157
},
@@ -153,6 +160,8 @@ export const Search: React.FC<SearchProps> = ({
153160

154161
const handleClear = useCallback(() => {
155162
setSearchValue(null);
163+
setValue(null);
164+
setSelectedObject(null);
156165
onClearAction?.callback();
157166
}, [onClearAction?.callback]);
158167

@@ -169,27 +178,72 @@ export const Search: React.FC<SearchProps> = ({
169178
: EnsembleRuntime.render([unwrapWidget(notFoundContent)]);
170179
}, [values?.notFoundContent]);
171180

181+
const selectDefaultValue = useMemo(() => {
182+
if (!values?.initialValue) return undefined;
183+
184+
if (isObject(values.initialValue)) {
185+
const extractedValue = extractValue({
186+
[itemTemplate?.name ?? ""]: values.initialValue,
187+
});
188+
return String(extractedValue);
189+
}
190+
191+
return String(values.initialValue);
192+
}, [values?.initialValue, extractValue, itemTemplate?.name]);
193+
172194
const renderLabel = useCallback(
173195
(label: React.ReactNode, labelValue: string | number): React.ReactNode => {
174-
if (isNil(rest.selectedLabel) || isEmpty(namedData)) {
175-
return label;
196+
// if we have selectedLabel and a selected object (from selection or initial object value)
197+
if (!isNil(rest.selectedLabel) && selectedObject) {
198+
return (
199+
<CustomScopeProvider
200+
value={{ value: { [itemTemplate?.name ?? ""]: selectedObject } }}
201+
>
202+
{EnsembleRuntime.render([unwrapWidget(rest.selectedLabel)])}
203+
</CustomScopeProvider>
204+
);
176205
}
177206

178-
const option = namedData.find(
179-
(item) => extractValue(item) === labelValue,
180-
);
181-
return (
182-
<CustomScopeProvider value={{ value: option }}>
183-
{EnsembleRuntime.render([unwrapWidget(rest.selectedLabel)])}
184-
</CustomScopeProvider>
185-
);
207+
// if we have selectedLabel but no selectedObject, try to find from namedData
208+
if (!isNil(rest.selectedLabel) && !isEmpty(namedData)) {
209+
const option = namedData.find(
210+
(item) => extractValue(item) === labelValue,
211+
);
212+
if (option) {
213+
return (
214+
<CustomScopeProvider value={{ value: option }}>
215+
{EnsembleRuntime.render([unwrapWidget(rest.selectedLabel)])}
216+
</CustomScopeProvider>
217+
);
218+
}
219+
}
220+
221+
return label;
186222
},
187-
[extractValue, namedData, rest.selectedLabel],
223+
[
224+
extractValue,
225+
itemTemplate?.name,
226+
namedData,
227+
rest.selectedLabel,
228+
selectedObject,
229+
],
188230
);
189231

190232
useEffect(() => {
191-
if (!value) setValue(values?.initialValue);
192-
}, [value, values?.initialValue]);
233+
if (!value && values?.initialValue) {
234+
if (isObject(values.initialValue)) {
235+
// if initial value is an object, extract the key for widget value and store full object
236+
const extractedValue = extractValue({
237+
[itemTemplate?.name ?? ""]: values.initialValue,
238+
});
239+
setValue(extractedValue);
240+
setSelectedObject(values.initialValue);
241+
} else {
242+
// if initial value is primitive, use it directly
243+
setValue(values.initialValue);
244+
}
245+
}
246+
}, [value, values?.initialValue, extractValue, itemTemplate?.name]);
193247

194248
return (
195249
<div
@@ -220,7 +274,7 @@ export const Search: React.FC<SearchProps> = ({
220274
<SelectComponent
221275
allowClear
222276
className={`${values?.styles?.names || ""} ${id}_input`}
223-
defaultValue={values?.initialValue}
277+
defaultValue={selectDefaultValue}
224278
filterOption={false}
225279
id={values?.id}
226280
labelRender={({ label, value: labelValue }): React.ReactNode =>

0 commit comments

Comments
 (0)