From 01d1315835825ff5f2688d656b5462dafcba6c87 Mon Sep 17 00:00:00 2001 From: Ahmad Abdolsaheb Date: Mon, 9 Dec 2019 22:05:09 +0100 Subject: [PATCH] feat: add minimal form to modal --- .../components/DonateFormChildViewForHOC.js | 26 +-- .../Donation/components/DonationModal.js | 90 ++++---- .../Donation/components/MinimalDonateForm.js | 203 ++++++++++++++++++ .../Donation/components/StripeCardForm.js | 18 +- config/donation-settings.js | 2 +- 5 files changed, 260 insertions(+), 79 deletions(-) create mode 100644 client/src/components/Donation/components/MinimalDonateForm.js diff --git a/client/src/components/Donation/components/DonateFormChildViewForHOC.js b/client/src/components/Donation/components/DonateFormChildViewForHOC.js index ff59231162..e9ffdc99bf 100644 --- a/client/src/components/Donation/components/DonateFormChildViewForHOC.js +++ b/client/src/components/Donation/components/DonateFormChildViewForHOC.js @@ -18,15 +18,16 @@ import { postChargeStripe } from '../../../utils/ajax'; import { userSelector } from '../../../redux'; const propTypes = { + changeCloseBtnLabel: PropTypes.func, donationAmount: PropTypes.number.isRequired, donationDuration: PropTypes.string.isRequired, email: PropTypes.string, getDonationButtonLabel: PropTypes.func.isRequired, - hideAmountOptionsCB: PropTypes.func.isRequired, isSignedIn: PropTypes.bool, stripe: PropTypes.shape({ createToken: PropTypes.func.isRequired - }) + }), + theme: PropTypes.string }; const initialState = { donationState: { @@ -38,7 +39,7 @@ const initialState = { const mapStateToProps = createSelector( userSelector, - ({ email }) => ({ email }) + ({ email, theme }) => ({ email, theme }) ); class DonateFormChildViewForHOC extends Component { @@ -59,7 +60,6 @@ class DonateFormChildViewForHOC extends Component { this.handleSubmit = this.handleSubmit.bind(this); this.postDonation = this.postDonation.bind(this); this.resetDonation = this.resetDonation.bind(this); - this.hideAmountOptions(false); } getUserEmail() { @@ -113,11 +113,6 @@ class DonateFormChildViewForHOC extends Component { }); } - hideAmountOptions(hide) { - const { hideAmountOptionsCB } = this.props; - hideAmountOptionsCB(hide); - } - postDonation(token) { const { donationAmount: amount, donationDuration: duration } = this.state; this.setState(state => ({ @@ -128,10 +123,12 @@ class DonateFormChildViewForHOC extends Component { } })); - // hide the donation options on the parent and scroll to top - this.hideAmountOptions(true); + // scroll to top window.scrollTo(0, 0); + // change the donation modal button to close + this.props.changeCloseBtnLabel(); + return postChargeStripe({ token, amount, @@ -179,7 +176,7 @@ class DonateFormChildViewForHOC extends Component { renderDonateForm() { const { isFormValid } = this.state; - const { getDonationButtonLabel } = this.props; + const { getDonationButtonLabel, theme } = this.props; return (
@@ -194,7 +191,10 @@ class DonateFormChildViewForHOC extends Component { value={this.getUserEmail()} /> - + + + - - - Support our nonprofit - - - ); } diff --git a/client/src/components/Donation/components/MinimalDonateForm.js b/client/src/components/Donation/components/MinimalDonateForm.js new file mode 100644 index 0000000000..c060734bbb --- /dev/null +++ b/client/src/components/Donation/components/MinimalDonateForm.js @@ -0,0 +1,203 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { createSelector } from 'reselect'; +import { Button, Row, Col } from '@freecodecamp/react-bootstrap'; +import { StripeProvider, Elements } from 'react-stripe-elements'; + +import { + amountsConfig, + durationsConfig, + defaultStateConfig +} from '../../../../../config/donation-settings'; +import { apiLocation } from '../../../../config/env.json'; +import DonateFormChildViewForHOC from './DonateFormChildViewForHOC'; +import { + userSelector, + isSignedInSelector, + signInLoadingSelector, + hardGoTo as navigate +} from '../../../redux'; + +import '../Donation.css'; +import DonateCompletion from './DonateCompletion.js'; +import { stripePublicKey } from '../../../../../config/env.json'; +import { stripeScriptLoader } from '../../../utils/scriptLoaders'; + +const numToCommas = num => + num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,'); + +const propTypes = { + changeCloseBtnLabel: PropTypes.func, + isDonating: PropTypes.bool, + isSignedIn: PropTypes.bool, + navigate: PropTypes.func.isRequired, + showLoading: PropTypes.bool.isRequired, + stripe: PropTypes.shape({ + createToken: PropTypes.func.isRequired + }) +}; + +const mapStateToProps = createSelector( + userSelector, + signInLoadingSelector, + isSignedInSelector, + ({ isDonating }, showLoading, isSignedIn) => ({ + isDonating, + isSignedIn, + showLoading + }) +); + +const mapDispatchToProps = { + navigate +}; + +const createOnClick = navigate => e => { + e.preventDefault(); + return navigate(`${apiLocation}/signin?returnTo=donate`); +}; + +class ModalDonateForm extends Component { + constructor(...args) { + super(...args); + + this.durations = durationsConfig; + this.amounts = amountsConfig; + + this.state = { + ...defaultStateConfig, + isDonating: this.props.isDonating, + stripe: null + }; + this.handleSelectPaymentType = this.handleSelectPaymentType.bind(this); + this.handleStripeLoad = this.handleStripeLoad.bind(this); + this.getDonationButtonLabel = this.getDonationButtonLabel.bind(this); + } + + componentDidMount() { + if (window.Stripe) { + this.handleStripeLoad(); + } else if (document.querySelector('#stripe-js')) { + document + .querySelector('#stripe-js') + .addEventListener('load', this.handleStripeLoad); + } else { + stripeScriptLoader(this.handleStripeLoad); + } + } + + componentWillUnmount() { + const stripeMountPoint = document.querySelector('#stripe-js'); + if (stripeMountPoint) { + stripeMountPoint.removeEventListener('load', this.handleStripeLoad); + } + } + + handleStripeLoad() { + // Create Stripe instance once Stripe.js loads + if (stripePublicKey) { + this.setState(state => ({ + ...state, + stripe: window.Stripe(stripePublicKey) + })); + } + } + + handleSelectPaymentType(e) { + this.setState({ + paymentType: e.currentTarget.value + }); + } + + getFormatedAmountLabel(amount) { + return `$${numToCommas(amount / 100)}`; + } + + getDonationButtonLabel() { + const { donationAmount, donationDuration } = this.state; + let donationBtnLabel = `Confirm your donation`; + if (donationDuration === 'onetime') { + donationBtnLabel = `Confirm your one-time donation of ${this.getFormatedAmountLabel( + donationAmount + )}`; + } else { + donationBtnLabel = `Confirm your donation of ${this.getFormatedAmountLabel( + donationAmount + )} ${donationDuration === 'month' ? 'per month' : 'per year'}`; + } + return donationBtnLabel; + } + + renderDonationOptions() { + const { + donationAmount, + donationDuration, + paymentType, + stripe + } = this.state; + + const { changeCloseBtnLabel } = this.props; + return ( +
+ {paymentType === 'Card' ? ( + + + + + + ) : ( +

+ PayPal is currently unavailable. Please use a Credit/Debit card + instead. +

+ )} +
+ ); + } + + render() { + const { isSignedIn, navigate, showLoading, isDonating } = this.props; + + if (isDonating) { + return ( + + + + + + ); + } + + return ( + + + {!showLoading && !isSignedIn ? ( + + ) : ( + this.renderDonationOptions() + )} + + + ); + } +} + +ModalDonateForm.displayName = 'ModalDonateForm'; +ModalDonateForm.propTypes = propTypes; + +export default connect( + mapStateToProps, + mapDispatchToProps +)(ModalDonateForm); diff --git a/client/src/components/Donation/components/StripeCardForm.js b/client/src/components/Donation/components/StripeCardForm.js index 0f1c1dbd3b..48eda6d9ec 100644 --- a/client/src/components/Donation/components/StripeCardForm.js +++ b/client/src/components/Donation/components/StripeCardForm.js @@ -1,10 +1,6 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { - CardNumberElement, - CardExpiryElement, - CardCVCElement -} from 'react-stripe-elements'; +import { CardNumberElement, CardExpiryElement } from 'react-stripe-elements'; import { ControlLabel, FormGroup } from '@freecodecamp/react-bootstrap'; const propTypes = { @@ -31,10 +27,6 @@ class StripeCardForm extends Component { cardExpiry: { complete: false, error: null - }, - cardCvc: { - complete: false, - error: null } } }; @@ -92,14 +84,6 @@ class StripeCardForm extends Component { style={style} /> - - Your Card CVC (3-digit security number): - - ); } diff --git a/config/donation-settings.js b/config/donation-settings.js index 6d5f860b7c..b68a0a9086 100644 --- a/config/donation-settings.js +++ b/config/donation-settings.js @@ -11,7 +11,7 @@ const amountsConfig = { }; const defaultAmount = { year: 25000, - month: 3500, + month: 500, onetime: 25000 }; const defaultStateConfig = {