import React from 'react'
import {Dropdown} from 'shared/components/Dropdown'
import DropdownMenu, {DropdownMenuEmpty} from 'shared/components/DropdownMenu'
import DropdownHead from 'shared/components/DropdownHead'
import DropdownInput from 'shared/components/DropdownInput'
import {mount} from 'enzyme'
// mock props
const itemOne = {text: 'foo'}
const itemTwo = {text: 'bar'}
const items = [itemOne, itemTwo]
// automate shallow render and providing new props
const setup = (override = {}) => {
const props = {
items: [],
selected: '',
onChoose: jest.fn(),
...override,
}
const defaultState = {
isOpen: false,
searchTerm: '',
filteredItems: items,
highlightedItemIndex: null,
}
const dropdown = mount()
return {
props,
dropdown,
defaultState,
}
}
describe('Components.Shared.Dropdown', () => {
describe('rendering', () => {
describe('initial render', () => {
it('renders the button', () => {
const {dropdown} = setup()
expect(dropdown.exists()).toBe(true)
})
it('does not show the list', () => {
const {dropdown} = setup({items})
const menu = dropdown.find(DropdownMenu)
expect(menu.exists()).toBe(false)
})
})
describe('the ', () => {
const {dropdown} = setup()
const head = dropdown.find(DropdownHead)
expect(head.exists()).toBe(true)
})
describe('when there are no items in the dropdown', () => {
it('renders the component', () => {
const {dropdown} = setup()
const empty = dropdown.find(DropdownMenuEmpty)
expect(empty.exists()).toBe(true)
})
})
describe('the ', () => {
it('does not display the input by default', () => {
const {dropdown} = setup()
const input = dropdown.find(DropdownInput)
expect(input.exists()).toBe(false)
})
it('displays the input when provided useAutoCompelete is true', () => {
const {dropdown} = setup({items, useAutoComplete: true})
let input = dropdown.find(DropdownInput)
expect(input.exists()).toBe(false)
dropdown.simulate('click')
input = dropdown.find(DropdownInput)
expect(input.exists()).toBe(true)
})
})
describe('user interactions', () => {
describe('opening the ', () => {
it('shows the menu when clicked', () => {
const {dropdown} = setup({items})
dropdown.simulate('click')
const menu = dropdown.find(DropdownMenu)
expect(dropdown.state().isOpen).toBe(true)
expect(menu.exists()).toBe(true)
})
it('hides the menu when clicked twice', () => {
const {dropdown} = setup({items})
// first click
dropdown.simulate('click')
// second click
dropdown.simulate('click')
const menu = dropdown.find(DropdownMenu)
expect(dropdown.state().isOpen).toBe(false)
expect(menu.exists()).toBe(false)
})
})
})
})
describe('instance methods', () => {
describe('applyFilter', () => {
it('filters the list by the searchTerm', () => {
const {dropdown} = setup({items, useAutoComplete: true})
dropdown.instance().applyFilter('fo')
expect(dropdown.state().filteredItems).toEqual([{text: 'foo'}])
})
})
describe('handleFilterChange', () => {
it('resets filteredList and searchTerm if the filter is empty', () => {
const {dropdown} = setup({items, useAutoComplete: true})
const event = {target: {value: ''}}
dropdown.instance().applyFilter('fo')
// assert that the list is filtered
expect(dropdown.state().filteredItems).toEqual([{text: 'foo'}])
dropdown.instance().handleFilterChange(event)
const {filteredItems, searchTerm} = dropdown.state()
expect(filteredItems).toEqual(items)
expect(searchTerm).toEqual('')
})
})
describe('handleClickOutside', () => {
it('sets isOpen to false', () => {
const {dropdown} = setup()
dropdown.simulate('click')
dropdown.instance().handleClickOutside()
expect(dropdown.state().isOpen).toBe(false)
})
})
describe('handleClick', () => {
it('fires the onClick prop', () => {
const onClick = jest.fn()
const {dropdown} = setup({onClick})
dropdown.instance().handleClick()
expect(onClick).toHaveBeenCalled()
})
it('toggles the isOpen', () => {
const {dropdown} = setup()
dropdown.instance().handleClick()
expect(dropdown.state().isOpen).toBe(true)
dropdown.instance().handleClick()
expect(dropdown.state().isOpen).toBe(false)
})
it('does nothing if disabled', () => {
const onClick = jest.fn()
const {dropdown} = setup({disabled: true, onClick})
dropdown.instance().handleClick()
expect(dropdown.state().isOpen).toBe(false)
expect(onClick).not.toHaveBeenCalled()
})
})
describe('handleSelection', () => {
it('it calls onChoose with the item provided', () => {
const onChoose = jest.fn(item => item)
const {dropdown} = setup({onChoose})
dropdown.simulate('click')
dropdown.instance().handleSelection(itemOne)()
expect(onChoose).toHaveBeenCalledTimes(1)
expect(onChoose.mock.calls[0][0]).toEqual(itemOne)
})
it('closes the menu', () => {
const {dropdown} = setup()
dropdown.simulate('click')
expect(dropdown.state().isOpen).toBe(true)
dropdown.instance().handleSelection(itemOne)()
expect(dropdown.state().isOpen).toBe(false)
})
it('resets state to defaults when opening', () => {
const {
dropdown,
defaultState: {searchTerm, filteredItems, highlightedItemIndex},
} = setup({items})
dropdown.setState({
searchTerm: 'foo',
filteredItems: [itemOne],
highlightedItemIndex: 100,
})
dropdown.instance().handleSelection(itemOne)()
expect(dropdown.state().isOpen).toBe(true)
expect(dropdown.state().searchTerm).toBe(searchTerm)
expect(dropdown.state().filteredItems).toEqual(filteredItems)
expect(dropdown.state().highlightedItemIndex).toBe(highlightedItemIndex)
})
})
describe('handleFilterKeyPress', () => {
describe('when Enter is pressed and there are items', () => {
it('sets state of isOpen to false', () => {
const {dropdown} = setup({items})
dropdown.setState({isOpen: true})
expect(dropdown.state().isOpen).toBe(true)
dropdown.instance().handleFilterKeyPress({key: 'Enter'})
expect(dropdown.state().isOpen).toBe(false)
})
it('fires onChoose with the items at the highlighted index', () => {
const onChoose = jest.fn(item => item)
const highlightedItemIndex = 1
const {dropdown} = setup({items, onChoose})
dropdown.setState({highlightedItemIndex})
dropdown.instance().handleFilterKeyPress({key: 'Enter'})
expect(onChoose).toHaveBeenCalledTimes(1)
expect(onChoose.mock.calls[0][0]).toEqual(items[highlightedItemIndex])
})
})
describe('when Escape is pressed', () => {
it('sets isOpen state to false', () => {
const {dropdown} = setup({items})
dropdown.setState({isOpen: true})
expect(dropdown.state().isOpen).toBe(true)
dropdown.instance().handleFilterKeyPress({key: 'Escape'})
expect(dropdown.state().isOpen).toBe(false)
})
})
describe('when ArrowUp is pressed', () => {
it('decrements the highlightedItemIndex', () => {
const {dropdown} = setup({items})
dropdown.setState({highlightedItemIndex: 1})
dropdown.instance().handleFilterKeyPress({key: 'ArrowUp'})
expect(dropdown.state().highlightedItemIndex).toBe(0)
})
it('does not decrement highlightedItemIndex past 0', () => {
const {dropdown} = setup({items})
dropdown.setState({highlightedItemIndex: 0})
dropdown.instance().handleFilterKeyPress({key: 'ArrowUp'})
expect(dropdown.state().highlightedItemIndex).toBe(0)
})
})
describe('when ArrowDown is pressed', () => {
describe('if no highlight has been set', () => {
it('starts highlighted index at 0', () => {
const {dropdown} = setup({items})
expect(dropdown.state().highlightedItemIndex).toBe(null)
dropdown.instance().handleFilterKeyPress({key: 'ArrowDown'})
expect(dropdown.state().highlightedItemIndex).toBe(0)
})
})
describe('if highlightedItemIndex has been set', () => {
it('it increments the index', () => {
const {dropdown} = setup({items})
dropdown.setState({highlightedItemIndex: 0})
dropdown.instance().handleFilterKeyPress({key: 'ArrowDown'})
expect(dropdown.state().highlightedItemIndex).toBe(1)
})
describe('when highilghtedItemIndex is at the end of the list', () => {
it('does not exceed the list length', () => {
const {dropdown} = setup({items})
dropdown.setState({highlightedItemIndex: 1})
const expectedIndex = items.length - 1
dropdown.instance().handleFilterKeyPress({key: 'ArrowDown'})
expect(dropdown.state().highlightedItemIndex).toBe(expectedIndex)
})
})
})
})
})
})
})