Remove the components prop in favor of the native children prop to prevent unnecessary re-renders which are breaking component animations

This commit is contained in:
Daniel Dudzic 2024-12-17 11:50:32 +01:00
parent 51613c3020
commit f32f30ef0a
No known key found for this signature in database
GPG key ID: 31B40D33E3465483
13 changed files with 271 additions and 371 deletions

View file

@ -1,8 +1,6 @@
import { Icon } from '@wordpress/components'; import { Icon } from '@wordpress/components';
import { chevronDown, chevronUp } from '@wordpress/icons'; import { chevronDown, chevronUp } from '@wordpress/icons';
import classNames from 'classnames'; import classNames from 'classnames';
import { useAccordionState } from '../../hooks/useAccordionState'; import { useAccordionState } from '../../hooks/useAccordionState';
// Provide defaults for all layout components so the generic version just works. // Provide defaults for all layout components so the generic version just works.
@ -24,6 +22,13 @@ const DefaultDescription = ( { children } ) => (
<div className="ppcp-r-accordion__description">{ children }</div> <div className="ppcp-r-accordion__description">{ children }</div>
); );
const AccordionContent = ( { isOpen, children } ) => {
if ( ! isOpen || ! children ) {
return null;
}
return <div className="ppcp-r-accordion__content">{ children }</div>;
};
const Accordion = ( { const Accordion = ( {
title, title,
id = '', id = '',
@ -65,9 +70,7 @@ const Accordion = ( {
) } ) }
</Header> </Header>
</button> </button>
{ isOpen && children && ( <AccordionContent isOpen={ isOpen }>{ children }</AccordionContent>
<div className="ppcp-r-accordion__content">{ children }</div>
) }
</div> </div>
); );
}; };

View file

@ -9,25 +9,19 @@ import {
} from './SettingsBlockElements'; } from './SettingsBlockElements';
const SettingsAccordion = ( { title, description, children, ...props } ) => ( const SettingsAccordion = ( { title, description, children, ...props } ) => (
<SettingsBlock <SettingsBlock { ...props } className="ppcp-r-settings-block__accordion">
{ ...props } <Accordion
className="ppcp-r-settings-block__accordion" title={ title }
components={ [ description={ description }
() => ( Header={ Header }
<Accordion TitleWrapper={ TitleWrapper }
title={ title } Title={ Title }
description={ description } Action={ Action }
Header={ Header } Description={ Description }
TitleWrapper={ TitleWrapper } >
Title={ Title } { children }
Action={ Action } </Accordion>
Description={ Description } </SettingsBlock>
>
{ children }
</Accordion>
),
] }
/>
); );
export default SettingsAccordion; export default SettingsAccordion;

View file

@ -3,32 +3,24 @@ import SettingsBlock from './SettingsBlock';
import { Header, Title, Action, Description } from './SettingsBlockElements'; import { Header, Title, Action, Description } from './SettingsBlockElements';
const ButtonSettingsBlock = ( { title, description, ...props } ) => ( const ButtonSettingsBlock = ( { title, description, ...props } ) => (
<SettingsBlock <SettingsBlock { ...props } className="ppcp-r-settings-block__button">
{ ...props } <Header>
className="ppcp-r-settings-block__button" <Title>{ title }</Title>
components={ [ <Description>{ description }</Description>
() => ( </Header>
<> <Action>
<Header> <Button
<Title>{ title }</Title> variant={ props.actionProps?.buttonType }
<Description>{ description }</Description> onClick={
</Header> props.actionProps?.callback
<Action> ? () => props.actionProps.callback()
<Button : undefined
variant={ props.actionProps?.buttonType } }
onClick={ >
props.actionProps?.callback { props.actionProps.value }
? () => props.actionProps.callback() </Button>
: undefined </Action>
} </SettingsBlock>
>
{ props.actionProps.value }
</Button>
</Action>
</>
),
] }
/>
); );
export default ButtonSettingsBlock; export default ButtonSettingsBlock;

View file

@ -11,56 +11,42 @@ const FeatureSettingsBlock = ( { title, description, ...props } ) => {
} }
return ( return (
<> <span className="ppcp-r-feature-item__notes">
<span className="ppcp-r-feature-item__notes"> { notes.map( ( note, index ) => (
{ notes.map( ( note, index ) => ( <span key={ index }>{ note }</span>
<span key={ index }>{ note }</span> ) ) }
) ) } </span>
</span>
</>
); );
}; };
return ( return (
<SettingsBlock <SettingsBlock { ...props } className="ppcp-r-settings-block__feature">
{ ...props } <Header>
className="ppcp-r-settings-block__feature" <Title>
components={ [ { title }
() => ( { props.actionProps?.featureStatus && (
<> <TitleBadge { ...props.actionProps?.badge } />
<Header> ) }
<Title> </Title>
{ title } <Description className="ppcp-r-settings-block__feature__description">
{ props.actionProps?.featureStatus && ( { description }
<TitleBadge { printNotes() }
{ ...props.actionProps?.badge } </Description>
/> </Header>
) } <Action>
</Title> <div className="ppcp-r-feature-item__buttons">
<Description className="ppcp-r-settings-block__feature__description"> { props.actionProps?.buttons.map( ( button ) => (
{ description } <Button
{ printNotes() } href={ button.url }
</Description> key={ button.text }
</Header> variant={ button.type }
<Action> >
<div className="ppcp-r-feature-item__buttons"> { button.text }
{ props.actionProps?.buttons.map( </Button>
( button ) => ( ) ) }
<Button </div>
href={ button.url } </Action>
key={ button.text } </SettingsBlock>
variant={ button.type }
>
{ button.text }
</Button>
)
) }
</div>
</Action>
</>
),
] }
/>
); );
}; };

View file

@ -42,28 +42,20 @@ const InputSettingsBlock = ( {
order = DEFAULT_ELEMENT_ORDER, order = DEFAULT_ELEMENT_ORDER,
...props ...props
} ) => ( } ) => (
<SettingsBlock <SettingsBlock { ...props } className="ppcp-r-settings-block__input">
{ ...props } { order.map( ( elementKey ) => {
className="ppcp-r-settings-block__input" const RenderElement = ELEMENT_RENDERERS[ elementKey ];
components={ [ return RenderElement ? (
() => ( <RenderElement
<> key={ elementKey }
{ order.map( ( elementKey ) => { title={ title }
const RenderElement = ELEMENT_RENDERERS[ elementKey ]; description={ description }
return RenderElement ? ( supplementaryLabel={ supplementaryLabel }
<RenderElement actionProps={ props.actionProps }
key={ elementKey } />
title={ title } ) : null;
description={ description } } ) }
supplementaryLabel={ supplementaryLabel } </SettingsBlock>
actionProps={ props.actionProps }
/>
) : null;
} ) }
</>
),
] }
/>
); );
export default InputSettingsBlock; export default InputSettingsBlock;

View file

@ -16,25 +16,18 @@ const PaymentMethodsBlock = ( { paymentMethods, className = '' } ) => {
return ( return (
<SettingsBlock <SettingsBlock
className={ `ppcp-r-settings-block__payment-methods ${ className }` } className={ `ppcp-r-settings-block__payment-methods ${ className }` }
components={ [ >
() => ( { paymentMethods.map( ( paymentMethod ) => (
<> <PaymentMethodItemBlock
{ paymentMethods.map( ( paymentMethod ) => ( key={ paymentMethod.id }
<PaymentMethodItemBlock { ...paymentMethod }
key={ paymentMethod.id } isSelected={ selectedMethod === paymentMethod.id }
{ ...paymentMethod } onSelect={ ( checked ) =>
isSelected={ handleSelect( paymentMethod.id, checked )
selectedMethod === paymentMethod.id }
} />
onSelect={ ( checked ) => ) ) }
handleSelect( paymentMethod.id, checked ) </SettingsBlock>
}
/>
) ) }
</>
),
] }
/>
); );
}; };

View file

@ -11,37 +11,32 @@ const RadioSettingsBlock = ( {
<SettingsBlock <SettingsBlock
{ ...props } { ...props }
className="ppcp-r-settings-block__radio ppcp-r-settings-block--expert-rdb" className="ppcp-r-settings-block__radio ppcp-r-settings-block--expert-rdb"
components={ [ >
() => ( <Header>
<> <Title>{ title }</Title>
<Header> <Description>{ description }</Description>
<Title>{ title }</Title> </Header>
<Description>{ description }</Description> { options.map( ( option ) => (
</Header> <PayPalRdbWithContent
{ options.map( ( option ) => ( key={ option.id }
<PayPalRdbWithContent id={ option.id }
key={ option.id } name={ props.actionProps?.name }
id={ option.id } value={ option.value }
name={ props.actionProps?.name } currentValue={ props.actionProps?.currentValue }
value={ option.value } handleRdbState={ ( newValue ) =>
currentValue={ props.actionProps?.currentValue } props.actionProps?.callback(
handleRdbState={ ( newValue ) => props.actionProps?.key,
props.actionProps?.callback( newValue
props.actionProps?.key, )
newValue }
) label={ option.label }
} description={ option.description }
label={ option.label } toggleAdditionalContent={ true }
description={ option.description } >
toggleAdditionalContent={ true } { option.additionalContent }
> </PayPalRdbWithContent>
{ option.additionalContent } ) ) }
</PayPalRdbWithContent> </SettingsBlock>
) ) }
</>
),
] }
/>
); );
export default RadioSettingsBlock; export default RadioSettingsBlock;

View file

@ -35,27 +35,19 @@ const SelectSettingsBlock = ( {
order = DEFAULT_ELEMENT_ORDER, order = DEFAULT_ELEMENT_ORDER,
...props ...props
} ) => ( } ) => (
<SettingsBlock <SettingsBlock { ...props } className="ppcp-r-settings-block__select">
{ ...props } { order.map( ( elementKey ) => {
className="ppcp-r-settings-block__select" const RenderElement = ELEMENT_RENDERERS[ elementKey ];
components={ [ return RenderElement ? (
() => ( <RenderElement
<> key={ elementKey }
{ order.map( ( elementKey ) => { title={ title }
const RenderElement = ELEMENT_RENDERERS[ elementKey ]; description={ description }
return RenderElement ? ( actionProps={ props.actionProps }
<RenderElement />
key={ elementKey } ) : null;
title={ title } } ) }
description={ description } </SettingsBlock>
actionProps={ props.actionProps }
/>
) : null;
} ) }
</>
),
] }
/>
); );
export default SelectSettingsBlock; export default SelectSettingsBlock;

View file

@ -1,16 +1,9 @@
const SettingsBlock = ( { className, components = [], children } ) => { const SettingsBlock = ( { className, children } ) => {
const blockClassName = [ 'ppcp-r-settings-block', className ].filter( const blockClassName = [ 'ppcp-r-settings-block', className ].filter(
Boolean Boolean
); );
return ( return <div className={ blockClassName.join( ' ' ) }>{ children }</div>;
<div className={ blockClassName.join( ' ' ) }>
{ children ||
components.map( ( Component, index ) => (
<Component key={ index } />
) ) }
</div>
);
}; };
export default SettingsBlock; export default SettingsBlock;

View file

@ -3,35 +3,25 @@ import SettingsBlock from './SettingsBlock';
import { Header, Title, Action, Description } from './SettingsBlockElements'; import { Header, Title, Action, Description } from './SettingsBlockElements';
const ToggleSettingsBlock = ( { title, description, ...props } ) => ( const ToggleSettingsBlock = ( { title, description, ...props } ) => (
<SettingsBlock <SettingsBlock { ...props } className="ppcp-r-settings-block__toggle">
{ ...props } <Action>
className="ppcp-r-settings-block__toggle" <ToggleControl
components={ [ className="ppcp-r-settings-block__toggle-control"
() => ( __nextHasNoMarginBottom={ true }
<Action> checked={ props.actionProps?.value }
<ToggleControl onChange={ ( newValue ) =>
className="ppcp-r-settings-block__toggle-control" props.actionProps?.callback(
__nextHasNoMarginBottom={ true } props.actionProps?.key,
checked={ props.actionProps?.value } newValue
onChange={ ( newValue ) => )
props.actionProps?.callback( }
props.actionProps?.key, />
newValue </Action>
) <Header>
} { title && <Title>{ title }</Title> }
/> { description && <Description>{ description }</Description> }
</Action> </Header>
), </SettingsBlock>
() => (
<Header>
{ title && <Title>{ title }</Title> }
{ description && (
<Description>{ description }</Description>
) }
</Header>
),
] }
/>
); );
export default ToggleSettingsBlock; export default ToggleSettingsBlock;

View file

@ -9,55 +9,40 @@ import {
const OrderIntent = ( { updateFormValue, settings } ) => { const OrderIntent = ( { updateFormValue, settings } ) => {
return ( return (
<SettingsBlock <SettingsBlock>
components={ [ <Header>
() => ( <Title>
<> { __( 'Order Intent', 'woocommerce-paypal-payments' ) }
<Header> </Title>
<Title> <Description>
{ __( { __(
'Order Intent', 'Choose between immediate capture or authorization-only, with manual capture in the Order section.',
'woocommerce-paypal-payments' 'woocommerce-paypal-payments'
) } ) }
</Title> </Description>
<Description> </Header>
{ __(
'Choose between immediate capture or authorization-only, with manual capture in the Order section.',
'woocommerce-paypal-payments'
) }
</Description>
</Header>
</>
),
() => (
<>
<ToggleSettingsBlock
title={ __(
'Authorize Only',
'woocommerce-paypal-payments'
) }
actionProps={ {
callback: updateFormValue,
key: 'authorizeOnly',
value: settings.authorizeOnly,
} }
/>
<ToggleSettingsBlock <ToggleSettingsBlock
title={ __( title={ __( 'Authorize Only', 'woocommerce-paypal-payments' ) }
'Capture Virtual-Only Orders', actionProps={ {
'woocommerce-paypal-payments' callback: updateFormValue,
) } key: 'authorizeOnly',
actionProps={ { value: settings.authorizeOnly,
callback: updateFormValue, } }
key: 'captureVirtualOnlyOrders', />
value: settings.captureVirtualOnlyOrders,
} } <ToggleSettingsBlock
/> title={ __(
</> 'Capture Virtual-Only Orders',
), 'woocommerce-paypal-payments'
] } ) }
/> actionProps={ {
callback: updateFormValue,
key: 'captureVirtualOnlyOrders',
value: settings.captureVirtualOnlyOrders,
} }
/>
</SettingsBlock>
); );
}; };

View file

@ -1,82 +1,73 @@
import { __, sprintf } from '@wordpress/i18n'; import { __, sprintf } from '@wordpress/i18n';
import { import {
Header,
SettingsBlock, SettingsBlock,
ToggleSettingsBlock, ToggleSettingsBlock,
Title, Title,
Description, Description,
} from '../../../../ReusableComponents/SettingsBlocks'; } from '../../../../ReusableComponents/SettingsBlocks';
import { Header } from '../../../../ReusableComponents/SettingsBlocks/SettingsBlockElements';
const SavePaymentMethods = ( { updateFormValue, settings } ) => { const SavePaymentMethods = ( { updateFormValue, settings } ) => {
return ( return (
<SettingsBlock <SettingsBlock className="ppcp-r-settings-block--save-payment-methods">
className="ppcp-r-settings-block--save-payment-methods" <Header>
components={ [ <Title>
() => ( { __(
<> 'Save payment methods',
<Header> 'woocommerce-paypal-payments'
<Title> ) }
{ __( </Title>
'Save payment methods', <Description>
{ __(
"Securely store customers' payment methods for future payments and subscriptions, simplifying checkout and enabling recurring transactions.",
'woocommerce-paypal-payments'
) }
</Description>
</Header>
<ToggleSettingsBlock
title={ __(
'Save PayPal and Venmo',
'woocommerce-paypal-payments'
) }
description={
<div
dangerouslySetInnerHTML={ {
__html: sprintf(
/* translators: 1: URL to Pay Later documentation, 2: URL to Alternative Payment Methods documentation */
__(
'Securely store your customers\' PayPal accounts for a seamless checkout experience. <br />This will disable all <a target="_blank" rel="noreferrer" href="%1$s">Pay Later</a> features and <a target="_blank" rel="noreferrer" href="%2$s">Alternative Payment Methods</a> on your site.',
'woocommerce-paypal-payments' 'woocommerce-paypal-payments'
) } ),
</Title> 'https://woocommerce.com/document/woocommerce-paypal-payments/#pay-later',
<Description> 'https://woocommerce.com/document/woocommerce-paypal-payments/#alternative-payment-methods'
{ __( ),
'Securely store customers payment methods for future payments and subscriptions, simplifying checkout and enabling recurring transactions.',
'woocommerce-paypal-payments'
) }
</Description>
</Header>
</>
),
() => (
<ToggleSettingsBlock
title={ __(
'Save PayPal and Venmo',
'woocommerce-paypal-payments'
) }
description={
<div
dangerouslySetInnerHTML={ {
__html: sprintf(
/* translators: 1: URL to Pay Later documentation, 2: URL to Alternative Payment Methods documentation */
__(
'Securely store your customers\' PayPal accounts for a seamless checkout experience. <br />This will disable all <a target="_blank" rel="noreferrer" href="%1$s">Pay Later</a> features and <a target="_blank" rel="noreferrer" href="%2$s">Alternative Payment Methods</a> on your site.',
'woocommerce-paypal-payments'
),
'https://woocommerce.com/document/woocommerce-paypal-payments/#pay-later',
'https://woocommerce.com/document/woocommerce-paypal-payments/#alternative-payment-methods'
),
} }
/>
}
actionProps={ {
value: settings.savePaypalAndVenmo,
callback: updateFormValue,
key: 'savePaypalAndVenmo',
} } } }
/> />
), }
() => ( actionProps={ {
<ToggleSettingsBlock value: settings.savePaypalAndVenmo,
title={ __( callback: updateFormValue,
'Save Credit and Debit Cards', key: 'savePaypalAndVenmo',
'woocommerce-paypal-payments' } }
) } />
description={ __(
"Securely store your customer's credit card.", <ToggleSettingsBlock
'woocommerce-paypal-payments' title={ __(
) } 'Save Credit and Debit Cards',
actionProps={ { 'woocommerce-paypal-payments'
callback: updateFormValue, ) }
key: 'saveCreditCardAndDebitCard', description={ __(
value: settings.saveCreditCardAndDebitCard, "Securely store your customer's credit card.",
} } 'woocommerce-paypal-payments'
/> ) }
), actionProps={ {
] } callback: updateFormValue,
/> key: 'saveCreditCardAndDebitCard',
value: settings.saveCreditCardAndDebitCard,
} }
/>
</SettingsBlock>
); );
}; };

View file

@ -36,36 +36,30 @@ const Troubleshooting = ( { updateFormValue, settings } ) => {
value: settings.logging, value: settings.logging,
} } } }
/> />
<SettingsBlock <SettingsBlock>
components={ [ <Header>
() => ( <Title>
<> { __(
<Header> 'Subscribed PayPal webhooks',
<Title> 'woocommerce-paypal-payments'
{ __( ) }
'Subscribed PayPal webhooks', </Title>
'woocommerce-paypal-payments' <Description>
) } { __(
</Title> 'The following PayPal webhooks are subscribed. More information about the webhooks is available in the',
<Description> 'woocommerce-paypal-payments'
{ __( ) }{ ' ' }
'The following PayPal webhooks are subscribed. More information about the webhooks is available in the', <a href="https://woocommerce.com/document/woocommerce-paypal-payments/#webhook-status">
'woocommerce-paypal-payments' { __(
) }{ ' ' } 'Webhook Status documentation',
<a href="https://woocommerce.com/document/woocommerce-paypal-payments/#webhook-status"> 'woocommerce-paypal-payments'
{ __( ) }
'Webhook Status documentation', </a>
'woocommerce-paypal-payments' .
) } </Description>
</a> </Header>
. <HooksTable data={ hooksExampleData() } />
</Description> </SettingsBlock>
</Header>
<HooksTable data={ hooksExampleData() } />
</>
),
] }
/>
<ButtonSettingsBlock <ButtonSettingsBlock
title={ __( title={ __(