feat: a/b test lock icon (#45499)
* feat: a/b test * Update client/i18n/locales/english/translations.json * Apply suggestions from code review
This commit is contained in:
@ -52,7 +52,6 @@
|
|||||||
"@reach/router": "1.3.4",
|
"@reach/router": "1.3.4",
|
||||||
"@stripe/react-stripe-js": "1.7.0",
|
"@stripe/react-stripe-js": "1.7.0",
|
||||||
"@stripe/stripe-js": "1.25.0",
|
"@stripe/stripe-js": "1.25.0",
|
||||||
"@types/canvas-confetti": "1.4.2",
|
|
||||||
"@types/react-scrollable-anchor": "0.6.1",
|
"@types/react-scrollable-anchor": "0.6.1",
|
||||||
"algoliasearch": "4.13.0",
|
"algoliasearch": "4.13.0",
|
||||||
"assert": "2.0.0",
|
"assert": "2.0.0",
|
||||||
@ -61,7 +60,6 @@
|
|||||||
"bezier-easing": "2.1.0",
|
"bezier-easing": "2.1.0",
|
||||||
"browser-cookies": "1.2.0",
|
"browser-cookies": "1.2.0",
|
||||||
"buffer": "6.0.3",
|
"buffer": "6.0.3",
|
||||||
"canvas-confetti": "1.5.1",
|
|
||||||
"chai": "4.3.6",
|
"chai": "4.3.6",
|
||||||
"crypto-browserify": "3.12.0",
|
"crypto-browserify": "3.12.0",
|
||||||
"date-fns": "2.27.0",
|
"date-fns": "2.27.0",
|
||||||
|
@ -23,7 +23,8 @@ import {
|
|||||||
defaultDonationFormState,
|
defaultDonationFormState,
|
||||||
userSelector,
|
userSelector,
|
||||||
postChargeStripe,
|
postChargeStripe,
|
||||||
postChargeStripeCard
|
postChargeStripeCard,
|
||||||
|
isAVariantSelector
|
||||||
} from '../../redux';
|
} from '../../redux';
|
||||||
import Spacer from '../helpers/spacer';
|
import Spacer from '../helpers/spacer';
|
||||||
import { Themes } from '../settings/theme';
|
import { Themes } from '../settings/theme';
|
||||||
@ -33,6 +34,7 @@ import type { AddDonationData } from './paypal-button';
|
|||||||
import PaypalButton from './paypal-button';
|
import PaypalButton from './paypal-button';
|
||||||
import StripeCardForm, { HandleAuthentication } from './stripe-card-form';
|
import StripeCardForm, { HandleAuthentication } from './stripe-card-form';
|
||||||
import WalletsWrapper from './walletsButton';
|
import WalletsWrapper from './walletsButton';
|
||||||
|
import SecurityLockIcon from './security-lock-icon';
|
||||||
|
|
||||||
import './donation.css';
|
import './donation.css';
|
||||||
|
|
||||||
@ -78,6 +80,7 @@ type DonateFormProps = {
|
|||||||
) => string;
|
) => string;
|
||||||
theme: Themes;
|
theme: Themes;
|
||||||
updateDonationFormState: (state: AddDonationData) => unknown;
|
updateDonationFormState: (state: AddDonationData) => unknown;
|
||||||
|
isAVariant: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = createSelector(
|
const mapStateToProps = createSelector(
|
||||||
@ -86,19 +89,22 @@ const mapStateToProps = createSelector(
|
|||||||
isDonatingSelector,
|
isDonatingSelector,
|
||||||
donationFormStateSelector,
|
donationFormStateSelector,
|
||||||
userSelector,
|
userSelector,
|
||||||
|
isAVariantSelector,
|
||||||
(
|
(
|
||||||
showLoading: DonateFormProps['showLoading'],
|
showLoading: DonateFormProps['showLoading'],
|
||||||
isSignedIn: DonateFormProps['isSignedIn'],
|
isSignedIn: DonateFormProps['isSignedIn'],
|
||||||
isDonating: DonateFormProps['isDonating'],
|
isDonating: DonateFormProps['isDonating'],
|
||||||
donationFormState: DonateFormState,
|
donationFormState: DonateFormState,
|
||||||
{ email, theme }: { email: string; theme: Themes }
|
{ email, theme }: { email: string; theme: Themes },
|
||||||
|
isAVariant: boolean
|
||||||
) => ({
|
) => ({
|
||||||
isSignedIn,
|
isSignedIn,
|
||||||
isDonating,
|
isDonating,
|
||||||
showLoading,
|
showLoading,
|
||||||
donationFormState,
|
donationFormState,
|
||||||
email,
|
email,
|
||||||
theme
|
theme,
|
||||||
|
isAVariant
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -315,7 +321,8 @@ class DonateForm extends Component<DonateFormProps, DonateFormComponentState> {
|
|||||||
t,
|
t,
|
||||||
isMinimalForm,
|
isMinimalForm,
|
||||||
isSignedIn,
|
isSignedIn,
|
||||||
isDonating
|
isDonating,
|
||||||
|
isAVariant
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const priorityTheme = defaultTheme ? defaultTheme : theme;
|
const priorityTheme = defaultTheme ? defaultTheme : theme;
|
||||||
const isOneTime = donationDuration === 'onetime';
|
const isOneTime = donationDuration === 'onetime';
|
||||||
@ -328,6 +335,7 @@ class DonateForm extends Component<DonateFormProps, DonateFormComponentState> {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<b className={isMinimalForm ? 'donation-label-modal' : ''}>
|
<b className={isMinimalForm ? 'donation-label-modal' : ''}>
|
||||||
|
{isAVariant === false && <SecurityLockIcon />}
|
||||||
{this.getDonationButtonLabel()}:
|
{this.getDonationButtonLabel()}:
|
||||||
</b>
|
</b>
|
||||||
<Spacer />
|
<Spacer />
|
||||||
@ -366,6 +374,7 @@ class DonateForm extends Component<DonateFormProps, DonateFormComponentState> {
|
|||||||
processing={processing}
|
processing={processing}
|
||||||
t={t}
|
t={t}
|
||||||
theme={priorityTheme}
|
theme={priorityTheme}
|
||||||
|
isAVariant={isAVariant}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -334,6 +334,10 @@ li.disabled > a {
|
|||||||
height: 22px;
|
height: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.confirm-donation-btn svg.svg-inline--fa.fa-lock {
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 355px) {
|
@media screen and (min-width: 355px) {
|
||||||
.form-payment-methods {
|
.form-payment-methods {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
|
14
client/src/components/Donation/security-lock-icon.tsx
Normal file
14
client/src/components/Donation/security-lock-icon.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { faLock } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
|
||||||
|
const SecurityLockIcon = (): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<FontAwesomeIcon icon={faLock} />
|
||||||
|
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SecurityLockIcon;
|
@ -17,6 +17,7 @@ import React, { useState } from 'react';
|
|||||||
import envData from '../../../../config/env.json';
|
import envData from '../../../../config/env.json';
|
||||||
import { Themes } from '../settings/theme';
|
import { Themes } from '../settings/theme';
|
||||||
import { AddDonationData } from './paypal-button';
|
import { AddDonationData } from './paypal-button';
|
||||||
|
import SecurityLockIcon from './security-lock-icon';
|
||||||
|
|
||||||
const { stripePublicKey }: { stripePublicKey: string | null } = envData;
|
const { stripePublicKey }: { stripePublicKey: string | null } = envData;
|
||||||
|
|
||||||
@ -34,6 +35,7 @@ interface FormPropTypes {
|
|||||||
t: (label: string) => string;
|
t: (label: string) => string;
|
||||||
theme: Themes;
|
theme: Themes;
|
||||||
processing: boolean;
|
processing: boolean;
|
||||||
|
isAVariant: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Element {
|
interface Element {
|
||||||
@ -49,7 +51,8 @@ const StripeCardForm = ({
|
|||||||
t,
|
t,
|
||||||
onDonationStateChange,
|
onDonationStateChange,
|
||||||
postStripeCardDonation,
|
postStripeCardDonation,
|
||||||
processing
|
processing,
|
||||||
|
isAVariant
|
||||||
}: FormPropTypes): JSX.Element => {
|
}: FormPropTypes): JSX.Element => {
|
||||||
const [isSubmissionValid, setSubmissionValidity] = useState(true);
|
const [isSubmissionValid, setSubmissionValidity] = useState(true);
|
||||||
const [isTokenizing, setTokenizing] = useState(false);
|
const [isTokenizing, setTokenizing] = useState(false);
|
||||||
@ -167,7 +170,8 @@ const StripeCardForm = ({
|
|||||||
disabled={!stripe || !elements || isSubmitting}
|
disabled={!stripe || !elements || isSubmitting}
|
||||||
type='submit'
|
type='submit'
|
||||||
>
|
>
|
||||||
Donate
|
{isAVariant === false && <SecurityLockIcon />}
|
||||||
|
{t('buttons.donate')}
|
||||||
</Button>
|
</Button>
|
||||||
</Form>
|
</Form>
|
||||||
);
|
);
|
||||||
|
@ -8,7 +8,6 @@ import {
|
|||||||
take
|
take
|
||||||
} from 'redux-saga/effects';
|
} from 'redux-saga/effects';
|
||||||
import i18next from 'i18next';
|
import i18next from 'i18next';
|
||||||
import { fireConfetti } from '../utils/fire-confetti';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
addDonation,
|
addDonation,
|
||||||
@ -28,8 +27,7 @@ import {
|
|||||||
postChargeStripeComplete,
|
postChargeStripeComplete,
|
||||||
postChargeStripeError,
|
postChargeStripeError,
|
||||||
postChargeStripeCardComplete,
|
postChargeStripeCardComplete,
|
||||||
postChargeStripeCardError,
|
postChargeStripeCardError
|
||||||
isAVariantSelector
|
|
||||||
} from './';
|
} from './';
|
||||||
|
|
||||||
const defaultDonationErrorMessage = i18next.t('donate.error-2');
|
const defaultDonationErrorMessage = i18next.t('donate.error-2');
|
||||||
@ -40,12 +38,6 @@ function* showDonateModalSaga() {
|
|||||||
yield delay(200);
|
yield delay(200);
|
||||||
const recentlyClaimedBlock = yield select(recentlyClaimedBlockSelector);
|
const recentlyClaimedBlock = yield select(recentlyClaimedBlockSelector);
|
||||||
yield put(openDonationModal());
|
yield put(openDonationModal());
|
||||||
if (recentlyClaimedBlock) {
|
|
||||||
const isAVariant = yield select(isAVariantSelector);
|
|
||||||
if (isAVariant === false) {
|
|
||||||
fireConfetti();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
yield take(appTypes.closeDonationModal);
|
yield take(appTypes.closeDonationModal);
|
||||||
if (recentlyClaimedBlock) {
|
if (recentlyClaimedBlock) {
|
||||||
yield put(preventBlockDonationRequests());
|
yield put(preventBlockDonationRequests());
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
import confetti from 'canvas-confetti';
|
|
||||||
|
|
||||||
export const fireConfetti = () => {
|
|
||||||
const count = 200;
|
|
||||||
const defaults = {
|
|
||||||
origin: { y: 0.7 },
|
|
||||||
zIndex: 10000
|
|
||||||
};
|
|
||||||
|
|
||||||
function fire(
|
|
||||||
particleRatio: number,
|
|
||||||
opts: {
|
|
||||||
spread?: number;
|
|
||||||
startVelocity?: number;
|
|
||||||
decay?: number;
|
|
||||||
scalar?: number;
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
confetti(
|
|
||||||
Object.assign({}, defaults, opts, {
|
|
||||||
particleCount: Math.floor(count * particleRatio)
|
|
||||||
})
|
|
||||||
)?.catch(err => console.log(err));
|
|
||||||
}
|
|
||||||
|
|
||||||
fire(0.25, {
|
|
||||||
spread: 26,
|
|
||||||
startVelocity: 55
|
|
||||||
});
|
|
||||||
fire(0.2, {
|
|
||||||
spread: 60
|
|
||||||
});
|
|
||||||
fire(0.35, {
|
|
||||||
spread: 100,
|
|
||||||
decay: 0.91,
|
|
||||||
scalar: 0.8
|
|
||||||
});
|
|
||||||
fire(0.1, {
|
|
||||||
spread: 120,
|
|
||||||
startVelocity: 25,
|
|
||||||
decay: 0.92,
|
|
||||||
scalar: 1.2
|
|
||||||
});
|
|
||||||
fire(0.1, {
|
|
||||||
spread: 120,
|
|
||||||
startVelocity: 45
|
|
||||||
});
|
|
||||||
};
|
|
@ -94,7 +94,7 @@ const patreonDefaultPledgeAmount = 500;
|
|||||||
|
|
||||||
const aBTestConfig = {
|
const aBTestConfig = {
|
||||||
isTesting: true,
|
isTesting: true,
|
||||||
type: 'badgeProgressModalCopy'
|
type: 'addSecurityLock'
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
28
package-lock.json
generated
28
package-lock.json
generated
@ -985,7 +985,6 @@
|
|||||||
"@reach/router": "1.3.4",
|
"@reach/router": "1.3.4",
|
||||||
"@stripe/react-stripe-js": "1.7.0",
|
"@stripe/react-stripe-js": "1.7.0",
|
||||||
"@stripe/stripe-js": "1.25.0",
|
"@stripe/stripe-js": "1.25.0",
|
||||||
"@types/canvas-confetti": "1.4.2",
|
|
||||||
"@types/react-scrollable-anchor": "0.6.1",
|
"@types/react-scrollable-anchor": "0.6.1",
|
||||||
"algoliasearch": "4.13.0",
|
"algoliasearch": "4.13.0",
|
||||||
"assert": "2.0.0",
|
"assert": "2.0.0",
|
||||||
@ -994,7 +993,6 @@
|
|||||||
"bezier-easing": "2.1.0",
|
"bezier-easing": "2.1.0",
|
||||||
"browser-cookies": "1.2.0",
|
"browser-cookies": "1.2.0",
|
||||||
"buffer": "6.0.3",
|
"buffer": "6.0.3",
|
||||||
"canvas-confetti": "1.5.1",
|
|
||||||
"chai": "4.3.6",
|
"chai": "4.3.6",
|
||||||
"crypto-browserify": "3.12.0",
|
"crypto-browserify": "3.12.0",
|
||||||
"date-fns": "2.27.0",
|
"date-fns": "2.27.0",
|
||||||
@ -15173,11 +15171,6 @@
|
|||||||
"@types/responselike": "*"
|
"@types/responselike": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/canvas-confetti": {
|
|
||||||
"version": "1.4.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/canvas-confetti/-/canvas-confetti-1.4.2.tgz",
|
|
||||||
"integrity": "sha512-t45KUDHlwrD9PJVRHc5z1SlXhO82BQEgMKUXGEV1KnWLFMPA6Y5LfUsLTHHzH9KcKDHZLEiYYH5nIDcjRKWNTg=="
|
|
||||||
},
|
|
||||||
"node_modules/@types/chai": {
|
"node_modules/@types/chai": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz",
|
||||||
@ -20421,15 +20414,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/canonical-json/-/canonical-json-0.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/canonical-json/-/canonical-json-0.0.4.tgz",
|
||||||
"integrity": "sha1-ZXnAcsPbXEd+xB3JePvyuPQQdKM="
|
"integrity": "sha1-ZXnAcsPbXEd+xB3JePvyuPQQdKM="
|
||||||
},
|
},
|
||||||
"node_modules/canvas-confetti": {
|
|
||||||
"version": "1.5.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/canvas-confetti/-/canvas-confetti-1.5.1.tgz",
|
|
||||||
"integrity": "sha512-Ncz+oZJP6OvY7ti4E1slxVlyAV/3g7H7oQtcCDXgwGgARxPnwYY9PW5Oe+I8uvspYNtuHviAdgA0LfcKFWJfpg==",
|
|
||||||
"funding": {
|
|
||||||
"type": "donate",
|
|
||||||
"url": "https://www.paypal.me/kirilvatev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/capture-exit": {
|
"node_modules/capture-exit": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz",
|
||||||
@ -56530,7 +56514,6 @@
|
|||||||
"@stripe/stripe-js": "1.25.0",
|
"@stripe/stripe-js": "1.25.0",
|
||||||
"@testing-library/jest-dom": "5.16.2",
|
"@testing-library/jest-dom": "5.16.2",
|
||||||
"@testing-library/react": "12.1.4",
|
"@testing-library/react": "12.1.4",
|
||||||
"@types/canvas-confetti": "1.4.2",
|
|
||||||
"@types/react-scrollable-anchor": "0.6.1",
|
"@types/react-scrollable-anchor": "0.6.1",
|
||||||
"algoliasearch": "4.13.0",
|
"algoliasearch": "4.13.0",
|
||||||
"assert": "2.0.0",
|
"assert": "2.0.0",
|
||||||
@ -56541,7 +56524,6 @@
|
|||||||
"bezier-easing": "2.1.0",
|
"bezier-easing": "2.1.0",
|
||||||
"browser-cookies": "1.2.0",
|
"browser-cookies": "1.2.0",
|
||||||
"buffer": "6.0.3",
|
"buffer": "6.0.3",
|
||||||
"canvas-confetti": "1.5.1",
|
|
||||||
"chai": "4.3.6",
|
"chai": "4.3.6",
|
||||||
"chokidar": "3.5.3",
|
"chokidar": "3.5.3",
|
||||||
"copy-webpack-plugin": "9.1.0",
|
"copy-webpack-plugin": "9.1.0",
|
||||||
@ -65612,11 +65594,6 @@
|
|||||||
"@types/responselike": "*"
|
"@types/responselike": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/canvas-confetti": {
|
|
||||||
"version": "1.4.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/canvas-confetti/-/canvas-confetti-1.4.2.tgz",
|
|
||||||
"integrity": "sha512-t45KUDHlwrD9PJVRHc5z1SlXhO82BQEgMKUXGEV1KnWLFMPA6Y5LfUsLTHHzH9KcKDHZLEiYYH5nIDcjRKWNTg=="
|
|
||||||
},
|
|
||||||
"@types/chai": {
|
"@types/chai": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz",
|
||||||
@ -70080,11 +70057,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/canonical-json/-/canonical-json-0.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/canonical-json/-/canonical-json-0.0.4.tgz",
|
||||||
"integrity": "sha1-ZXnAcsPbXEd+xB3JePvyuPQQdKM="
|
"integrity": "sha1-ZXnAcsPbXEd+xB3JePvyuPQQdKM="
|
||||||
},
|
},
|
||||||
"canvas-confetti": {
|
|
||||||
"version": "1.5.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/canvas-confetti/-/canvas-confetti-1.5.1.tgz",
|
|
||||||
"integrity": "sha512-Ncz+oZJP6OvY7ti4E1slxVlyAV/3g7H7oQtcCDXgwGgARxPnwYY9PW5Oe+I8uvspYNtuHviAdgA0LfcKFWJfpg=="
|
|
||||||
},
|
|
||||||
"capture-exit": {
|
"capture-exit": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz",
|
||||||
|
Reference in New Issue
Block a user