Add location field to new listing form

This commit is contained in:
Kimmo Puputti 2017-03-16 20:36:25 +02:00
parent 3e0d211b3b
commit 60509c4663
2 changed files with 73 additions and 36 deletions

View file

@ -3,43 +3,66 @@ import { Field, reduxForm, propTypes as formPropTypes } from 'redux-form';
import { intlShape, injectIntl } from 'react-intl';
import { isEqual } from 'lodash';
import { arrayMove } from 'react-sortable-hoc';
import { noEmptyArray, maxLength, required } from '../../util/validators';
import { AddImages } from '../../components';
import {
noEmptyArray,
maxLength,
required,
autocompleteSearchRequired,
autocompletePlaceSelected,
} from '../../util/validators';
import { AddImages, LocationAutocompleteInput } from '../../components';
import css from './EditListingForm.css';
const ACCEPT_IMAGES = 'image/*';
const TITLE_MAX_LENGTH = 60;
// Custom inputs with validator messages
const RenderField = ({ input, label, type, meta }) => {
const { touched, error } = meta;
const component = type === 'textarea'
? <textarea {...input} placeholder={label} />
: <input {...input} placeholder={label} type={type} />;
return (
<div>
<label htmlFor={input.name}>{label}</label>
const { bool, func, object, shape, string } = PropTypes;
/**
* Hoc to convert a component used within a Field to one that renders
* a label and possible errors.
*
* @param {Component|String} Comp component or a String that is used
* to create an input element
*
* @return {Component} new component that wraps the given one with a
* label and possible errors
*/
const enhancedField = Comp => {
const EnhancedField = props => {
const { input, label, meta } = props;
const { touched, error } = meta;
let component = null;
if (typeof Comp !== 'string') {
component = <Comp {...props} />;
} else if (Comp === 'textarea') {
component = <textarea {...input} placeholder={label} />;
} else {
component = <input {...input} placeholder={label} type={Comp} />;
}
return (
<div>
<label htmlFor={input.name}>{label}</label>
{component}
{touched && error ? <span className={css.error}>{error}</span> : null}
</div>
</div>
);
};
);
};
EnhancedField.displayName = `EnhancedField(${Comp.displayName || Comp.name})`;
const { bool, func, object, shape, string } = PropTypes;
EnhancedField.propTypes = {
input: shape({
onChange: func.isRequired,
name: string.isRequired,
}).isRequired,
label: string.isRequired,
meta: shape({
touched: bool,
error: string,
}).isRequired,
};
RenderField.propTypes = {
input: shape({
onChange: func.isRequired,
name: string.isRequired,
}).isRequired,
label: string.isRequired,
type: string.isRequired,
meta: shape({
touched: bool,
error: string,
}).isRequired,
return EnhancedField;
};
// Add image wrapper. Label is the only visible element, file input is hidden.
@ -120,14 +143,19 @@ class EditListingForm extends Component {
);
const maxLength60 = maxLength(maxLengthStr, TITLE_MAX_LENGTH);
const imageRequiredStr = intl.formatMessage({ id: 'EditListingForm.imageRequired' });
const locationRequiredMessage = intl.formatMessage({
id: 'EditListingForm.locationRequired',
});
const locationNotRecognizedMessage = intl.formatMessage({
id: 'EditListingForm.locationNotRecognized',
});
return (
<form onSubmit={handleSubmit}>
<Field
name="title"
label="Title"
component={RenderField}
type="text"
component={enhancedField('text')}
validate={[required(requiredStr), maxLength60]}
/>
@ -160,11 +188,21 @@ class EditListingForm extends Component {
/>
</AddImages>
<Field
name="location"
label="Location"
format={null}
component={enhancedField(LocationAutocompleteInput)}
validate={[
autocompleteSearchRequired(locationRequiredMessage),
autocompletePlaceSelected(locationNotRecognizedMessage),
]}
/>
<Field
name="description"
label="Description"
component={RenderField}
type="textarea"
component={enhancedField('textarea')}
validate={[required(requiredStr)]}
/>
<button type="submit" disabled={pristine || submitting || disabled}>{saveActionMsg}</button>

View file

@ -14,14 +14,13 @@ import {
} from './EditListingPage.duck';
const formatRequestData = values => {
const { description, images, title } = values;
const { description, images, title, location } = values;
const { selectedPlace: { address, origin } } = location;
return {
address: 'Bulevardi 14, 00200 Helsinki, Finland',
address,
description,
geolocation: {
lat: 40.6,
lng: 73.9,
},
geolocation: origin,
images: images.map(i => i.imageId),
title,
};