Commit b53bed79 authored by Aanand Prasad's avatar Aanand Prasad

WIP: convert review components to styled components

parent 258367e5
Pipeline #4696 failed with stages
in 3 minutes and 55 seconds
......@@ -25,6 +25,7 @@
"redux": "^3.6.0",
"redux-form": "^7.0.3",
"striptags": "^3.1.0",
"styled-components": "^2.4.0",
"uuid": "^3.1.0",
"wax-editor-react": "^0.1.8",
"xpub-connect": "^0.0.2",
......
import styled from 'styled-components'
const AdminSection = styled.div`
margin-bottom: var(--grid-unit);
`
export default AdminSection
.root {
import styled from 'styled-components'
const Columns = styled.div`
display: grid;
grid-column-gap: 2em;
grid-template-areas: "manuscript review files";
grid-template-columns: minmax(200px, 80ch) minmax(200px, 50ch) minmax(10ch, 10ch);
grid-template-areas: 'manuscript admin';
grid-template-columns: minmax(200px, 80ch) minmax(200px, 50ch);
justify-content: center;
}
`
.manuscript {
const Manuscript = styled.div`
grid-area: manuscript;
}
.review {
grid-area: review;
}
.files  {
margin-left: 4em;
position: absolute;
}
`
.attach {
grid-area: files;
}
const Admin = styled.div`
grid-area: admin;
`
.layout-metadatas  {
background: red;
}
export { Columns, Manuscript, Admin }
import styled from 'styled-components'
const Tab = styled.div`
padding: var(--sub-grid-unit) 1em;
font-size: var(--font-size-base-small);
border-width: 0 var(--border-width) var(--border-width) 0;
border-style: var(--border-style);
border-color: ${({ active }) =>
active ? 'var(--color-primary)' : 'var(--color-border)'};
`
export default Tab
import React from 'react'
import styled from 'styled-components'
import Tab from './Tab'
import classes from './Tabs.local.scss'
// TODO: allow the tab content to be separate from the key
const Root = styled.div``
const TabsContainer = styled.div`
display: flex;
margin-bottom: var(--grid-unit);
`
const Title = styled.div`
border-bottom: var(--border-width) var(--border-style) var(--color-border);
padding: var(--sub-grid-unit) 1em;
`
const TabContainer = styled.div``
const Content = styled.div``
class Tabs extends React.Component {
constructor(props) {
super(props)
......@@ -35,27 +47,23 @@ class Tabs extends React.Component {
const { activeKey } = this.state
return (
<div className={classes.root}>
<div className={classes.tabs}>
{title && <span className={classes.title}>{title}</span>}
<Root>
<TabsContainer>
{title && <Title>{title}</Title>}
{sections.map(({ key, label }) => (
<span
className={classes.tab}
key={key}
onClick={() => this.setActiveKey(key)}
>
<TabContainer key={key} onClick={() => this.setActiveKey(key)}>
<Tab active={activeKey === key}>{label || key}</Tab>
</span>
</TabContainer>
))}
</div>
</TabsContainer>
{activeKey && (
<div className={classes.content}>
<Content>
{sections.find(section => section.key === activeKey).content}
</div>
</Content>
)}
</div>
</Root>
)
}
}
......
import React from 'react'
import styled from 'styled-components'
import { NoteViewer } from 'xpub-edit'
import { Attachment } from '@pubsweet/ui'
import classes from './Decision.local.scss'
const Heading = styled.div``
const Note = styled.div``
const Content = styled.div``
const Recommendation = styled.div``
const Decision = ({ decision }) => (
<div>
<div>
<div className={classes.heading}>Note</div>
<Heading>Note</Heading>
<div className={classes.note}>
<div className={classes.content}>
<Note>
<Content>
<NoteViewer value={decision.note.content} />
</div>
</Content>
{decision.note.attachments &&
decision.note.attachments.map(attachment => (
<Attachment key={attachment.url} value={attachment} />
))}
</div>
</Note>
</div>
<div>
<div className={classes.heading}>Decision</div>
<Heading>Decision</Heading>
<div className={classes.decision}>{decision.recommendation}</div>
<Recommendation>{decision.recommendation}</Recommendation>
</div>
</div>
)
......
.heading {
font-weight: bold;
margin-bottom: 10px;
margin-top: 20px;
}
.note {
display: flex;
}
.content {
flex: 1;
}
.decision {
font-family: var(--font-reviewer);
}
import React from 'react'
import { FormSection } from 'redux-form'
import { NoteEditor } from 'xpub-edit'
import { Attachments, Button, RadioGroup, ValidatedField } from '@pubsweet/ui'
import {
Attachments,
Button,
ControlGroup,
RadioGroup,
ValidatedField,
} from '@pubsweet/ui'
import { withJournal } from 'xpub-journal'
import { required } from 'xpub-validators'
import classes from './DecisionForm.local.scss'
import AdminSection from '../atoms/AdminSection'
const NoteInput = input => (
<NoteEditor placeholder="Enter your decision…" title="Decision" {...input} />
......@@ -20,38 +29,36 @@ const RecommendationInput = journal => input => (
const DecisionForm = ({ journal, valid, handleSubmit, uploadFile }) => (
<form onSubmit={handleSubmit}>
<div className={classes.section}>
<AdminSection>
<FormSection name="note">
<div className={classes.note}>
<div className={classes.content}>
<ValidatedField
component={NoteInput}
name="content"
validate={[required]}
/>
</div>
<ControlGroup inline>
<ValidatedField
component={NoteInput}
name="content"
validate={[required]}
/>
<ValidatedField
component={AttachmentsInput(uploadFile)}
name="attachments"
/>
</div>
</ControlGroup>
</FormSection>
</div>
</AdminSection>
<div className={classes.section}>
<AdminSection>
<ValidatedField
component={RecommendationInput(journal)}
name="recommendation"
validate={[required]}
/>
</div>
</AdminSection>
<div>
<AdminSection>
<Button disabled={!valid} primary type="submit">
Submit
</Button>
</div>
</AdminSection>
</form>
)
......
.section {
margin-bottom: 20px;
margin-top: 20px;
}
.note {
display: flex;
}
.content {
flex: 1;
}
import React from 'react'
import moment from 'moment'
// import classnames from 'classnames'
import { Link } from 'react-router-dom'
import SimpleEditor from 'wax-editor-react'
import classes from './DecisionLayout.local.scss'
import DecisionForm from './DecisionForm'
import DecisionReviews from './DecisionReviews'
import ReviewMetadata from '../metadata/ReviewMetadata'
import Decision from './Decision'
import Tabs from '../tabs/Tabs'
import { Columns, Manuscript, Admin } from '../atoms/Columns'
import AdminSection from '../atoms/AdminSection'
import Tabs from '../atoms/Tabs'
// TODO -- is passing arrays of react components as props an ok practice?
/*
......@@ -73,21 +74,29 @@ const DecisionLayout = ({
decisionSections.push({
content: (
<div>
<ReviewMetadata version={currentVersion} />
<Link
to={`/projects/${project.id}/versions/${
currentVersion.id
}/reviewers`}
>
Assign Reviewers
</Link>
<DecisionReviews version={currentVersion} />
<DecisionForm
decision={decision}
handleSubmit={handleSubmit}
uploadFile={uploadFile}
valid={valid}
/>
<AdminSection>
<ReviewMetadata version={currentVersion} />
</AdminSection>
<AdminSection>
<Link
to={`/projects/${project.id}/versions/${
currentVersion.id
}/reviewers`}
>
Assign Reviewers
</Link>
</AdminSection>
<AdminSection>
<DecisionReviews version={currentVersion} />
</AdminSection>
<AdminSection>
<DecisionForm
decision={decision}
handleSubmit={handleSubmit}
uploadFile={uploadFile}
valid={valid}
/>
</AdminSection>
</div>
),
key,
......@@ -110,23 +119,23 @@ const DecisionLayout = ({
}
return (
<div className={classes.root}>
<div className={classes.column}>
<Columns>
<Manuscript>
<Tabs
activeKey={editorSections[editorSections.length - 1].key}
sections={editorSections}
title="Versions"
/>
</div>
</Manuscript>
<div className={classes.column}>
<Admin>
<Tabs
activeKey={decisionSections[decisionSections.length - 1].key}
sections={decisionSections}
title="Versions"
/>
</div>
</div>
</Admin>
</Columns>
)
}
......
.root {
display: grid;
grid-column-gap: 2em;
grid-template-areas: "manuscript decision files";
grid-template-columns: minmax(200px, 80ch) minmax(200px, 50ch) minmax(10ch, 10ch);
justify-content: center;
}
.column {
flex: 1;
height: 100%;
overflow-y: hidden;
}
import React from 'react'
import styled from 'styled-components'
import { compose, withState, withHandlers } from 'recompose'
import { withJournal } from 'xpub-journal'
import { Button } from '@pubsweet/ui'
import Review from '../review/Review'
import classes from './DecisionReview.local.scss'
const ToggleReview = ({ open, toggle }) => (
<button className={classes.toggle} onClick={toggle}>
<Button onClick={toggle} plain>
{open ? 'Hide' : 'Show'}
</button>
</Button>
)
const Bullet = ({ journal, recommendation }) => {
const recommendationColor = journal.recommendations.find(
item => item.value === recommendation,
).color
const recommendationColor = () =>
recommendation
? journal.recommendations.find(item => item.value === recommendation)
.color
: 'black'
return (
<span
className={classes.indicator}
style={{
backgroundColor: recommendation ? recommendationColor : 'black',
}}
/>
)
const Dot = styled.span`
border-radius: 100%;
display: inline-block;
height: 10px;
margin-right: 10px;
width: 10px;
background-color: ${recommendationColor};
`
return <Dot />
}
const ReviewHeading = ({
......@@ -32,25 +37,45 @@ const ReviewHeading = ({
ordinal,
recommendation,
toggleOpen,
}) => (
<div className={classes.heading}>
<Bullet journal={journal} recommendation={recommendation} />
}) => {
const Root = styled.div`
display: flex;
align-items: baseline;
`
const Ordinal = styled.span``
const Name = styled.span``
const Controls = styled.span`
flex-grow: 1;
text-align: right;
`
<span className={classes.ordinal}>Review {ordinal}</span>
<span className={classes.name}>{name || 'Anonymous'}</span>
<span className={classes.dots} />
<ToggleReview open toggle={toggleOpen} />
</div>
)
return (
<Root>
<Bullet journal={journal} recommendation={recommendation} />
<Ordinal>Review {ordinal}</Ordinal>
&nbsp;
<Name>{name || 'Anonymous'}</Name>
<Controls>
<ToggleReview open={open} toggle={toggleOpen} />
</Controls>
</Root>
)
}
const DecisionReview = ({ review, reviewer, journal, open, toggleOpen }) => {
const { recommendation } = review.Recommendation
const { name, ordinal } = reviewer
const Root = styled.div`
margin-bottom: var(--grid-unit);
`
const ReviewBody = styled.div`
margin-left: 1em;
`
return (
<div>
<Root>
<ReviewHeading
journal={journal}
name={name}
......@@ -61,11 +86,11 @@ const DecisionReview = ({ review, reviewer, journal, open, toggleOpen }) => {
/>
{open && (
<div className={classes.review}>
<ReviewBody>
<Review review={review} />
</div>
</ReviewBody>
)}
</div>
</Root>
)
}
......
.heading {
align-items: baseline;
display: flex;
}
.indicator {
border-radius: 100%;
display: inline-block;
height: 10px;
margin-right: 10px;
width: 10px;
}
.ordinal {
font-weight: bold;
margin-right: 10px;
}
.name {
background: white;
font-family: var(--font-mono);
margin-right: 10px;
&::after {
color: #aaa;
content: ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . " ". . . . . . . . . . . . . . . . . . . . ";
float: left;
font-size: 0.8em;
font-weight: 400;
letter-spacing: -2px;
white-space: nowrap;
width: 0;
}
}
.dots {
background-color: #eee;
flex: 1;
}
.toggle {
background: white;
border: none;
border-bottom: 2px solid transparent;
color: var(--color-primary);
cursor: pointer;
font-size: inherit;
font-style: italic;
margin-left: 10px;
&:hover {
border-bottom: 2px solid var(color-primary);
}
}
.review {
margin-left: 20px;
}
import React from 'react'
import { withJournal } from 'xpub-journal'
import DecisionReview from './DecisionReview'
import classes from './DecisionReviews.local.scss'
// TODO: read reviewer ordinal and name from project reviewer
const DecisionReviews = ({ journal, version }) => (
<div className={classes.root}>
<div>
{version.reviewers &&
version.reviewers
.filter(review => review.submitted)
.map((review, index) => (
<div className={classes.review} key={review.id}>
<div key={review.id}>
<DecisionReview
open
review={review}
......
.root {
margin-top: 20px;
}
.review:not(:last-child) {
margin-bottom: 20px;
margin-top: 20px;
}
import React from 'react'
import styled from 'styled-components'
import { File } from '@pubsweet/ui'
import classes from './ReviewMetadata.local.scss'
const Root = styled.div``
const Title = styled.div``
const Table = styled.table`
border-spacing: 0;
`
const Heading = styled.th`
font-weight: inherit;
color: var(--color-quiet);
padding: 0 1em 0 0;
`
const Cell = styled.td`
padding: 0;
`
const ReviewMetadata = ({ version, handlingEditors }) => (
<div>
<div className={classes.title}>Metadata</div>
<Root>
<Title>Metadata</Title>
<table>
<Table>
<tbody>
<tr>
<th className={classes.heading}>peer review:</th>
<td>
<Heading>peer review:</Heading>
<Cell>
{version.declarations.openPeerReview === 'yes' ? 'open' : 'closed'}
</td>
</Cell>
</tr>
{!!handlingEditors && (
<tr>
<th className={classes.heading}>handling editor:</th>
<td>
<Heading>handling editor:</Heading>
<Cell>
{handlingEditors.map(user => (
<span key={user.username}>{user.username}</span>
))}
</td>
</Cell>
</tr>
)}
{!!version.files.supplementary.length && (
<tr>
<th className={classes.heading}>
<Heading>
{version.files.supplementary.length} supplementary{' '}
{version.files.supplementary.length === 1 ? 'file' : 'files'}:
</th>
<td>
</Heading>
<Cell>
{version.files.supplementary.map(file => (
<File key={file.url} value={file} />
))}
</td>
</Cell>
</tr>