Searchable Select
Intro
You want an easy to use mis-frontend api for searchable select
where dropdown options shown depends on the text you type in...
We have a component <SearchableDropdown />
with the following api footprint.
- Label You can have a label if the component is part of a form
- You can have default value, either if its a value or a promise for value from some remote source.
- It have exposed some event hooks like
onChange
,onBlur
. - It plays nice with formik hooks in case of a large form
- size refers to
bootstrap 5.XX
sizing withcol-xx-xx
. We havemarginTop
where you can just specify a number and much more fun. Lets delve into the important parts
<SearchableDropdown
label="Organization:"
apiCall={orgApi.pullAllOrganization}
search_key="organization_name"
label_name="organization_name"
value_name="organization_id"
size="col-sm-3"
marginTop="0"
fetcher={async (setWaitingState) => {
//set state must be there
setWaitingState(true);
let results = await orgApi.getOneOrganization(
searchParams.get(URLMapper["organization_id"]) as string
);
setWaitingState(false);
if (results && results.organization_id && results.organization_name) {
return {
value: results.organization_id,
label: results.organization_name,
};
}
return null;
}}
onChange={(value: any) => {
handleQueryChange("organization_id", value?.value);
}}
/>
Default Value
SearchableDropdown
can be given a default value as preselected which will be shown on the page when the page renders.
Default value is passed as prop
with key of suggestedValue
will be something like this below
/**default value supposed to be a Option data type which definition is like**/
type Option = { value: string; label: string }; //you dont need to define this just follow the structure.
<SearchableDropdown
suggestedValue={{ value: "value-1", label: "option label-1" }}
/>;
Default Value from remote Source
The above example is when we have a value we need it to be a default value. What if we have only some source of key, either and id
of a record and we want to fetch that record and the result to be our default value.
Steps 1 Key points
- Remove
suggestedValue
from your componentprops
list. - Make sure you have an
id
and end point you want to fetch data from and when you have your response you need to format into{value:string,label:string}
structure whichSearchableDropdown
understands
Step 2 fetcher
SearchableDropdown
expects optional function called fetcher, which as long as its defined it will be called when component renders. this function have to roles.
- Fetching data from remote source.
- Changing loading state between
true/false
.
fetcher
is promise based function which declared with async/await
style. For now its have only one parameter which is setter function for loading state.
fetcher
supposed to return results in Option
type or just null
Below is oversimplified selecton
<SearchableDropdown
fetcher={async (setWaitingState) => {
setWaitingState(true); //setting loading to true
let results = await api.get(record_id); //getting and a record from remote
setWaitingState(false); //fetching done so loading if false
if (results && results.organization_id && results.organization_name) {
/**Formating results into design accepted by SearchableDropdown */
return {
value: results.organization_id,
label: results.organization_name,
};
}
return null;
}}
/>
Search from Typing.
The searching part. How do we achieve the typing searching mechanisms.
SearchableDropdown
have prop
called apiCall
which basically you use to pass a function which will receive three parameters.
- endpoint key with which you search the record ex.
end_date
,username
,document_id
with value exampleusername=Qamar
and its supposed to return only the record object and nothing more. and from that record returned you'll choose which value will bevalue
and which will belabel
Sometimes you have value at hand and you dont want to shoot another API call
<SearchableDropdown
label="Organization:"
apiCall={OrgApi.pullAllOrganization}
search_key="organization_name"
label_name="organization_name"
value_name="organization_id"
size="col-sm-6 mt-2"
marginTop="0"
suggestedValue={{
label: data?.RequisitionData.organization_name,//this values
value: data?.RequisitionData.organization_id,//this value
}}
// Or we can do the belows TODO - DOCUMENT
// fetcher={async (setWaitingState) => {
// //set state must be there
// setWaitingState(true);
// let results = await orgApi.getOneOrganization("4");
// setWaitingState(false);
// if (
// results &&
// results.organization_id &&
// results.organization_name
// ) {
// return {
// value: results.organization_id,
// label: results.organization_name,
// };
// }
// return null;
// }}
onChange={(value: any) => {
formik.setFieldValue("organization_id", value?.value);
}}
onBlur={() => formik.setFieldTouched("organization_id", true)}
error={
formik.touched.organization_id &&
formik.errors.organization_id
? formik.errors.organization_id
: undefined
}
/>
```
Lets add more Searchable components
I we want to search the bill using its bill id
<SearchableBills
css="col-sm-6"
data_holder="sb_id"
label="Bill #"
isDisabled={!organization}
pre_filter_string={
organization ? `organization_id=${organization?.organization_id}` : ""
}
optionlabel={(data: any) => {
//construct label to be shown as options on the dropdowns
console.log(data);
return data.sb_id;
}}
formik={formik}
/>
Same applies when we're searching for Bill items
<SearchableBillItems
css="col-sm-6"
data_holder="sb_id"
label="Bill #"
{/* isDisabled={!organization} //just an ption */}
pre_filter_string={
organization ? `organization_id=${organization?.organization_id}` : ""
}
optionlabel={(data: any) => {
//construct label to be shown as options on the dropdowns
console.log(data);
return data.sb_id;
}}
formik={formik}
/>
Lets search the imei or serial and pick pssi_id on change event
<FetchPssidImei onChange={(e) => console.log(e)} />
<FetchPssidSerial onChange={(e) => console.log(e)} />