@automattic/newspack-blocks 4.28.0 → 4.29.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/blocks/checkout-button/block.json +3 -0
- package/dist/editor-rtl.css +16 -13
- package/dist/editor.asset.php +1 -1
- package/dist/editor.css +16 -13
- package/dist/editor.js +27 -25
- package/dist/modal.asset.php +1 -1
- package/dist/modal.js +1 -1
- package/dist/modalCheckout-rtl.css +1 -1
- package/dist/modalCheckout.asset.php +1 -1
- package/dist/modalCheckout.css +1 -1
- package/dist/modalCheckout.js +1 -1
- package/includes/class-modal-checkout.php +131 -168
- package/includes/class-newspack-blocks.php +1 -0
- package/includes/modal-checkout/class-checkout-data.php +23 -0
- package/newspack-blocks.php +2 -2
- package/package.json +5 -5
- package/src/blocks/checkout-button/block.json +3 -0
- package/src/blocks/checkout-button/coupon-control.js +141 -0
- package/src/blocks/checkout-button/edit.js +6 -2
- package/src/blocks/checkout-button/edit.scss +3 -2
- package/src/blocks/checkout-button/view.php +9 -2
- package/src/blocks/donate/frontend/class-newspack-blocks-donate-renderer-frequency-based.php +2 -2
- package/src/blocks/donate/frontend/class-newspack-blocks-donate-renderer-tiers-based.php +1 -1
- package/src/modal-checkout/checkout.scss +1 -53
- package/src/modal-checkout/index.js +7 -89
- package/src/modal-checkout/modal.js +0 -12
- package/src/modal-checkout/templates/form-checkout.php +10 -20
- package/src/modal-checkout/templates/form-coupon.php +9 -0
- package/src/modal-checkout/templates/thankyou.php +1 -1
- package/vendor/composer/installed.php +2 -2
- package/webpack.config.js +6 -12
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { debounce } from 'lodash';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* WordPress dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { __ } from '@wordpress/i18n';
|
|
10
|
+
import { BaseControl, TextControl, FormTokenField, Button, Spinner } from '@wordpress/components';
|
|
11
|
+
import { useState, useEffect, useMemo } from '@wordpress/element';
|
|
12
|
+
import apiFetch from '@wordpress/api-fetch';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Searchable WooCommerce coupon picker for the Checkout Button block.
|
|
16
|
+
*
|
|
17
|
+
* Type to search existing coupons; selecting one stores its code in the block's
|
|
18
|
+
* `coupon` attribute, clearing it removes the coupon. Mirrors the product picker.
|
|
19
|
+
*
|
|
20
|
+
* @param {Object} props
|
|
21
|
+
* @param {string} props.value Currently selected coupon code.
|
|
22
|
+
* @param {Function} props.onChange Called with the selected coupon code (or '').
|
|
23
|
+
* @return {JSX.Element} The control.
|
|
24
|
+
*/
|
|
25
|
+
export default function CouponControl( { value, onChange } ) {
|
|
26
|
+
const [ inFlight, setInFlight ] = useState( false );
|
|
27
|
+
const [ suggestions, setSuggestions ] = useState( [] );
|
|
28
|
+
const [ selected, setSelected ] = useState( false );
|
|
29
|
+
const [ isChanging, setIsChanging ] = useState( false );
|
|
30
|
+
|
|
31
|
+
function fetchSuggestions( search ) {
|
|
32
|
+
setInFlight( true );
|
|
33
|
+
return apiFetch( {
|
|
34
|
+
path: `/wc/v3/coupons?search=${ encodeURIComponent( search ) }`,
|
|
35
|
+
} )
|
|
36
|
+
.then( coupons => {
|
|
37
|
+
setSuggestions( coupons.map( coupon => coupon.code ) );
|
|
38
|
+
} )
|
|
39
|
+
.finally( () => setInFlight( false ) );
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function fetchSaved() {
|
|
43
|
+
setInFlight( true );
|
|
44
|
+
return apiFetch( {
|
|
45
|
+
path: `/wc/v3/coupons?code=${ encodeURIComponent( value ) }`,
|
|
46
|
+
} )
|
|
47
|
+
.then( coupons => {
|
|
48
|
+
// Keep the stored value even if the lookup returns nothing.
|
|
49
|
+
setSelected( coupons.length ? coupons[ 0 ].code : value );
|
|
50
|
+
} )
|
|
51
|
+
.catch( () => setSelected( value ) )
|
|
52
|
+
.finally( () => setInFlight( false ) );
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
useEffect( () => {
|
|
56
|
+
setIsChanging( false );
|
|
57
|
+
if ( value ) {
|
|
58
|
+
fetchSaved();
|
|
59
|
+
} else {
|
|
60
|
+
setSelected( false );
|
|
61
|
+
}
|
|
62
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
63
|
+
}, [ value ] );
|
|
64
|
+
|
|
65
|
+
function onTokenChange( tokens ) {
|
|
66
|
+
setIsChanging( false );
|
|
67
|
+
onChange( tokens[ 0 ] || '' );
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const debouncedFetch = useMemo(
|
|
71
|
+
() => debounce( fetchSuggestions, 200 ),
|
|
72
|
+
// fetchSuggestions only closes over stable state setters, so an empty
|
|
73
|
+
// dependency array is intentional.
|
|
74
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
75
|
+
[]
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// Cancel a pending debounced fetch if the control unmounts, so it can't
|
|
79
|
+
// fire and update state after the component is gone.
|
|
80
|
+
useEffect( () => () => debouncedFetch.cancel(), [ debouncedFetch ] );
|
|
81
|
+
|
|
82
|
+
function handleInputChange( search ) {
|
|
83
|
+
if ( search.length > 2 ) {
|
|
84
|
+
setInFlight( true );
|
|
85
|
+
debouncedFetch( search );
|
|
86
|
+
} else {
|
|
87
|
+
// Cancel any fetch queued for a longer previous query so it can't
|
|
88
|
+
// fire after the input was shortened or cleared.
|
|
89
|
+
debouncedFetch.cancel();
|
|
90
|
+
setInFlight( false );
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if ( value && ! selected && inFlight ) {
|
|
95
|
+
return <Spinner />;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if ( selected && ! isChanging ) {
|
|
99
|
+
return (
|
|
100
|
+
<div className="newspack-checkout-button__coupon-field">
|
|
101
|
+
<BaseControl label={ __( 'Coupon', 'newspack-blocks' ) } id="newspack-checkout-button-coupon">
|
|
102
|
+
<TextControl value={ selected } __next40pxDefaultSize disabled />
|
|
103
|
+
<Button variant="link" onClick={ () => setIsChanging( true ) }>
|
|
104
|
+
{ __( 'Edit', 'newspack-blocks' ) }
|
|
105
|
+
</Button>{ ' ' }
|
|
106
|
+
<Button
|
|
107
|
+
variant="link"
|
|
108
|
+
isDestructive
|
|
109
|
+
onClick={ () => {
|
|
110
|
+
setSelected( false );
|
|
111
|
+
onChange( '' );
|
|
112
|
+
} }
|
|
113
|
+
>
|
|
114
|
+
{ __( 'Remove', 'newspack-blocks' ) }
|
|
115
|
+
</Button>
|
|
116
|
+
</BaseControl>
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<div className="newspack-checkout-button__coupon-field">
|
|
123
|
+
<FormTokenField
|
|
124
|
+
placeholder={ __( 'Type to search for a coupon…', 'newspack-blocks' ) }
|
|
125
|
+
label={ __( 'Coupon', 'newspack-blocks' ) }
|
|
126
|
+
maxLength={ 1 }
|
|
127
|
+
value={ [] }
|
|
128
|
+
onChange={ onTokenChange }
|
|
129
|
+
onInputChange={ handleInputChange }
|
|
130
|
+
suggestions={ suggestions }
|
|
131
|
+
__next40pxDefaultSize
|
|
132
|
+
/>
|
|
133
|
+
{ inFlight && <Spinner /> }
|
|
134
|
+
{ selected && (
|
|
135
|
+
<Button variant="link" onClick={ () => setIsChanging( false ) }>
|
|
136
|
+
{ __( 'Cancel', 'newspack-blocks' ) }
|
|
137
|
+
</Button>
|
|
138
|
+
) }
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
@@ -41,6 +41,7 @@ import apiFetch from '@wordpress/api-fetch';
|
|
|
41
41
|
*/
|
|
42
42
|
import './edit.scss';
|
|
43
43
|
import RedirectAfterSuccess from '../../components/redirect-after-success';
|
|
44
|
+
import CouponControl from './coupon-control';
|
|
44
45
|
|
|
45
46
|
function getVariationName( variation ) {
|
|
46
47
|
const attributes = [];
|
|
@@ -158,7 +159,7 @@ function ProductControl( props ) {
|
|
|
158
159
|
return <Spinner />;
|
|
159
160
|
}
|
|
160
161
|
return (
|
|
161
|
-
<div className="newspack-checkout-button__product-field"
|
|
162
|
+
<div className="newspack-checkout-button__product-field">
|
|
162
163
|
{ selected && ! isChanging ? (
|
|
163
164
|
<>
|
|
164
165
|
<BaseControl label={ __( 'Product', 'newspack-blocks' ) } id="selected-product-control">
|
|
@@ -201,7 +202,7 @@ function ProductControl( props ) {
|
|
|
201
202
|
|
|
202
203
|
function CheckoutButtonEdit( props ) {
|
|
203
204
|
const { attributes, setAttributes, className } = props;
|
|
204
|
-
const { placeholder, style, text, product, price, variation, width } = attributes;
|
|
205
|
+
const { placeholder, style, text, product, price, variation, width, coupon } = attributes;
|
|
205
206
|
|
|
206
207
|
const [ productData, setProductData ] = useState( {} );
|
|
207
208
|
const [ variations, setVariations ] = useState( [] );
|
|
@@ -337,6 +338,9 @@ function CheckoutButtonEdit( props ) {
|
|
|
337
338
|
</>
|
|
338
339
|
) }
|
|
339
340
|
</ProductControl>
|
|
341
|
+
{ newspack_blocks_data?.coupons_enabled && (
|
|
342
|
+
<CouponControl value={ coupon } onChange={ value => setAttributes( { coupon: value } ) } />
|
|
343
|
+
) }
|
|
340
344
|
<WidthControl selectedWidth={ width } setAttributes={ setAttributes } />
|
|
341
345
|
</PanelBody>
|
|
342
346
|
<PanelBody title={ __( 'After purchase', 'newspack-blocks' ) }>
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
@use "newspack-colors" as np-colors;
|
|
2
2
|
|
|
3
3
|
.newspack-checkout-button {
|
|
4
|
-
&__product-field
|
|
4
|
+
&__product-field,
|
|
5
|
+
&__coupon-field {
|
|
6
|
+
margin-bottom: 16px;
|
|
5
7
|
&__error {
|
|
6
8
|
color: np-colors.$error-500;
|
|
7
9
|
font-size: 0.9em;
|
|
@@ -64,4 +66,3 @@
|
|
|
64
66
|
width: auto;
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
|
-
|
|
@@ -62,6 +62,7 @@ function render_callback( $attributes ) {
|
|
|
62
62
|
$after_success_behavior = $attributes['afterSuccessBehavior'] ?? '';
|
|
63
63
|
$after_success_button_label = $attributes['afterSuccessButtonLabel'] ?? '';
|
|
64
64
|
$after_success_url = $attributes['afterSuccessURL'] ?? '';
|
|
65
|
+
$coupon = $attributes['coupon'] ?? '';
|
|
65
66
|
$is_variable = $attributes['is_variable'];
|
|
66
67
|
|
|
67
68
|
if ( $is_variable && $variation_id ) {
|
|
@@ -114,6 +115,12 @@ function render_callback( $attributes ) {
|
|
|
114
115
|
$hidden_fields .= $after_success_button_label ? '<input type="hidden" name="after_success_button_label" value="' . esc_attr( $after_success_button_label ) . '" />' : '';
|
|
115
116
|
$hidden_fields .= $after_success_url ? '<input type="hidden" name="after_success_url" value="' . esc_attr( $after_success_url ) . '" />' : '';
|
|
116
117
|
}
|
|
118
|
+
// Always emit the coupon field (not gated on the gateway check): it is
|
|
119
|
+
// applied server-side for both the modal and the redirect checkout flows.
|
|
120
|
+
// Strict check so a coupon code of "0" is still emitted.
|
|
121
|
+
if ( '' !== $coupon ) {
|
|
122
|
+
$hidden_fields .= '<input type="hidden" name="coupon" value="' . esc_attr( $coupon ) . '" />';
|
|
123
|
+
}
|
|
117
124
|
|
|
118
125
|
ob_start();
|
|
119
126
|
/**
|
|
@@ -153,8 +160,8 @@ function render_callback( $attributes ) {
|
|
|
153
160
|
$checkout_data = Checkout_Data::get_checkout_data( $product );
|
|
154
161
|
|
|
155
162
|
$form = sprintf(
|
|
156
|
-
'<form
|
|
157
|
-
|
|
163
|
+
'<form %1$s>%2$s %3$s</form>',
|
|
164
|
+
Checkout_Data::data_checkout_attr( $checkout_data ),
|
|
158
165
|
$button,
|
|
159
166
|
$hidden_fields
|
|
160
167
|
);
|
package/src/blocks/donate/frontend/class-newspack-blocks-donate-renderer-frequency-based.php
CHANGED
|
@@ -177,7 +177,7 @@ class Newspack_Blocks_Donate_Renderer_Frequency_Based extends Newspack_Blocks_Do
|
|
|
177
177
|
id='tab-panel-<?php echo esc_attr( $frequency_slug . '-' . $uid ); ?>'
|
|
178
178
|
role='tabpanel'
|
|
179
179
|
aria-labelledby='tab-newspack-donate-<?php echo esc_attr( $frequency_slug . '-' . $uid ); ?>'
|
|
180
|
-
|
|
180
|
+
<?php Checkout_Data::print_data_checkout_attr( Checkout_Data::get_donation_checkout_data( $frequency_slug, $amount ) ); ?>
|
|
181
181
|
<?php ( $frequency_slug === $configuration['defaultFrequency'] ? 'tabindex="0"' : '' ); ?>
|
|
182
182
|
>
|
|
183
183
|
<?php echo self::render_frequency_selection( $frequency_slug, $frequency_name, $uid, $configuration ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
|
@@ -262,7 +262,7 @@ class Newspack_Blocks_Donate_Renderer_Frequency_Based extends Newspack_Blocks_Do
|
|
|
262
262
|
?>
|
|
263
263
|
<div
|
|
264
264
|
class='wp-block-newspack-blocks-donate__tier donation-tier__<?php echo esc_attr( $frequency_slug ); ?>'
|
|
265
|
-
|
|
265
|
+
<?php Checkout_Data::print_data_checkout_attr( Checkout_Data::get_donation_checkout_data( $frequency_slug, $amount ) ); ?>
|
|
266
266
|
>
|
|
267
267
|
<?php if ( 3 === $index ) : // The "other" tier. ?>
|
|
268
268
|
<input
|
|
@@ -64,7 +64,7 @@ class Newspack_Blocks_Donate_Renderer_Tiers_Based extends Newspack_Blocks_Donate
|
|
|
64
64
|
data-frequency-slug="<?php echo esc_attr( $frequency_slug ); ?>"
|
|
65
65
|
data-amount="<?php echo esc_attr( $amount ); ?>"
|
|
66
66
|
data-tier-index="<?php echo esc_attr( $index ); ?>"
|
|
67
|
-
|
|
67
|
+
<?php Checkout_Data::print_data_checkout_attr( Checkout_Data::get_donation_checkout_data( $frequency_slug, $amount ) ); ?>
|
|
68
68
|
>
|
|
69
69
|
<?php echo wp_kses_post( Newspack_Blocks::get_formatted_amount( $amount, $frequency_slug ) ); ?>
|
|
70
70
|
</span>
|
|
@@ -95,56 +95,12 @@
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
.order-review-wrapper {
|
|
98
|
-
|
|
99
|
-
// stylelint-disable-next-line selector-id-pattern
|
|
100
|
-
#order_review_heading {
|
|
101
|
-
border-radius: 0;
|
|
102
|
-
min-height: 0;
|
|
103
|
-
padding: 0;
|
|
104
|
-
|
|
105
|
-
&:hover {
|
|
106
|
-
background: transparent;
|
|
107
|
-
color: var(--newspack-ui-color-neutral-60);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
&:focus-visible {
|
|
111
|
-
outline: 0;
|
|
112
|
-
text-decoration: underline;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
svg {
|
|
116
|
-
margin: 0 -8px;
|
|
117
|
-
transition: rotate 125ms ease-in-out;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
98
|
+
margin-bottom: var(--newspack-ui-spacer-5, 24px);
|
|
120
99
|
|
|
121
100
|
// stylelint-disable-next-line selector-id-pattern
|
|
122
101
|
#order_review {
|
|
123
102
|
margin: 0;
|
|
124
103
|
}
|
|
125
|
-
|
|
126
|
-
.transaction-details-content {
|
|
127
|
-
display: grid;
|
|
128
|
-
grid-template-rows: 0fr;
|
|
129
|
-
margin-bottom: var(--newspack-ui-spacer-2, 12px);
|
|
130
|
-
transition: grid-template-rows 250ms ease-in-out;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
.transaction-details-content-inner {
|
|
134
|
-
overflow: hidden;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
&.transaction-details-expanded {
|
|
139
|
-
// stylelint-disable-next-line selector-id-pattern
|
|
140
|
-
#order_review_heading svg {
|
|
141
|
-
rotate: 180deg;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
.transaction-details-content {
|
|
145
|
-
grid-template-rows: 1fr;
|
|
146
|
-
margin-bottom: var(--newspack-ui-spacer-5, 24px);
|
|
147
|
-
}
|
|
148
104
|
}
|
|
149
105
|
}
|
|
150
106
|
|
|
@@ -410,14 +366,6 @@
|
|
|
410
366
|
}
|
|
411
367
|
}
|
|
412
368
|
|
|
413
|
-
// Checkout form heading.
|
|
414
|
-
.woocommerce .order-details-summary {
|
|
415
|
-
// Top margin not needed in newspack ui modal.
|
|
416
|
-
h2 {
|
|
417
|
-
margin-top: 0;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
369
|
// Override checkout payment method box.
|
|
422
370
|
|
|
423
371
|
.wc_payment_methods {
|
|
@@ -189,110 +189,23 @@ import { domReady, onCheckoutPlaceOrderProcessing } from './utils';
|
|
|
189
189
|
$wrapper.removeClass( 'hidden' );
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
const $details = $( '#after_customer_details' );
|
|
193
|
-
const expanded = $details.hasClass( 'transaction-details-expanded' );
|
|
194
|
-
|
|
195
192
|
// Move new order review table to the payment methods.
|
|
196
193
|
const $payment_methods = $( '.payment_methods' );
|
|
197
194
|
if ( $payment_methods.length ) {
|
|
198
195
|
const $el = $wrapper.clone();
|
|
199
|
-
// Make sure Transaction Details toggle's aria-expanded value is correct in cloned version.
|
|
200
|
-
if ( expanded ) {
|
|
201
|
-
$( '[id="order_review_heading"]', $el ).attr( 'aria-expanded', 'true' );
|
|
202
|
-
}
|
|
203
196
|
$( '.order-review-wrapper' ).remove();
|
|
204
197
|
$payment_methods.after( $el );
|
|
205
|
-
} else if ( ! expanded ) {
|
|
206
|
-
// If there's no payment method, make sure to expand the Transaction Details on load.
|
|
207
|
-
$wrapper.find( '#order_review_heading' ).trigger( 'click' );
|
|
208
198
|
}
|
|
209
199
|
} );
|
|
210
200
|
|
|
211
201
|
/**
|
|
212
|
-
*
|
|
202
|
+
* Serialize the checkout form for cart-recalculation AJAX requests.
|
|
213
203
|
*/
|
|
214
|
-
function setProductSummary( productDetails, allowHtml = true ) {
|
|
215
|
-
const $productDetails = $( '#modal-checkout-product-details strong' );
|
|
216
|
-
if ( ! $productDetails.length || ! productDetails ) {
|
|
217
|
-
return false;
|
|
218
|
-
}
|
|
219
|
-
if ( allowHtml && $productDetails.html() !== productDetails ) {
|
|
220
|
-
$productDetails.html( productDetails );
|
|
221
|
-
} else if ( ! allowHtml && $productDetails.text() !== productDetails ) {
|
|
222
|
-
$productDetails.text( productDetails );
|
|
223
|
-
}
|
|
224
|
-
return true;
|
|
225
|
-
}
|
|
226
|
-
let productSummaryRequest = false;
|
|
227
204
|
function getCheckoutPostData() {
|
|
228
205
|
const $checkoutForm = $( 'form.checkout' );
|
|
229
206
|
// Repeat-trial checks can only resolve once the checkout form includes a billing email.
|
|
230
207
|
return $checkoutForm.length ? $checkoutForm.serialize() : '';
|
|
231
208
|
}
|
|
232
|
-
function requestUpdatedProductSummary() {
|
|
233
|
-
if ( productSummaryRequest ) {
|
|
234
|
-
productSummaryRequest.abort();
|
|
235
|
-
}
|
|
236
|
-
const request = $.ajax( {
|
|
237
|
-
url: newspackBlocksModalCheckout.ajax_url,
|
|
238
|
-
method: 'POST',
|
|
239
|
-
data: {
|
|
240
|
-
action: 'get_cart_product_summary',
|
|
241
|
-
modal_checkout: 1,
|
|
242
|
-
post_data: getCheckoutPostData(),
|
|
243
|
-
},
|
|
244
|
-
success: response => {
|
|
245
|
-
if ( productSummaryRequest === request ) {
|
|
246
|
-
setProductSummary( response );
|
|
247
|
-
}
|
|
248
|
-
},
|
|
249
|
-
complete: () => {
|
|
250
|
-
if ( productSummaryRequest === request ) {
|
|
251
|
-
productSummaryRequest = false;
|
|
252
|
-
}
|
|
253
|
-
},
|
|
254
|
-
} );
|
|
255
|
-
productSummaryRequest = request;
|
|
256
|
-
}
|
|
257
|
-
function updateProductSummaryFromOrderReview() {
|
|
258
|
-
const $cartItem = $( '.order-review-wrapper .woocommerce-checkout-review-order-table tr.cart_item' ).first();
|
|
259
|
-
if ( ! $cartItem.length ) {
|
|
260
|
-
return false;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const $productName = $cartItem.find( '.product-name' ).clone();
|
|
264
|
-
$productName.find( '.product-quantity' ).remove();
|
|
265
|
-
// Keep the DOM path text-only; the AJAX fallback is the HTML path because PHP runs it through wp_kses.
|
|
266
|
-
const productName = $productName.text().replace( /\s+/g, ' ' ).trim();
|
|
267
|
-
const productTotal = $cartItem.find( '.product-total' ).text().replace( /\s+/g, ' ' ).trim();
|
|
268
|
-
if ( productName && productTotal ) {
|
|
269
|
-
return setProductSummary( `${ productName }: ${ productTotal }`, false );
|
|
270
|
-
}
|
|
271
|
-
return false;
|
|
272
|
-
}
|
|
273
|
-
function syncProductSummary() {
|
|
274
|
-
if ( updateProductSummaryFromOrderReview() ) {
|
|
275
|
-
if ( productSummaryRequest ) {
|
|
276
|
-
productSummaryRequest.abort();
|
|
277
|
-
productSummaryRequest = false;
|
|
278
|
-
}
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
|
-
requestUpdatedProductSummary();
|
|
282
|
-
}
|
|
283
|
-
$( document ).on( 'updated_checkout', syncProductSummary );
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Toggle Transaction Details
|
|
287
|
-
*/
|
|
288
|
-
$( document ).on( 'click', '#order_review_heading', function () {
|
|
289
|
-
// Toggle the aria-expanded attribute.
|
|
290
|
-
$( this ).attr( 'aria-expanded', function ( index, attr ) {
|
|
291
|
-
return attr === 'false' ? 'true' : 'false';
|
|
292
|
-
} );
|
|
293
|
-
// Toggle the CSS class to show/hide the Transaction Details.
|
|
294
|
-
$( '#after_customer_details' ).toggleClass( 'transaction-details-expanded' );
|
|
295
|
-
} );
|
|
296
209
|
|
|
297
210
|
/**
|
|
298
211
|
* Get updated cart total to update the "Place Order" button.
|
|
@@ -748,7 +661,12 @@ import { domReady, onCheckoutPlaceOrderProcessing } from './utils';
|
|
|
748
661
|
}
|
|
749
662
|
}
|
|
750
663
|
|
|
751
|
-
|
|
664
|
+
// Anchor the summary to the hidden product-details carrier, falling back to
|
|
665
|
+
// #after_customer_details when the carrier isn't present (e.g. multi-item carts).
|
|
666
|
+
const $anchor = $( '#modal-checkout-product-details' ).length
|
|
667
|
+
? $( '#modal-checkout-product-details' )
|
|
668
|
+
: $( '#after_customer_details' );
|
|
669
|
+
$anchor.after( '<div id="checkout_details">' + html.join( '' ) + '</div>' );
|
|
752
670
|
}
|
|
753
671
|
|
|
754
672
|
/**
|
|
@@ -165,12 +165,6 @@ domReady( () => {
|
|
|
165
165
|
onCheckoutPlaceOrderError( container, hideProcessingPaymentScreen );
|
|
166
166
|
|
|
167
167
|
onCheckoutReady( container, () => {
|
|
168
|
-
// Make sure the order summary renders the correct text.
|
|
169
|
-
const summaryTextNode = productDetails?.querySelector( 'strong' );
|
|
170
|
-
if ( summaryTextNode ) {
|
|
171
|
-
summaryTextNode.textContent = checkoutData.price_summary;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
168
|
// Display initial errors if any.
|
|
175
169
|
if ( modalCheckout.initialErrors ) {
|
|
176
170
|
const errorContainer = document.createElement( 'div' );
|
|
@@ -444,11 +438,6 @@ domReady( () => {
|
|
|
444
438
|
if ( shouldPromptRegistration() ) {
|
|
445
439
|
ev.preventDefault();
|
|
446
440
|
|
|
447
|
-
const priceSummary = checkoutData.price_summary;
|
|
448
|
-
const content = priceSummary
|
|
449
|
-
? `<div class="order-details-summary ${ CLASS_PREFIX }__box ${ CLASS_PREFIX }__box--text-center"><p><strong>${ priceSummary }</strong></p></div>`
|
|
450
|
-
: '';
|
|
451
|
-
|
|
452
441
|
// Generate cart asynchroneously.
|
|
453
442
|
const cartReq = generateCart( checkoutData );
|
|
454
443
|
|
|
@@ -499,7 +488,6 @@ domReady( () => {
|
|
|
499
488
|
title: newspackBlocksModal.labels.register_modal_title,
|
|
500
489
|
},
|
|
501
490
|
},
|
|
502
|
-
content,
|
|
503
491
|
trigger: ev.submitter,
|
|
504
492
|
closeOnSuccess: isModalCheckout,
|
|
505
493
|
} );
|
|
@@ -42,27 +42,17 @@ if ( ! $checkout->is_registration_enabled() && $checkout->is_registration_requir
|
|
|
42
42
|
?>
|
|
43
43
|
<div class="order-review-wrapper hidden">
|
|
44
44
|
<?php do_action( 'woocommerce_checkout_before_order_review_heading' ); ?>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
<div id="order_review" class="woocommerce-checkout-review-order newspack-ui__box">
|
|
55
|
-
<?php
|
|
56
|
-
if ( wp_is_block_theme() && function_exists( 'woocommerce_order_review' ) ) {
|
|
57
|
-
woocommerce_order_review();
|
|
58
|
-
} else {
|
|
59
|
-
do_action( 'woocommerce_checkout_order_review' );
|
|
60
|
-
}
|
|
61
|
-
?>
|
|
62
|
-
</div>
|
|
63
|
-
<?php do_action( 'woocommerce_checkout_after_order_review' ); ?>
|
|
64
|
-
</div>
|
|
45
|
+
<?php do_action( 'woocommerce_checkout_before_order_review' ); ?>
|
|
46
|
+
<div id="order_review" class="woocommerce-checkout-review-order newspack-ui__box">
|
|
47
|
+
<?php
|
|
48
|
+
if ( wp_is_block_theme() && function_exists( 'woocommerce_order_review' ) ) {
|
|
49
|
+
woocommerce_order_review();
|
|
50
|
+
} else {
|
|
51
|
+
do_action( 'woocommerce_checkout_order_review' );
|
|
52
|
+
}
|
|
53
|
+
?>
|
|
65
54
|
</div>
|
|
55
|
+
<?php do_action( 'woocommerce_checkout_after_order_review' ); ?>
|
|
66
56
|
</div>
|
|
67
57
|
<?php do_action( 'woocommerce_checkout_after_customer_details' ); ?>
|
|
68
58
|
<button class="newspack-ui__button newspack-ui__button--secondary newspack-ui__button--wide" id="checkout_edit_billing" type="button"><?php echo esc_html( Modal_Checkout::get_modal_checkout_labels( 'checkout_edit_billing' ) ); ?></button>
|
|
@@ -12,6 +12,15 @@ if ( ! wc_coupons_enabled() ) { // @codingStandardsIgnoreLine.
|
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
// Hide the coupon form when a coupon was auto-applied from a Checkout Button
|
|
16
|
+
// block — the reader didn't enter the code, so don't prompt them for one.
|
|
17
|
+
if (
|
|
18
|
+
class_exists( '\Newspack_Blocks\Modal_Checkout' )
|
|
19
|
+
&& \Newspack_Blocks\Modal_Checkout::should_hide_coupon_form()
|
|
20
|
+
) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
15
24
|
?>
|
|
16
25
|
<form class="modal_checkout_coupon woocommerce-form-coupon" method="post">
|
|
17
26
|
<h3><?php esc_html_e( 'Apply a coupon code', 'newspack-blocks' ); ?></h3>
|
|
@@ -60,7 +60,7 @@ $checkout_data = Checkout_Data::get_checkout_data( $order );
|
|
|
60
60
|
<path d="M16.7 7.1l-6.3 8.5-3.3-2.5-.9 1.2 4.5 3.4L17.9 8z"></path>
|
|
61
61
|
</svg>
|
|
62
62
|
</span>
|
|
63
|
-
<p id="modal-checkout-product-details"
|
|
63
|
+
<p id="modal-checkout-product-details" <?php Checkout_Data::print_data_checkout_attr( $checkout_data ); ?>>
|
|
64
64
|
<strong>
|
|
65
65
|
<?php
|
|
66
66
|
echo esc_html( Modal_Checkout::get_post_checkout_success_text() );
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
'name' => 'automattic/newspack-blocks',
|
|
4
4
|
'pretty_version' => 'dev-main',
|
|
5
5
|
'version' => 'dev-main',
|
|
6
|
-
'reference' => '
|
|
6
|
+
'reference' => '559ee55cab1698e9bea567b8f4bb267cc577fef7',
|
|
7
7
|
'type' => 'wordpress-plugin',
|
|
8
8
|
'install_path' => __DIR__ . '/../../',
|
|
9
9
|
'aliases' => array(),
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
'automattic/newspack-blocks' => array(
|
|
14
14
|
'pretty_version' => 'dev-main',
|
|
15
15
|
'version' => 'dev-main',
|
|
16
|
-
'reference' => '
|
|
16
|
+
'reference' => '559ee55cab1698e9bea567b8f4bb267cc577fef7',
|
|
17
17
|
'type' => 'wordpress-plugin',
|
|
18
18
|
'install_path' => __DIR__ . '/../../',
|
|
19
19
|
'aliases' => array(),
|
package/webpack.config.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
const fs = require( 'fs' );
|
|
11
11
|
const getBaseWebpackConfig = require( 'newspack-scripts/config/getWebpackConfig' );
|
|
12
|
+
const { findSourceFile, resolveSourceFile } = require( 'newspack-scripts/config/resolveSource' );
|
|
12
13
|
const path = require( 'path' );
|
|
13
14
|
const isDevelopment = process.env.NODE_ENV !== 'production';
|
|
14
15
|
const blockListFile = process.env.npm_config_block_list || 'block-list.json';
|
|
@@ -20,26 +21,19 @@ const blockList = JSON.parse( fs.readFileSync( blockListFile ) );
|
|
|
20
21
|
const editorSetup = path.join( __dirname, 'src', 'setup', 'editor' );
|
|
21
22
|
|
|
22
23
|
function blockScripts( type, inputDir, blocks ) {
|
|
23
|
-
return blocks.map( block => path.join( inputDir, 'blocks', block,
|
|
24
|
+
return blocks.map( block => findSourceFile( path.join( inputDir, 'blocks', block, type ) ) ).filter( Boolean );
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
const blocksDir = path.join( __dirname, 'src', 'blocks' );
|
|
27
28
|
const blocks = fs
|
|
28
29
|
.readdirSync( blocksDir )
|
|
29
30
|
.filter( block => isDevelopment || blockList.production.includes( block ) )
|
|
30
|
-
.filter( block =>
|
|
31
|
+
.filter( block => findSourceFile( path.join( blocksDir, block, 'editor' ) ) );
|
|
31
32
|
|
|
32
33
|
// Helps split up each block into its own folder view script
|
|
33
34
|
const viewBlocksScripts = blocks.reduce( ( viewBlocks, block ) => {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
let fileExists = fs.existsSync( viewScriptPath );
|
|
37
|
-
if ( ! fileExists ) {
|
|
38
|
-
// Try TS.
|
|
39
|
-
viewScriptPath = path.join( ...pathToBlock, 'view.ts' );
|
|
40
|
-
fileExists = fs.existsSync( viewScriptPath );
|
|
41
|
-
}
|
|
42
|
-
if ( fileExists ) {
|
|
35
|
+
const viewScriptPath = findSourceFile( path.join( blocksDir, block, 'view' ) );
|
|
36
|
+
if ( viewScriptPath ) {
|
|
43
37
|
viewBlocks[ block + '/view' ] = viewScriptPath;
|
|
44
38
|
}
|
|
45
39
|
return viewBlocks;
|
|
@@ -56,7 +50,7 @@ const entry = {
|
|
|
56
50
|
placeholder_blocks: placeholderBlocksScript,
|
|
57
51
|
editor: editorScript,
|
|
58
52
|
block_styles: blockStylesScript,
|
|
59
|
-
modal: path.join( __dirname, 'src/modal-checkout/modal
|
|
53
|
+
modal: resolveSourceFile( path.join( __dirname, 'src/modal-checkout/modal' ) ),
|
|
60
54
|
modalCheckout: path.join( __dirname, 'src/modal-checkout' ),
|
|
61
55
|
frequencyBased: path.join( __dirname, 'src/blocks/donate/frequency-based' ),
|
|
62
56
|
tiersBased: path.join( __dirname, 'src/blocks/donate/tiers-based' ),
|