diff --git a/packages/component-faraday-ui/src/IconTooltip.js b/packages/component-faraday-ui/src/IconTooltip.js new file mode 100644 index 0000000000000000000000000000000000000000..6ce6a9538daa5547439508d79f08445764bf53d4 --- /dev/null +++ b/packages/component-faraday-ui/src/IconTooltip.js @@ -0,0 +1,34 @@ +import React, { Fragment } from 'react' +import 'react-tippy/dist/tippy.css' +import { Tooltip } from 'react-tippy' +import { ThemeProvider, withTheme } from 'styled-components' + +import { IconButton } from './' + +const IconTooltip = ({ + theme, + primary, + interactive, + icon = 'help-circle', + ...rest +}) => ( + <Tooltip + arrow + data-test-id="icon-tooltip" + html={<InfoTooltip theme={theme} {...rest} />} + interactive={interactive} + position="bottom" + theme="light" + trigger="click" + > + <IconButton icon={icon} primary={primary} /> + </Tooltip> +) + +const InfoTooltip = ({ theme, content }) => ( + <ThemeProvider theme={theme}> + <Fragment>{typeof content === 'function' ? content() : content}</Fragment> + </ThemeProvider> +) + +export default withTheme(IconTooltip) diff --git a/packages/component-faraday-ui/src/IconTooltip.md b/packages/component-faraday-ui/src/IconTooltip.md new file mode 100644 index 0000000000000000000000000000000000000000..d397a885bca75db646913433dfad3428945b5093 --- /dev/null +++ b/packages/component-faraday-ui/src/IconTooltip.md @@ -0,0 +1,18 @@ +A icon tooltip. + +```js +const { Fragment } = require('react'); + +const TooltipContent = () => <Fragment> + <Text secondary> + When an author, editor, or reviewer has a financial/personal interest or + belief that could affect his/her objectivity, or inappropriately + influence his/her actions, a potential conflict of interest exists.{' '} + <ActionLink to="https://www.hindawi.com/editors/coi/"> + More info + </ActionLink> + </Text> +</Fragment>; + +<IconTooltip primary interactive content={TooltipContent} /> +``` diff --git a/packages/component-faraday-ui/src/RadioWithComments.js b/packages/component-faraday-ui/src/RadioWithComments.js new file mode 100644 index 0000000000000000000000000000000000000000..00b1e65315be5a5fbde61159cf0aeb1f3aff047e --- /dev/null +++ b/packages/component-faraday-ui/src/RadioWithComments.js @@ -0,0 +1,65 @@ +import React from 'react' +import { get } from 'lodash' +import { Field } from 'redux-form' +import styled from 'styled-components' +import { required as requiredValidator } from 'xpub-validators' +import { ValidatedField, YesOrNo } from '@pubsweet/ui' + +import { Label, Item, IconTooltip, Row, Textarea, marginHelper } from './' + +const RadioWithComments = ({ + required, + radioLabel, + formValues, + commentsOn, + commentsLabel, + radioFieldName, + tooltipContent, + commentsFieldName, + ...rest +}) => ( + <Root {...rest}> + <Row alignItems="center" justify="flex-start"> + <Item> + <Label required={required}>{radioLabel}</Label> + {tooltipContent && ( + <IconTooltip content={tooltipContent} interactive primary /> + )} + </Item> + </Row> + + <Row alignItems="center" justify="flex-start" mb={1}> + <Field + component={({ input }) => <YesOrNo {...input} />} + name={radioFieldName} + /> + </Row> + + {get(formValues, radioFieldName, '') === commentsOn && ( + <Row alignItems="center" justify="flex-start"> + <Item data-test="submission-conflicts-text" vertical> + <Label required={required}>{commentsLabel}</Label> + <ValidatedField + component={Textarea} + name={commentsFieldName} + validate={required ? [requiredValidator] : []} + /> + </Item> + </Row> + )} + </Root> +) + +export default RadioWithComments + +RadioWithComments.defaultProps = { + mb: 2, +} + +// #region styles +const Root = styled.div` + width: 100%; + + ${marginHelper}; +` +// #endregion diff --git a/packages/component-faraday-ui/src/RadioWithComments.md b/packages/component-faraday-ui/src/RadioWithComments.md new file mode 100644 index 0000000000000000000000000000000000000000..f1dc3e453339646e71a7b702ff7e1faf3fd02158 --- /dev/null +++ b/packages/component-faraday-ui/src/RadioWithComments.md @@ -0,0 +1,48 @@ +A radio group with a comment. + +```js +const { Fragment } = require('react') +const { compose } = require('recompose') +const { connect } = require('react-redux') +const { reduxForm, getFormValues } = require('redux-form') + +const Wrapper = compose( + connect(state => ({ + formValues: getFormValues('styleguide')(state), + })), + reduxForm({ + form: 'styleguide', + }), +)(({ formValues }) => ( + <Fragment> + <RadioWithComments + required + formValues={formValues} + radioFieldName="hasConflicts" + commentsFieldName="conflictsComments" + commentsOn="yes" + commentsLabel="Conflict of interest details" + radioLabel="Do any authors have conflicts of interest to declare?" + /> + <RadioWithComments + required + formValues={formValues} + radioFieldName="hasDataAvailability" + commentsFieldName="dataAvailabilityComments" + commentsOn="no" + commentsLabel="Data availability statement" + radioLabel="Have you included a data availability statement in your manuscript?" + /> + <RadioWithComments + required + formValues={formValues} + radioFieldName="hasFunding" + commentsFieldName="fundingComments" + commentsOn="no" + commentsLabel="Funding statement" + radioLabel="Have you provided a funding statement in your manuscript?" + /> + </Fragment> +)) +;<Wrapper /> +``` diff --git a/packages/component-faraday-ui/src/TextTooltip.js b/packages/component-faraday-ui/src/TextTooltip.js index e8e45dcc321336d4ae6fd6f0b0bbbea924d66e1f..8bcd4cc0352c3754f269295dab914b7c3ed089aa 100644 --- a/packages/component-faraday-ui/src/TextTooltip.js +++ b/packages/component-faraday-ui/src/TextTooltip.js @@ -1,8 +1,8 @@ import React, { Fragment } from 'react' import 'react-tippy/dist/tippy.css' import { Tooltip } from 'react-tippy' -import { ThemeProvider, withTheme } from 'styled-components' import { Text, Row } from 'pubsweet-component-faraday-ui' +import { ThemeProvider, withTheme } from 'styled-components' const TitleTooltip = ({ theme = {}, title = '' }) => ( <ThemeProvider theme={theme}> diff --git a/packages/component-faraday-ui/src/Textarea.js b/packages/component-faraday-ui/src/Textarea.js index 88839b48d176003eae8b48b05ffff40ed9d2eb2e..51424d3f0cbd66d05397f0855dd3dc58153fe9f3 100644 --- a/packages/component-faraday-ui/src/Textarea.js +++ b/packages/component-faraday-ui/src/Textarea.js @@ -9,7 +9,7 @@ const minHeight = props => css` ` /** @component */ -export default styled.textarea` +const Textarea = styled.textarea` border-radius: ${th('borderRadius')}; border-color: ${({ hasError }) => hasError ? th('colorError') : th('colorFurniture')}; @@ -31,3 +31,9 @@ export default styled.textarea` background-color: ${th('colorBackgroundHue')}; } ` + +Textarea.defaultProps = { + mb: 1, +} + +export default Textarea diff --git a/packages/component-faraday-ui/src/index.js b/packages/component-faraday-ui/src/index.js index 1d0b23bd12ed90f4cef94a9d53b457982186f20a..58e1b75c133850dc8fd9d5a8aff082219805476b 100644 --- a/packages/component-faraday-ui/src/index.js +++ b/packages/component-faraday-ui/src/index.js @@ -19,12 +19,14 @@ export { default as DownloadZipFiles } from './DownloadZipFiles' export { default as FileItem } from './File' export { default as FileSection } from './FileSection' export { default as IconButton } from './IconButton' +export { default as IconTooltip } from './IconTooltip' export { default as Label } from './Label' export { default as ManuscriptCard } from './ManuscriptCard' export { default as ReviewerBreakdown } from './ReviewerBreakdown' export { default as PersonInfo } from './PersonInfo' export { default as PersonInvitation } from './PersonInvitation' export { default as PreviewFile } from './PreviewFile' +export { default as RadioWithComments } from './RadioWithComments' export { default as RemoteOpener } from './RemoteOpener' export { default as SortableList } from './SortableList' export { default as Tag } from './Tag' diff --git a/packages/component-wizard/src/components/StepTwo.js b/packages/component-wizard/src/components/StepTwo.js index 3fec44a08d26384cb232e86cb6ee4a27aa5ad181..519712e13660a4b9acd0cb05108c334a1a605a2f 100644 --- a/packages/component-wizard/src/components/StepTwo.js +++ b/packages/component-wizard/src/components/StepTwo.js @@ -1,18 +1,17 @@ import React, { Fragment } from 'react' import { get } from 'lodash' import { Field } from 'redux-form' -import { Tooltip } from 'react-tippy' -import styled from 'styled-components' import { required } from 'xpub-validators' import { Row, Text, Item, Label, - IconButton, + ActionLink, WizardAuthors, + RadioWithComments, } from 'pubsweet-component-faraday-ui' -import { H2, Menu, YesOrNo, TextField, ValidatedField } from '@pubsweet/ui' +import { H2, Menu, TextField, ValidatedField } from '@pubsweet/ui' import { Empty } from './' @@ -22,6 +21,7 @@ const StepTwo = ({ journal, addAuthor, changeForm, + formValues, hasConflicts, authorsError, manuscriptTypes, @@ -84,73 +84,62 @@ const StepTwo = ({ version={version} /> - <Row alignItems="center" justify="flex-start"> - <Item> - <Label required>Is there a potential conflict of interest?</Label> - <Tooltip - arrow - data-test="submission-tooltip" - html={<ConflictsTooltip />} - interactive - position="bottom" - theme="light" - trigger="click" - > - <IconButton icon="help-circle" primary /> - </Tooltip> - </Item> - </Row> - - <Row alignItems="center" justify="flex-start" mb={1}> - <Field - component={({ input }) => <YesOrNo {...input} />} - name="conflicts.hasConflicts" - /> - </Row> - - {hasConflicts && ( - <Row alignItems="center" justify="flex-start"> - <Item data-test="submission-conflicts-text" vertical> - <Label required>Conflict of interest details</Label> - <ValidatedField - component={TextField} - name="conflicts.message" - validate={[required]} - /> - </Item> - </Row> - )} + <RadioWithComments + commentsFieldName="conflicts.message" + commentsLabel="Conflict of interest details" + commentsOn="yes" + formValues={formValues} + radioFieldName="conflicts.hasConflicts" + radioLabel="Do any authors have conflicts of interest to declare?" + required + tooltipContent={ConflictsTooltip} + /> + <RadioWithComments + commentsFieldName="conflicts.dataAvailabilityMessage" + commentsLabel="Data availability statement" + commentsOn="no" + formValues={formValues} + radioFieldName="conflicts.hasDataAvailability" + radioLabel="Have you included a data availability statement in your manuscript?" + required + tooltipContent={DataAvailabilityTooltip} + /> + <RadioWithComments + commentsFieldName="conflicts.fundingMessage" + commentsLabel="Funding statement" + commentsOn="no" + formValues={formValues} + radioFieldName="conflicts.hasFunding" + radioLabel="Have you provided a funding statement in your manuscript?" + required + tooltipContent={FundingTooltip} + /> </Fragment> ) export default StepTwo const ConflictsTooltip = () => ( - <TooltipText> + <Text secondary> When an author, editor, or reviewer has a financial/personal interest or belief that could affect his/her objectivity, or inappropriately influence - his/her actions, a potential conflict of interest exists. - <MoreInfoLink - href="https://www.hindawi.com/editors/coi/" - rel="noopener noreferrer" - target="_blank" - > - More info - </MoreInfoLink> - </TooltipText> + his/her actions, a potential conflict of interest exists.{' '} + <ActionLink to="https://www.hindawi.com/editors/coi/">More info</ActionLink> + </Text> ) -// #region styled-components -const TooltipText = styled.span` - color: #586971; - display: inline-block; - font-family: "'Myriad Pro'"; - font-size: 14px; - text-align: left; -` +const DataAvailabilityTooltip = () => ( + <Text> + Statement about where data supporting the results reported in a published + article can be found, including, where applicable, hyperlinks to publicly + archived datasets analysed or generated during the study. + </Text> +) -const MoreInfoLink = styled.a` - color: #586971; - margin-left: 4px; -` -// #endregion +const FundingTooltip = () => ( + <Text> + Statement about how the research and publication of an article is funded, + naming each financially supporting body followed by any associated grant + numbers in square brackets. + </Text> +) diff --git a/packages/component-wizard/src/components/SubmissionWizard.js b/packages/component-wizard/src/components/SubmissionWizard.js index 68fc863379b6e6f206942b7a461bd9cfaebf927d..a2379d079b4ecc5a46db9cdb9bd90226c7685e1f 100644 --- a/packages/component-wizard/src/components/SubmissionWizard.js +++ b/packages/component-wizard/src/components/SubmissionWizard.js @@ -130,7 +130,7 @@ export default compose( }, ), withStateHandlers( - { step: 0 }, + { step: 1 }, { nextStep: ({ step }) => () => ({ step: Math.min(wizardSteps.length - 1, step + 1), @@ -141,11 +141,11 @@ export default compose( withProps(setInitialValues), withProps( ({ - formValues, - formSyncErrors, - submitFailed, step, location, + formValues, + submitFailed, + formSyncErrors, reduxAuthorError, }) => ({ isFirstStep: step === 0, diff --git a/packages/xpub-faraday/app/config/journal/index.js b/packages/xpub-faraday/app/config/journal/index.js index e3666200803bc17b8d68f62b8695bc96b8c6d87c..036b421ea20328566a7a875a23b59c978f9cc294 100644 --- a/packages/xpub-faraday/app/config/journal/index.js +++ b/packages/xpub-faraday/app/config/journal/index.js @@ -5,9 +5,9 @@ export { default as recommendations } from './recommendations' export { default as sections } from './sections' export { default as editors } from './editors' export { default as roles } from './roles' -export { default as wizard } from './submit-wizard' export { default as issueTypes } from './issues-types' export { default as articleTypes } from './article-types-tbrm' export { default as articleSections } from './article-sections-tbrm' export { default as manuscriptTypes } from './manuscript-types' export { default as title } from './title' +export { default as wizard } from './wizard' diff --git a/packages/xpub-faraday/app/config/journal/submit-wizard.js b/packages/xpub-faraday/app/config/journal/submit-wizard.js deleted file mode 100644 index 721801a6752d04af76db334f1ef5b1ab98a1e8d5..0000000000000000000000000000000000000000 --- a/packages/xpub-faraday/app/config/journal/submit-wizard.js +++ /dev/null @@ -1,187 +0,0 @@ -import React from 'react' -import styled from 'styled-components' -import { AbstractEditor, TitleEditor } from 'xpub-edit' -import { Menu, YesOrNo, CheckboxGroup } from '@pubsweet/ui' -import { required, minChars, minSize } from 'xpub-validators' -import { AuthorList, Files } from 'pubsweet-components-faraday/src/components' - -import { declarations } from './' -import issueTypes from './issues-types' -import manuscriptTypes from './manuscript-types' -import { - requiredFiles, - parseEmptyHtml, - editModeEnabled, - requiredBasedOnType, -} from './wizard-validators' - -const min3Chars = minChars(3) -const declarationsMinSize = minSize(declarations.options.length) - -// #region styles -const StyledLabel = styled.label` - display: inline-block; - font-weight: bold; - margin-bottom: ${({ margin }) => margin || '5px'}; - margin-top: ${({ margin }) => margin || '5px'}; -` - -const StyledSpacing = styled.div` - width: 100%; - height: 15px; -` -// #endregion - -const yesNoWithLabel = ({ label, ...rest }) => ( - <div> - <StyledLabel>{label}</StyledLabel> - <YesOrNo {...rest} /> - </div> -) -const Spacing = () => <StyledSpacing /> -const Label = ({ label }) => <StyledLabel margin="15px">{label}</StyledLabel> - -const journal = { - label: 'Hindawi Faraday', - value: 'hindawi-faraday', -} - -export default { - showProgress: true, - formSectionKeys: [ - 'authors', - 'metadata', - 'declarations', - 'conflicts', - 'files', - ], - submissionRedirect: '/confirmation-page', - dispatchFunctions: [], - steps: [ - { - label: 'Journal details', - title: '1. Journal & Field Selection', - children: [ - { - fieldId: 'label-Journal', - renderComponent: Label, - label: 'Journal', - }, - { - fieldId: 'metadata.journal', - renderComponent: Menu, - options: [journal], - value: journal.value, - validate: [required], - }, - { - fieldId: 'label-Issue', - renderComponent: Label, - label: 'Issue', - }, - { - fieldId: 'metadata.issue', - renderComponent: Menu, - options: issueTypes, - validate: [required], - }, - ], - }, - { - label: 'Pre-submission checklist', - title: '2. Pre-submission Checklist', - subtitle: - 'Before moving forward make sure you have all the needed details prepared by reviewing and checking off the items on this list.', - children: [ - { - fieldId: 'declarations', - renderComponent: CheckboxGroup, - options: declarations.options, - validate: [required, declarationsMinSize], - }, - ], - }, - { - label: 'Manuscript & Authors Details', - title: '3. Manuscript & Authors Details', - subtitle: - 'Please provide the details of all the authors of this manuscript, in the order that they appear on the manuscript. Your details are already pre-filled since, in order tu submit a manuscript you must be one of the authors.', - children: [ - { - fieldId: 'metadata.title', - renderComponent: TitleEditor, - placeholder: 'Manuscript title', - title: 'Manuscript title', - validate: [parseEmptyHtml], - }, - { - fieldId: 'spacing-title', - renderComponent: Spacing, - }, - { - fieldId: 'label-manuscriptType', - renderComponent: Label, - label: 'Manuscript Type', - }, - { - fieldId: 'metadata.type', - renderComponent: Menu, - options: manuscriptTypes, - validate: [required], - }, - { - fieldId: 'spacing-type', - renderComponent: Spacing, - }, - { - fieldId: 'metadata.abstract', - renderComponent: AbstractEditor, - title: 'Abstract', - placeholder: 'Write an abstract', - validate: [requiredBasedOnType], - }, - { - fieldId: 'spacing-abstract', - renderComponent: Spacing, - }, - { - fieldId: 'authors', - renderComponent: AuthorList, - validate: [required], - }, - { - fieldId: 'authorForm', - renderComponent: Spacing, - validate: [editModeEnabled], - }, - { - fieldId: 'conflicts.hasConflicts', - renderComponent: yesNoWithLabel, - label: 'Is there a potential conflict of interest?', - validate: [required], - }, - { - dependsOn: { - field: 'conflicts.hasConflicts', - condition: 'yes', - }, - fieldId: 'conflicts.message', - renderComponent: AbstractEditor, - label: 'Conflict of interest details', - validate: [required, min3Chars], - }, - ], - }, - { - label: 'Files upload', - title: '4. Manuscript Files Upload', - children: [ - { - fieldId: 'files', - renderComponent: Files, - validate: [requiredFiles], - }, - ], - }, - ], -} diff --git a/packages/xpub-faraday/app/config/journal/wizard-validators.js b/packages/xpub-faraday/app/config/journal/wizard-validators.js deleted file mode 100644 index 5cd9967296f36260d983077530a7efc4509624a4..0000000000000000000000000000000000000000 --- a/packages/xpub-faraday/app/config/journal/wizard-validators.js +++ /dev/null @@ -1,39 +0,0 @@ -import { get, isEmpty } from 'lodash' - -import manuscriptTypes from './manuscript-types' - -const requiredTypes = manuscriptTypes - .filter(t => t.abstractRequired) - .map(t => t.value) - -export const parseEmptyHtml = value => { - if (value && value.replace('<p></p>', '').replace('<h1></h1>', '')) { - return undefined - } - return 'Required' -} - -export const requiredBasedOnType = (value, formValues) => { - if ( - requiredTypes.includes(get(formValues, 'metadata.type')) && - isEmpty(get(formValues, 'metadata.abstract', parseEmptyHtml(value))) - ) { - return 'Required' - } - return undefined -} - -export const editModeEnabled = (value, allValues) => { - if (value) { - return 'You have some unsaved author details.' - } - return undefined -} - -export const requiredFiles = (values, formValues) => { - const manuscripts = get(formValues, 'files.manuscripts') - if (!manuscripts || manuscripts.length === 0) { - return 'At least one main manuscript file is needed.' - } - return undefined -} diff --git a/packages/xpub-faraday/app/config/journal/wizard.js b/packages/xpub-faraday/app/config/journal/wizard.js new file mode 100644 index 0000000000000000000000000000000000000000..2886acdc12ef0941856529d12a8871522ff4652b --- /dev/null +++ b/packages/xpub-faraday/app/config/journal/wizard.js @@ -0,0 +1,17 @@ +export default { + questionsForAuthors: [ + { + formKey: 'conflicts.hasConflicts', + label: 'Do any authors have conflict of interest to declare?', + }, + { + formKey: 'conflicts.dataAvailability', + label: + 'Have you included a data availabity statement in your manuscript?', + }, + { + formKey: 'conflicts.funding', + label: 'Have you provided a funding statement in your manuscript?', + }, + ], +} diff --git a/packages/xpub-faraday/config/validations.js b/packages/xpub-faraday/config/validations.js index cc8177ab05dcc13822008b3720b0ec2f674739d4..bfbe1e411e088132bfddcaed2084a459082078d6 100644 --- a/packages/xpub-faraday/config/validations.js +++ b/packages/xpub-faraday/config/validations.js @@ -37,6 +37,10 @@ module.exports = { conflicts: Joi.object({ hasConflicts: Joi.any().valid(['yes', 'no']), message: Joi.string(), + hasDataAvailability: Joi.any().valid(['yes', 'no']), + dataAvailabilityMessage: Joi.string(), + hasFunding: Joi.any().valid(['yes', 'no']), + fundingMessage: Joi.string(), }), commentsToReviewers: Joi.string(), files: Joi.object({