From df98ebbc23bc627acb7925eb3e7785bfa35bf910 Mon Sep 17 00:00:00 2001 From: Alexandru Munteanu <alexandru.munteanu@thinslices.com> Date: Tue, 16 Jan 2018 17:24:13 +0200 Subject: [PATCH] Refactor wizard into smaller components --- .../src/components/{Steps.js => Progress.js} | 2 +- .../{Steps.local.scss => Progress.local.scss} | 5 +- .../component-wizard/src/components/Wizard.js | 158 +----------------- .../src/components/Wizard.local.scss | 23 --- .../src/components/WizardFormStep.js | 55 ++++++ .../src/components/WizardPage.js | 54 ++++++ .../src/components/WizardStep.js | 58 +++++++ .../src/components/WizardStep.local.scss | 27 +++ .../component-wizard/src/components/index.js | 5 +- .../app/config/journal/article-sections.js | 30 ---- .../app/config/journal/article-types.js | 18 -- .../xpub-faraday/app/config/journal/index.js | 3 +- .../app/config/journal/manuscript-types.js | 42 +++++ .../app/config/journal/metadata.js | 5 - .../app/config/journal/submit-wizard.js | 39 ++--- packages/xpub-faraday/app/routes.js | 4 +- packages/xpub-faraday/config/validations.js | 17 +- 17 files changed, 275 insertions(+), 270 deletions(-) rename packages/component-wizard/src/components/{Steps.js => Progress.js} (96%) rename packages/component-wizard/src/components/{Steps.local.scss => Progress.local.scss} (92%) create mode 100644 packages/component-wizard/src/components/WizardFormStep.js create mode 100644 packages/component-wizard/src/components/WizardPage.js create mode 100644 packages/component-wizard/src/components/WizardStep.js create mode 100644 packages/component-wizard/src/components/WizardStep.local.scss delete mode 100644 packages/xpub-faraday/app/config/journal/article-sections.js delete mode 100644 packages/xpub-faraday/app/config/journal/article-types.js create mode 100644 packages/xpub-faraday/app/config/journal/manuscript-types.js diff --git a/packages/component-wizard/src/components/Steps.js b/packages/component-wizard/src/components/Progress.js similarity index 96% rename from packages/component-wizard/src/components/Steps.js rename to packages/component-wizard/src/components/Progress.js index 0bee01f14..b22a07f8b 100644 --- a/packages/component-wizard/src/components/Steps.js +++ b/packages/component-wizard/src/components/Progress.js @@ -2,7 +2,7 @@ import React from 'react' import PropTypes from 'prop-types' import { compose, withHandlers, withContext, getContext } from 'recompose' -import classes from './Steps.local.scss' +import classes from './Progress.local.scss' const Separator = () => <div className={classes.separator} /> diff --git a/packages/component-wizard/src/components/Steps.local.scss b/packages/component-wizard/src/components/Progress.local.scss similarity index 92% rename from packages/component-wizard/src/components/Steps.local.scss rename to packages/component-wizard/src/components/Progress.local.scss index b02d38008..3ac86ec24 100644 --- a/packages/component-wizard/src/components/Steps.local.scss +++ b/packages/component-wizard/src/components/Progress.local.scss @@ -3,7 +3,7 @@ display: flex; flex-direction: row; justify-content: space-between; - margin: 0 40px 40px 40px; + margin: 0 40px 70px 40px; min-width: 400px; } @@ -33,8 +33,9 @@ .stepTitle { font-size: 14px; - left: -40px; + left: -45px; position: absolute; + text-align: center; top: 25px; white-space: normal; width: 120px; diff --git a/packages/component-wizard/src/components/Wizard.js b/packages/component-wizard/src/components/Wizard.js index d6cb3b09b..aaf92a3d7 100644 --- a/packages/component-wizard/src/components/Wizard.js +++ b/packages/component-wizard/src/components/Wizard.js @@ -1,112 +1,13 @@ import React from 'react' -import PropTypes from 'prop-types' import classnames from 'classnames' -import { connect } from 'react-redux' -import { pick, debounce } from 'lodash' -import { reduxForm } from 'redux-form' -import { withJournal } from 'xpub-journal' -import { ConnectPage } from 'xpub-connect' -import { ValidatedField, Button } from '@pubsweet/ui' -import { selectCollection, selectFragment } from 'xpub-selectors' -import { - compose, - withHandlers, - withState, - getContext, - withContext, - withProps, -} from 'recompose' -import { actions } from 'pubsweet-client' import classes from './Wizard.local.scss' -import { Steps } from './' +import WizardFormStep from './WizardFormStep' +import Progress from './Progress' -const { Step } = Steps +const { Step } = Progress -const validate = values => { - const errors = {} - return errors -} - -const onChange = (values, dispatch, { project, version }) => { - dispatch( - actions.updateFragment(project, { - id: version.id, - rev: version.rev, - ...values, - }), - ) -} - -const WizardStep = ({ - children: stepChildren, - title, - buttons, - nextStep, - prevStep, - handleSubmit, - isFinal, - isFirst, - goBack, -}) => ( - <div className={classnames(classes.step)}> - <form - className={classnames(classes.form)} - name="metadata" - onSubmit={handleSubmit} - > - <h3>{title}</h3> - {stepChildren && - stepChildren.map( - ({ fieldId, validate, renderComponent: Comp, ...rest }) => ( - <ValidatedField - component={input => <Comp {...rest} {...input} />} - key={fieldId} - name={fieldId} - validate={validate} - /> - ), - )} - <div className={classnames(classes.buttons)}> - <Button onClick={isFirst ? goBack : prevStep}> - {isFirst ? 'Cancel' : 'Back'} - </Button> - <Button primary type="submit"> - {isFinal ? 'Finish' : 'Next'} - </Button> - </div> - </form> - </div> -) - -const FormStep = compose( - getContext({ - goBack: PropTypes.func, - isFinal: PropTypes.bool, - isFirst: PropTypes.bool, - project: PropTypes.object, - version: PropTypes.object, - wizard: PropTypes.object, - }), - withProps(({ version, wizard }) => ({ - initialValues: pick(version, wizard.formSectionKeys), - readonly: !!version.submitted, - })), - reduxForm({ - form: 'wizard', - destroyOnUnmount: false, - forceUnregisterOnUnmount: true, - validate, - onSubmit: (values, dispatch, { nextStep, isFinal }) => { - if (!isFinal) { - nextStep() - } - }, - onChange: debounce(onChange, 1000, { maxWait: 5000 }), - }), -)(WizardStep) - -const Wizard = ({ +export default ({ journal: { wizard: { showProgress, steps } }, getSteps, step, @@ -116,57 +17,12 @@ const Wizard = ({ }) => ( <div className={classnames(classes.container)}> {showProgress && ( - <Steps currentStep={step}> + <Progress currentStep={step}> {getSteps().map((step, index) => ( <Step index={index} key={step} title={step} /> ))} - </Steps> + </Progress> )} - <FormStep {...steps[step]} nextStep={nextStep} prevStep={prevStep} /> + <WizardFormStep {...steps[step]} nextStep={nextStep} prevStep={prevStep} /> </div> ) - -export default compose( - ConnectPage(({ match }) => [ - actions.getCollection({ id: match.params.project }), - actions.getFragment( - { id: match.params.project }, - { id: match.params.version }, - ), - ]), - connect((state, { match }) => { - const project = selectCollection(state, match.params.project) - const version = selectFragment(state, match.params.version) - - return { project, version } - }), - withJournal, - withState('step', 'changeStep', 0), - withHandlers({ - getSteps: ({ journal: { wizard: { steps } } }) => () => - steps.map(w => w.label), - nextStep: ({ changeStep, journal: { wizard: { steps } } }) => () => { - changeStep(step => (step === steps.length - 1 ? step : step + 1)) - }, - prevStep: ({ changeStep }) => () => - changeStep(step => (step <= 0 ? step : step - 1)), - }), - withContext( - { - goBack: PropTypes.func, - isFinal: PropTypes.bool, - isFirst: PropTypes.bool, - project: PropTypes.object, - version: PropTypes.object, - wizard: PropTypes.object, - }, - ({ history: { goBack }, step, project, version, journal: { wizard } }) => ({ - goBack, - isFinal: step === wizard.steps.length - 1, - isFirst: step === 0, - project, - version, - wizard, - }), - ), -)(Wizard) diff --git a/packages/component-wizard/src/components/Wizard.local.scss b/packages/component-wizard/src/components/Wizard.local.scss index f108dfb46..77e5cd336 100644 --- a/packages/component-wizard/src/components/Wizard.local.scss +++ b/packages/component-wizard/src/components/Wizard.local.scss @@ -6,26 +6,3 @@ width: 900px; } -.step { - align-items: stretch; - border: 1px solid #222; - display: flex; - flex-direction: column; - justify-content: flex-start; - padding: 0 20px; - width: 700px; -} - -.form { - display: flex; - flex-direction: column; -} - -.buttons { - align-items: center; - align-self: center; - display: flex; - justify-content: space-around; - margin: 15px 0; - width: 400px; -} diff --git a/packages/component-wizard/src/components/WizardFormStep.js b/packages/component-wizard/src/components/WizardFormStep.js new file mode 100644 index 000000000..e031cbc24 --- /dev/null +++ b/packages/component-wizard/src/components/WizardFormStep.js @@ -0,0 +1,55 @@ +import PropTypes from 'prop-types' +import { connect } from 'react-redux' +import { debounce, pick } from 'lodash' +import { actions } from 'pubsweet-client' +import { reduxForm, formValueSelector } from 'redux-form' +import { compose, getContext, withProps } from 'recompose' + +import WizardStep from './WizardStep' + +const wizardSelector = formValueSelector('wizard') + +const onChange = (values, dispatch, { project, version }) => { + dispatch( + actions.updateFragment(project, { + id: version.id, + rev: version.rev, + ...values, + }), + ) +} + +export default compose( + getContext({ + goBack: PropTypes.func, + isFinal: PropTypes.bool, + isFirst: PropTypes.bool, + project: PropTypes.object, + version: PropTypes.object, + wizard: PropTypes.object, + }), + withProps(({ version, wizard }) => ({ + initialValues: pick(version, wizard.formSectionKeys), + readonly: !!version.submitted, + })), + connect((state, { wizard: { formSectionKeys } }) => ({ + formValues: formSectionKeys.reduce( + (acc, el) => ({ + ...acc, + [el]: wizardSelector(state, el), + }), + {}, + ), + })), + reduxForm({ + form: 'wizard', + destroyOnUnmount: false, + forceUnregisterOnUnmount: true, + onSubmit: (values, dispatch, { nextStep, isFinal }) => { + if (!isFinal) { + nextStep() + } + }, + onChange: debounce(onChange, 1000, { maxWait: 5000 }), + }), +)(WizardStep) diff --git a/packages/component-wizard/src/components/WizardPage.js b/packages/component-wizard/src/components/WizardPage.js new file mode 100644 index 000000000..1e6087869 --- /dev/null +++ b/packages/component-wizard/src/components/WizardPage.js @@ -0,0 +1,54 @@ +import PropTypes from 'prop-types' +import { connect } from 'react-redux' +import { actions } from 'pubsweet-client' +import { withJournal } from 'xpub-journal' +import { ConnectPage } from 'xpub-connect' +import { selectCollection, selectFragment } from 'xpub-selectors' +import { compose, withHandlers, withState, withContext } from 'recompose' + +import Wizard from './Wizard' + +export default compose( + ConnectPage(({ match }) => [ + actions.getCollection({ id: match.params.project }), + actions.getFragment( + { id: match.params.project }, + { id: match.params.version }, + ), + ]), + connect((state, { match }) => { + const project = selectCollection(state, match.params.project) + const version = selectFragment(state, match.params.version) + + return { project, version } + }), + withJournal, + withState('step', 'changeStep', 0), + withHandlers({ + getSteps: ({ journal: { wizard: { steps } } }) => () => + steps.map(w => w.label), + nextStep: ({ changeStep, journal: { wizard: { steps } } }) => () => { + changeStep(step => (step === steps.length - 1 ? step : step + 1)) + }, + prevStep: ({ changeStep }) => () => + changeStep(step => (step <= 0 ? step : step - 1)), + }), + withContext( + { + goBack: PropTypes.func, + isFinal: PropTypes.bool, + isFirst: PropTypes.bool, + project: PropTypes.object, + version: PropTypes.object, + wizard: PropTypes.object, + }, + ({ history: { goBack }, step, project, version, journal: { wizard } }) => ({ + goBack, + isFinal: step === wizard.steps.length - 1, + isFirst: step === 0, + project, + version, + wizard, + }), + ), +)(Wizard) diff --git a/packages/component-wizard/src/components/WizardStep.js b/packages/component-wizard/src/components/WizardStep.js new file mode 100644 index 000000000..672f22d3f --- /dev/null +++ b/packages/component-wizard/src/components/WizardStep.js @@ -0,0 +1,58 @@ +import React from 'react' +import { get } from 'lodash' +import classnames from 'classnames' +import { ValidatedField, Button } from '@pubsweet/ui' + +import classes from './WizardStep.local.scss' + +export default ({ + children: stepChildren, + title, + buttons, + nextStep, + prevStep, + handleSubmit, + isFinal, + isFirst, + goBack, + formValues, +}) => ( + <div className={classnames(classes.step)}> + <form className={classnames(classes.form)} onSubmit={handleSubmit}> + <h3 className={classnames(classes.title)}>{title}</h3> + {stepChildren && + stepChildren.map( + ({ + fieldId, + validate, + dependsOn, + renderComponent: Comp, + ...rest + }) => { + if ( + dependsOn && + get(formValues, dependsOn.field) !== dependsOn.condition + ) { + return null + } + return ( + <ValidatedField + component={input => <Comp {...rest} {...input} />} + key={fieldId} + name={fieldId} + validate={validate} + /> + ) + }, + )} + <div className={classnames(classes.buttons)}> + <Button onClick={isFirst ? goBack : prevStep}> + {isFirst ? 'Cancel' : 'Back'} + </Button> + <Button primary type="submit"> + {isFinal ? 'Finish' : 'Next'} + </Button> + </div> + </form> + </div> +) diff --git a/packages/component-wizard/src/components/WizardStep.local.scss b/packages/component-wizard/src/components/WizardStep.local.scss new file mode 100644 index 000000000..1b69b7a3a --- /dev/null +++ b/packages/component-wizard/src/components/WizardStep.local.scss @@ -0,0 +1,27 @@ +.step { + align-items: stretch; + border: 1px solid #222; + display: flex; + flex-direction: column; + justify-content: flex-start; + padding: 0 20px; + width: 700px; + + .title { + align-self: center; + } +} + +.form { + display: flex; + flex-direction: column; +} + +.buttons { + align-items: center; + align-self: center; + display: flex; + justify-content: space-around; + margin: 15px 0; + width: 400px; +} diff --git a/packages/component-wizard/src/components/index.js b/packages/component-wizard/src/components/index.js index e51f2b7f2..6fc7c641c 100644 --- a/packages/component-wizard/src/components/index.js +++ b/packages/component-wizard/src/components/index.js @@ -1,4 +1,7 @@ -export { default as Steps } from './Steps' export { default as Wizard } from './Wizard' +export { default as Progress } from './Progress' export { default as Dropdown } from './Dropdown' +export { default as WizardPage } from './WizardPage' +export { default as WizardStep } from './WizardStep' export { default as SortableList } from './SortableList' +export { default as WizardFormStep } from './WizardFormStep' diff --git a/packages/xpub-faraday/app/config/journal/article-sections.js b/packages/xpub-faraday/app/config/journal/article-sections.js deleted file mode 100644 index ab5c5e94d..000000000 --- a/packages/xpub-faraday/app/config/journal/article-sections.js +++ /dev/null @@ -1,30 +0,0 @@ -export default [ - { - label: 'Cognitive Psychology', - value: 'cognitive-psychology', - }, - { - label: 'Social Psychology', - value: 'social-psychology', - }, - { - label: 'Personality Psychology', - value: 'personality-psychology', - }, - { - label: 'Developmental Psychology', - value: 'developmental-psychology', - }, - { - label: 'Clinical Psychology', - value: 'clinical-psychology', - }, - { - label: 'Organizational Behavior', - value: 'organizational-behavior', - }, - { - label: 'Methodology and Research Practice', - value: 'methodology', - }, -] diff --git a/packages/xpub-faraday/app/config/journal/article-types.js b/packages/xpub-faraday/app/config/journal/article-types.js deleted file mode 100644 index a5d08e5f9..000000000 --- a/packages/xpub-faraday/app/config/journal/article-types.js +++ /dev/null @@ -1,18 +0,0 @@ -export default [ - { - label: 'Original Research Report', - value: 'original-research', - }, - { - label: 'Review', - value: 'review', - }, - { - label: 'Opinion/Commentary', - value: 'opinion', - }, - { - label: 'Registered Report', - value: 'registered-report', - }, -] diff --git a/packages/xpub-faraday/app/config/journal/index.js b/packages/xpub-faraday/app/config/journal/index.js index 505657754..fccf0c181 100644 --- a/packages/xpub-faraday/app/config/journal/index.js +++ b/packages/xpub-faraday/app/config/journal/index.js @@ -3,9 +3,8 @@ export { default as declarations } from './declarations' export { default as decisions } from './decisions' export { default as recommendations } from './recommendations' export { default as sections } from './sections' -export { default as articleSections } from './article-sections' -export { default as articleTypes } from './article-types' 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 manuscriptTypes } from './manuscript-types' diff --git a/packages/xpub-faraday/app/config/journal/manuscript-types.js b/packages/xpub-faraday/app/config/journal/manuscript-types.js new file mode 100644 index 000000000..93488dc15 --- /dev/null +++ b/packages/xpub-faraday/app/config/journal/manuscript-types.js @@ -0,0 +1,42 @@ +export default [ + { label: 'Research', value: 'research', author: true, peerReview: true }, + { label: 'Review', value: 'review', author: true, peerReview: true }, + { + label: 'Clinical study', + value: 'clinical-study', + author: true, + peerReview: true, + }, + { + label: 'Case report', + value: 'case-report', + author: true, + peerReview: true, + }, + { + label: 'Letter to the editor', + value: 'letter-to-editor', + author: true, + peerReview: false, + }, + { label: 'Editorial', value: 'editorial', author: false, peerReview: false }, + { + label: 'Corrigendum', + value: 'corrigendum', + author: false, + peerReview: false, + }, + { label: 'Erratum', value: 'erratum', author: false, peerReview: false }, + { + label: 'Expression of concern', + value: 'expression-of-concern', + author: false, + peerReview: false, + }, + { + label: 'Retraction', + value: 'retraction', + author: false, + peerReview: false, + }, +] diff --git a/packages/xpub-faraday/app/config/journal/metadata.js b/packages/xpub-faraday/app/config/journal/metadata.js index 422009e41..7d662e97b 100644 --- a/packages/xpub-faraday/app/config/journal/metadata.js +++ b/packages/xpub-faraday/app/config/journal/metadata.js @@ -3,8 +3,3 @@ export default { name: 'Hindawi Faraday', logo: '/assets/hindawi-logo.png', } - -export const journal = { - label: 'Hindawi Faraday', - value: 'hindawi-faraday', -} diff --git a/packages/xpub-faraday/app/config/journal/submit-wizard.js b/packages/xpub-faraday/app/config/journal/submit-wizard.js index e15084176..aac92887f 100644 --- a/packages/xpub-faraday/app/config/journal/submit-wizard.js +++ b/packages/xpub-faraday/app/config/journal/submit-wizard.js @@ -12,12 +12,16 @@ import { required, minChars, minSize } from 'xpub-validators' import { declarations } from './' import issueTypes from './issues-types' +import manuscriptTypes from './manuscript-types' const min3Chars = minChars(3) const declarationsMinSize = minSize(declarations.options.length) + const yesNoWithLabel = ({ label, ...rest }) => ( <div> - <label>{label}</label> + <label style={{ display: 'inline-block', marginBottom: 5, marginTop: 5 }}> + {label} + </label> <YesOrNo {...rest} /> </div> ) @@ -29,13 +33,7 @@ const journal = { export default { showProgress: true, - formSectionKeys: [ - 'metadata', - 'declarations', - 'suggestions', - 'notes', - 'files', - ], + formSectionKeys: ['metadata', 'declarations', 'conflicts', 'notes', 'files'], steps: [ { label: 'Journal details', @@ -79,38 +77,35 @@ export default { 'Please provide the details of all the authors of this manuscript....', children: [ { - fieldId: 'step3-1', + fieldId: 'metadata.title', renderComponent: TitleEditor, placeholder: 'Manuscript title', title: 'Manuscript title', }, { - fieldId: 'step3-2', + fieldId: 'metadata.type', renderComponent: Menu, label: 'Manuscript type', - options: [ - { label: 'Type 1', value: 'type1' }, - { label: 'Type 2', value: 'type2' }, - ], + options: manuscriptTypes, + validate: [required], }, { - fieldId: 'step3-3', + fieldId: 'metadata.abstract', renderComponent: AbstractEditor, title: 'Abstract', placeholder: 'Write an abstract', }, - // { - // fieldId: 'authors', - // renderComponent: 'sortable-list', - // label: 'Authors details', - // }, { - fieldId: 'step3-4', + fieldId: 'conflicts.hasConflicts', renderComponent: yesNoWithLabel, label: 'Is there a potential conflict of interest?', }, { - fieldId: 'step3-5', + dependsOn: { + field: 'conflicts.hasConflicts', + condition: 'yes', + }, + fieldId: 'conflicts.message', renderComponent: TextField, label: 'Conflict of interest details', validate: [required, min3Chars], diff --git a/packages/xpub-faraday/app/routes.js b/packages/xpub-faraday/app/routes.js index dff1dde95..4a15dc071 100644 --- a/packages/xpub-faraday/app/routes.js +++ b/packages/xpub-faraday/app/routes.js @@ -13,7 +13,7 @@ import { import DashboardPage from 'pubsweet-component-xpub-dashboard/src/components/DashboardPage' -import { Wizard } from 'pubsweet-component-wizard/src/components' +import { WizardPage } from 'pubsweet-component-wizard/src/components' // import { Wizard } from './component-wizard' @@ -23,7 +23,7 @@ const Routes = () => ( <PrivateRoute component={DashboardPage} exact path="/" /> <PrivateRoute component={LogoutPage} exact path="/logout" /> <PrivateRoute - component={Wizard} + component={WizardPage} exact path="/projects/:project/versions/:version/submit" /> diff --git a/packages/xpub-faraday/config/validations.js b/packages/xpub-faraday/config/validations.js index 5d302cd8c..46db4f99f 100644 --- a/packages/xpub-faraday/config/validations.js +++ b/packages/xpub-faraday/config/validations.js @@ -21,21 +21,12 @@ module.exports = { issue: Joi.string(), title: Joi.string(), abstract: Joi.string(), - articleType: Joi.string(), - articleSection: Joi.array().items(Joi.string()), - // authors: Joi.array(), - keywords: Joi.array(), + type: Joi.string(), }), declarations: Joi.array(), - suggestions: Joi.object({ - reviewers: Joi.object({ - suggested: Joi.array().items(Joi.string()), - opposed: Joi.array().items(Joi.string()), - }), - editors: Joi.object({ - suggested: Joi.array().items(Joi.string()), - opposed: Joi.array().items(Joi.string()), - }), + conflicts: Joi.object({ + hasConflicts: Joi.any().valid(['yes', 'no']), + message: Joi.string(), }), files: Joi.object({ manuscript: Joi.object({ -- GitLab