diff --git a/docs/assets/pubsweet-logo.svg b/docs/assets/pubsweet-logo.svg index 88f361552dfa2c7b9f398651768b1dd19c3069ca..2ed2fd0b8bdce89ab118c15500a5e3164bd984b9 100644 --- a/docs/assets/pubsweet-logo.svg +++ b/docs/assets/pubsweet-logo.svg @@ -1,4 +1,4 @@ -<svg width="762" height="763" viewBox="0 0 762 763" fill="none" xmlns="http://www.w3.org/2000/svg"> +<svg viewBox="0 0 762 763" fill="none" xmlns="http://www.w3.org/2000/svg"> <mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="380" y="190" width="382" height="573"> <path d="M241.963 50.406L329.077 1.66893e-08C394.807 113.976 399.688 259.021 329.294 381.218C258.941 503.34 131.271 571.618 -2.6226e-08 571.634V470.702L242.484 330.705V50.707L241.963 50.406Z" transform="translate(380.802 190.503)" fill="white"/> </mask> diff --git a/docs/styleguide.config.js b/docs/styleguide.config.js index adaaa53134117a62d3fbe74e0cef88e44aa549e3..87a73106a95281604188c90a8f59f90349b12283 100644 --- a/docs/styleguide.config.js +++ b/docs/styleguide.config.js @@ -134,7 +134,6 @@ module.exports = { '**/components/Manage/**', '**/components/**/*.config.js', '**/components/*-server/**', - '**/components/MediumDraft/CustomImageSideButton.jsx', '**/node_modules/**', '**/*.test.{js,jsx}', ], diff --git a/packages/components/Signup/Signup.md b/packages/components/Signup/Signup.md new file mode 100644 index 0000000000000000000000000000000000000000..91cd341d7cc97a55b5bc85bb56147bea04240a85 --- /dev/null +++ b/packages/components/Signup/Signup.md @@ -0,0 +1,39 @@ +A signup form + +```js +const { withFormik } = require('formik') + +const SignupForm = withFormik({ + initialValues: { + username: '', + password: '', + }, + mapPropsToValues: props => ({ + username: props.username, + password: props.password, + }), + displayName: 'Signup', + handleSubmit: val => console.log(val), +})(Signup) +;<SignupForm /> +``` + +Can also have a logo: + +```js +const { withFormik } = require('formik') + +const SignupForm = withFormik({ + initialValues: { + username: '', + password: '', + }, + mapPropsToValues: props => ({ + username: props.username, + password: props.password, + }), + displayName: 'Signup', + handleSubmit: val => console.log(val), +})(Signup) +;<SignupForm logo="pubsweet-logo.svg" /> +``` diff --git a/packages/components/Signup/index.js b/packages/components/Signup/index.js new file mode 100644 index 0000000000000000000000000000000000000000..ce5d7de642c988287390b34b9c4cb4540b9a0306 --- /dev/null +++ b/packages/components/Signup/index.js @@ -0,0 +1,3 @@ +import Signup from './src/SignupContainer' + +export default Signup diff --git a/packages/components/Signup/package.json b/packages/components/Signup/package.json new file mode 100644 index 0000000000000000000000000000000000000000..00ea31c6c7913812bfa23d049f20175100e80a3b --- /dev/null +++ b/packages/components/Signup/package.json @@ -0,0 +1,28 @@ +{ + "name": "editoria-component-signup", + "version": "1.0.41", + "description": "Basic signup form component for PubSweet", + "main": "index.js", + "author": "Collaborative Knowledge Foundation", + "license": "MIT", + "dependencies": { + "@pubsweet/ui": "^9.1.3", + "@pubsweet/ui-toolkit": "2.0.7", + "formik": "^1.3.0", + "prop-types": "^15.5.10", + "recompose": "^0.30.0" + }, + "peerDependencies": { + "graphql-tag": "^2.10.0", + "pubsweet-client": ">=1.0.0", + "react": ">=16", + "react-apollo": "^2.3.3", + "react-router": "^4.3.1", + "styled-components": "^4.1.3" + }, + "repository": { + "type": "git", + "url": "https://gitlab.coko.foundation/pubsweet/pubsweet", + "path": "Signup" + } +} diff --git a/packages/components/Signup/src/Signup.jsx b/packages/components/Signup/src/Signup.jsx new file mode 100644 index 0000000000000000000000000000000000000000..732cec4a7885d08ba24a5780736d4b2bcb4870f7 --- /dev/null +++ b/packages/components/Signup/src/Signup.jsx @@ -0,0 +1,72 @@ +import React from 'react' +import { Field } from 'formik' +import { override } from '@pubsweet/ui-toolkit' +import styled from 'styled-components' + +import { + CenteredColumn, + Link, + H1, + ErrorText, + Button, + TextField, +} from '@pubsweet/ui' + +const FormContainer = styled.div` + ${override('Login.FormContainer')}; +` + +const Logo = styled.div` + width: 100%; + max-width: 100px; + margin: 0 auto; + ${override('Login.Logo')}; +` +Logo.displayName = 'Logo' + +const UsernameInput = props => ( + <TextField label="Username" {...props.field} placeholder="Username" /> +) + +const EmailInput = props => ( + <TextField label="Email" {...props.field} placeholder="Email" type="email" /> +) +const PasswordInput = props => ( + <TextField + label="Password" + {...props.field} + placeholder="Password" + type="password" + /> +) + +const Signup = ({ error, handleSubmit, logo = null }) => ( + <CenteredColumn small> + {logo && ( + <Logo> + <img alt="pubsweet-logo" src={`${logo}`} /> + </Logo> + )} + <FormContainer> + <H1>Sign up</H1> + + {error && <ErrorText>{error}</ErrorText>} + + <form onSubmit={handleSubmit}> + <Field component={UsernameInput} name="username" /> + <Field component={EmailInput} name="email" /> + <Field component={PasswordInput} name="password" /> + <Button primary type="submit"> + Sign up + </Button> + </form> + + <div> + <span>Already have an account? </span> + <Link to="/login">Login</Link> + </div> + </FormContainer> + </CenteredColumn> +) + +export default Signup diff --git a/packages/components/Signup/src/SignupContainer.js b/packages/components/Signup/src/SignupContainer.js new file mode 100644 index 0000000000000000000000000000000000000000..36bf4d98e1d6d9a9677261aef2419f756fa6d81b --- /dev/null +++ b/packages/components/Signup/src/SignupContainer.js @@ -0,0 +1,32 @@ +import { compose } from 'recompose' +import { withFormik } from 'formik' +import { graphql } from 'react-apollo' +import { SIGNUP_USER } from './graphql/mutations' + +import Signup from './Signup' + +const handleSubmit = (values, { props, setSubmitting, setErrors }) => + props.signupUser({ + variables: { input: values }, + }) + +const enhancedFormik = withFormik({ + initialValues: { + username: '', + email: '', + password: '', + }, + mapPropsToValues: props => ({ + username: props.username, + password: props.password, + email: props.email, + }), + displayName: 'signup', + handleSubmit, +})(Signup) + +export default compose( + graphql(SIGNUP_USER, { + name: 'signupUser', + }), +)(enhancedFormik) diff --git a/packages/components/Signup/src/graphql/mutations/index.js b/packages/components/Signup/src/graphql/mutations/index.js new file mode 100644 index 0000000000000000000000000000000000000000..49150319715409f25dfbb390757159ef33523eb8 --- /dev/null +++ b/packages/components/Signup/src/graphql/mutations/index.js @@ -0,0 +1,12 @@ +import gql from 'graphql-tag' + +export const SIGNUP_USER = gql` + mutation($input: UserInput) { + createUser(input: $input) { + id + type + username + email + } + } +` diff --git a/packages/components/Signup/test/Signup.test.jsx b/packages/components/Signup/test/Signup.test.jsx new file mode 100644 index 0000000000000000000000000000000000000000..c58f45b04822b5d76f9a0c809248a7e52fde856b --- /dev/null +++ b/packages/components/Signup/test/Signup.test.jsx @@ -0,0 +1,38 @@ +import { shallow } from 'enzyme' +import React from 'react' +import { Field } from 'formik' +import { Button, ErrorText } from '@pubsweet/ui' + +import Signup from '../src/Signup' + +describe('<Signup />', () => { + const makeWrapper = (props = {}) => shallow(<Signup {...props} />) + + it('renders the login form', () => { + const wrapper = makeWrapper() + expect(wrapper.debug()).toMatchSnapshot() + expect(wrapper.find(Field)).toHaveLength(3) + expect(wrapper.find(Button)).toHaveLength(1) + expect(wrapper.find({ to: '/login' })).toHaveLength(1) + }) + + it('shows error', () => { + const wrapper = makeWrapper({ error: 'Yikes!' }) + expect(wrapper.find(ErrorText)).toHaveLength(1) + }) + + it('can hide logo', () => { + const logo = 'data:image/gif;base64,R0lGODlhDwAPAKECAAAAzMzM/////' + const wrapper1 = makeWrapper({ logo }) + const wrapper2 = makeWrapper() + expect(wrapper1.find('Logo')).toHaveLength(1) + expect(wrapper2.find('Logo')).toHaveLength(0) + }) + + it('triggers submit handler', () => { + const handleSubmit = jest.fn() + const wrapper = makeWrapper({ handleSubmit }) + wrapper.find('form').simulate('submit') + expect(handleSubmit).toHaveBeenCalled() + }) +}) diff --git a/packages/components/Signup/test/__snapshots__/Signup.test.jsx.snap b/packages/components/Signup/test/__snapshots__/Signup.test.jsx.snap new file mode 100644 index 0000000000000000000000000000000000000000..7ebce37b08af00279d7cf785eafb5a30c654cdc3 --- /dev/null +++ b/packages/components/Signup/test/__snapshots__/Signup.test.jsx.snap @@ -0,0 +1,27 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`<Signup /> renders the login form 1`] = ` +"<styled.div small={true}> + <styled.div> + <styled.h1> + Sign up + </styled.h1> + <form onSubmit={[undefined]}> + <FormikConnect(FieldInner) component={[Function: UsernameInput]} name=\\"username\\" /> + <FormikConnect(FieldInner) component={[Function: EmailInput]} name=\\"email\\" /> + <FormikConnect(FieldInner) component={[Function: PasswordInput]} name=\\"password\\" /> + <styled.button primary={true} type=\\"submit\\"> + Sign up + </styled.button> + </form> + <div> + <span> + Already have an account? + </span> + <Styled(Link) to=\\"/login\\"> + Login + </Styled(Link)> + </div> + </styled.div> +</styled.div>" +`; diff --git a/yarn.lock b/yarn.lock index 3bbf5d4dda732dbf3230a488ca5dc7d6efb60db9..c2266698c592b7c4b8c0ea294f701ad8d80dbecf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5637,11 +5637,6 @@ es-to-primitive@^1.1.1, es-to-primitive@^1.2.0: is-date-object "^1.0.1" is-symbol "^1.0.2" -es6-error@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" - integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== - es6-object-assign@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" @@ -7645,7 +7640,7 @@ hoist-non-react-statics@^2.1.0, hoist-non-react-statics@^2.3.1: resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz#d2ca2dfc19c5a91c5a6615ce8e564ef0347e2a40" integrity sha512-6Bl6XsDT1ntE0lHbIhr4Kp2PGcleGZ66qu5Jqk8lc0Xc/IeG6gVLmwUGs/K0Us+L8VWoKgj0uWdPMataOsm31w== -hoist-non-react-statics@^2.5.0, hoist-non-react-statics@^2.5.4, hoist-non-react-statics@^2.5.5: +hoist-non-react-statics@^2.5.0, hoist-non-react-statics@^2.5.5: version "2.5.5" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw== @@ -9864,7 +9859,15 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^3.0.0" -lodash-es@^4.17.10, lodash-es@^4.17.11: +lock-verify@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lock-verify/-/lock-verify-2.0.2.tgz#148e4f85974915c9e3c34d694b7de9ecb18ee7a8" + integrity sha512-QNVwK0EGZBS4R3YQ7F1Ox8p41Po9VGl2QG/2GsuvTbkJZYSsPeWHKMbbH6iZMCHWSMww5nrJroZYnGzI4cePuw== + dependencies: + npm-package-arg "^5.1.2 || 6" + semver "^5.4.1" + +lodash-es@^4.17.11: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.11.tgz#145ab4a7ac5c5e52a3531fb4f310255a152b4be0" integrity sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q== @@ -13940,33 +13943,7 @@ reduce-function-call@^1.0.1: dependencies: balanced-match "^0.4.2" -redux-form@^7.0.3: - version "7.4.2" - resolved "https://registry.yarnpkg.com/redux-form/-/redux-form-7.4.2.tgz#d6061088fb682eb9fc5fb9749bd8b102f03154b0" - integrity sha512-QxC36s4Lelx5Cr8dbpxqvl23dwYOydeAX8c6YPmgkz/Dhj053C16S2qoyZN6LO6HJ2oUF00rKsAyE94GwOUhFA== - dependencies: - es6-error "^4.1.1" - hoist-non-react-statics "^2.5.4" - invariant "^2.2.4" - is-promise "^2.1.0" - lodash "^4.17.10" - lodash-es "^4.17.10" - prop-types "^15.6.1" - react-lifecycles-compat "^3.0.4" - -redux-logger@^3.0.1: - version "3.0.6" - resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-3.0.6.tgz#f7555966f3098f3c88604c449cf0baf5778274bf" - integrity sha1-91VZZvMJjzyIYExEnPC69XeCdL8= - dependencies: - deep-diff "^0.3.5" - -redux-thunk@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" - integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw== - -redux@^3.6.0, redux@^3.7.1, redux@^3.7.2: +redux@^3.7.1, redux@^3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b" integrity sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==