EditListingLocationPanel and form within

This commit is contained in:
Vesa Luusua 2017-05-11 15:43:10 +03:00
parent c3a5036497
commit d14fcef98f
11 changed files with 229 additions and 0 deletions

View file

@ -0,0 +1,6 @@
.root {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}

View file

@ -0,0 +1,50 @@
import React, { PropTypes } from 'react';
import classNames from 'classnames';
import { FormattedMessage } from 'react-intl';
import { EditListingLocationForm } from '../../containers';
import css from './EditListingLocationPanel.css';
const EditListingLocationPanel = props => {
const { className, rootClassName, listing, onSubmit } = props;
const rootClass = rootClassName || css.root;
const classes = classNames(rootClass, className);
const { attributes: { address, geolocation } } = listing || { attributes: {} };
// Only render current search if full place object is available in the URL params
// TODO bounds and country are missing - those need to be queried directly from Google Places
const locationFieldsPresent = address && geolocation;
const initialSearchFormValues = {
location: locationFieldsPresent
? {
search: address,
selectedPlace: { address, origin: geolocation },
}
: null,
};
return (
<div className={classes}>
<h1><FormattedMessage id="EditListingLocationPanel.title" /></h1>
<EditListingLocationForm initialValues={initialSearchFormValues} onSubmit={onSubmit} />
</div>
);
};
const { func, object, string } = PropTypes;
EditListingLocationPanel.defaultProps = {
className: null,
rootClassName: null,
listing: null,
};
EditListingLocationPanel.propTypes = {
className: string,
rootClassName: string,
listing: object, // TODO Should be propTypes.listing after API support is added.
onSubmit: func.isRequired,
};
export default EditListingLocationPanel;

View file

@ -8,6 +8,7 @@ import CurrencyInput from './CurrencyInput/CurrencyInput';
import DateInput from './DateInput/DateInput';
import Discussion from './Discussion/Discussion';
import EditListingDescriptionPanel from './EditListingDescriptionPanel/EditListingDescriptionPanel';
import EditListingLocationPanel from './EditListingLocationPanel/EditListingLocationPanel';
import EditListingWizard from './EditListingWizard/EditListingWizard';
import ExternalLink from './ExternalLink/ExternalLink';
import FilterPanel from './FilterPanel/FilterPanel';
@ -50,6 +51,7 @@ export {
DateInput,
Discussion,
EditListingDescriptionPanel,
EditListingLocationPanel,
EditListingWizard,
ExternalLink,
FilterPanel,

View file

@ -0,0 +1,10 @@
.root {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
.submitButton {
margin-top: 1rem;
}

View file

@ -0,0 +1,11 @@
/* eslint-disable no-console */
import EditListingLocationForm from './EditListingLocationForm';
export const Empty = {
component: EditListingLocationForm,
props: {
onSubmit: values => {
console.log('Submit EditListingLocationForm with (unformatted) values:', values);
},
},
};

View file

@ -0,0 +1,98 @@
import React, { Component, PropTypes } from 'react';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { Field, reduxForm, formValueSelector, propTypes as formPropTypes } from 'redux-form';
import { intlShape, injectIntl } from 'react-intl';
import * as propTypes from '../../util/propTypes';
import { enhancedField } from '../../util/forms';
import { autocompleteSearchRequired, autocompletePlaceSelected } from '../../util/validators';
import { LocationAutocompleteInput, Button } from '../../components';
import css from './EditListingLocationForm.css';
export class EditListingLocationFormComponent extends Component {
constructor(props) {
super(props);
// We must create the enhanced components outside the render function
// to avoid losing focus.
// See: https://github.com/erikras/redux-form/releases/tag/v6.0.0-alpha.14
this.EnhancedLocationAutocompleteInput = enhancedField(LocationAutocompleteInput);
}
render() {
const {
disabled,
handleSubmit,
intl,
invalid,
saveActionMsg,
submitting,
} = this.props;
const titleRequiredMessage = intl.formatMessage({ id: 'EditListingLocationForm.location' });
const locationRequiredMessage = intl.formatMessage({
id: 'EditListingLocationForm.locationRequired',
});
const locationNotRecognizedMessage = intl.formatMessage({
id: 'EditListingLocationForm.locationNotRecognized',
});
return (
<form onSubmit={handleSubmit}>
<Field
name="location"
label={titleRequiredMessage}
format={null}
component={this.EnhancedLocationAutocompleteInput}
validate={[
autocompleteSearchRequired(locationRequiredMessage),
autocompletePlaceSelected(locationNotRecognizedMessage),
]}
/>
<Button
className={css.submitButton}
type="submit"
disabled={invalid || submitting || disabled}
>
{saveActionMsg}
</Button>
</form>
);
}
}
EditListingLocationFormComponent.defaultProps = {
saveActionMsg: 'Next: pricing',
selectedPlace: null,
};
const { func, string } = PropTypes;
EditListingLocationFormComponent.propTypes = {
...formPropTypes,
intl: intlShape.isRequired,
onSubmit: func.isRequired,
saveActionMsg: string,
selectedPlace: propTypes.place,
};
const formName = 'EditListingLocationForm';
// When a field depends on the value of another field, we must connect
// to the store and select the required values to inject to the
// component.
//
// See: http://redux-form.com/6.6.1/examples/selectingFormValues/
const selector = formValueSelector(formName);
const mapStateToProps = state => {
const location = selector(state, 'location');
return {
selectedPlace: location && location.selectedPlace ? location.selectedPlace : null,
};
};
export default compose(connect(mapStateToProps), reduxForm({ form: formName }), injectIntl)(
EditListingLocationFormComponent
);

View file

@ -0,0 +1,21 @@
// TODO: renderdeep doesn't work due to Google Maps API integration
import React from 'react';
import { renderShallow } from '../../util/test-helpers';
import { fakeIntl, fakeFormProps } from '../../util/test-data';
import { EditListingLocationFormComponent } from './EditListingLocationForm';
const noop = () => null;
describe('EditListingLocationForm', () => {
it('matches snapshot', () => {
const tree = renderShallow(
<EditListingLocationFormComponent
{...fakeFormProps}
intl={fakeIntl}
dispatch={noop}
onSubmit={v => v}
/>
);
expect(tree).toMatchSnapshot();
});
});

View file

@ -0,0 +1,22 @@
exports[`EditListingLocationForm matches snapshot 1`] = `
<form
onSubmit={[Function]}>
<Field
component={[Function]}
format={null}
label="EditListingLocationForm.location"
name="location"
validate={
Array [
[Function],
[Function],
]
} />
<Button
className={null}
rootClassName=""
type="submit">
Next: pricing
</Button>
</form>
`;

View file

@ -5,6 +5,7 @@ import ChangePasswordForm from './ChangePasswordForm/ChangePasswordForm';
import CheckoutPage from './CheckoutPage/CheckoutPage';
import ContactDetailsPage from './ContactDetailsPage/ContactDetailsPage';
import EditListingDescriptionForm from './EditListingDescriptionForm/EditListingDescriptionForm';
import EditListingLocationForm from './EditListingLocationForm/EditListingLocationForm';
import EditListingPage from './EditListingPage/EditListingPage';
import EditProfilePage from './EditProfilePage/EditProfilePage';
import SearchForm from './SearchForm/SearchForm';
@ -36,6 +37,7 @@ export {
CheckoutPage,
ContactDetailsPage,
EditListingDescriptionForm,
EditListingLocationForm,
EditListingPage,
EditProfilePage,
SearchForm,

View file

@ -25,6 +25,8 @@ import * as ChangeAccountPasswordForm
import * as ChangePasswordForm from './containers/ChangePasswordForm/ChangePasswordForm.example';
import * as EditListingDescriptionForm
from './containers/EditListingDescriptionForm/EditListingDescriptionForm.example';
import * as EditListingLocationForm
from './containers/EditListingLocationForm/EditListingLocationForm.example';
import * as SearchForm from './containers/SearchForm/SearchForm.example';
import * as LoginForm from './containers/LoginForm/LoginForm.example';
import * as PasswordForgottenForm
@ -42,6 +44,7 @@ export {
CurrencyInput,
DateInput,
EditListingDescriptionForm,
EditListingLocationForm,
EditListingWizard,
ListingCard,
LocationAutocompleteInput,

View file

@ -39,6 +39,10 @@
"EditListingDescriptionForm.titlePlaceholder": "What's the name of your sauna?",
"EditListingDescriptionForm.titleRequired": "You need to add a name.",
"EditListingDescriptionPanel.title": "Add your sauna",
"EditListingLocationForm.location": "Location",
"EditListingLocationForm.locationRequired": "You need to provide a location",
"EditListingLocationForm.locationNotRecognized": "We didn't recognize this location. Please try another location.",
"EditListingLocationPanel.title": "Where's your sauna located?",
"EditListingPage.titleCreateListing": "Create a listing",
"EditListingPage.titleEditListing": "Edit listing",
"HeroSection.subTitle": "The largest online community to rent music studios",