Skip to main content

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.

  1. Label You can have a label if the component is part of a form
  2. You can have default value, either if its a value or a promise for value from some remote source.
  3. It have exposed some event hooks like onChange,onBlur.
  4. It plays nice with formik hooks in case of a large form
  5. size refers to bootstrap 5.XX sizing with col-xx-xx. We have marginTop 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

  1. Remove suggestedValue from your component props list.
  2. 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 which SearchableDropdown 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.

  1. Fetching data from remote source.
  2. 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.

  1. endpoint key with which you search the record ex. end_date,username,document_id with value example username=Qamar and its supposed to return only the record object and nothing more. and from that record returned you'll choose which value will be value and which will be label

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)} />