segmentio / evergreen

🌲 Evergreen React UI Framework by Segment
https://evergreen.segment.com
MIT License
12.39k stars 832 forks source link

Add ability to render custom autocomplete option when items passed to autocomplete component is array of objects #1345

Open techsemicolon opened 3 years ago

techsemicolon commented 3 years ago

Overview

🌲🌲 Hello everyone 🌲🌲

I recently started exploring evergreen ui framework and I really like it.

I found a use case where I wanted to implement <Autocomplete> component having items prop which is array of objects. I found a that currently autocomplete supports only array of strings for autocomplete items. So I am making this PR to help others who would like to use this component in similar way. :)

<Autocomplete ...../> component currently uses itemToString function to stringify the each item from the items array passed through props.

Now, if you have items like this :

const fruits = [
  { id: "1", name: "Banana", image: "http://images.com/banana.png"},
  { id: "2", name: "Apple", image: "http://images.com/apple.png"},
  //... 
];

Each AutocompleteItem of from the autocomplete dropdown receives props which are like this :

{
  "id": "downshift-x-item-y",
  "children": "[object object]", // This is a string here
  "isHighlighted": false,
  "isSelected": false,
  "key": "[object object]", // This is a string here
  "style": {"position": "absolute", "top": 0, "left": 0, "width": "100%", "height": 32}
}

This adds limitation as we do not have access to the item object to render custom option for autocomplete dropdown. Even when Autocomplete component does have renderItem prop, it is now limited to items with string elements.

This PR resolves this by passing the item in the renderItem prop. This enhances flexibility of the renderItem to handle custom objects as well as strings.

For example :

If you have items with structure like this :

const itemsWithdetails = [
    {
        unique_id : 1,
        image_src: "http://images.com/1.png",
        name: "Item 1"
    },
    {
        unique_id : 2,
        image_src: "http://images.com/2.png",
        name: "Item 2"
    },
    //...
];

Then you can make autocomplete option using available renderItem like below :

<Box padding={40}>
    <Autocomplete
        itemSize={50}
        onChange={handleChange}
        itemToString={item => (item ? item.name : '')}
        items={itemsWithdetails}
        // Custom render dropdown option below with access to original item object, which is then used to get image, name etc 
        // from the item object in the custom renderItem
        renderItem={({ isHighlighted, isSelected, item, ...props }) => (
        <Pane
            display="flex"
            alignItems="center"
            justifyContent="left"
            gap={10}
            key={item.unique_id}
            {...props}
            paddingX={12}
            cursor="pointer"
        >
            <Image src={item.image_src} width={50} />
            <Text color="grey">{item.name}</Text>
        </Pane>
        )}
    >
        {({ getInputProps, getRef, getToggleButtonProps, inputValue, toggleMenu }) => (
        <Box ref={ref => getRef(ref)} display="inline-block">
            <TextInput placeholder="Custom item renderer" value={inputValue} {...getInputProps()} />
            <Button onClick={toggleMenu} {...getToggleButtonProps()}>
            Trigger
            </Button>
        </Box>
        )}
    </Autocomplete>
</Box>

I added detailed story for this as well as new component tests. Hope this helps :)

Screenshots (if applicable)

Screen Shot 2021-10-09 at 6 15 16 PM

Documentation

🌲🌲 Happy Coding 🌲🌲

netlify[bot] commented 3 years ago

✔️ Deploy Preview for evergreen-storybook ready!

🔨 Explore the source changes: 2e63bab0941927f5d0c8e3e4e98a0538c0fb3a3c

🔍 Inspect the deploy log: https://app.netlify.com/sites/evergreen-storybook/deploys/616284f68224b6000739dbe1

😎 Browse the preview: https://deploy-preview-1345--evergreen-storybook.netlify.app