💄 Fix multiple spinners in nested Busy-wrappers

This commit is contained in:
Philipp Stracker 2024-12-09 18:48:56 +01:00
parent 1826b95c08
commit 70e2015e1f
No known key found for this signature in database
3 changed files with 30 additions and 14 deletions

View file

@ -12,5 +12,6 @@
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
margin: 0;
} }
} }

View file

@ -3,34 +3,43 @@ import {
isValidElement, isValidElement,
cloneElement, cloneElement,
useMemo, useMemo,
createContext,
useContext,
} from '@wordpress/element'; } from '@wordpress/element';
import classNames from 'classnames'; import classNames from 'classnames';
import { CommonHooks } from '../../data'; import { CommonHooks } from '../../data';
import SpinnerOverlay from './SpinnerOverlay'; import SpinnerOverlay from './SpinnerOverlay';
// Create context to track the busy state across nested wrappers
const BusyContext = createContext( false );
/** /**
* Wraps interactive child elements and modifies their behavior based on the global `isBusy` state. * Wraps interactive child elements and modifies their behavior based on the global `isBusy` state.
* Allows custom processing of child props via the `onBusy` callback. * Allows custom processing of child props via the `onBusy` callback.
* *
* @param {Object} props - Component properties. * @param {Object} props - Component properties.
* @param {Children} props.children - Child components to wrap. * @param {Children} props.children - Child components to wrap.
* @param {boolean} props.enabled - Enables or disables the busy-state logic. * @param {boolean} props.enabled - Enables or disables the busy-state logic.
* @param {string} props.className - Additional class names for the wrapper. * @param {boolean} props.busySpinner - Allows disabling the spinner in busy-state.
* @param {Function} props.onBusy - Callback to process child props when busy. * @param {string} props.className - Additional class names for the wrapper.
* @param {Function} props.onBusy - Callback to process child props when busy.
*/ */
const BusyStateWrapper = ( { const BusyStateWrapper = ( {
children, children,
enabled = true, enabled = true,
busySpinner = true,
className = '', className = '',
onBusy = () => ( { disabled: true } ), onBusy = () => ( { disabled: true } ),
} ) => { } ) => {
const { isBusy } = CommonHooks.useBusyState(); const { isBusy } = CommonHooks.useBusyState();
const hasBusyParent = useContext( BusyContext );
const markAsBusy = isBusy && enabled; const isBusyComponent = isBusy && enabled;
const showSpinner = busySpinner && isBusyComponent && ! hasBusyParent;
const wrapperClassName = classNames( 'ppcp-r-busy-wrapper', className, { const wrapperClassName = classNames( 'ppcp-r-busy-wrapper', className, {
'ppcp--is-loading': markAsBusy, 'ppcp--is-loading': isBusyComponent,
} ); } );
const memoizedChildren = useMemo( const memoizedChildren = useMemo(
@ -39,18 +48,20 @@ const BusyStateWrapper = ( {
isValidElement( child ) isValidElement( child )
? cloneElement( ? cloneElement(
child, child,
markAsBusy ? onBusy( child.props ) : {} isBusyComponent ? onBusy( child.props ) : {}
) )
: child : child
), ),
[ children, markAsBusy, onBusy ] [ children, isBusyComponent, onBusy ]
); );
return ( return (
<div className={ wrapperClassName }> <BusyContext.Provider value={ isBusyComponent }>
{ markAsBusy && <SpinnerOverlay /> } <div className={ wrapperClassName }>
{ memoizedChildren } { showSpinner && <SpinnerOverlay /> }
</div> { memoizedChildren }
</div>
</BusyContext.Provider>
); );
}; };

View file

@ -23,6 +23,7 @@ const Navigation = ( { stepDetails, onNext, onPrev, onExit } ) => {
<div className="ppcp-r-navigation"> <div className="ppcp-r-navigation">
<BusyStateWrapper <BusyStateWrapper
className="ppcp-r-navigation--left" className="ppcp-r-navigation--left"
busySpinner={ false }
enabled={ ! isFirst } enabled={ ! isFirst }
> >
<Button <Button
@ -46,7 +47,10 @@ const Navigation = ( { stepDetails, onNext, onPrev, onExit } ) => {
const NextButton = ( { showNext, isDisabled, onNext, onExit } ) => { const NextButton = ( { showNext, isDisabled, onNext, onExit } ) => {
return ( return (
<BusyStateWrapper className="ppcp-r-navigation--right"> <BusyStateWrapper
className="ppcp-r-navigation--right"
busySpinner={ false }
>
<Button variant="link" onClick={ onExit }> <Button variant="link" onClick={ onExit }>
{ __( 'Save and exit', 'woocommerce-paypal-payments' ) } { __( 'Save and exit', 'woocommerce-paypal-payments' ) }
</Button> </Button>