fix: add donation ga and tests (#38003)
* fix: add donation ga * feat: add ga-saga tests
This commit is contained in:
@@ -34,6 +34,7 @@ const numToCommas = num =>
|
||||
num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
|
||||
|
||||
const propTypes = {
|
||||
handleProcessing: PropTypes.func,
|
||||
isDonating: PropTypes.bool,
|
||||
isSignedIn: PropTypes.bool,
|
||||
navigate: PropTypes.func.isRequired,
|
||||
@@ -191,7 +192,7 @@ class DonateForm extends Component {
|
||||
}
|
||||
|
||||
renderDonationOptions() {
|
||||
const { stripe } = this.props;
|
||||
const { stripe, handleProcessing } = this.props;
|
||||
const { donationAmount, donationDuration, paymentType } = this.state;
|
||||
return (
|
||||
<div>
|
||||
@@ -203,6 +204,7 @@ class DonateForm extends Component {
|
||||
donationAmount={donationAmount}
|
||||
donationDuration={donationDuration}
|
||||
getDonationButtonLabel={this.getDonationButtonLabel}
|
||||
handleProcessing={handleProcessing}
|
||||
hideAmountOptionsCB={this.hideAmountOptionsCB}
|
||||
/>
|
||||
</Elements>
|
||||
|
@@ -24,6 +24,7 @@ const propTypes = {
|
||||
donationDuration: PropTypes.string.isRequired,
|
||||
email: PropTypes.string,
|
||||
getDonationButtonLabel: PropTypes.func.isRequired,
|
||||
handleProcessing: PropTypes.func,
|
||||
isSignedIn: PropTypes.bool,
|
||||
showCloseBtn: PropTypes.func,
|
||||
stripe: PropTypes.shape({
|
||||
@@ -146,8 +147,11 @@ class DonateFormChildViewForHOC extends Component {
|
||||
|
||||
// change the donation modal button label to close
|
||||
// or display the close button for the cert donation section
|
||||
if (this.props.showCloseBtn) {
|
||||
this.props.showCloseBtn();
|
||||
if (this.props.handleProcessing) {
|
||||
this.props.handleProcessing(
|
||||
this.state.donationDuration,
|
||||
Math.round(this.state.donationAmount / 100)
|
||||
);
|
||||
}
|
||||
|
||||
return postChargeStripe({
|
||||
|
@@ -11,11 +11,11 @@ import Heart from '../../assets/icons/Heart';
|
||||
import Cup from '../../assets/icons/Cup';
|
||||
import MinimalDonateForm from './MinimalDonateForm';
|
||||
|
||||
import ga from '../../analytics';
|
||||
import {
|
||||
closeDonationModal,
|
||||
isDonationModalOpenSelector,
|
||||
isBlockDonationModalSelector
|
||||
isBlockDonationModalSelector,
|
||||
executeGA
|
||||
} from '../../redux';
|
||||
|
||||
import { challengeMetaSelector } from '../../templates/Challenges/redux';
|
||||
@@ -36,7 +36,8 @@ const mapStateToProps = createSelector(
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators(
|
||||
{
|
||||
closeDonationModal
|
||||
closeDonationModal,
|
||||
executeGA
|
||||
},
|
||||
dispatch
|
||||
);
|
||||
@@ -45,18 +46,44 @@ const propTypes = {
|
||||
activeDonors: PropTypes.number,
|
||||
block: PropTypes.string,
|
||||
closeDonationModal: PropTypes.func.isRequired,
|
||||
executeGA: PropTypes.func,
|
||||
isBlockDonation: PropTypes.bool,
|
||||
show: PropTypes.bool
|
||||
};
|
||||
|
||||
function DonateModal({ show, block, isBlockDonation, closeDonationModal }) {
|
||||
function DonateModal({
|
||||
show,
|
||||
block,
|
||||
isBlockDonation,
|
||||
closeDonationModal,
|
||||
executeGA
|
||||
}) {
|
||||
const [closeLabel, setCloseLabel] = React.useState(false);
|
||||
const showCloseBtn = () => {
|
||||
const handleProcessing = (duration, amount) => {
|
||||
executeGA({
|
||||
type: 'event',
|
||||
data: {
|
||||
category: 'donation',
|
||||
action: 'Modal strip form submission',
|
||||
label: duration,
|
||||
value: amount
|
||||
}
|
||||
});
|
||||
setCloseLabel(true);
|
||||
};
|
||||
|
||||
if (show) {
|
||||
ga.modalview('/donation-modal');
|
||||
executeGA({ type: 'modal', data: '/donation-modal' });
|
||||
executeGA({
|
||||
type: 'event',
|
||||
data: {
|
||||
category: 'Donation',
|
||||
action: `Displayed ${
|
||||
isBlockDonation ? 'block' : 'progress'
|
||||
} donation modal`,
|
||||
nonInteraction: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const donationText = (
|
||||
@@ -101,7 +128,7 @@ function DonateModal({ show, block, isBlockDonation, closeDonationModal }) {
|
||||
<Modal.Body>
|
||||
{isBlockDonation ? blockDonationText : progressDonationText}
|
||||
<Spacer />
|
||||
<MinimalDonateForm showCloseBtn={showCloseBtn} />
|
||||
<MinimalDonateForm handleProcessing={handleProcessing} />
|
||||
<Spacer />
|
||||
<Row>
|
||||
<Col sm={4} smOffset={4} xs={8} xsOffset={2}>
|
||||
|
@@ -19,8 +19,8 @@ import './Donation.css';
|
||||
|
||||
const propTypes = {
|
||||
defaultTheme: PropTypes.string,
|
||||
handleProcessing: PropTypes.func,
|
||||
isDonating: PropTypes.bool,
|
||||
showCloseBtn: PropTypes.func,
|
||||
stripe: PropTypes.shape({
|
||||
createToken: PropTypes.func.isRequired
|
||||
})
|
||||
@@ -79,7 +79,7 @@ class MinimalDonateForm extends Component {
|
||||
|
||||
render() {
|
||||
const { donationAmount, donationDuration, stripe } = this.state;
|
||||
const { showCloseBtn, defaultTheme } = this.props;
|
||||
const { handleProcessing, defaultTheme } = this.props;
|
||||
|
||||
return (
|
||||
<Row>
|
||||
@@ -93,7 +93,7 @@ class MinimalDonateForm extends Component {
|
||||
getDonationButtonLabel={() =>
|
||||
`Confirm your donation of $5 per month`
|
||||
}
|
||||
showCloseBtn={showCloseBtn}
|
||||
handleProcessing={handleProcessing}
|
||||
/>
|
||||
</Elements>
|
||||
</StripeProvider>
|
||||
|
@@ -5,9 +5,8 @@ import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { Link } from 'gatsby';
|
||||
|
||||
import ga from '../../../analytics';
|
||||
import { makeExpandedBlockSelector, toggleBlock } from '../redux';
|
||||
import { completedChallengesSelector } from '../../../redux';
|
||||
import { completedChallengesSelector, executeGA } from '../../../redux';
|
||||
import Caret from '../../../assets/icons/Caret';
|
||||
import { blockNameify } from '../../../../utils/blockNameify';
|
||||
import GreenPass from '../../../assets/icons/GreenPass';
|
||||
@@ -29,12 +28,13 @@ const mapStateToProps = (state, ownProps) => {
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators({ toggleBlock }, dispatch);
|
||||
bindActionCreators({ toggleBlock, executeGA }, dispatch);
|
||||
|
||||
const propTypes = {
|
||||
blockDashedName: PropTypes.string,
|
||||
challenges: PropTypes.array,
|
||||
completedChallenges: PropTypes.arrayOf(PropTypes.string),
|
||||
executeGA: PropTypes.func,
|
||||
intro: PropTypes.shape({
|
||||
fields: PropTypes.shape({ slug: PropTypes.string.isRequired }),
|
||||
frontmatter: PropTypes.shape({
|
||||
@@ -58,19 +58,25 @@ export class Block extends Component {
|
||||
}
|
||||
|
||||
handleBlockClick() {
|
||||
const { blockDashedName, toggleBlock } = this.props;
|
||||
ga.event({
|
||||
category: 'Map Block Click',
|
||||
action: blockDashedName
|
||||
const { blockDashedName, toggleBlock, executeGA } = this.props;
|
||||
executeGA({
|
||||
type: 'event',
|
||||
data: {
|
||||
category: 'Map Block Click',
|
||||
action: blockDashedName
|
||||
}
|
||||
});
|
||||
return toggleBlock(blockDashedName);
|
||||
}
|
||||
|
||||
handleChallengeClick(slug) {
|
||||
return () => {
|
||||
return ga.event({
|
||||
category: 'Map Challenge Click',
|
||||
action: slug
|
||||
return this.props.executeGA({
|
||||
type: 'event',
|
||||
data: {
|
||||
category: 'Map Challenge Click',
|
||||
action: slug
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@@ -44,6 +44,7 @@ test('<Block expanded snapshot', () => {
|
||||
test('<Block /> should handle toggle clicks correctly', async () => {
|
||||
const toggleSpy = jest.fn();
|
||||
const toggleMapSpy = jest.fn();
|
||||
const executeGA = jest.fn();
|
||||
|
||||
const props = {
|
||||
blockDashedName: 'block-a',
|
||||
@@ -51,6 +52,7 @@ test('<Block /> should handle toggle clicks correctly', async () => {
|
||||
completedChallenges: mockCompleted,
|
||||
intro: mockIntroNodes[0],
|
||||
isExpanded: false,
|
||||
executeGA: executeGA,
|
||||
toggleBlock: toggleSpy,
|
||||
toggleMapModal: toggleMapSpy
|
||||
};
|
||||
|
@@ -2,8 +2,7 @@ import React, { Fragment, Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import ga from '../../analytics';
|
||||
import { fetchUser, isSignedInSelector } from '../../redux';
|
||||
import { fetchUser, isSignedInSelector, executeGA } from '../../redux';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
const mapStateToProps = createSelector(
|
||||
@@ -13,7 +12,7 @@ const mapStateToProps = createSelector(
|
||||
})
|
||||
);
|
||||
|
||||
const mapDispatchToProps = { fetchUser };
|
||||
const mapDispatchToProps = { fetchUser, executeGA };
|
||||
|
||||
class CertificationLayout extends Component {
|
||||
componentDidMount() {
|
||||
@@ -21,7 +20,7 @@ class CertificationLayout extends Component {
|
||||
if (!isSignedIn) {
|
||||
fetchUser();
|
||||
}
|
||||
ga.pageview(pathname);
|
||||
this.props.executeGA({ type: 'page', data: pathname });
|
||||
}
|
||||
render() {
|
||||
return <Fragment>{this.props.children}</Fragment>;
|
||||
@@ -31,6 +30,7 @@ class CertificationLayout extends Component {
|
||||
CertificationLayout.displayName = 'CertificationLayout';
|
||||
CertificationLayout.propTypes = {
|
||||
children: PropTypes.any,
|
||||
executeGA: PropTypes.func,
|
||||
fetchUser: PropTypes.func.isRequired,
|
||||
isSignedIn: PropTypes.bool,
|
||||
pathname: PropTypes.string.isRequired
|
||||
|
@@ -6,13 +6,13 @@ import { createSelector } from 'reselect';
|
||||
import Helmet from 'react-helmet';
|
||||
import fontawesome from '@fortawesome/fontawesome';
|
||||
|
||||
import ga from '../../analytics';
|
||||
import {
|
||||
fetchUser,
|
||||
isSignedInSelector,
|
||||
onlineStatusChange,
|
||||
isOnlineSelector,
|
||||
userSelector
|
||||
userSelector,
|
||||
executeGA
|
||||
} from '../../redux';
|
||||
import { flashMessageSelector, removeFlashMessage } from '../Flash/redux';
|
||||
|
||||
@@ -68,6 +68,7 @@ const metaKeywords = [
|
||||
|
||||
const propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
executeGA: PropTypes.func,
|
||||
fetchUser: PropTypes.func.isRequired,
|
||||
flashMessage: PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
@@ -101,27 +102,27 @@ const mapStateToProps = createSelector(
|
||||
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators(
|
||||
{ fetchUser, removeFlashMessage, onlineStatusChange },
|
||||
{ fetchUser, removeFlashMessage, onlineStatusChange, executeGA },
|
||||
dispatch
|
||||
);
|
||||
|
||||
class DefaultLayout extends Component {
|
||||
componentDidMount() {
|
||||
const { isSignedIn, fetchUser, pathname } = this.props;
|
||||
const { isSignedIn, fetchUser, pathname, executeGA } = this.props;
|
||||
if (!isSignedIn) {
|
||||
fetchUser();
|
||||
}
|
||||
ga.pageview(pathname);
|
||||
executeGA({ type: 'page', data: pathname });
|
||||
|
||||
window.addEventListener('online', this.updateOnlineStatus);
|
||||
window.addEventListener('offline', this.updateOnlineStatus);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { pathname } = this.props;
|
||||
const { pathname, executeGA } = this.props;
|
||||
const { pathname: prevPathname } = prevProps;
|
||||
if (pathname !== prevPathname) {
|
||||
ga.pageview(pathname);
|
||||
executeGA({ type: 'page', data: pathname });
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user