mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-06 09:08:09 +08:00
✨ Introduce new BusyStateWrapper component
This commit is contained in:
parent
5c50a3d970
commit
1826b95c08
9 changed files with 176 additions and 103 deletions
|
@ -0,0 +1,10 @@
|
|||
.ppcp-r-busy-wrapper {
|
||||
position: relative;
|
||||
|
||||
&.ppcp--is-loading {
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
|
||||
--spinner-overlay-color: #fff4;
|
||||
}
|
||||
}
|
|
@ -31,10 +31,4 @@
|
|||
&__toggled-content {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
&.ppcp--is-loading {
|
||||
pointer-events: none;
|
||||
|
||||
--spinner-overlay-color: #fff4;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,11 @@
|
|||
|
||||
#ppcp-settings-container {
|
||||
@import './global';
|
||||
@import './components/reusable-components/onboarding-header';
|
||||
@import './components/reusable-components/busy-state';
|
||||
@import './components/reusable-components/button';
|
||||
@import './components/reusable-components/settings-toggle-block';
|
||||
@import './components/reusable-components/separator';
|
||||
@import './components/reusable-components/onboarding-header';
|
||||
@import './components/reusable-components/settings-toggle-block';
|
||||
@import './components/reusable-components/payment-method-icons';
|
||||
@import "./components/reusable-components/payment-method-item";
|
||||
@import './components/reusable-components/settings-wrapper';
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
import {
|
||||
Children,
|
||||
isValidElement,
|
||||
cloneElement,
|
||||
useMemo,
|
||||
} from '@wordpress/element';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { CommonHooks } from '../../data';
|
||||
import SpinnerOverlay from './SpinnerOverlay';
|
||||
|
||||
/**
|
||||
* Wraps interactive child elements and modifies their behavior based on the global `isBusy` state.
|
||||
* Allows custom processing of child props via the `onBusy` callback.
|
||||
*
|
||||
* @param {Object} props - Component properties.
|
||||
* @param {Children} props.children - Child components to wrap.
|
||||
* @param {boolean} props.enabled - Enables or disables the busy-state logic.
|
||||
* @param {string} props.className - Additional class names for the wrapper.
|
||||
* @param {Function} props.onBusy - Callback to process child props when busy.
|
||||
*/
|
||||
const BusyStateWrapper = ( {
|
||||
children,
|
||||
enabled = true,
|
||||
className = '',
|
||||
onBusy = () => ( { disabled: true } ),
|
||||
} ) => {
|
||||
const { isBusy } = CommonHooks.useBusyState();
|
||||
|
||||
const markAsBusy = isBusy && enabled;
|
||||
|
||||
const wrapperClassName = classNames( 'ppcp-r-busy-wrapper', className, {
|
||||
'ppcp--is-loading': markAsBusy,
|
||||
} );
|
||||
|
||||
const memoizedChildren = useMemo(
|
||||
() =>
|
||||
Children.map( children, ( child ) =>
|
||||
isValidElement( child )
|
||||
? cloneElement(
|
||||
child,
|
||||
markAsBusy ? onBusy( child.props ) : {}
|
||||
)
|
||||
: child
|
||||
),
|
||||
[ children, markAsBusy, onBusy ]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={ wrapperClassName }>
|
||||
{ markAsBusy && <SpinnerOverlay /> }
|
||||
{ memoizedChildren }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BusyStateWrapper;
|
|
@ -1,23 +1,17 @@
|
|||
import { ToggleControl } from '@wordpress/components';
|
||||
import { useRef } from '@wordpress/element';
|
||||
|
||||
import SpinnerOverlay from './SpinnerOverlay';
|
||||
|
||||
const SettingsToggleBlock = ( {
|
||||
isToggled,
|
||||
setToggled,
|
||||
isLoading = false,
|
||||
disabled = false,
|
||||
...props
|
||||
} ) => {
|
||||
const toggleRef = useRef( null );
|
||||
const blockClasses = [ 'ppcp-r-toggle-block' ];
|
||||
|
||||
if ( isLoading ) {
|
||||
blockClasses.push( 'ppcp--is-loading' );
|
||||
}
|
||||
|
||||
const handleLabelClick = () => {
|
||||
if ( ! toggleRef.current || isLoading ) {
|
||||
if ( ! toggleRef.current || disabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -52,13 +46,12 @@ const SettingsToggleBlock = ( {
|
|||
ref={ toggleRef }
|
||||
checked={ isToggled }
|
||||
onChange={ ( newState ) => setToggled( newState ) }
|
||||
disabled={ isLoading }
|
||||
disabled={ disabled }
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{ props.children && isToggled && (
|
||||
<div className="ppcp-r-toggle-block__toggled-content">
|
||||
{ isLoading && <SpinnerOverlay /> }
|
||||
{ props.children }
|
||||
</div>
|
||||
) }
|
||||
|
|
|
@ -20,6 +20,7 @@ import {
|
|||
} from '../../../../hooks/useHandleConnections';
|
||||
|
||||
import ConnectionButton from './ConnectionButton';
|
||||
import BusyStateWrapper from '../../../ReusableComponents/BusyStateWrapper';
|
||||
|
||||
const FORM_ERRORS = {
|
||||
noClientId: __(
|
||||
|
@ -89,7 +90,7 @@ const AdvancedOptionsForm = () => {
|
|||
handleConnectViaIdAndSecret( {
|
||||
validation: validateManualConnectionForm,
|
||||
} ),
|
||||
[ validateManualConnectionForm ]
|
||||
[ handleConnectViaIdAndSecret, validateManualConnectionForm ]
|
||||
);
|
||||
|
||||
useEffect( () => {
|
||||
|
@ -124,69 +125,79 @@ const AdvancedOptionsForm = () => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<SettingsToggleBlock
|
||||
label={ __(
|
||||
'Enable Sandbox Mode',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
'Activate Sandbox mode to safely test PayPal with sample data. Once your store is ready to go live, you can easily switch to your production account.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
isToggled={ !! isSandboxMode }
|
||||
setToggled={ setSandboxMode }
|
||||
isLoading={ isBusy }
|
||||
>
|
||||
<ConnectionButton
|
||||
title={ __(
|
||||
'Connect Account',
|
||||
<BusyStateWrapper>
|
||||
<SettingsToggleBlock
|
||||
label={ __(
|
||||
'Enable Sandbox Mode',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
showIcon={ false }
|
||||
variant="secondary"
|
||||
isSandbox={
|
||||
true /* This button always connects to sandbox */
|
||||
}
|
||||
/>
|
||||
</SettingsToggleBlock>
|
||||
description={ __(
|
||||
'Activate Sandbox mode to safely test PayPal with sample data. Once your store is ready to go live, you can easily switch to your production account.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
isToggled={ !! isSandboxMode }
|
||||
setToggled={ setSandboxMode }
|
||||
>
|
||||
<ConnectionButton
|
||||
title={ __(
|
||||
'Connect Account',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
showIcon={ false }
|
||||
variant="secondary"
|
||||
isSandbox={
|
||||
true /* This button always connects to sandbox */
|
||||
}
|
||||
/>
|
||||
</SettingsToggleBlock>
|
||||
</BusyStateWrapper>
|
||||
<Separator withLine={ false } />
|
||||
<SettingsToggleBlock
|
||||
label={
|
||||
__( 'Manually Connect', 'woocommerce-paypal-payments' ) +
|
||||
( isBusy ? ' ...' : '' )
|
||||
}
|
||||
description={ advancedUsersDescription }
|
||||
isToggled={ !! isManualConnectionMode }
|
||||
setToggled={ setManualConnectionMode }
|
||||
isLoading={ isBusy }
|
||||
<BusyStateWrapper
|
||||
onBusy={ ( props ) => ( {
|
||||
disabled: true,
|
||||
label: props.label + ' ...',
|
||||
} ) }
|
||||
>
|
||||
<DataStoreControl
|
||||
control={ TextControl }
|
||||
ref={ refClientId }
|
||||
label={ clientIdLabel }
|
||||
value={ clientId }
|
||||
onChange={ setClientId }
|
||||
className={ classNames( {
|
||||
'has-error': ! clientValid,
|
||||
} ) }
|
||||
/>
|
||||
{ clientValid || (
|
||||
<p className="client-id-error">
|
||||
{ FORM_ERRORS.invalidClientId }
|
||||
</p>
|
||||
) }
|
||||
<DataStoreControl
|
||||
control={ TextControl }
|
||||
ref={ refClientSecret }
|
||||
label={ secretKeyLabel }
|
||||
value={ clientSecret }
|
||||
onChange={ setClientSecret }
|
||||
type="password"
|
||||
/>
|
||||
<Button variant="secondary" onClick={ handleManualConnect }>
|
||||
{ __( 'Connect Account', 'woocommerce-paypal-payments' ) }
|
||||
</Button>
|
||||
</SettingsToggleBlock>
|
||||
<SettingsToggleBlock
|
||||
label={ __(
|
||||
'Manually Connect',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ advancedUsersDescription }
|
||||
isToggled={ !! isManualConnectionMode }
|
||||
setToggled={ setManualConnectionMode }
|
||||
>
|
||||
<DataStoreControl
|
||||
control={ TextControl }
|
||||
ref={ refClientId }
|
||||
label={ clientIdLabel }
|
||||
value={ clientId }
|
||||
onChange={ setClientId }
|
||||
className={ classNames( {
|
||||
'has-error': ! clientValid,
|
||||
} ) }
|
||||
/>
|
||||
{ clientValid || (
|
||||
<p className="client-id-error">
|
||||
{ FORM_ERRORS.invalidClientId }
|
||||
</p>
|
||||
) }
|
||||
<DataStoreControl
|
||||
control={ TextControl }
|
||||
ref={ refClientSecret }
|
||||
label={ secretKeyLabel }
|
||||
value={ clientSecret }
|
||||
onChange={ setClientSecret }
|
||||
type="password"
|
||||
/>
|
||||
<Button variant="secondary" onClick={ handleManualConnect }>
|
||||
{ __(
|
||||
'Connect Account',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</Button>
|
||||
</SettingsToggleBlock>
|
||||
</BusyStateWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
useProductionConnection,
|
||||
useSandboxConnection,
|
||||
} from '../../../../hooks/useHandleConnections';
|
||||
import BusyStateWrapper from '../../../ReusableComponents/BusyStateWrapper';
|
||||
|
||||
const ConnectionButton = ( {
|
||||
title,
|
||||
|
@ -15,13 +16,11 @@ const ConnectionButton = ( {
|
|||
variant = 'primary',
|
||||
showIcon = true,
|
||||
} ) => {
|
||||
const { isBusy } = CommonHooks.useBusyState();
|
||||
const { handleSandboxConnect } = useSandboxConnection();
|
||||
const { handleProductionConnect } = useProductionConnection();
|
||||
const className = classNames( 'ppcp-r-connection-button', {
|
||||
'sandbox-mode': isSandbox,
|
||||
'live-mode': ! isSandbox,
|
||||
'ppcp--is-loading': isBusy,
|
||||
} );
|
||||
|
||||
const handleConnectClick = async () => {
|
||||
|
@ -33,15 +32,16 @@ const ConnectionButton = ( {
|
|||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
className={ className }
|
||||
variant={ variant }
|
||||
icon={ showIcon ? openSignup : null }
|
||||
onClick={ handleConnectClick }
|
||||
disabled={ isBusy }
|
||||
>
|
||||
<span className="button-title">{ title }</span>
|
||||
</Button>
|
||||
<BusyStateWrapper>
|
||||
<Button
|
||||
className={ className }
|
||||
variant={ variant }
|
||||
icon={ showIcon ? openSignup : null }
|
||||
onClick={ handleConnectClick }
|
||||
>
|
||||
<span className="button-title">{ title }</span>
|
||||
</Button>
|
||||
</BusyStateWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import classNames from 'classnames';
|
|||
|
||||
import { OnboardingHooks } from '../../../../data';
|
||||
import useIsScrolled from '../../../../hooks/useIsScrolled';
|
||||
import BusyStateWrapper from '../../../ReusableComponents/BusyStateWrapper';
|
||||
|
||||
const Navigation = ( { stepDetails, onNext, onPrev, onExit } ) => {
|
||||
const { title, isFirst, percentage, showNext, canProceed } = stepDetails;
|
||||
|
@ -20,7 +21,10 @@ const Navigation = ( { stepDetails, onNext, onPrev, onExit } ) => {
|
|||
return (
|
||||
<div className={ className }>
|
||||
<div className="ppcp-r-navigation">
|
||||
<div className="ppcp-r-navigation--left">
|
||||
<BusyStateWrapper
|
||||
className="ppcp-r-navigation--left"
|
||||
enabled={ ! isFirst }
|
||||
>
|
||||
<Button
|
||||
variant="link"
|
||||
onClick={ isFirst ? onExit : onPrev }
|
||||
|
@ -31,7 +35,7 @@ const Navigation = ( { stepDetails, onNext, onPrev, onExit } ) => {
|
|||
{ title }
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
</BusyStateWrapper>
|
||||
{ ! isFirst &&
|
||||
NextButton( { showNext, isDisabled, onNext, onExit } ) }
|
||||
<ProgressBar percent={ percentage } />
|
||||
|
@ -42,7 +46,7 @@ const Navigation = ( { stepDetails, onNext, onPrev, onExit } ) => {
|
|||
|
||||
const NextButton = ( { showNext, isDisabled, onNext, onExit } ) => {
|
||||
return (
|
||||
<div className="ppcp-r-navigation--right">
|
||||
<BusyStateWrapper className="ppcp-r-navigation--right">
|
||||
<Button variant="link" onClick={ onExit }>
|
||||
{ __( 'Save and exit', 'woocommerce-paypal-payments' ) }
|
||||
</Button>
|
||||
|
@ -55,7 +59,7 @@ const NextButton = ( { showNext, isDisabled, onNext, onExit } ) => {
|
|||
{ __( 'Continue', 'woocommerce-paypal-payments' ) }
|
||||
</Button>
|
||||
) }
|
||||
</div>
|
||||
</BusyStateWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import AccordionSection from '../../ReusableComponents/AccordionSection';
|
|||
|
||||
import AdvancedOptionsForm from './Components/AdvancedOptionsForm';
|
||||
import { CommonHooks } from '../../../data';
|
||||
import BusyStateWrapper from '../../ReusableComponents/BusyStateWrapper';
|
||||
|
||||
const StepWelcome = ( { setStep, currentStep } ) => {
|
||||
const { storeCountry, storeCurrency } = CommonHooks.useWooSettings();
|
||||
|
@ -34,16 +35,18 @@ const StepWelcome = ( { setStep, currentStep } ) => {
|
|||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</p>
|
||||
<Button
|
||||
className="ppcp-r-button-activate-paypal"
|
||||
variant="primary"
|
||||
onClick={ () => setStep( currentStep + 1 ) }
|
||||
>
|
||||
{ __(
|
||||
'Activate PayPal Payments',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</Button>
|
||||
<BusyStateWrapper>
|
||||
<Button
|
||||
className="ppcp-r-button-activate-paypal"
|
||||
variant="primary"
|
||||
onClick={ () => setStep( currentStep + 1 ) }
|
||||
>
|
||||
{ __(
|
||||
'Activate PayPal Payments',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</Button>
|
||||
</BusyStateWrapper>
|
||||
</div>
|
||||
<Separator className="ppcp-r-page-welcome-mode-separator" />
|
||||
<WelcomeDocs
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue