diff --git a/web/components/dropdown.css b/web/components/dropdown.css index 437e7aba6..a8098d301 100644 --- a/web/components/dropdown.css +++ b/web/components/dropdown.css @@ -1,91 +1,94 @@ .dropdownContainer { display: flex; justify-content: center; width: 100%; - padding: 20px; } .dropdownMenu { display: flex; align-items: center; width: 100%; height: 45px; max-width: 350px; padding: 0 10px; background-color: var(--modal-bg); border-color: var(--dropdown-select-border); border-radius: 5px; border-style: solid; } .dropdownMenu { cursor: pointer; } .dropdownDisplayText { color: var(--change-member-role-modal-generic-text); font-family: var(--font-stack); padding: 5px; } .dropdownIcon { margin-left: auto; font-size: var(--m-font-16); color: var(--dropdown-chevron-color); } .dropdownList { display: flex; flex-direction: column; align-items: center; max-width: 370px; max-height: 200px; overflow-y: auto; - padding: 0 10px; + margin: 0 10px; list-style: none; - margin-top: -15px; width: 100%; + position: absolute; + margin-top: 48px; + z-index: 1; + background-color: var(--dropdown-option-bg); + box-shadow: 0px 1px 3px 0px var(--shadow-dark-35); } .dropdownListItem { color: var(--dropdown-text); background-color: var(--dropdown-option-bg); width: 100%; height: 45px; display: flex; align-items: center; } .dropdownListItem:hover { background-color: var(--dropdown-option-hover-bg); cursor: pointer; } .dropdownListItemButton { background-color: transparent; background-repeat: no-repeat; border: none; cursor: pointer; overflow: hidden; } .dropdownListDisplayText { color: var(--change-member-role-modal-generic-text); font-family: var(--font-stack); padding: 15px; } .dropdownListCheckIcon { margin-left: auto; margin-right: 10px; color: var(--dropdown-selected-option-check-color); } .dropdownDisabled { color: var(--dropdown-disabled-color); border-color: var(--dropdown-disabled-color); } .dropdownDisabled:hover { cursor: not-allowed; } diff --git a/web/components/dropdown.react.js b/web/components/dropdown.react.js index 19a1df140..01eefd305 100644 --- a/web/components/dropdown.react.js +++ b/web/components/dropdown.react.js @@ -1,110 +1,120 @@ // @flow import classNames from 'classnames'; -import invariant from 'invariant'; import * as React from 'react'; import SWMansionIcon from 'lib/components/swmansion-icon.react.js'; import css from './dropdown.css'; -type DropdownOption = { +export type DropdownOption = { +id: string, +name: string, }; type DropdownProps = { +options: $ReadOnlyArray, - +activeSelection: string, + +activeSelection: ?string, +setActiveSelection: string => mixed, + +defaultLabel?: string, +disabled?: boolean, }; function Dropdown(props: DropdownProps): React.Node { - const { options, activeSelection, setActiveSelection, disabled } = props; + const { + options, + activeSelection, + setActiveSelection, + defaultLabel, + disabled, + } = props; + const [isOpen, setIsOpen] = React.useState(false); + const [selectedIndex, setSelectedIndex] = React.useState(() => + options.findIndex(option => option.id === activeSelection), + ); const dropdownMenuClassNames = classNames({ [css.dropdownMenu]: true, [css.dropdownDisabled]: !!disabled, }); const dropdownTextClassNames = classNames({ [css.dropdownDisplayText]: true, [css.dropdownDisabled]: !!disabled, }); const dropdownIconClassNames = classNames({ [css.dropdownIcon]: true, [css.dropdownDisabled]: !!disabled, }); const toggleMenu = React.useCallback(() => { if (disabled) { return; } setIsOpen(!isOpen); }, [disabled, isOpen]); const handleSelection = React.useCallback( - (selection: DropdownOption) => { + (selection: DropdownOption, index: number) => { setActiveSelection(selection.id); + setSelectedIndex(index); setIsOpen(false); }, [setActiveSelection], ); - const activeDisplayedOption = React.useMemo(() => { - const activeOption = options.find(option => option.id === activeSelection); - invariant(activeOption, 'Active option must be in options list'); - return activeOption.name; - }, [activeSelection, options]); - const dropdownList = React.useMemo(() => { if (!isOpen) { return null; } - const dropdownOptions = options.map(option => { + const dropdownOptions = options.map((option, index) => { const checkIcon = option.id === activeSelection ? ( ) : null; return (
  • handleSelection(option)} + onClick={() => handleSelection(option, index)} >
    {checkIcon}
  • ); }); return ; }, [activeSelection, handleSelection, isOpen, options]); + const selectedOptionText = + selectedIndex > -1 + ? options[selectedIndex].name + : defaultLabel ?? 'Select an option'; + return ( <>
    -

    {activeDisplayedOption}

    +

    {selectedOptionText}

    {dropdownList} ); } export default Dropdown;