diff --git a/src/components/EditListingLocationPanel/EditListingLocationPanel.css b/src/components/EditListingLocationPanel/EditListingLocationPanel.css new file mode 100644 index 00000000..d452fbfb --- /dev/null +++ b/src/components/EditListingLocationPanel/EditListingLocationPanel.css @@ -0,0 +1,6 @@ +.root { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; +} diff --git a/src/components/EditListingLocationPanel/EditListingLocationPanel.js b/src/components/EditListingLocationPanel/EditListingLocationPanel.js new file mode 100644 index 00000000..c6da85da --- /dev/null +++ b/src/components/EditListingLocationPanel/EditListingLocationPanel.js @@ -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 ( +
+

+ +
+ ); +}; + +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; diff --git a/src/components/index.js b/src/components/index.js index 65149f99..7070c72a 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -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, diff --git a/src/containers/EditListingLocationForm/EditListingLocationForm.css b/src/containers/EditListingLocationForm/EditListingLocationForm.css new file mode 100644 index 00000000..e3d80601 --- /dev/null +++ b/src/containers/EditListingLocationForm/EditListingLocationForm.css @@ -0,0 +1,10 @@ +.root { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; +} + +.submitButton { + margin-top: 1rem; +} diff --git a/src/containers/EditListingLocationForm/EditListingLocationForm.example.js b/src/containers/EditListingLocationForm/EditListingLocationForm.example.js new file mode 100644 index 00000000..08955f3a --- /dev/null +++ b/src/containers/EditListingLocationForm/EditListingLocationForm.example.js @@ -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); + }, + }, +}; diff --git a/src/containers/EditListingLocationForm/EditListingLocationForm.js b/src/containers/EditListingLocationForm/EditListingLocationForm.js new file mode 100644 index 00000000..aba33637 --- /dev/null +++ b/src/containers/EditListingLocationForm/EditListingLocationForm.js @@ -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 ( +
+ + + + + ); + } +} + +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 +); diff --git a/src/containers/EditListingLocationForm/EditListingLocationForm.test.js b/src/containers/EditListingLocationForm/EditListingLocationForm.test.js new file mode 100644 index 00000000..eb52664e --- /dev/null +++ b/src/containers/EditListingLocationForm/EditListingLocationForm.test.js @@ -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( + v} + /> + ); + expect(tree).toMatchSnapshot(); + }); +}); diff --git a/src/containers/EditListingLocationForm/__snapshots__/EditListingLocationForm.test.js.snap b/src/containers/EditListingLocationForm/__snapshots__/EditListingLocationForm.test.js.snap new file mode 100644 index 00000000..73370bea --- /dev/null +++ b/src/containers/EditListingLocationForm/__snapshots__/EditListingLocationForm.test.js.snap @@ -0,0 +1,22 @@ +exports[`EditListingLocationForm matches snapshot 1`] = ` +
+ + + +`; diff --git a/src/containers/index.js b/src/containers/index.js index 5f89165c..2306f6e7 100644 --- a/src/containers/index.js +++ b/src/containers/index.js @@ -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, diff --git a/src/examples.js b/src/examples.js index c33f2603..e5ad79ef 100644 --- a/src/examples.js +++ b/src/examples.js @@ -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, diff --git a/src/translations/en.json b/src/translations/en.json index f3ee77da..dab5288e 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -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",