Skip to content

Instantly share code, notes, and snippets.

@kharioki
Created June 30, 2021 03:16
Show Gist options
  • Save kharioki/a2350ae15b385ff0416b793a33ed7aaf to your computer and use it in GitHub Desktop.
Save kharioki/a2350ae15b385ff0416b793a33ed7aaf to your computer and use it in GitHub Desktop.
A basic dropdown component implemented with react
/*
Prompt:
We have defined a basic dropdown via the Dropdown and DropdownItem components below, with example usage
in the ExampleNav component. The Dropdown and DropdownItem components have some problems, and also
have room for improvements (doesn't everything?) A couple items TODO here (make sure to explain with comments!)
0. How are you today? 😊
I am great. This is an interesting exercise
1. Please fix any obvious issues and make you see with the dropdown.
2. Please then make improvements to the dropdown and make it a but more React-y. Assume this was cobbled
together or brought in hastily from StackOverflow.
- Okey, so I have re-written the class components to be functional components - es6 style
3. Given that this dropdown might form the basis of an Account picker, a searchable list of Product Tours, one
step of a flow to configure alerting, etc... Please explore with text or code the way you would approach this.
- I have included a SearchDropdown component - where I would handle the searching/searchable list functionality
4. If we wanted to sync this dropdown selection to the server with
httpPatch('user', { [`dropdown-state-${key}`]: {true,false} }) where would this be included OR how best to
handle this (properties needing to be synced to the backend)?
- I would include this on the ExampleNav component as it is the parent/ancestor component.
- which means I would need to track the selected dropdown state at the ExampleNav and pass down both the selectedItem and the function to set selected item
down to the dropdown component
5. Anything else to add here?
6. Unrelated to this, what is one new feature you would add to React?
- automatic lazy-loading static files like images
PS: No need to worry about CSS, actually making it "run" etc...
*/
import { useState, useEffect } from 'react';
// import { httpPatch } from 'lib/http';
const Dropdown = (props) => {
// initial open state of dropdown component is false
const [isOpen, setIsOpen] = useState(false);
const toggle = () => {
// toggle state of dropdown from open to closed, and vice-versa
setIsOpen(!isOpen);
}
return (
<div className="dropdown">
<button
type="button"
className="dropdown-button"
id="dropdownButton"
aria-haspopup="true"
aria-expanded={isOpen}
onClick={toggle}
>
{props.label}
</button>
{
// only show the dropdown items when the button is clicked and state is set to isOpen
isOpen && (
<ul
className={`${isOpen ? 'dropdown-open' : ''} dropdown-menu`}
aria-labelledby="dropdownButton"
role="menu"
>
{props.children}
</ul>
)}
</div>
)
}
const SearchDropdown = () => {
// have an initial text to search as an empty string
const [searchText, setSearchText] = useState("");
// have an initial empty array of search results
const [searchResults, setSearchResults] = useState([]);
const handleChange = (input) => {
// update searchText state with input value
setSearchText(input);
}
/*
handling search
depending on the database you have, you may opt to fetch items as you type on the search box. you can use a useEffect function
const getItems = async () => {
await fetch(URL, { options })
.then(res => res.json{})
.then(data => setSearchResults(data))
}
useEffect(() => {
getItems();
}, [searchText]);
- You may want to use a lodash.debounce to await a few secs before firing the getItems();
*/
/* optionally you may pre-fetch the data you want to search - and then filter items as you type on the search box
same as above only now the searchText is not a peer dependency
*/
return (
<div className="dropdown">
<input
type="search"
id="searchText"
value={searchText}
placeholder="Search..."
onChange={e => handleChange(e.target.value)}
/>
{
// only show the dropdown items when search results are returned
searchResults &&
searchResults.map((item, index) => (
<div className="dropdown-item" key={index}>
<p >{item.name}</p>
</div>
)
)}
</div>
)
}
// We may have many routes/pages, and since the DropdownItem component is reusable, its much nicer to abstract all the pages this way
const pages = [
{
id: 1,
name: "Page 1",
link: "/page1"
},
{
id: 2,
name: "Page 2",
link: "/page2"
},
{
id: 3,
name: "Page 3",
link: "/page3"
},
{
id: 4,
name: "Page 4",
link: "/page4"
},
];
const morePages = [
{
id: 5,
name: "Page 5",
link: "/page5"
},
{
id: 9,
name: "Page 9",
link: "/page9"
},
];
const DropdownItem = ({ page }) => {
return (
<a href={page.link}>
<p>
{page.name}
</p>
</a>
)
}
const ExampleNav = () => {
return (
<nav>
<a href="/page1">Page 1</a>
<Dropdown label="More items">
{pages.map(page => (
<DropdownItem key={page.id} page={page} />
))}
</Dropdown>
<Dropdown label="Even more items">
{morePages.map(page => (
<DropdownItem key={page.id} page={page} />
))}
</Dropdown>
<SearchDropdown />
</nav>
)
}
export default ExampleNav;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment