From 408159172c67de9a9f092d156d051bce41462b64 Mon Sep 17 00:00:00 2001 From: Kimmo Puputti Date: Tue, 4 Apr 2017 13:14:28 +0300 Subject: [PATCH 1/3] Add fakeFormProps to test redux-form-wrapped forms --- src/util/test-data.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/util/test-data.js b/src/util/test-data.js index b2ecf6a3..1cc8becb 100644 --- a/src/util/test-data.js +++ b/src/util/test-data.js @@ -47,3 +47,33 @@ export const fakeIntl = { formatTime: d => d, now: d => d, }; + +const noop = () => null; + +export const fakeFormProps = { + anyTouched: false, + asyncValidating: false, + dirty: false, + form: 'fakeTestForm', + invalid: false, + pristine: true, + clearSubmit: noop, + touch: noop, + untouch: noop, + submit: noop, + reset: noop, + initialize: noop, + handleSubmit: noop, + destroy: noop, + clearAsyncError: noop, + change: noop, + blur: noop, + autofill: noop, + asyncValidate: noop, + valid: true, + submitSucceeded: false, + submitFailed: false, + submitting: false, + pure: true, + initialized: true, +}; From ee578ba9b7801d2972bb0202f7ef8aaeaaadbb0f Mon Sep 17 00:00:00 2001 From: Kimmo Puputti Date: Tue, 4 Apr 2017 13:14:53 +0300 Subject: [PATCH 2/3] Use span for error message as other fields do --- src/components/StripeBankAccountToken/StripeBankAccountToken.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/StripeBankAccountToken/StripeBankAccountToken.js b/src/components/StripeBankAccountToken/StripeBankAccountToken.js index 869d9df9..3dedb07c 100644 --- a/src/components/StripeBankAccountToken/StripeBankAccountToken.js +++ b/src/components/StripeBankAccountToken/StripeBankAccountToken.js @@ -9,7 +9,7 @@ import css from './StripeBankAccountToken.css'; const DEBOUNCE_WAIT_TIME = 200; -const ErrorMessage = props =>

{props.children}

; +const ErrorMessage = props => {props.children}; ErrorMessage.propTypes = { children: PropTypes.any.isRequired }; /** From a4201469f6719e751635c83caeb1befd0ba27201 Mon Sep 17 00:00:00 2001 From: Kimmo Puputti Date: Tue, 4 Apr 2017 13:16:06 +0300 Subject: [PATCH 3/3] Show StripeBankAccountToken field when place is selected --- .../EditListingForm/EditListingForm.css | 4 + .../EditListingForm/EditListingForm.js | 57 ++++++++-- .../EditListingForm/EditListingForm.test.js | 11 +- .../EditListingForm.test.js.snap | 101 +++++++++++++++--- .../EditListingPage/EditListingPage.duck.js | 9 +- .../EditListingPage/EditListingPage.js | 6 +- .../EditListingPage.test.js.snap | 2 +- src/translations/en.json | 1 + 8 files changed, 160 insertions(+), 31 deletions(-) diff --git a/src/containers/EditListingForm/EditListingForm.css b/src/containers/EditListingForm/EditListingForm.css index 9eb8ee48..92fdec71 100644 --- a/src/containers/EditListingForm/EditListingForm.css +++ b/src/containers/EditListingForm/EditListingForm.css @@ -40,3 +40,7 @@ composes: error; } + +.submitButton { + margin-top: 1rem; +} diff --git a/src/containers/EditListingForm/EditListingForm.js b/src/containers/EditListingForm/EditListingForm.js index 5d012dfd..16dc3871 100644 --- a/src/containers/EditListingForm/EditListingForm.js +++ b/src/containers/EditListingForm/EditListingForm.js @@ -1,9 +1,12 @@ import React, { Component, PropTypes } from 'react'; -import { Field, reduxForm, propTypes as formPropTypes } from 'redux-form'; +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 { isEqual } from 'lodash'; import { arrayMove } from 'react-sortable-hoc'; import config from '../../config'; +import * as propTypes from '../../util/propTypes'; import { noEmptyArray, maxLength, @@ -17,6 +20,7 @@ import { LocationAutocompleteInput, Button, Input, + StripeBankAccountToken, } from '../../components'; import css from './EditListingForm.css'; @@ -111,7 +115,7 @@ RenderAddImage.propTypes = { type: string.isRequired, }; -class EditListingForm extends Component { +export class EditListingFormComponent extends Component { constructor(props) { super(props); this.handleInitialize = this.handleInitialize.bind(this); @@ -155,6 +159,7 @@ class EditListingForm extends Component { pristine, saveActionMsg = 'Save listing', submitting, + selectedPlace, } = this.props; const titleRequiredMessage = intl.formatMessage({ id: 'EditListingForm.titleRequired' }); const maxLengthMessage = intl.formatMessage( @@ -172,11 +177,17 @@ class EditListingForm extends Component { const locationNotRecognizedMessage = intl.formatMessage({ id: 'EditListingForm.locationNotRecognized', }); + const bankAccountNumberRequiredMessage = intl.formatMessage({ + id: 'EditListingForm.bankAccountNumberRequired', + }); const descriptionRequiredMessage = intl.formatMessage({ id: 'EditListingForm.descriptionRequired', }); const pricePlaceholderMessage = intl.formatMessage({ id: 'EditListingForm.pricePlaceholder' }); + const country = selectedPlace ? selectedPlace.country : null; + const currency = config.currencyConfig.currency; + return (
- + + {country + ? + : null} + + ); } } -EditListingForm.defaultProps = { initData: {} }; +EditListingFormComponent.defaultProps = { initData: {}, selectedPlace: null }; -EditListingForm.propTypes = { +EditListingFormComponent.propTypes = { ...formPropTypes, initData: shape({ title: string, description: string }), + selectedPlace: propTypes.place, intl: intlShape.isRequired, onImageUpload: func.isRequired, onUpdateImageOrder: func.isRequired, onSubmit: func.isRequired, }; -export default reduxForm({ form: 'EditListingForm' })(injectIntl(EditListingForm)); +const formName = 'EditListingForm'; + +// 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)( + EditListingFormComponent +); diff --git a/src/containers/EditListingForm/EditListingForm.test.js b/src/containers/EditListingForm/EditListingForm.test.js index da6a706f..7dca798e 100644 --- a/src/containers/EditListingForm/EditListingForm.test.js +++ b/src/containers/EditListingForm/EditListingForm.test.js @@ -4,13 +4,18 @@ // (react-sortable-hoc uses them) import React from 'react'; import { renderShallow } from '../../util/test-helpers'; -import EditListingForm from './EditListingForm'; +import { fakeIntl, fakeFormProps } from '../../util/test-data'; +import { EditListingFormComponent } from './EditListingForm'; + +const noop = () => null; describe('EditListingForm', () => { it('matches snapshot', () => { const tree = renderShallow( - v} onSubmit={v => v} onUpdateImageOrder={v => v} diff --git a/src/containers/EditListingForm/__snapshots__/EditListingForm.test.js.snap b/src/containers/EditListingForm/__snapshots__/EditListingForm.test.js.snap index 577ff376..fd0320f9 100644 --- a/src/containers/EditListingForm/__snapshots__/EditListingForm.test.js.snap +++ b/src/containers/EditListingForm/__snapshots__/EditListingForm.test.js.snap @@ -1,19 +1,86 @@ exports[`EditListingForm matches snapshot 1`] = ` - +
+ + +

+ Images +

+ + + + + + + + `; diff --git a/src/containers/EditListingPage/EditListingPage.duck.js b/src/containers/EditListingPage/EditListingPage.duck.js index 681ff0c6..7f4bcb25 100644 --- a/src/containers/EditListingPage/EditListingPage.duck.js +++ b/src/containers/EditListingPage/EditListingPage.duck.js @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import { showListingsSuccess as globalShowListingsSuccess } from '../../ducks/sdk.duck'; const requestAction = actionType => params => ({ type: actionType, payload: { params } }); @@ -48,6 +47,7 @@ export default function reducer(state = initialState, action = {}) { case CREATE_LISTING_SUCCESS: return { ...state, submittedListingId: payload.data.id, redirectToListing: true }; case CREATE_LISTING_ERROR: + // eslint-disable-next-line no-console console.error(payload); return { ...state, createListingsError: payload, redirectToListing: false }; @@ -56,6 +56,7 @@ export default function reducer(state = initialState, action = {}) { case SHOW_LISTINGS_SUCCESS: return initialState; case SHOW_LISTINGS_ERROR: + // eslint-disable-next-line no-console console.error(payload); return { ...state, showListingsError: payload, redirectToListing: false }; @@ -75,6 +76,7 @@ export default function reducer(state = initialState, action = {}) { return { ...state, images }; } case UPLOAD_IMAGE_ERROR: { + // eslint-disable-next-line no-console console.error(payload); const { id, error } = payload; const { file } = state.images[id]; @@ -138,8 +140,11 @@ export function requestShowListing(actionPayload) { }; } -export function requestCreateListing(actionPayload) { +export function requestCreateListing(data) { return (dispatch, getState, sdk) => { + const { bankAccountToken, country, ...actionPayload } = data; + // eslint-disable-next-line no-console + console.log('TODO: call API with Stripe token:', bankAccountToken, 'and country:', country); dispatch(createListing(actionPayload)); return sdk.listings .create(actionPayload) diff --git a/src/containers/EditListingPage/EditListingPage.js b/src/containers/EditListingPage/EditListingPage.js index 839a245c..eafc7fc0 100644 --- a/src/containers/EditListingPage/EditListingPage.js +++ b/src/containers/EditListingPage/EditListingPage.js @@ -14,8 +14,8 @@ import { } from './EditListingPage.duck'; const formatRequestData = values => { - const { description, images, location, price, title } = values; - const { selectedPlace: { address, origin } } = location; + const { description, images, location, price, title, bankAccountToken } = values; + const { selectedPlace: { address, origin, country } } = location; return { address, @@ -24,6 +24,8 @@ const formatRequestData = values => { images: images.map(i => i.imageId), price, title, + country, + bankAccountToken, }; }; diff --git a/src/containers/EditListingPage/__snapshots__/EditListingPage.test.js.snap b/src/containers/EditListingPage/__snapshots__/EditListingPage.test.js.snap index 43ab073c..e8583f92 100644 --- a/src/containers/EditListingPage/__snapshots__/EditListingPage.test.js.snap +++ b/src/containers/EditListingPage/__snapshots__/EditListingPage.test.js.snap @@ -1,7 +1,7 @@ exports[`EditListingPageComponent matches snapshot 1`] = ` -