Created
June 30, 2021 03:16
-
-
Save kharioki/a2350ae15b385ff0416b793a33ed7aaf to your computer and use it in GitHub Desktop.
A basic dropdown component implemented with react
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| 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