codeforthailand/election-live

View on GitHub
src/components/PartyDropdown.js

Summary

Maintainability
A
1 hr
Test Coverage
import React, { useEffect, useRef, useState } from "react"
import { parties, partyLogo, partyPath } from "../models/information"
import { labelColor, DISPLAY_FONT, media, DESKTOP_MIN_WIDTH } from "../styles"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faSearch } from "@fortawesome/free-solid-svg-icons"
import { Link } from "gatsby"
import Fuse from "fuse.js"
import _ from "lodash"

const searcher = new Fuse(parties, {
  keys: ["codeEN", "codeTH", "name"],
})

export default ({ partyId }) => {
  const [dropdownOpen, setDropdownOpen] = useState(!partyId ? true : false)
  const [currentParty, setCurrentParty] = useState(
    partyId ? _.find(parties, p => p.id === partyId) : parties[0]
  )

  const [searchKeyword, setSearchKeyword] = useState("")

  const dropdownRef = useRef()

  const handleClickOutside = e => {
    if (
      !!dropdownRef.current &&
      !dropdownRef.current.contains(e.target) &&
      !!currentParty
    ) {
      setDropdownOpen(false)
    }
  }
  useEffect(() => {
    document.addEventListener("mousedown", handleClickOutside)
    return () => document.removeEventListener("mousedown", handleClickOutside)
  })

  function partyItem(p) {
    return (
      <div
        css={{
          display: "grid",
          gridTemplateColumns: "30px auto",
          textAlign: "left",
        }}
      >
        <img
          alt=""
          src={partyLogo(p.name)}
          css={{
            maxHeight: "100%",
            maxWidth: "100%",
            width: "auto",
            height: "auto",
            top: 0,
            bottom: 0,
            left: 0,
            right: 0,
            margin: "auto",
          }}
        />
        <div
          css={{ fontSize: "1rem", paddingLeft: 8, fontFamily: DISPLAY_FONT }}
        >
          <b>{p.name}</b>
        </div>
      </div>
    )
  }

  function renderDefaultDropdown() {
    return (
      <div
        css={{
          cursor: "pointer",
          alignItems: "center",
          position: "relative",
          padding: 10,
          alignItems: "center",
          boxShadow: "0 2px 4px 0 rgba(0,0,0,0.12)",
        }}
        onClick={() => setDropdownOpen(true)}
      >
        {partyItem(currentParty)}
        <div
          css={{
            position: "absolute",
            right: 20,
            top: "calc(50% - 10px)",
            border: "solid #212121",
            borderWidth: "0 2px 2px 0",
            padding: 4,
            transform: "rotate(45deg)",
          }}
        />
      </div>
    )
  }

  function renderDropdown() {
    let filteredParties = parties
    if (searchKeyword.length > 0) {
      filteredParties = searcher.search(searchKeyword)
    }

    return (
      <div
        css={{
          position: "absolute",
          zIndex: 1,
          background: "white",
          width: "100%",
          [media(DESKTOP_MIN_WIDTH)]: {
            width: "inherit",
          },
          boxShadow: "0 2px 4px 0 rgba(0,0,0,0.12)",
        }}
        ref={dropdownRef}
      >
        <div css={{ position: "relative", padding: 10 }}>
          <input
            autoFocus
            css={{
              border: `1px solid ${labelColor}`,
              width: "100%",
              boxSizing: "border-box",
              padding: 10,
              fontSize: 16,
              "&:focus": { outline: 0 },
            }}
            value={searchKeyword}
            placeholder="ชื่อพรรคการเมือง"
            onChange={e => {
              setSearchKeyword(e.target.value)
            }}
          />
          <div
            css={{
              top: 20,
              position: "absolute",
              right: 20,
              color: labelColor,
            }}
          >
            <FontAwesomeIcon icon={faSearch} />
          </div>
        </div>
        <div
          css={{
            height: "calc(70vh - 200px)",
            overflowX: "hidden",
            overflowY: "auto",
            WebkitOverflowScrolling: "touch",
            padding: "0px 10px 10px 10px",
          }}
        >
          <ul css={{ listStyle: "none", padding: 0, margin: 0 }}>
            {filteredParties
              .filter(p => p.name !== "ไทยรักษาชาติ")
              .sort((a, b) => a.name.localeCompare(b.name))
              .map(p => (
                <Link
                  key={p.id}
                  to={partyPath(p)}
                  style={{ color: "black", textDecoration: "none" }}
                  onClick={() => {
                    setDropdownOpen(false)
                    setCurrentParty(p)
                  }}
                >
                  <li
                    css={{
                      padding: "12px 0px",
                      borderBottom: "1px solid gray",
                      position: "relative",
                    }}
                  >
                    {partyItem(p)}
                  </li>
                </Link>
              ))}
          </ul>
        </div>
      </div>
    )
  }

  return (
    <div
      css={{
        position: "relative",
        background: "white",
        width: "inherit",
      }}
    >
      {dropdownOpen ? renderDropdown() : null}
      {renderDefaultDropdown()}
    </div>
  )
}