diff --git a/apps/full-site-editing/full-site-editing-plugin/donations/context.js b/apps/full-site-editing/full-site-editing-plugin/donations/context.js
new file mode 100644
index 00000000000000..7a0e02fc466829
--- /dev/null
+++ b/apps/full-site-editing/full-site-editing-plugin/donations/context.js
@@ -0,0 +1,11 @@
+/**
+ * WordPress dependencies
+ */
+// eslint-disable-next-line wpcalypso/import-docblock
+import { createContext } from '@wordpress/element';
+
+const Context = createContext( {
+ activeTab: 'one-time',
+} );
+
+export default Context;
diff --git a/apps/full-site-editing/full-site-editing-plugin/donations/controls.js b/apps/full-site-editing/full-site-editing-plugin/donations/controls.js
new file mode 100644
index 00000000000000..53054049a74bbd
--- /dev/null
+++ b/apps/full-site-editing/full-site-editing-plugin/donations/controls.js
@@ -0,0 +1,42 @@
+/**
+ * WordPress dependencies
+ */
+// eslint-disable-next-line wpcalypso/import-docblock
+import { ExternalLink, PanelBody, ToggleControl } from '@wordpress/components';
+import { __ } from '@wordpress/i18n';
+import { InspectorControls } from '@wordpress/block-editor';
+
+const Controls = ( props ) => {
+ const { attributes, setAttributes, products, siteSlug } = props;
+ const { monthlyPlanId, annuallyPlanId, showCustomAmount } = attributes;
+ return (
+
+
+
+ setAttributes( { monthlyPlanId: value ? products[ '1 month' ] : null } )
+ }
+ label={ __( 'Show monthly donations', 'full-site-editing' ) }
+ />
+
+ setAttributes( { annuallyPlanId: value ? products[ '1 year' ] : null } )
+ }
+ label={ __( 'Show annual donations', 'full-site-editing' ) }
+ />
+ setAttributes( { showCustomAmount: value } ) }
+ label={ __( 'Show custom amount option', 'full-site-editing' ) }
+ />
+
+ { __( 'View donation earnings', 'full-site-editing' ) }
+
+
+
+ );
+};
+
+export default Controls;
diff --git a/apps/full-site-editing/full-site-editing-plugin/donations/donations.js b/apps/full-site-editing/full-site-editing-plugin/donations/donations.js
deleted file mode 100644
index 77733c928c91a8..00000000000000
--- a/apps/full-site-editing/full-site-editing-plugin/donations/donations.js
+++ /dev/null
@@ -1,110 +0,0 @@
-/**
- * External dependencies
- */
-import classNames from 'classnames';
-
-/**
- * WordPress dependencies
- */
-import { InspectorControls, __experimentalBlock as Block } from '@wordpress/block-editor';
-import { Button, ExternalLink, PanelBody, ToggleControl } from '@wordpress/components';
-import { useEffect, useState } from '@wordpress/element';
-import { __ } from '@wordpress/i18n';
-
-/**
- * Internal dependencies
- */
-import StripeNudge from './stripe-nudge';
-
-const Donations = ( { attributes, products, setAttributes, siteSlug, stripeConnectUrl } ) => {
- const { oneTimePlanId, monthlyPlanId, annuallyPlanId, showCustomAmount } = attributes;
- const [ activeTab, setActiveTab ] = useState( 'one-time' );
-
- const isTabActive = ( tab ) => activeTab === tab;
-
- useEffect( () => {
- if ( ! oneTimePlanId ) {
- setAttributes( { oneTimePlanId: products[ 'one-time' ] } );
- }
- }, [ oneTimePlanId ] );
-
- return (
-
- { stripeConnectUrl && }
-
- { ( monthlyPlanId || annuallyPlanId ) && (
-
-
- { monthlyPlanId && (
-
- ) }
- { annuallyPlanId && (
-
- ) }
-
- ) }
-
-
- { isTabActive( 'one-time' ) && __( 'Make a one-time donation', 'full-site-editing' ) }
- { isTabActive( 'monthly' ) && __( 'Make a monthly donation', 'full-site-editing' ) }
- { isTabActive( 'annually' ) && __( 'Make a yearly donation', 'full-site-editing' ) }
-
- { showCustomAmount && (
-
{ __( 'Or enter a custom amount', 'full-site-editing' ) }
- ) }
-
-
-
-
- {
- setAttributes( {
- monthlyPlanId: showMonthlyDonations ? products[ '1 month' ] : null,
- } );
- } }
- label={ __( 'Show monthly donations', 'full-site-editing' ) }
- />
- {
- setAttributes( {
- annuallyPlanId: showAnnuallyDonations ? products[ '1 year' ] : null,
- } );
- } }
- label={ __( 'Show annual donations', 'full-site-editing' ) }
- />
- {
- setAttributes( {
- showCustomAmount: newShowCustomAmountValue,
- } );
- } }
- label={ __( 'Show custom amount option', 'full-site-editing' ) }
- />
-
- { __( 'View donation earnings', 'full-site-editing' ) }
-
-
-
-
- );
-};
-
-export default Donations;
diff --git a/apps/full-site-editing/full-site-editing-plugin/donations/edit.js b/apps/full-site-editing/full-site-editing-plugin/donations/edit.js
index 6f1874bd30b80e..813de56d8d95ec 100644
--- a/apps/full-site-editing/full-site-editing-plugin/donations/edit.js
+++ b/apps/full-site-editing/full-site-editing-plugin/donations/edit.js
@@ -7,14 +7,17 @@ import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
-import Donations from './donations';
+import Tabs from './tabs';
import LoadingError from './loading-error';
import LoadingStatus from './loading-status';
import UpgradePlan from './upgrade-plan';
import fetchDefaultProducts from './fetch-default-products';
import fetchStatus from './fetch-status';
-const Edit = ( { attributes, setAttributes } ) => {
+const Edit = ( props ) => {
+ const { attributes } = props;
+ const { currency } = attributes;
+
const [ isLoading, setIsLoading ] = useState( true );
const [ loadingError, setLoadingError ] = useState( '' );
const [ shouldUpgrade, setShouldUpgrade ] = useState( false );
@@ -28,14 +31,13 @@ const Edit = ( { attributes, setAttributes } ) => {
setIsLoading( false );
};
- const filterProducts = ( productList, productCurrency ) => {
- return productList.reduce( ( filteredProducts, { id, currency, type, interval } ) => {
- if ( currency === productCurrency && type === 'donation' ) {
+ const filterProducts = ( productList ) =>
+ productList.reduce( ( filteredProducts, { id, currency: productCurrency, type, interval } ) => {
+ if ( productCurrency === currency && type === 'donation' ) {
filteredProducts[ interval ] = id;
}
return filteredProducts;
}, {} );
- };
const hasRequiredProducts = ( productIdsPerInterval ) => {
const intervals = Object.keys( productIdsPerInterval );
@@ -56,13 +58,13 @@ const Edit = ( { attributes, setAttributes } ) => {
setStripeConnectUrl( result.connect_url );
setSiteSlug( result.site_slug );
- const filteredProducts = filterProducts( result.products, 'USD' );
+ const filteredProducts = filterProducts( result.products );
if ( hasRequiredProducts( filteredProducts ) ) {
setProducts( filteredProducts );
} else {
- fetchDefaultProducts( 'USD' ).then(
- ( defaultProducts ) => setProducts( filterProducts( defaultProducts, 'USD' ) ),
+ fetchDefaultProducts( currency ).then(
+ ( defaultProducts ) => setProducts( filterProducts( defaultProducts ) ),
apiError
);
}
@@ -89,11 +91,10 @@ const Edit = ( { attributes, setAttributes } ) => {
}
return (
-
);
diff --git a/apps/full-site-editing/full-site-editing-plugin/donations/index.js b/apps/full-site-editing/full-site-editing-plugin/donations/index.js
index eec152e91022af..bad1600fc0627c 100644
--- a/apps/full-site-editing/full-site-editing-plugin/donations/index.js
+++ b/apps/full-site-editing/full-site-editing-plugin/donations/index.js
@@ -4,14 +4,106 @@
import apiFetch from '@wordpress/api-fetch';
import { getBlockType, registerBlockType, unregisterBlockType } from '@wordpress/blocks';
import { __, _x } from '@wordpress/i18n';
-/* eslint-enable wpcalypso/import-docblock */
/**
* Internal dependencies
*/
import edit from './edit';
+
+/**
+ * Style dependencies
+ */
import './style.scss';
+const name = 'a8c/donations';
+
+const settings = {
+ title: __( 'Donations (a8c-only)', 'full-site-editing' ),
+ description: __(
+ 'Collect one-time, monthly, or annually recurring donations.',
+ 'full-site-editing'
+ ),
+ attributes: {
+ currency: {
+ type: 'string',
+ default: 'USD',
+ },
+ oneTimePlanId: {
+ type: 'number',
+ default: null,
+ },
+ monthlyPlanId: {
+ type: 'number',
+ default: null,
+ },
+ annuallyPlanId: {
+ type: 'number',
+ default: null,
+ },
+ showCustomAmount: {
+ type: 'boolean',
+ default: true,
+ },
+ oneTimeHeading: {
+ type: 'string',
+ default: __( 'Make a one-time donation', 'full-site-editing' ),
+ },
+ monthlyHeading: {
+ type: 'string',
+ default: __( 'Make a monthly donation', 'full-site-editing' ),
+ },
+ annualHeading: {
+ type: 'string',
+ default: __( 'Make a yearly donation', 'full-site-editing' ),
+ },
+ chooseAmountText: {
+ type: 'string',
+ default: __( 'Choose an amount (USD)', 'full-site-editing' ),
+ },
+ customAmountText: {
+ type: 'string',
+ default: __( 'Or enter a custom amount', 'full-site-editing' ),
+ },
+ extraText: {
+ type: 'string',
+ default: __( 'Your contribution is appreciated.', 'full-site-editing' ),
+ },
+ oneTimeButtonText: {
+ type: 'string',
+ default: __( 'Donate', 'full-site-editing' ),
+ },
+ monthlyButtonText: {
+ type: 'string',
+ default: __( 'Donate monthly', 'full-site-editing' ),
+ },
+ annualButtonText: {
+ type: 'string',
+ default: __( 'Donate yearly', 'full-site-editing' ),
+ },
+ },
+ category: 'common',
+ icon: (
+
+ ),
+ supports: {
+ html: false,
+ },
+ edit,
+ save: () => null,
+};
+
/**
* Appends a "paid" tag to the Donations block title if site requires an upgrade.
*/
@@ -25,7 +117,7 @@ const addPaidBlockFlags = async () => {
}
const shouldUpgrade = membershipsStatus.should_upgrade_to_access_memberships;
if ( shouldUpgrade ) {
- const donationsBlock = getBlockType( 'a8c/donations' );
+ const donationsBlock = getBlockType( name );
if ( ! donationsBlock ) {
return;
}
@@ -36,8 +128,8 @@ const addPaidBlockFlags = async () => {
'full-site-editing'
);
- unregisterBlockType( 'a8c/donations' );
- registerBlockType( 'a8c/donations', {
+ unregisterBlockType( name );
+ registerBlockType( name, {
...donationsBlock,
title: `${ donationsBlock.title } (${ paidFlag })`,
} );
@@ -45,67 +137,7 @@ const addPaidBlockFlags = async () => {
};
function registerDonationsBlock() {
- registerBlockType( 'a8c/donations', {
- title: __( 'Donations (a8c-only)', 'full-site-editing' ),
- description: __(
- 'Collect one-time, monthly, or annually recurring donations.',
- 'full-site-editing'
- ),
- attributes: {
- oneTimePlanId: {
- type: 'number',
- default: null,
- },
- monthlyPlanId: {
- type: 'number',
- default: null,
- },
- annuallyPlanId: {
- type: 'number',
- default: null,
- },
- showCustomAmount: {
- type: 'boolean',
- default: false,
- },
- },
- category: 'common',
- icon: (
-
- ),
- supports: {
- html: false,
- },
- edit,
- save() {
- return
Donations block
;
- },
- } );
+ registerBlockType( name, settings );
// Done after registration so the status API request doesn't suspend the execution.
addPaidBlockFlags();
diff --git a/apps/full-site-editing/full-site-editing-plugin/donations/stripe-nudge.js b/apps/full-site-editing/full-site-editing-plugin/donations/stripe-nudge.js
index f25bdd17663921..d7fe7216b90588 100644
--- a/apps/full-site-editing/full-site-editing-plugin/donations/stripe-nudge.js
+++ b/apps/full-site-editing/full-site-editing-plugin/donations/stripe-nudge.js
@@ -1,20 +1,13 @@
-/**
- * External dependencies
- */
/**
* WordPress dependencies
*/
-// eslint-disable-next-line no-restricted-imports
+// eslint-disable-next-line wpcalypso/import-docblock,no-restricted-imports
import { Button, Dashicon } from '@wordpress/components';
import { Warning } from '@wordpress/block-editor';
import { compose } from '@wordpress/compose';
import { __ } from '@wordpress/i18n';
import { withDispatch } from '@wordpress/data';
-/**
- * Internal dependencies
- */
-
const StripeNudge = ( { autosaveAndRedirect, stripeConnectUrl } ) => (
{
+ const { attributes, interval, setAttributes } = props;
+
+ const getAttribute = ( attributeName ) => {
+ if ( attributeName in attributesPerInterval ) {
+ return attributes[ attributesPerInterval[ attributeName ][ interval ] ];
+ }
+ return attributes[ attributeName ];
+ };
+
+ const setAttribute = ( attributeName, value ) => {
+ if ( attributeName in attributesPerInterval ) {
+ return setAttributes( {
+ [ attributesPerInterval[ attributeName ][ interval ] ]: value,
+ } );
+ }
+ return setAttributes( { [ attributeName ]: value } );
+ };
+ const currency = getAttribute( 'currency' );
+
+ const minAmount = minimumTransactionAmountForCurrency( currency );
+ // TODO: This generates good amounts for USD, but let's revisit once we support more currencies.
+ const tiers = [
+ minAmount * 10, // USD 5
+ minAmount * 30, // USD 15
+ minAmount * 200, // USD 100
+ ];
+ const customAmountPlaceholder = minAmount * 100; // USD 50
+
+ return (
+
+ { ( { activeTab } ) => (
+
+
setAttribute( 'heading', value ) }
+ inlineToolbar
+ />
+ setAttribute( 'chooseAmountText', value ) }
+ inlineToolbar
+ />
+
+ { tiers.map( ( amount ) => (
+
+
{ formatCurrency( amount, currency ) }
+
+ ) ) }
+
+ { getAttribute( 'showCustomAmount' ) && (
+ <>
+ setAttribute( 'customAmountText', value ) }
+ inlineToolbar
+ />
+
+
+ { CURRENCIES[ currency ].symbol }
+
+ { formatCurrency( customAmountPlaceholder, currency, { symbol: '' } ) }
+
+
+
+ >
+ ) }
+ ——
+ setAttribute( 'extraText', value ) }
+ inlineToolbar
+ />
+ setAttribute( 'buttonText', value ) }
+ inlineToolbar
+ />
+
+ ) }
+
+ );
+};
+
+export default Tab;
diff --git a/apps/full-site-editing/full-site-editing-plugin/donations/tabs.js b/apps/full-site-editing/full-site-editing-plugin/donations/tabs.js
new file mode 100644
index 00000000000000..864c45f124285d
--- /dev/null
+++ b/apps/full-site-editing/full-site-editing-plugin/donations/tabs.js
@@ -0,0 +1,88 @@
+/**
+ * External dependencies
+ */
+import classNames from 'classnames';
+
+/**
+ * WordPress dependencies
+ */
+import { __experimentalBlock as Block } from '@wordpress/block-editor';
+import { Button } from '@wordpress/components';
+import { useEffect, useState } from '@wordpress/element';
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import Context from './context';
+import Controls from './controls';
+import Tab from './tab';
+import StripeNudge from './stripe-nudge';
+
+const Tabs = ( props ) => {
+ const { attributes, products, setAttributes, stripeConnectUrl } = props;
+ const { oneTimePlanId, monthlyPlanId, annuallyPlanId } = attributes;
+ const [ activeTab, setActiveTab ] = useState( 'one-time' );
+
+ const isTabActive = ( tab ) => activeTab === tab;
+
+ const tabs = {
+ 'one-time': { title: __( 'One-Time', 'full-site-editing' ) },
+ ...( monthlyPlanId && { '1 month': { title: __( 'Monthly', 'full-site-editing' ) } } ),
+ ...( annuallyPlanId && { '1 year': { title: __( 'Yearly', 'full-site-editing' ) } } ),
+ };
+
+ // Sets the plans when the block is inserted.
+ useEffect( () => {
+ // Since there is no setting for disabling the one-time option, we can assume that the block has been just
+ // inserted if the attribute `oneTimePlanId` is not set.
+ if ( ! oneTimePlanId ) {
+ setAttributes( {
+ oneTimePlanId: products[ 'one-time' ],
+ monthlyPlanId: products[ '1 month' ],
+ annuallyPlanId: products[ '1 year' ],
+ } );
+ }
+ }, [ oneTimePlanId ] );
+
+ // Activates the one-time tab if the interval of the current active tab is disabled.
+ useEffect( () => {
+ if ( ! monthlyPlanId && isTabActive( '1 month' ) ) {
+ setActiveTab( 'one-time' );
+ }
+
+ if ( ! annuallyPlanId && isTabActive( '1 year' ) ) {
+ setActiveTab( 'one-time' );
+ }
+ }, [ monthlyPlanId, annuallyPlanId ] );
+
+ return (
+
+ { stripeConnectUrl && }
+
+ { Object.keys( tabs ).length > 1 && (
+
+ { Object.entries( tabs ).map( ( [ interval, { title } ] ) => (
+
+ ) ) }
+
+ ) }
+
+
+ { Object.keys( tabs ).map( ( interval ) => (
+
+ ) ) }
+
+
+
+
+
+ );
+};
+
+export default Tabs;