From 7e90245dba84647fd40404cfef28d339408010df Mon Sep 17 00:00:00 2001 From: Andrew Watkins Date: Wed, 28 Feb 2018 14:05:24 -0700 Subject: [PATCH] Refactor Dropdown component and specs --- ui/src/shared/components/Dropdown.js | 187 ++++++--------------- ui/src/shared/components/DropdownHead.js | 35 ++++ ui/src/shared/components/DropdownInput.js | 42 +++++ ui/src/shared/components/DropdownMenu.js | 156 +++++++++++++++++ ui/test/shared/components/Dropdown.test.js | 39 ++--- 5 files changed, 304 insertions(+), 155 deletions(-) create mode 100644 ui/src/shared/components/DropdownHead.js create mode 100644 ui/src/shared/components/DropdownInput.js create mode 100644 ui/src/shared/components/DropdownMenu.js diff --git a/ui/src/shared/components/Dropdown.js b/ui/src/shared/components/Dropdown.js index ec400d823..a99b6b4e0 100644 --- a/ui/src/shared/components/Dropdown.js +++ b/ui/src/shared/components/Dropdown.js @@ -1,10 +1,10 @@ import React, {Component} from 'react' import PropTypes from 'prop-types' -import {Link} from 'react-router' import classnames from 'classnames' import OnClickOutside from 'shared/components/OnClickOutside' -import FancyScrollbar from 'shared/components/FancyScrollbar' -import {DROPDOWN_MENU_MAX_HEIGHT} from 'shared/constants/index' +import DropdownMenu, {DropdownMenuEmpty} from 'shared/components/DropdownMenu' +import DropdownInput from 'shared/components/DropdownInput' +import DropdownHead from 'shared/components/DropdownHead' export class Dropdown extends Component { constructor(props) { @@ -123,111 +123,33 @@ export class Dropdown extends Component { }) } - renderMenu() { - const { - actions, - addNew, - items, - menuWidth, - menuLabel, - menuClass, - useAutoComplete, - selected, - } = this.props - const {filteredItems, highlightedItemIndex} = this.state - const menuItems = useAutoComplete ? filteredItems : items - - return ( - - ) - } - render() { const { items, + addNew, + actions, selected, + disabled, + iconName, className, menuClass, - iconName, + menuWidth, + menuLabel, buttonSize, buttonColor, toggleStyle, useAutoComplete, +<<<<<<< HEAD disabled, tabIndex, +======= +>>>>>>> Refactor Dropdown component and specs } = this.props - const {isOpen, searchTerm, filteredItems} = this.state - const menuItems = useAutoComplete ? filteredItems : items + const {isOpen, searchTerm, filteredItems, highlightedItemIndex} = this.state + const menuItems = useAutoComplete ? filteredItems : items const disabledClass = disabled ? 'disabled' : null + return (
(this.dropdownRef = r)} - data-test="dropdown-button" + data-test="dropdown-toggle" > {useAutoComplete && isOpen - ?
- - -
- :
- {iconName - ? - : null} - - {selected} - - -
} - {isOpen && menuItems.length ? this.renderMenu() : null} - {isOpen && !menuItems.length - ? - : null} + ? + : } + {isOpen && menuItems.length + ? + : }
) } diff --git a/ui/src/shared/components/DropdownHead.js b/ui/src/shared/components/DropdownHead.js new file mode 100644 index 000000000..a861a1f90 --- /dev/null +++ b/ui/src/shared/components/DropdownHead.js @@ -0,0 +1,35 @@ +import React from 'react' +import PropTypes from 'prop-types' +import classnames from 'classnames' + +const DropdownHead = ({ + iconName, + selected, + buttonSize, + toggleStyle, + buttonColor, + disabledClass, +}) => +
+ {iconName && } + + {selected} + + +
+ +const {string, shape} = PropTypes + +DropdownHead.propTypes = { + iconName: string, + selected: string, + buttonSize: string, + toggleStyle: shape(), + buttonColor: string, + disabledClass: string, +} + +export default DropdownHead diff --git a/ui/src/shared/components/DropdownInput.js b/ui/src/shared/components/DropdownInput.js new file mode 100644 index 000000000..dc5050414 --- /dev/null +++ b/ui/src/shared/components/DropdownInput.js @@ -0,0 +1,42 @@ +import React from 'react' +import PropTypes from 'prop-types' + +const DropdownInput = ({ + searchTerm, + buttonSize, + buttonColor, + toggleStyle, + disabledClass, + onFilterChange, + onFilterKeyPress, +}) => +
+ + +
+ +export default DropdownInput + +const {func, shape, string} = PropTypes + +DropdownInput.propTypes = { + searchTerm: string, + buttonSize: string, + buttonColor: string, + toggleStyle: shape({}), + disabledClass: string, + onFilterChange: func, + onFilterKeyPress: func, +} diff --git a/ui/src/shared/components/DropdownMenu.js b/ui/src/shared/components/DropdownMenu.js new file mode 100644 index 000000000..2bb0b3205 --- /dev/null +++ b/ui/src/shared/components/DropdownMenu.js @@ -0,0 +1,156 @@ +import React from 'react' +import PropTypes from 'prop-types' +import {Link} from 'react-router' + +import classnames from 'classnames' +import {DROPDOWN_MENU_MAX_HEIGHT} from 'shared/constants/index' +import FancyScrollbar from 'shared/components/FancyScrollbar' + +// AddNewResource is an optional parameter that takes the user to another +// route defined by url prop +const AddNewButton = ({url, text}) => +
  • + + {text} + +
  • + +const DropdownMenu = ({ + items, + addNew, + actions, + selected, + onAction, + menuClass, + menuWidth, + menuLabel, + onSelection, + onHighlight, + useAutoComplete, + highlightedItemIndex, +}) => { + return ( + + ) +} + +export const DropdownMenuEmpty = ({useAutoComplete, menuClass}) => + + +const {arrayOf, bool, number, shape, string, func} = PropTypes + +AddNewButton.propTypes = { + url: string, + text: string, +} + +DropdownMenuEmpty.propTypes = { + useAutoComplete: bool, + menuClass: string, +} + +DropdownMenu.propTypes = { + onAction: func, + actions: arrayOf( + shape({ + icon: string.isRequired, + text: string.isRequired, + handler: func.isRequired, + }) + ), + items: arrayOf( + shape({ + text: string.isRequired, + }) + ).isRequired, + onClick: func, + addNew: shape({ + url: string.isRequired, + text: string.isRequired, + }), + selected: string.isRequired, + iconName: string, + className: string, + buttonColor: string, + menuWidth: string, + menuLabel: string, + menuClass: string, + useAutoComplete: bool, + disabled: bool, + searchTerm: string, + onSelection: func, + onHighlight: func, + highlightedItemIndex: number, +} + +export default DropdownMenu diff --git a/ui/test/shared/components/Dropdown.test.js b/ui/test/shared/components/Dropdown.test.js index 8b0388a6e..845f6a9e3 100644 --- a/ui/test/shared/components/Dropdown.test.js +++ b/ui/test/shared/components/Dropdown.test.js @@ -1,6 +1,9 @@ import React from 'react' -import Dropdown from 'shared/components/dropdown' +import Dropdown from 'shared/components/Dropdown' +import DropdownMenu from 'shared/components/DropdownMenu' + import {shallow} from 'enzyme' +const items = [{text: 'foo'}, {text: 'bar'}] const setup = (override = {}) => { const props = { @@ -10,10 +13,12 @@ const setup = (override = {}) => { ...override, } - const wrapper = shallow() + const dropdown = shallow().dive({ + 'data-test': 'dropdown-button', + }) return { - wrapper, + dropdown, props, } } @@ -21,36 +26,28 @@ const setup = (override = {}) => { describe('Components.Shared.Dropdown', () => { describe('rednering', () => { describe('initial render', () => { - it('renders the dropdown button', () => { - const {wrapper} = setup() + it('renders the dropdown menu button', () => { + const {dropdown} = setup() - const actual = wrapper.dive({'data-test': 'dropdown-button'}) - expect(actual.length).toBe(1) + expect(dropdown.exists()).toBe(true) }) it('does not show the list', () => { - const items = [{text: 'foo'}, {text: 'bar'}] - const {wrapper} = setup({items}) + const {dropdown} = setup({items}) - const dropdown = wrapper.dive({'data-test': 'dropdown-button'}) - const list = dropdown.find({'data-test': 'dropdown-items'}) - expect(list.length).toBe(0) + const menu = dropdown.find(DropdownMenu) + expect(menu.exists()).toBe(false) }) }) describe('user interactions', () => { - it('shows the list when clicked', () => { - const items = [{text: 'foo'}, {text: 'bar'}] - const {wrapper} = setup({items}) + it('shows the menu when clicked', () => { + const {dropdown} = setup({items}) - const dropdown = wrapper.dive({'data-test': 'dropdown-button'}) dropdown.simulate('click') - const ul = dropdown.find({'data-test': 'dropdown-ul'}) - expect(ul.length).toBe(1) - - const list = ul.find({'data-test': 'dropdown-item'}) - expect(list.length).toBe(items.length) + const menu = dropdown.find(DropdownMenu) + expect(menu.exists()).toBe(true) }) }) })