Skip to content

Instantly share code, notes, and snippets.

@ferdinandsalis
Created November 12, 2017 20:26
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ferdinandsalis/703737daec26daffe056cd043b6f7551 to your computer and use it in GitHub Desktop.
Save ferdinandsalis/703737daec26daffe056cd043b6f7551 to your computer and use it in GitHub Desktop.
import React from 'react'
import revalidation from 'revalidation'
import gql from 'graphql-tag.macro'
import { filter } from 'graphql-anywhere'
import { curry } from 'ramda'
import { compose, withProps, withHandlers } from 'recompose'
import { graphql } from 'react-apollo'
import { Button } from '../common/styled'
import { getValue } from '../form/utils'
import { isRequired } from '../../utils/validation'
import { connectionQuery } from './connection'
import Field from '../form/field'
import Fieldset from '../form/fieldset'
import Input from '../form/input'
const Form = ({
revalidation: {
form,
onChange,
valid,
onSubmit,
submitted,
updateValueAndValidate,
errors = []
},
onSubmit: submitCb
}) => (
<form>
<Field
required
name="name"
label="Name des Standortes"
onChange={updateValueAndValidate}
value={form.name}
errors={errors.name}
control={Input}
placeholder="Lager"
/>
<Fieldset heading="Adresse">
<Field
required
name="street"
label="Straße"
value={form.address.street}
errors={errors.address.street}
onChange={e => onChange(['address', 'street'], getValue(e))}
control={Input}
placeholder="Parkring 18/4"
/>
<Field
required
name="postalcode"
label="Postleitzahl"
value={form.address.postalcode}
errors={errors.postalcode}
onChange={e => onChange(['address', 'postalcode'], getValue(e))}
control={Input}
placeholder="1010"
/>
<Field
required
name="locality"
label="Ort"
value={form.address.locality}
errors={errors.address.locality}
onChange={e => onChange(['address', 'locality'], getValue(e))}
control={Input}
placeholder="Wien"
/>
<Field
required
name="country"
label="Land"
value={form.address.country}
errors={errors.address.country}
onChange={e => onChange(['address', 'country'], getValue(e))}
control={Input}
placeholder="AT"
/>
</Fieldset>
<Button
children="Speichern"
disabled={!valid}
onClick={e => {
e.preventDefault()
onSubmit(({ valid, form }) => {
if (valid) {
return submitCb(form)
} else {
return null
}
})
}}
/>
</form>
)
const model = {
name: '',
address: {
street: '',
postalcode: '',
locality: '',
country: ''
}
}
const rules = {
name: [[isRequired, 'Name darf nicht leer sein']],
address: {
street: [[isRequired, 'Straße darf nicht leer sein']],
postalcode: [[isRequired, 'Postleitzahl darf nicht leer sein']],
locality: [[isRequired, 'Ort darf nicht leer sein']],
country: [[isRequired, 'Land darf nicht leer sein']]
}
}
export const formFragment = gql`
fragment LocationForm on Location {
name
address {
street
postalcode
locality
country
}
}
`
const BaseForm = revalidation(Form)
// # Create Form
export const createMutation = gql`
mutation createLocation($input: CreateLocationInput!) {
createLocation(input: $input) {
location {
id
locationId
...LocationForm
}
}
}
${formFragment}
`
const withCreateMutation = graphql(createMutation, { name: 'createLocation' })
const handleCreate = curry(async ({ onSuccess, createLocation }, values) => {
try {
const result = await createLocation({
variables: { input: { location: values } },
refetchQueries: [
{
query: connectionQuery,
variables: {
limit: 25,
offset: 0,
condition: null,
orderBy: { attribute: 'CREATED_AT', descending: true }
}
}
]
})
typeof onSuccess === 'function' && onSuccess(result)
} catch (error) {
console.log(error)
}
})
export const CreateForm = compose(
withCreateMutation,
withHandlers({ handleCreate }),
withProps(({ handleCreate }) => ({
initialState: model,
rules: rules,
onSubmit: handleCreate,
validateSingle: true,
validateOnChange: true
}))
)(BaseForm)
// # Update Form
export const initialDataQuery = gql`
query showLocation($id: ID!) {
location(id: $id) {
id
...LocationForm
}
}
${formFragment}
`
const withInitialData = graphql(initialDataQuery, {
options: ({ id }) => ({ variables: { id } })
})
export const updateMutation = gql`
mutation updateLocation($input: UpdateLocationInput!) {
updateLocation(input: $input) {
location {
id
locationId
...LocationForm
}
}
}
${formFragment}
`
const withUpdateMutation = graphql(updateMutation, { name: 'updateLocation' })
const handleUpdate = curry(
async ({ onSuccess, id, updateLocation }, values) => {
try {
const result = await updateLocation({
variables: { input: { id, locationPatch: values } }
})
typeof onSuccess === 'function' && onSuccess(result)
} catch (error) {
console.log(error)
}
}
)
export const UpdateForm = compose(
withInitialData,
withUpdateMutation,
withHandlers({ handleUpdate }),
withProps(({ data, handleUpdate }) => ({
initialState: model,
updateForm: data.loading ? null : filter(formFragment, data.location),
rules: rules,
onSubmit: handleUpdate,
validateSingle: true,
validateOnChange: true
}))
)(BaseForm)
@b2whats
Copy link

b2whats commented Nov 27, 2017

Show implement '../form/field' please

@ferdinandsalis
Copy link
Author

@b2whats sorry for the late reply. The field is really quite dumb.

Here you go

import { head, equals } from 'ramda'
import React, { Component } from 'react'
import { isValid } from 'revalidation'
import { Text } from 'galeriste-design'
import Label from '../form/label'
import Container from '../form/field-container'
import Input from '../form/input'

const createErrorMessage = errorMsgs =>
  isValid(errorMsgs) ? null : (
    <Text mt={2} f={1} color="red.5">
      {head(errorMsgs)}
    </Text>
  )

class Field extends Component {
  shouldComponentUpdate({ value, errors, asyncErrors }) {
    const unchanged =
      equals(value, this.props.value) &&
      equals(errors, this.props.errors) &&
      equals(asyncErrors, this.props.asyncErrors)

    return unchanged ? false : true
  }

  render() {
    const {
      name,
      label,
      required,
      errors,
      asyncErrors,
      value = '',
      onChange,
      control: Control = Input,
      onBlur = () => {},
      ...props
    } = this.props

    return (
      <Container>
        <Label required={required} name={name}>
          {label}
        </Label>
        <Control
          name={name}
          value={value}
          onBlur={onBlur}
          onChange={onChange}
          hasErrors={!isValid(errors)}
          {...props}
        />
        {createErrorMessage(errors)}
        {createErrorMessage(asyncErrors)}
      </Container>
    )
  }
}

export default Field

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment