...
 
Commits (257)
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -38,12 +38,13 @@
"styleguide": "cd packages/styleguide && yarn styleguide",
"test": "lerna run test",
"start": "docker-compose up",
"test:e2e": "cd packages/hindawi-e2e && yarn cypress:open"
"test:e2e": "cd packages/hindawi-e2e && yarn cypress:open",
"start:services": "cd packages/xpub-faraday && yarn start:services",
"server": "cd packages/xpub-faraday && yarn server"
},
"lint-staged": {
"*.js": [
"prettier --write",
"git add"
"prettier --write"
],
"*.css": "stylelint",
"*.scss": "stylelint"
......@@ -59,5 +60,6 @@
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
}
},
"dependencies": {}
}
......@@ -115,6 +115,8 @@ export const authorCanViewReportsDetails = (
)
}
export const canInviteReviewersAsEiC = state => !currentUserIs(state, 'isEiC')
export const reviewersCanViewReviewerReports = (
state,
collection = {},
......@@ -270,9 +272,11 @@ export const getHERecommendation = (state, collectionId, fragmentId) => {
)
}
const cannotEICandAdminMakeDecision = ['accepted', 'rejected', 'deleted']
export const canMakeDecision = (state, collection = {}) => {
const status = get(collection, 'status', 'draft')
const isEIC = currentUserIs(state, 'adminEiC')
return isEIC
return isEIC && !cannotEICandAdminMakeDecision.includes(status)
}
const collectionReviewerReports = state =>
......@@ -303,7 +307,7 @@ export const canHEOnlyReject = (collection = {}) => {
return canHEOnlyRejectStatuses.includes(status)
}
const canEditManuscriptStatuses = ['draft', 'technicalChecks', 'inQA']
const cannotEditManuscriptStatuses = ['withdrawn', 'rejected', 'accepted']
export const canEditManuscript = (state, collection = {}, fragment = {}) => {
const isAdmin = currentUserIs(state, 'isAdmin')
if (
......@@ -313,7 +317,7 @@ export const canEditManuscript = (state, collection = {}, fragment = {}) => {
return false
const status = get(collection, 'status', 'draft')
return canEditManuscriptStatuses.includes(status)
return !cannotEditManuscriptStatuses.includes(status)
}
const canOverrideTechnicalChecksStatuses = ['technicalChecks', 'inQA']
......@@ -463,14 +467,23 @@ export const getInvitationsWithReviewersForFragment = (state, fragmentId) =>
export const canMakeHERecommendation = (state, { collection, statuses }) => {
const validHE = isHEToManuscript(state, get(collection, 'id', ''))
if (!validHE) return false
const statusImportance = get(
statuses,
`${get(collection, 'status', 'draft')}.importance`,
1,
)
if (!(statusImportance > 1 && statusImportance < 10)) return false
const heInvitedImportance = get(statuses, `heInvited.importance`)
const pendingApprovalImportance = get(statuses, `pendingApproval.importance`)
if (
!(
statusImportance > heInvitedImportance &&
statusImportance < pendingApprovalImportance
)
) {
return false
}
return true
}
......
import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import { Link } from 'react-router-dom'
import { withHandlers } from 'recompose'
......@@ -12,10 +13,13 @@ const ActionLink = ({
icon,
size,
onClick,
fontIcon,
disabled,
children,
renderLink,
iconSize = 2,
paddingBottom,
paddingRight,
iconSize = 1.8,
iconPosition = 'left',
...rest
}) => (
......@@ -26,6 +30,16 @@ const ActionLink = ({
{icon}
</Icon>
)}
{fontIcon &&
(iconPosition === 'left' && (
<FontIconButton
className={`${fontIcon} fontIconStyle`}
onClick={onClick}
paddingBottom={paddingBottom}
paddingRight={paddingRight}
size={size}
/>
))}
{renderLink()}
{icon &&
iconPosition === 'right' && (
......@@ -33,9 +47,44 @@ const ActionLink = ({
{icon}
</Icon>
)}
{fontIcon &&
(iconPosition === 'right' && (
<FontIconButton
className={`${fontIcon} fontIconStyle`}
onClick={onClick}
paddingBottom={paddingBottom}
paddingRight={paddingRight}
size={size}
/>
))}
</Root>
)
ActionLink.propTypes = {
/** Link/URL specifying where to navigate, outside or inside the app.
* If present the component will behave like a navigation link. */
to: PropTypes.string,
/** What icon to be used. */
icon: PropTypes.string,
/** Size of the icon. */
iconSize: PropTypes.number,
/** Position of the icon. */
iconPosition: PropTypes.oneOf(['left', 'right']),
/** Callback function fired when the component is clicked. */
onClick: PropTypes.func,
/** If true the component will be disabled (can't be interacted with). */
disabled: PropTypes.bool,
}
ActionLink.defaultProps = {
iconSize: 2,
to: '',
disabled: true,
icon: '',
onClick: () => {},
iconPosition: 'left',
}
export default withHandlers({
renderLink: ({ to, internal, disabled, onClick, size, children }) => () => {
if (to && !internal) {
......@@ -62,27 +111,27 @@ const ExternalLink = styled.a`
color: ${th('colorSecondary')};
font-family: ${th('fontReading')};
text-decoration: underline;
&:hover {
color: ${th('colorSecondary')};
font-family: ${th('fontReading')};
text-decoration: underline;
}
`
const CustomLink = ExternalLink.withComponent(Link)
const Root = styled.div`
align-items: center;
justify-content: center;
display: ${props => (props.to ? 'inline-flex' : 'flex')};
&:hover * {
color: ${th('colorSecondary')};
}
${marginHelper};
${paddingHelper};
height: ${props => (props.height ? `${props.height}px` : 'max-content')};
width: max-content;
& span {
padding-top: 5px;
}
`
const FontIconButton = styled.span`
padding-bottom: calc(${props => props.paddingBottom} * ${th('gridUnit')} / 2);
padding-right: calc(${props => props.paddingRight} * ${th('gridUnit')} / 2);
font-size: calc(${props => props.size} * ${th('gridUnit')});
`
// #endregion
A clickable text button.
```js
<ActionLink onClick={() => console.log('I am clicked.')}>Default action</ActionLink>
<ActionLink onClick={() => console.log('I am clicked.')}>
Default action
</ActionLink>
```
A disabled text buton.
......@@ -24,7 +26,7 @@ A text button with an icon on the right.
</ActionLink>
```
A text link.
A navigation link.
```js
<ActionLink icon="eye" iconPosition="right" to="https://www.google.com">
......
import React, { Fragment } from 'react'
import { get, once } from 'lodash'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import { H2, Button } from '@pubsweet/ui'
import { th } from '@pubsweet/ui-toolkit'
......@@ -7,13 +8,12 @@ import { compose, setDisplayName, withProps } from 'recompose'
import { Item, Row, Text } from 'pubsweet-component-faraday-ui'
const AppBar = ({
fixed,
logo: Logo,
menu: Menu,
createDraft,
canCreateDraft = true,
currentUser = {},
fixed = true,
isSubmit,
canCreateDraft,
currentUser,
autosave: Autosave,
journal: { metadata: { backgroundImage } },
}) => (
......@@ -28,7 +28,6 @@ const AppBar = ({
<Autosave />
</Item>
{createDraft &&
!isSubmit &&
currentUser.user && (
<Button
data-test-id="new-manuscript"
......@@ -46,7 +45,7 @@ const AppBar = ({
</RightContainer>
</Root>
{!canCreateDraft && (
<RibbonRow bgColor={th('colorInfo')} fixed={fixed}>
<RibbonRow fixed={fixed}>
<Text pb={1 / 2} pt={1}>
Your account is not confirmed. Please check your email.
</Text>
......@@ -55,10 +54,30 @@ const AppBar = ({
</Fragment>
)
AppBar.propTypes = {
/** If true, it will be fixed at the top of the screen. */
fixed: PropTypes.bool,
/** Logo that will be added to the fragment. */
logo: PropTypes.func,
currentUser: PropTypes.shape({
user: PropTypes.object,
isAuthenticated: PropTypes.bool,
}),
/** If false an error message will appear. */
canCreateDraft: PropTypes.bool,
/** Pass the menu component. */
menu: PropTypes.func,
/** Custom component that will be used as an autosave indicator. */
autosave: PropTypes.func,
}
AppBar.defaultProps = {
autosave: () => <div />,
fixed: true,
currentUser: {},
canCreateDraft: true,
logo: () => <div />,
menu: () => <div />,
autosave: () => <div />,
}
export default compose(
......@@ -117,7 +136,6 @@ const JournalBackground = styled.div`
rgba(0, 0, 0, 0.05)
);
`
const Root = styled.div`
align-items: center;
background-color: ${th('appBar.colorBackground')};
......@@ -140,6 +158,7 @@ const Root = styled.div`
`
const RibbonRow = styled(Row)`
background-color: ${th('colorInfo')};
position: ${props => (props.fixed ? 'fixed' : 'relative')};
top: ${props => (props.fixed ? th('appBar.height') : '0')};
`
......
......@@ -18,7 +18,7 @@ const autosave = {
const HindawiLogo = () => (
<Logo
onClick={() => console.log('Hindawi best publish!')}
title="Hindawi"
title="Anca"
src="https://upload.wikimedia.org/wikipedia/en/thumb/c/ca/Hindawi.svg/1200px-Hindawi.svg.png"
/>
)
......
import React from 'react'
import { get } from 'lodash'
import { Icon } from '@pubsweet/ui'
import styled from 'styled-components'
import { th } from '@pubsweet/ui-toolkit'
import {
......@@ -11,7 +10,7 @@ import {
withStateHandlers,
} from 'recompose'
import { Text } from './'
import { Text, IconButton } from './'
const AppBarMenu = ({
goTo,
......@@ -25,9 +24,12 @@ const AppBarMenu = ({
<Root>
<User data-test-id="admin-menu-button" onClick={toggleMenu}>
<Text>{username}</Text>
<Icon secondary size={2}>
{expanded ? 'chevron-up' : 'chevron-down'}
</Icon>
<IconButton
fontIcon={expanded ? 'arrowUp' : 'arrowDown'}
ml={1}
secondary
size={2}
/>
</User>
{expanded && (
<Dropdown data-test-id="admin-menu-dropdown">
......
import React, { Fragment } from 'react'
import PropTypes from 'prop-types'
import { isNumber, get } from 'lodash'
import styled from 'styled-components'
import { th } from '@pubsweet/ui-toolkit'
import { required } from 'xpub-validators'
import { reduxForm, Field } from 'redux-form'
import { H3, ValidatedField, TextField, Checkbox, Spinner } from '@pubsweet/ui'
import { MenuCountry, ItemOverrideAlert } from 'pubsweet-component-faraday-ui'
import { H3, Spinner, Checkbox, TextField, ValidatedField } from '@pubsweet/ui'
import {
compose,
withState,
......@@ -13,18 +16,8 @@ import {
setDisplayName,
} from 'recompose'
import { MenuCountry } from 'pubsweet-component-faraday-ui'
import { validators } from './helpers'
import {
Tag,
Row,
Item,
Label,
OpenModal,
PersonInfo,
IconButton,
ItemOverrideAlert,
} from './'
import { Tag, Label, Row, Item, PersonInfo, IconButton, OpenModal } from './'
const Empty = () => <div />
......@@ -59,7 +52,7 @@ const AuthorTitle = ({
>
{showModal => (
<IconButton
icon="trash-2"
fontIcon="deleteIcon"
iconSize={2}
onClick={showModal}
right={48}
......@@ -70,7 +63,7 @@ const AuthorTitle = ({
)}
<IconButton
disabled={isAuthorEdit}
icon="edit-2"
fontIcon="editIcon"
iconSize={2}
onClick={toggleEditMode}
right={8}
......@@ -81,7 +74,7 @@ const AuthorTitle = ({
<Fragment>
<IconButton
data-test-id="author-card-cancel"
icon="x-circle"
fontIcon="removeIcon"
iconSize={2}
onClick={toggleEditMode}
right={48}
......@@ -95,7 +88,7 @@ const AuthorTitle = ({
<IconButton
data-test-id="author-card-save"
disabled={formSubmitting}
icon="check-circle"
fontIcon="saveIcon"
iconSize={2}
onClick={saveChanges}
right={8}
......@@ -131,6 +124,7 @@ const AuthorTitle = ({
// #region AuthorEdit
const AuthorEdit = ({
countries,
author,
editMode,
listIndex,
......@@ -193,13 +187,13 @@ const AuthorEdit = ({
validate={[required]}
/>
</Item>
<ItemOverrideAlert data-test-id="author-card-country" vertical>
<ItemOverrideAlert vertical>
<Label required>Country</Label>
<ValidatedField
component={input => (
<MenuCountry {...input} placeholder="Please select" />
)}
component={MenuCountry}
data-test-id="author-card-country"
name="country"
placeholder="Please select"
validate={[required]}
/>
</ItemOverrideAlert>
......@@ -287,6 +281,45 @@ const AuthorCard = ({
</Root>
)
AuthorCard.propTypes = {
/** The author details. */
item: PropTypes.shape({
email: PropTypes.string,
firstName: PropTypes.string,
lastName: PropTypes.string,
affiliation: PropTypes.string,
country: PropTypes.string,
}).isRequired,
/** Callback function fired when deleting an author after confirmation.
* @param {Author} author
* @returns A function that receives the modal properties as an argument.
* */
deleteAuthor: PropTypes.func,
/** Whether the author is currently being edited. */
isAuthorEdit: PropTypes.bool,
/** Callback function fired when editing an author.
* Called with the author's index or null when closing edit mode.
* @param {number} authorIndex
* */
onEdit: PropTypes.func, // eslint-disable-line
/** Callback function fired when saving a new author.
* The added author is passed as a parameter. */
saveNewAuthor: PropTypes.func,
/** Callback function fired when editing an author.
* @param {object} values
* @param {function} dispatch
* @param {object} props */
authorEditorSubmit: PropTypes.func,
}
AuthorCard.defaultProps = {
onEdit: null,
deleteAuthor: null,
isAuthorEdit: false,
saveNewAuthor: null,
authorEditorSubmit: null,
}
export default compose(
withState('editMode', 'setEditMode', ({ item }) => item.id === 'newAuthor'),
withHandlers({
......@@ -299,6 +332,7 @@ export default compose(
)(AuthorCard)
// #region styles
const AuthorTags = styled.div`
align-items: center;
display: flex;
......@@ -340,4 +374,4 @@ const StyledSpinner = styled.div`
right: ${th('gridUnit')};
top: calc(${th('gridUnit')} * 2);
`
// #endregion
// #endregion
\ No newline at end of file
A component that shows details about an author. It has two modes: a presentation mode and an edit mode. This component can be a part of a submission wizard as well as in a sortable list.
An author card.
```js
......@@ -11,12 +13,14 @@ const author = {
}
;<div>
<AuthorCard
onEdit={() => console.log('s-a dat click pe edit')}
onEdit={e => console.log('s-a dat click pe edit', e)}
index={0}
item={author}
deleteAuthor={item => () => {
console.log('delete author', item)
}}
saveNewAuthor={(...args) => console.log('save new authot', args)}
authorEditorSubmit={(...args) => console.log('edit the author', args)}
/>
<AuthorCard
onEdit={() => console.log('s-a dat click pe edit')}
......
import React, { Fragment } from 'react'
import { get } from 'lodash'
import PropTypes from 'prop-types'
import { withProps } from 'recompose'
import styled from 'styled-components'
import { th } from '@pubsweet/ui-toolkit'
import React, { Fragment } from 'react'
import { DateParser } from '@pubsweet/ui'
import { th } from '@pubsweet/ui-toolkit'
import { Label, Item, Row, Text, FileItem } from './'
......@@ -54,6 +55,22 @@ const AuthorReply = ({
)}
</Root>
)
AuthorReply.propTypes = {
/** Reply of author. */
replyContent: PropTypes.string,
/** Name of author that replied. */
authorName: PropTypes.string,
/** Date of submitted reply. */
submittedOn: PropTypes.number,
/** Reply File. */
replyFile: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
}
AuthorReply.defaultProps = {
replyContent: '',
replyFile: {},
authorName: '',
submittedOn: Date.now(),
}
export default withProps(
({ fragment: { authors, submitted }, authorReply }) => ({
......
import React from 'react'
import styled from 'styled-components'
import { th } from '@pubsweet/ui-toolkit'
import PropTypes from 'prop-types'
import Tag from './Tag'
import Text from './Text'
......@@ -23,6 +23,29 @@ const AuthorTag = ({
</Root>
)
AuthorTag.propTypes = {
/** The author you want to be on the card. */
author: PropTypes.shape({
id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
firstName: PropTypes.string,
lastName: PropTypes.string,
isCorresponding: PropTypes.bool,
isSubmitting: PropTypes.bool,
affiliationNumber: PropTypes.number,
}),
}
AuthorTag.defaultProps = {
author: {
id: undefined,
firstName: undefined,
lastName: undefined,
isCorresponding: undefined,
isSubmitting: undefined,
affiliationNumber: undefined,
},
}
export default AuthorTag
// #region styles
......
......@@ -3,6 +3,7 @@ import { get } from 'lodash'
import styled from 'styled-components'
import { th } from '@pubsweet/ui-toolkit'
import { compose, withProps, withStateHandlers } from 'recompose'
import PropTypes from 'prop-types'
import {
Row,
......@@ -42,13 +43,13 @@ const parseAffiliations = (authors = []) =>
)
const AuthorTagList = ({
authors = [],
authors,
affiliationList,
separator = `, `,
authorKey = 'id',
withTooltip = false,
withAffiliations = false,
showAffiliation = false,
separator,
authorKey,
withTooltip,
withAffiliations,
showAffiliation,
toggleAffiliation,
}) => (
<Root>
......@@ -112,6 +113,27 @@ export default compose(
})),
)(AuthorTagList)
AuthorTagList.propTypes = {
/** The identificator label that will be seen on the card. */
authorKey: PropTypes.string,
/** All authors we want to be seen on the card. */
authors: PropTypes.arrayOf(PropTypes.object),
/** Separator between authors. */
separator: PropTypes.string,
/** Tooltip about author details. */
withTooltip: PropTypes.bool,
/** Show authors affifiations. */
withAffiliations: PropTypes.bool,
}
AuthorTagList.defaultProps = {
authorKey: 'id',
authors: [],
separator: `, `,
withTooltip: false,
withAffiliations: false,
}
// #region styles
const Root = styled.div`
align-items: center;
......
......@@ -107,12 +107,14 @@ Use a different separator and key for mapping the authors.
```js
const authors = [
{
id: 1,
email: 'john.doe@gmail.com',
firstName: 'John',
lastName: 'Doe',
isSubmitting: true,
},
{
id: 2,
email: 'michael.felps@gmail.com',
firstName: 'Michael',
lastName: 'Felps',
......@@ -120,6 +122,7 @@ const authors = [
isCorresponding: true,
},
{
id: 3,
email: 'barrack.obama@gmail.com',
firstName: 'Barrack',
lastName: 'Obama',
......
......@@ -6,6 +6,7 @@ import styled from 'styled-components'
import { th } from '@pubsweet/ui-toolkit'
import { Icon, Spinner } from '@pubsweet/ui'
import { compose, setDisplayName, withStateHandlers } from 'recompose'
import PropTypes from 'prop-types'
import Text from './Text'
......@@ -99,6 +100,14 @@ export default compose(
setDisplayName('AutosaveIndicator'),
)(AutosaveIndicator)
AutosaveIndicator.propTypes = {
/** Displays the status of the form, in progress, saved or error. */
autosave: PropTypes.object, // eslint-disable-line
}
AutosaveIndicator.defaultProps = {
autosave: {},
}
// #region styles
const Root = styled.div`
align-items: center;
......
import React from 'react'
import { has } from 'lodash'
import PropTypes from 'prop-types'
import { isUndefined } from 'lodash'
import styled from 'styled-components'
import { Icon, H3 } from '@pubsweet/ui'
import { override, th } from '@pubsweet/ui-toolkit'
......@@ -33,13 +34,22 @@ const CustomHeader = ({
</Header>
)
const ContextualBox = ({ label, children, rightChildren, ...props }) =>
has(props, 'expanded') ? (
const ContextualBox = ({
label,
children,
rightChildren,
toggle,
expanded,
...rest
}) =>
!isUndefined(expanded) ? (
<ControlledAccordion
expanded={expanded}
header={CustomHeader}
label={label}
rightChildren={rightChildren}
{...props}
toggle={toggle}
{...rest}
>
{children}
</ControlledAccordion>
......@@ -48,7 +58,7 @@ const ContextualBox = ({ label, children, rightChildren, ...props }) =>
header={CustomHeader}
label={label}
rightChildren={rightChildren}
{...props}
{...rest}
>
{children}
</Accordion>
......@@ -56,6 +66,31 @@ const ContextualBox = ({ label, children, rightChildren, ...props }) =>
export default ContextualBox
ContextualBox.propTypes = {
/** Label of the contextual box. */
label: PropTypes.string,
/** Component or html to be rendered on the right side. */
// rightChildren: PropTypes.any,
rightChildren: PropTypes.oneOfType([
PropTypes.node,
PropTypes.func,
PropTypes.string,
]),
/** The state of the contextual box. If passed from a parent then the component
* is controlled and can be expanded/collapsed remotely.
*/
expanded: PropTypes.bool, // eslint-disable-line
/** Callback function used to control the state of the component.
* To be used together with the `expanded` prop.
*/
toggle: PropTypes.func, // eslint-disable-line
}
ContextualBox.defaultProps = {
label: '',
rightChildren: undefined,
}
// #region styles
const Header = styled.div.attrs(props => ({
'data-test-id': props['data-test-id'] || 'accordion-header',
......
*Component to show or hide it's children. Can be controlled from a parent component by passing the expanded state and toggle callback.*
---
A collapseable contextual box.
```js
......@@ -113,11 +117,12 @@ const MyRightComponent = ({ headLabel }) => (
</ContextualBox>
```
A controlled ContextualBox.
A controlled ContextualBox. This is usually used together with the RemoteOpener component.
```js
const MyRightComponent = () => <div>works like a charm!</div>
;<RemoteOpener>
{({ expanded, toggle }) => (
<div>
......
import React from 'react'
import PropTypes from 'prop-types'
import { Spinner } from '@pubsweet/ui'
import { compose, withState } from 'recompose'
import { Item } from 'pubsweet-component-faraday-ui'
......@@ -9,6 +10,7 @@ const DownloadZipFiles = ({ disabled, fetching, children, downloadFiles }) => (
<Item
flex={0}
justify="flex-end"
ml={1}
mr={1}
onClick={!disabled ? downloadFiles : null}
>
......@@ -16,6 +18,17 @@ const DownloadZipFiles = ({ disabled, fetching, children, downloadFiles }) => (
</Item>
)
DownloadZipFiles.propTypes = {
/** Name for the downloaded archive file. */
archiveName: PropTypes.string.isRequired, // eslint-disable-line
/** If the user is a reviewer. */
isReviewer: PropTypes.bool, // eslint-disable-line
}
DownloadZipFiles.defaultProps = {
isReviewer: false,
}
export default compose(
withState('fetching', 'setFetching', false),
withZipDownload,
......
import React from 'react'
import { Icon } from '@pubsweet/ui'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import { th } from '@pubsweet/ui-toolkit'
import { ActionLink } from 'pubsweet-component-faraday-ui/src'
import { marginHelper } from './styledHelpers'
// icon not like in the design, but untill we + Pubsweet decide on an Icon
// implementation that supports custom icons this will do. Jen is working on it
const DragHandle = props => (
<Handle {...props}>
<Icon secondary size={2}>
menu
</Icon>
<DragIcon fontIcon="moveIcon" size={2.5} />
</Handle>
)
DragHandle.displayName = 'DragHandle'
DragHandle.protoTypes = {
/** Designed size for icon */
size: PropTypes.number,
}
DragHandle.defaultProps = {
size: 2,
}
export default DragHandle
// #region styles
const DragIcon = styled(ActionLink)`
display: flex;
align-items: center;
justify-content: center;
color: ${th('colorSecondary')};
font-size: calc(${th('gridUnit')} * 2.5);
cursor: move;
`
const Handle = styled.div`
align-self: stretch;
align-items: center;
......
......@@ -4,6 +4,7 @@ import { withProps, withHandlers, compose } from 'recompose'
import styled from 'styled-components'
import { th } from '@pubsweet/ui-toolkit'
import { DateParser } from '@pubsweet/ui'
import PropTypes from 'prop-types'
import { Label, Item, Row, Text, Tag } from './'
import { getReportComments } from './helpers'
......@@ -24,11 +25,9 @@ const EditorialReportCard = ({
<Root>
<Row justify="space-between" mb={2}>
<Item vertical>
{editorRole === 'HE' ? (
<Label mb={1 / 2}>Recommendation</Label>
) : (
<Label mb={1 / 2}>Decision</Label>
)}
<Label mb={1 / 2}>
{editorRole === 'HE' ? 'Recommendation' : 'Decision'}
</Label>
<Text>{recommendation}</Text>
</Item>
......@@ -111,6 +110,35 @@ export default compose(
),
)(EditorialReportCard)
EditorialReportCard.propTypes = {
/** Label that will be publicly viewed, for example, a message for the author will be seen by other roles also. */
publicLabel: PropTypes.string,
/** Label that will only be viewed as private message, for example, by the Editorial Team. */
privateLabel: PropTypes.string,
/** Message by editorial team and other information. */
report: PropTypes.shape({
id: PropTypes.string,
userId: PropTypes.string,
comments: PropTypes.arrayOf(PropTypes.object),
createdOn: PropTypes.number,
updatedOn: PropTypes.number,
submittedOn: PropTypes.number,
recommendation: PropTypes.string,
recommendationType: PropTypes.string,
reviewer: PropTypes.object,
}),
/** Object containing the list of recommendations. */
journal: PropTypes.shape({
recommendation: PropTypes.arrayOf(PropTypes.object),
}),
}
EditorialReportCard.defaultProps = {
publicLabel: '',
privateLabel: '',
report: {},
journal: {},
}
// #region styles
const Root = styled.div`
box-shadow: ${th('boxShadow')};
......
......@@ -7,7 +7,7 @@ import styled from 'styled-components'
import { th } from '@pubsweet/ui-toolkit'
import { withProps, withHandlers, compose } from 'recompose'
import { Label, IconButton, Text } from './'
import { Label, Text, IconButton } from './'
import { marginHelper } from './styledHelpers'
const parseFileSize = size => {
......@@ -51,8 +51,8 @@ const FileItem = ({
</FileInfo>
{hasPreview && (
<IconButton
icon="eye"
iconSize={2}
fontIcon="previewIcon"
iconSize={1.8}
ml={1}
mr={1}
onClick={onPreview}
......@@ -61,8 +61,8 @@ const FileItem = ({
/>
)}
<IconButton
icon="download"
iconSize={2}
fontIcon="downloadIcon"
iconSize={1.8}
ml={hasPreview ? 0 : 1}
mr={1}
onClick={onDownload}
......@@ -71,8 +71,8 @@ const FileItem = ({
/>
{hasDelete && (
<IconButton
icon="trash"
iconSize={2}
fontIcon="deleteIcon"
iconSize={1.8}
mr={1}
onClick={onDelete}
pt={1 / 2}
......@@ -91,11 +91,17 @@ FileItem.propTypes = {
}).isRequired,
/** Used when part of a sortable list. */
dragHandle: PropTypes.oneOfType([PropTypes.element, PropTypes.func]),
/** Handler for the preview button. */
/** Callback function fired when clicking the preview icon.
* @param {File} file
*/
onPreview: PropTypes.func,
/** Handler for the download button. */
/** Callback function fired when clicking the download icon.
* @param {File} file
*/
onDownload: PropTypes.func,
/** Handler for the delete button. */
/** Callback function fired when clicking the delete icon.
* @param {File} file
*/
onDelete: PropTypes.func,
}
......@@ -120,7 +126,7 @@ export default compose(
// #region styles
const Root = styled.div`
align-items: center;
align-items: baseline;
background-color: ${th('colorBackgroundHue')};
box-shadow: ${({ shadow }) => (shadow ? th('boxShadow') : 'none')};
border-radius: ${th('borderRadius')};
......
......@@ -3,6 +3,7 @@ import styled from 'styled-components'
import { th } from '@pubsweet/ui-toolkit'
import { FilePicker, Spinner } from '@pubsweet/ui'
import { compose, withState, withHandlers, withProps } from 'recompose'
import PropTypes from 'prop-types'
import { radiusHelpers } from './styledHelpers'
import { withNativeFileDrop, withFileSectionDrop } from './helpers'
......@@ -109,6 +110,42 @@ const FileSection = ({
</Root>
)
FileSection.propTypes = {
/** Files that are uploaded. */
files: PropTypes.arrayOf(PropTypes.object),
/** Error you get on uploading. */
error: PropTypes.string,
/** Titles of manuscript, cover letter and supplimental files. */
title: PropTypes.string,
/** Id of manuscript, cover letter and supplimental files. */
listId: PropTypes.string,
/** Function used for draging and hovering over items. */
moveItem: PropTypes.func,
/** Extensions allowed to be uploaded. */
allowedFileExtensions: PropTypes.arrayOf(PropTypes.string),
/** Callback function fired when a file is picked. */
onFilePick: PropTypes.func,
/** Callback function fired when preview icon is pressed. */
onPreview: PropTypes.func,
/** Callback function fired when download icon is pressed. */
onDownload: PropTypes.func,
/** Callback function fired when delete icon is pressed. */
onDelete: PropTypes.func,
}
FileSection.defaultProps = {
files: {},
error: '',
title: '',
listId: '',
moveItem: () => {},
allowedFileExtensions: [],
onFilePick: () => {},
onPreview: () => {},
onDownload: () => {},
onDelete: () => {},
}
export default compose(
withState('error', 'setStateError', ''),
withHandlers({
......
import React from 'react'
import { Icon } from '@pubsweet/ui'
import styled from 'styled-components'
import { th } from '@pubsweet/ui-toolkit'
import { positionHelper, marginHelper, paddingHelper } from './styledHelpers'
const IconButton = styled.div`
......@@ -11,7 +12,7 @@ const IconButton = styled.div`
opacity: ${props => (props.disabled ? 0.7 : 1)};
pointer-events: ${props => (props.unclickable ? 'none' : 'auto')};
&:hover {
opacity: 0.7;
opacity: ${props => (props.noHover ? 1 : 0.7)};
}
&[disabled] {
......@@ -24,20 +25,41 @@ const IconButton = styled.div`
export default ({
icon,
size,
onClick,
unclickable,
iconSize = 3,
primary,
fontIcon,
disabled,
unclickable,
iconSize = 1.8,
className,
...props
}) => (
<IconButton
className={className}
disabled={disabled}
display="inline"
onClick={!disabled ? onClick : null}
primary={primary}
unclickable={unclickable}
{...props}
>
<Icon size={iconSize} {...props}>
{icon}
</Icon>
{icon && (
<Icon size={iconSize} {...props}>
{icon}
</Icon>
)}
{fontIcon && (
<FontIconButton
className={`${fontIcon} fontIconStyle`}
size={iconSize}
{...props}
/>
)}
</IconButton>
)
const FontIconButton = styled.span`
font-size: calc(${props => props.size} * ${th('gridUnit')});
${paddingHelper};
`
......@@ -6,7 +6,7 @@ import { IconButton, Label, marginHelper } from './'
const IconCard = ({ label, icon, iconSize, onClick, ...rest }) => (
<Root onClick={onClick} {...rest}>
<IconButton icon={icon} iconSize={iconSize} primary />
<IconButton icon={icon} iconSize={iconSize} />
<Label>{label}</Label>
</Root>
)
......@@ -26,6 +26,9 @@ const Root = styled.div`
height: calc(${th('gridUnit')} * 19);
width: calc(${th('gridUnit')} * 26);
svg {
stroke: ${th('colorPrimary')};
}
${marginHelper};
`
......
......@@ -2,6 +2,7 @@ import React, { Fragment } from 'react'
import 'react-tippy/dist/tippy.css'
import { Tooltip } from 'react-tippy'
import { ThemeProvider, withTheme } from 'styled-components'
import PropTypes from 'prop-types'
import { IconButton } from './'
......@@ -9,8 +10,8 @@ const IconTooltip = ({
theme,
primary,
interactive,
icon = 'help-circle',
iconSize = 3,
fontIcon = 'tooltipIcon',
iconSize = 2,
...rest
}) => (
<Tooltip
......@@ -22,7 +23,13 @@ const IconTooltip = ({
theme="light"
trigger="click"
>
<IconButton icon={icon} iconSize={iconSize} mt={1 / 4} primary={primary} />
<IconButton
fontIcon={fontIcon}
iconSize={iconSize}
mb={1 / 2}
ml={1}
primary={primary}
/>
</Tooltip>
)
......@@ -31,5 +38,22 @@ const InfoTooltip = ({ theme, content }) => (
<Fragment>{typeof content === 'function' ? content() : content}</Fragment>
</ThemeProvider>
)
IconTooltip.propTypes = {
/** What icon to be used. */
icon: PropTypes.string,
/** Size of the icon. */
iconSize: PropTypes.number,
/** What content to be used in tooltip. */
content: PropTypes.func,
/** If true the content can be clicked (can be interacted with). */
interactive: PropTypes.bool,
}
IconTooltip.defaultProps = {
icon: 'help-circle',
iconSize: 2,
content: () => {},
interactive: false,
}
export default withTheme(IconTooltip)
import React from 'react'
import PropTypes from 'prop-types'
import { compose } from 'recompose'
import styled from 'styled-components'
import { reduxForm } from 'redux-form'
......@@ -83,10 +84,9 @@ const InviteReviewers = ({ handleSubmit, reset }) => (
<ItemOverrideAlert vertical>
<Label required>Country</Label>
<ValidatedField
component={input => (
<MenuCountry {...input} placeholder="Please select" />
)}
component={MenuCountry}
name="country"
placeholder="Please select"
validate={[required]}
/>
</ItemOverrideAlert>
......@@ -94,6 +94,18 @@ const InviteReviewers = ({ handleSubmit, reset }) => (
</Root>
)
InviteReviewers.propTypes = {
/** Callback function fired after confirming a reviewer invitation.
* @param {Reviewer} reviewer
* @param {object} props
*/
onInvite: PropTypes.func, // eslint-disable-line
}
InviteReviewers.defaultProps = {
onInvite: () => {},
}
export default compose(
withFetching,
withModal(({ isFetching, modalKey }) => ({
......
......@@ -2,16 +2,25 @@ import React from 'react'
import { H4 } from '@pubsweet/ui'
import styled from 'styled-components'
import { th } from '@pubsweet/ui-toolkit