@pubsweet/ui
Version:
React component library for use in pubsweet apps
202 lines (177 loc) • 5.32 kB
JavaScript
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import { override, th } from '@pubsweet/ui-toolkit'
import { Button as UIButton, Icon as UIIcon } from '../atoms'
const Icon = styled(UIIcon)`
font-family: ${th('fontInterface')};
font-size: ${th('fontSizeBase')};
height: ${th('fontSizeBase')};
vertical-align: top;
/* stylelint-disable-next-line order/properties-alphabetical-order */
${override('ui.Button.Icon')}
`
const ChevronIcon = styled(Icon)`
float: right;
`
const StyledDropdown = styled.div`
display: inline-block;
position: relative;
margin: 0;
/* stylelint-disable-next-line order/properties-alphabetical-order */
${override('ui.Dropdown.Wrapper')}
`
const DropdownTitle = styled(UIButton)`
cursor: pointer;
display: inline-block;
white-space: nowrap;
text-decoration: none;
padding: ${props => (props.size === 'small' ? '2px' : '8px')};
color: ${props => props.color};
&:hover {
color: ${props => props.color};
}
text-transform: capitalize;
/* stylelint-disable-next-line order/properties-alphabetical-order */
${override('ui.Dropdown.Title')}
`
const DropdownMenu = styled.ul`
background-color: ${th('colorBackground')};
border-color: ${th('colorBorder')};
border-style: ${th('borderStyle')};
border-width: ${th('borderWidth')};
color: ${th('colorText')};
display: ${props => (props.menuIsOpen ? 'block' : 'none')};
font-family: ${th('fontInterface')};
font-size: ${th('fontSizeBase')};
list-style-type: none;
margin: calc(${th('gridUnit')} / 4) 0 0 0;
padding: 0;
position: absolute;
top: ${props => (props.direction === 'down' ? '100%' : '-')};
bottom: ${props => (props.direction === 'down' ? '-' : '100%')};
width: 100%;
z-index: 9;
/* stylelint-disable-next-line order/properties-alphabetical-order */
${override('ui.Dropdown.Menu')}
`
const Item = styled.li`
cursor: pointer;
font-family: ${th('fontInterface')};
font-size: ${th('fontSizeBase')};
padding: ${th('gridUnit')};
white-space: normal;
word-break: break-word;
&:hover {
background-color: ${th('colorBackgroundHue')};
}
/* stylelint-disable-next-line order/properties-alphabetical-order */
${override('ui.Dropdown.MenuItem')}
`
const Dropdown = ({
children,
direction,
icon,
iconPosition,
itemsList,
primary,
size,
dataTestId,
color,
}) => {
const [menuIsOpen, setMenuIsOpen] = useState(false)
let colorIcon
if (color) {
colorIcon = color
} else {
primary ? (colorIcon = '#fff') : (colorIcon = '#0B65CB')
}
const getIconDirection = () => {
if (direction === 'up') {
return menuIsOpen ? 'chevron-down' : 'chevron-up'
}
return !menuIsOpen ? 'chevron-down' : 'chevron-up'
}
return (
<StyledDropdown size={size}>
<DropdownTitle
color={color}
data-test-id={dataTestId}
onBlur={() => setMenuIsOpen(false)}
onClick={() => setMenuIsOpen(!menuIsOpen)}
primary={primary}
size={size}
>
<span data-testid="iconButton">
{icon && iconPosition === 'start' && (
<Icon
color={colorIcon}
data-testid="leftIconButton"
primary={primary}
>
{icon}{' '}
</Icon>
)}
<span>{children}</span>
{icon && iconPosition === 'end' && (
<Icon color={colorIcon} data-testid="rightIconButton">
{icon}{' '}
</Icon>
)}
</span>
<ChevronIcon color={colorIcon}>{getIconDirection()}</ChevronIcon>
</DropdownTitle>
<DropdownMenu direction={direction} menuIsOpen={menuIsOpen}>
{itemsList.map(item => (
<Item
key={item.id}
onClick={() => {
item.onClick()
setMenuIsOpen(false)
}}
{...item.props}
data-test-id={item.dataTestId}
onMouseDown={e => e.preventDefault()}
>
{item.title}
</Item>
))}
</DropdownMenu>
</StyledDropdown>
)
}
Dropdown.propTypes = {
/** Content of the button of the dropdown */
children: PropTypes.node.isRequired,
dataTestId: PropTypes.string,
/** Dropdow direction */
direction: PropTypes.oneOf(['up', 'down']),
/** Icon name (An icon name, from the Feather icon set.) */
icon: PropTypes.string,
/** Icon Position (Defines the position of the icon (if is is at the start of the button , or at the end)) */
iconPosition: PropTypes.oneOf(['start', 'end']),
/** List of the items for the drop down items */
itemsList: PropTypes.arrayOf(
PropTypes.shape({
/** The key for the item */
id: PropTypes.string.isRequired,
/** The click function for the item */
onClick: PropTypes.func,
/** The title for the item */
title: PropTypes.node.isRequired,
}),
),
size: PropTypes.string,
/** Primary property for the dropdown, if it is false(or not set) then the dropdown will be secondary */
primary: PropTypes.bool,
}
Dropdown.defaultProps = {
direction: 'down',
icon: null,
iconPosition: 'start',
itemsList: [],
primary: false,
size: '',
dataTestId: '',
}
export default Dropdown