PluginProbe
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More / 2.3.2
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More v2.3.2
2.4.1 2.4.0 2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.2 2.2.1 2.2.0 2.1.2 2.1.1 trunk 0.0.1 0.0.2 0.0.3 0.0.4 0.0.5 0.0.6 0.0.7 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 All 68 releases
better-payment / assets / js / woocommerce-subscription-total.js

woocommerce-subscription-total.js in Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More 2.3.2, at assets/js/woocommerce-subscription-total.js

149 lines 4.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Better Payment — "Subscription Total" for the WooCommerce Cart and
3 * Checkout BLOCKS.
4 *
5 * Hand-authored (NOT produced by any webpack build — there is no src/
6 * counterpart), matching assets/js/woocommerce-blocks.js.
7 *
8 * The classic cart/checkout render this row in PHP. The blocks render their
9 * totals in React, so there is no PHP hook to render into: the summary
10 * travels as Store API cart data (CartTotals::store_api_payload()) and this
11 * script paints it into the ExperimentalOrderMeta slot, which both the Cart
12 * block and the Checkout block expose below their totals.
13 *
14 * **Every string arrives pre-formatted and pre-translated from PHP.** Nothing
15 * here formats currency, dates or copy — doing so would drift from the
16 * classic row on the same shop, and would have to re-implement a dozen
17 * WooCommerce currency settings plus the site's date format and locale. It is
18 * also why no `value` prop is passed to TotalsItem: that prop takes a number
19 * in minor units and formats it itself.
20 *
21 * Dependencies (registered handle deps): wc-blocks-checkout, wp-element,
22 * wp-plugins.
23 *
24 * > Do not go looking for these exports in `blocks-checkout.js` — that file is
25 * > a ~3KB webpack stub which resolves the real module out of a shared chunk
26 * > (`wc-cart-checkout-base`) and assigns it to `window.wc.blocksCheckout`.
27 * > Grepping the stub for `ExperimentalOrderMeta` finds nothing and looks like
28 * > proof the API is gone. It is not.
29 */
30 ( function () {
31 'use strict';
32
33 const blocksCheckout = window.wc?.blocksCheckout;
34 const element = window.wp?.element;
35 const plugins = window.wp?.plugins;
36
37 const OrderMetaSlot = blocksCheckout?.ExperimentalOrderMeta;
38 const TotalsItem = blocksCheckout?.TotalsItem;
39 const TotalsWrapper = blocksCheckout?.TotalsWrapper;
40
41 // ExperimentalOrderMeta is, as the name says, experimental: a WooCommerce
42 // release can rename or drop it. Bail rather than throw — the shop keeps a
43 // working cart, just without the row. TotalsItem/TotalsWrapper are
44 // degraded to plain markup instead, since they are only presentational.
45 if ( ! OrderMetaSlot || ! element || ! plugins?.registerPlugin ) {
46 return;
47 }
48
49 const NAMESPACE = 'better-payment';
50 const { createElement } = element;
51
52 /**
53 * One small line under the amount.
54 *
55 * @param {string} text Pre-formatted text.
56 * @param {string} key React key.
57 * @param {string} extraCls Optional extra class name.
58 * @return {Object|null} Element, or null when there is nothing to say.
59 */
60 const metaLine = ( text, key, extraCls ) =>
61 text
62 ? createElement(
63 'span',
64 {
65 key,
66 className:
67 'bp-subscription-total__meta' +
68 ( extraCls ? ' ' + extraCls : '' ),
69 },
70 text
71 )
72 : null;
73
74 const SubscriptionTotal = ( { extensions } ) => {
75 const data = extensions?.[ NAMESPACE ];
76
77 // No subscription in the cart — or an older WooCommerce that did not
78 // deliver our extension data at all.
79 if ( ! data?.has_subscription ) {
80 return null;
81 }
82
83 // The small lines that sit under the label/amount pair.
84 const description = createElement(
85 'span',
86 { className: 'bp-subscription-total__lines' },
87 metaLine( data.next_billing, 'next' ),
88 metaLine(
89 data.cancel_note,
90 'cancel',
91 'bp-subscription-total__cancel'
92 )
93 );
94
95 // TotalsItem only renders `value` when it is a React ELEMENT or a
96 // finite number — a bare string renders as nothing at all. The amount
97 // is already formatted server-side, so it has to be wrapped.
98 const amount = createElement(
99 'span',
100 { className: 'bp-subscription-total__amount' },
101 data.amount
102 );
103
104 // Rendering through WooCommerce's own TotalsItem/TotalsWrapper is what
105 // puts this row on the same grid as Subtotal and Total: the label sits
106 // left, the amount right, and the wrapper supplies the separator and
107 // the padding the surrounding rows use. Hand-rolled markup lands
108 // outside that padding and reads as a detached block glued to the
109 // bottom of the summary.
110 if ( ! TotalsItem ) {
111 return createElement(
112 'div',
113 {
114 className:
115 'bp-subscription-total bp-subscription-total--fallback',
116 },
117 createElement(
118 'span',
119 { className: 'bp-subscription-total__label' },
120 data.label
121 ),
122 amount,
123 description
124 );
125 }
126
127 const row = createElement( TotalsItem, {
128 className: 'bp-subscription-total',
129 label: data.label,
130 value: amount,
131 description,
132 } );
133
134 return TotalsWrapper ? createElement( TotalsWrapper, null, row ) : row;
135 };
136
137 plugins.registerPlugin( 'better-payment-subscription-total', {
138 // One fill serves the Cart block AND the Checkout block — the slot is
139 // rendered by both, so the two surfaces cannot drift apart.
140 render: () =>
141 createElement(
142 OrderMetaSlot,
143 null,
144 createElement( SubscriptionTotal )
145 ),
146 scope: 'woocommerce-checkout',
147 } );
148 } )();
149