diff --git a/jest.config.mjs b/jest.config.mjs index 25552207926f..3540d474c0f3 100644 --- a/jest.config.mjs +++ b/jest.config.mjs @@ -3,11 +3,12 @@ export default { testEnvironment: "node", setupFiles: ["./src/setupTests.js"], transform: { - "^.+\\.jsx?$": "babel-jest", + "^.+\\.(m|c)?jsx?$": "babel-jest", }, moduleNameMapper: { "\\.(scss|css)$": "/src/components/__mocks__/styleMock.js", "\\.svg$": "/src/components/__mocks__/svgMock.js", + "\\.(png|jpg|jpeg|ico)$": "/src/components/__mocks__/fileMock.js", }, moduleFileExtensions: [ "js", diff --git a/src/components/Dropdown/Dropdown.jsx b/src/components/Dropdown/Dropdown.jsx index e838402810c6..600e7388e5fb 100644 --- a/src/components/Dropdown/Dropdown.jsx +++ b/src/components/Dropdown/Dropdown.jsx @@ -1,127 +1,53 @@ import PropTypes from "prop-types"; -import { Component } from "react"; +import { useEffect, useRef, useState } from "react"; -export default class Dropdown extends Component { - static propTypes = { - className: PropTypes.string, - items: PropTypes.array, - }; - - state = { - active: false, - }; +export default function Dropdown({ className = "", items = [] }) { + const [active, setActive] = useState(false); + const activeRef = useRef(false); + const dropdownRef = useRef(null); + const dropdownButtonRef = useRef(null); + const linksRef = useRef([]); + const openedByClickRef = useRef(false); - componentDidMount() { - document.addEventListener("keyup", this._closeDropdownOnEsc, true); - document.addEventListener("focus", this._closeDropdownIfFocusLost, true); - document.addEventListener("click", this._closeDropdownIfFocusLost, true); - } + // Keep activeRef in sync for use inside stable event listeners + useEffect(() => { + activeRef.current = active; + }, [active]); - componentWillUnmount() { - document.removeEventListener("keyup", this._closeDropdownOnEsc, true); - document.removeEventListener("focus", this._closeDropdownIfFocusLost, true); - document.removeEventListener("click", this._closeDropdownIfFocusLost, true); - } + useEffect(() => { + const handleEsc = (event) => { + if (event.key === "Escape" && activeRef.current) { + setActive(false); + dropdownButtonRef.current.focus(); + } + }; - _closeDropdownOnEsc = (event) => { - if (event.key === "Escape" && this.state.active) { - this.setState({ active: false }, () => { - this.dropdownButton.focus(); - }); - } - }; + const handleFocusLost = (event) => { + if (activeRef.current && !dropdownRef.current.contains(event.target)) { + setActive(false); + } + }; - _closeDropdownIfFocusLost = (event) => { - if (this.state.active && !this.dropdown.contains(event.target)) { - this.setState({ active: false }); - } - }; + document.addEventListener("keyup", handleEsc, true); + document.addEventListener("focus", handleFocusLost, true); + document.addEventListener("click", handleFocusLost, true); - render() { - const { className = "", items = [] } = this.props; + return () => { + document.removeEventListener("keyup", handleEsc, true); + document.removeEventListener("focus", handleFocusLost, true); + document.removeEventListener("click", handleFocusLost, true); + }; + }, []); - return ( -