Skip to content
Snippets Groups Projects

Hin 1138 country menu

Merged Larisa Andrici requested to merge HIN-1138-Country-Menu into develop
Files
2
import React from 'react'
import { Menu } from '@pubsweet/ui'
import { startsWith, toLower, get } from 'lodash'
import { compose, withState, withHandlers } from 'recompose'
import styled from 'styled-components'
import { th } from '@pubsweet/ui-toolkit'
import { th, override } from '@pubsweet/ui-toolkit'
import { withCountries } from 'pubsweet-component-faraday-ui'
@@ -13,51 +12,119 @@ const filteredCountries = (countries, userInput) =>
const firstFilteredCountry = props =>
filteredCountries(props.countries, props.userInput)[0]
const CustomOpener = ({
const Opener = ({
selected,
userInput,
toggleMenu,
placeholder,
optionLabel,
removeSelectedOption,
onChange,
onEnter,
}) => (
<Input
onChange={onChange}
onClick={toggleMenu}
onClick={selected ? removeSelectedOption : toggleMenu}
onKeyUp={onEnter}
placeholder={selected ? optionLabel(selected) : placeholder}
value={userInput}
placeholder={placeholder}
value={selected ? optionLabel(selected) : userInput}
/>
)
const MenuCountry = ({ countries = [], ...input }) => (
<Menu
maxHeight={250}
maxWidth={200}
{...input}
options={filteredCountries(countries, input.userInput)}
placeholder="Please select"
renderOpener={CustomOpener}
/>
)
const Menu = props => (
<Root>
{props.label && <Label onClick={props.optionLabel}>{props.label}</Label>}
{props.open && <CloseOverlay onClick={props.toggleMenu} />}
<Main>
<Opener
open={props.open}
optionLabel={props.optionLabel}
placeholder={props.placeHolder}
removeSelectedOption={props.removeSelectedOption}
selected={props.selected}
toggleMenu={props.toggleMenu}
{...props}
/>
<OptionsContainer>
{props.open && (
<Options maxHeight={props.maxHeight} open={props.open}>
{props.options.map(option => (
<MenuOption
handleSelect={props.handleSelect}
key={option.value}
label={option.label}
selected={props.selected}
value={option.value}
/>
))}
</Options>
)}
</OptionsContainer>
</Main>
</Root>
)
const MenuOption = ({ label, selected, value, handleSelect }) => (
<Option
active={value === selected}
key={value}
onClick={() => {
handleSelect(value)
}}
>
{label || value}
</Option>
)
const enhance = compose(
withCountries,
withState('userInput', 'updateUserInput', ''),
withState('open', 'updateOptionsVizibility', false),
withState('selected', 'setSelectedValue', ''),
withHandlers({
onChange: ({ updateUserInput, onChange }) => value => {
// this value is an input DOM event while typing and a dropdown value when
// selected
if (typeof value === 'string') {
onChange(value)
}
optionLabel: ({ countries }) => value => {
const label = countries.find(country => country.value === value)
? countries.find(country => country.value === value).label
: ''
return label
},
handleSelect: ({ setSelectedValue, updateOptionsVizibility }) => value => {
setSelectedValue(value)
updateOptionsVizibility(false)
},
}),
withHandlers({
onChange: ({ updateUserInput }) => value => {
updateUserInput(get(value, 'target.value', ''))
},
onEnter: props => event => {
if (event.which === 13) {
props.onChange(firstFilteredCountry(props).value)
props.handleSelect(firstFilteredCountry(props).value)
props.updateUserInput(firstFilteredCountry(props).label)
}
},
toggleMenu: ({ updateOptionsVizibility, open }) => event => {
updateOptionsVizibility(!open)
},
removeSelectedOption: ({
setSelectedValue,
updateOptionsVizibility,
updateUserInput,
}) => event => {
setSelectedValue('')
updateUserInput('')
updateOptionsVizibility(true)
},
}),
)
@@ -69,13 +136,101 @@ const Input = styled.input`
border: ${th('accordion.border')};
border-radius: ${th('borderRadius')};
padding: 0 ${th('gridUnit')};
font-family: ${th('fontHeading')};
::placeholder {
color: ${th('colorText')};
opacity: 1;
font-family: ${th('fontWriting')};
font-style: italic;
}
:focus {
border-color: ${th('action.colorActive')}
outline: none;
}
`
const Root = styled.div``
const CloseOverlay = styled.div`
background-color: transparent;
position: fixed;
bottom: 0;
left: 0;
top: 0;
right: 0;
z-index: 10;
${override('ui.MenuCountry.CloseOverlay')};
`
const Label = styled.label`
font-size: ${th('fontSizeBaseSmall')};
line-height: ${th('lineHeightBaseSmall')};
display: block;
${override('ui.Label')};
${override('ui.MenuCountry.Label')};
`
const Main = styled.div.attrs(props => ({
role: 'listbox',
}))`
position: relative;
${override('ui.MenuCountry.Main')};
`
const OptionsContainer = styled.div`
position: absolute;
left: 0;
right: 0;
${override('ui.MenuCountry.OptionsContainer')};
`
const Options = styled.div`
position: absolute;
top: 0;
left: 0;
right: 0;
background-color: ${th('colorBackground')};
border: ${th('borderWidth')} ${th('borderStyle')} ${th('colorBorder')};
border-radius: ${th('borderRadius')};
overflow-y: auto;
max-height: ${({ maxHeight }) => `${maxHeight}px`};
max-width: ${({ maxWidth }) => `${maxWidth}px`};
z-index: 100;
${override('ui.MenuCountry.Options')};
`
const Option = styled.div.attrs(props => ({
role: 'option',
tabIndex: '0',
'aria-selected': props.active,
}))`
color: ${props => (props.active ? props.theme.textColor : '#444')};
font-weight: ${props => (props.active ? '600' : 'inherit')};
cursor: pointer;
font-family: ${th('fontAuthor')};
padding: calc(${th('gridUnit')} - ${th('borderWidth')} * 2)
calc(${th('gridUnit')} * 2);
border: ${th('borderWidth')} ${th('borderStyle')} transparent;
border-width: ${th('borderWidth')} 0 ${th('borderWidth')} 0;
white-space: nowrap;
&:hover {
background: ${th('colorBackgroundHue')};
border-color: ${th('colorBorder')};
}
&:first-child:hover {
border-top-color: ${th('colorBackgroundHue')};
}
&:last-child:hover {
border-bottom-color: ${th('colorBackgroundHue')};
}
${override('ui.MenuCountry.Option')};
`