🔀 Merge branch 'trunk'

# Conflicts:
#	modules/ppcp-settings/resources/js/Components/ReusableComponents/TopNavigation.js
This commit is contained in:
Philipp Stracker 2025-01-29 16:46:56 +01:00
commit 686f131246
No known key found for this signature in database
14 changed files with 572 additions and 232 deletions

View file

@ -1,4 +1,4 @@
import { useEffect, useMemo } from '@wordpress/element';
import { useEffect, useMemo, useState } from '@wordpress/element';
import classNames from 'classnames';
import { OnboardingHooks, CommonHooks } from '../data';
@ -31,6 +31,8 @@ const SettingsApp = () => {
loading: ! onboardingIsReady,
} );
const [ activePanel, setActivePanel ] = useState( 'overview' );
const Content = useMemo( () => {
if ( ! onboardingIsReady || ! merchantIsReady ) {
return <SpinnerOverlay />;
@ -44,12 +46,18 @@ const SettingsApp = () => {
return <OnboardingScreen />;
}
return <SettingsScreen />;
return (
<SettingsScreen
activePanel={ activePanel }
setActivePanel={ setActivePanel }
/>
);
}, [
isSendOnlyCountry,
merchantIsReady,
onboardingCompleted,
onboardingIsReady,
activePanel,
] );
return <div className={ wrapperClass }>{ Content }</div>;

View file

@ -1,26 +1,14 @@
import { useCallback, useEffect, useState } from '@wordpress/element';
import { useCallback, useEffect } from '@wordpress/element';
// TODO: Migrate to Tabs (TabPanel v2) once its API is publicly available, as it provides programmatic tab switching support: https://github.com/WordPress/gutenberg/issues/52997
import { TabPanel } from '@wordpress/components';
import { getQuery, updateQueryString } from '../../utils/navigation';
const TabNavigation = ( { tabs } ) => {
const { panel } = getQuery();
import { updateQueryString } from '../../utils/navigation';
const TabNavigation = ( { tabs, activePanel, setActivePanel } ) => {
const isValidTab = ( tabsList, checkTab ) => {
return tabsList.some( ( tab ) => tab.name === checkTab );
};
const getValidInitialPanel = () => {
if ( ! panel || ! isValidTab( tabs, panel ) ) {
return tabs[ 0 ].name;
}
return panel;
};
const [ activePanel, setActivePanel ] = useState( getValidInitialPanel );
const updateActivePanel = useCallback(
( tabName ) => {
if ( isValidTab( tabs, tabName ) ) {
@ -29,7 +17,7 @@ const TabNavigation = ( { tabs } ) => {
console.warn( `Invalid tab name: ${ tabName }` );
}
},
[ tabs ]
[ tabs, setActivePanel ]
);
useEffect( () => {
@ -43,7 +31,7 @@ const TabNavigation = ( { tabs } ) => {
onSelect={ updateActivePanel }
tabs={ tabs }
>
{ ( { Component } ) => Component }
{ () => '' }
</TabPanel>
);
};

View file

@ -6,6 +6,7 @@ import classNames from 'classnames';
import useIsScrolled from '../../hooks/useIsScrolled';
import { useNavigation } from '../../hooks/useNavigation';
import BusyStateWrapper from './BusyStateWrapper';
import TabNavigation from './TabNavigation';
const TopNavigation = ( {
title,
@ -16,6 +17,9 @@ const TopNavigation = ( {
showProgressBar = false,
progressBarPercent = 0,
subNavigation = null,
tabs = [],
activePanel = '',
setActivePanel = () => {},
} ) => {
const { goToWooCommercePaymentsTab } = useNavigation();
const { isScrolled } = useIsScrolled();
@ -44,27 +48,38 @@ const TopNavigation = ( {
<>
<nav className={ className }>
<div className="ppcp-r-navigation">
<BusyStateWrapper
className="ppcp-r-navigation--left"
busySpinner={ false }
enabled={ ! exitOnTitleClick }
>
<Button
variant="link"
onClick={ handleTitleClick }
className="is-title"
<div className="ppcp-r-navigation--row">
<BusyStateWrapper
className="ppcp-r-navigation--left"
busySpinner={ false }
enabled={ ! exitOnTitleClick }
>
<Icon icon={ chevronLeft } />
<span className={ titleClassName }>{ title }</span>
</Button>
</BusyStateWrapper>
<Button
variant="link"
onClick={ handleTitleClick }
className="is-title"
>
<Icon icon={ chevronLeft } />
<span className={ titleClassName }>
{ title }
</span>
</Button>
</BusyStateWrapper>
<BusyStateWrapper
className="ppcp-r-navigation--right"
busySpinner={ false }
>
{ children }
</BusyStateWrapper>
<BusyStateWrapper
className="ppcp-r-navigation--right"
busySpinner={ false }
>
{ children }
</BusyStateWrapper>
</div>
{ tabs.length > 0 && (
<TabNavigation
tabs={ tabs }
activePanel={ activePanel }
setActivePanel={ setActivePanel }
/>
) }
{ showProgressBar && (
<ProgressBar percent={ progressBarPercent } />

View file

@ -4,13 +4,24 @@ import { __ } from '@wordpress/i18n';
import TopNavigation from '../../../ReusableComponents/TopNavigation';
import { useSaveSettings } from '../../../../hooks/useSaveSettings';
const SettingsNavigation = ( { canSave = true } ) => {
const SettingsNavigation = ( {
canSave = true,
tabs,
activePanel,
setActivePanel,
} ) => {
const { persistAll } = useSaveSettings();
const title = __( 'PayPal Payments', 'woocommerce-paypal-payments' );
return (
<TopNavigation title={ title } exitOnTitleClick={ true }>
<TopNavigation
title={ title }
exitOnTitleClick={ true }
tabs={ tabs }
activePanel={ activePanel }
setActivePanel={ setActivePanel }
>
{ canSave && (
<Button variant="primary" onClick={ persistAll }>
{ __( 'Save', 'woocommerce-paypal-payments' ) }

View file

@ -1,17 +1,18 @@
import Container from '../../ReusableComponents/Container';
import TabNavigation from '../../ReusableComponents/TabNavigation';
import { getSettingsTabs } from './Tabs';
import SettingsNavigation from './Components/Navigation';
import { getSettingsTabs } from './Tabs';
const SettingsScreen = () => {
const SettingsScreen = ( { activePanel, setActivePanel } ) => {
const tabs = getSettingsTabs();
const { Component } = tabs.find( ( tab ) => tab.name === activePanel );
return (
<>
<SettingsNavigation />
<Container page="settings">
<TabNavigation tabs={ tabs }></TabNavigation>
</Container>
<SettingsNavigation
tabs={ tabs }
activePanel={ activePanel }
setActivePanel={ setActivePanel }
/>
<Container page="settings">{ Component }</Container>
</>
);
};

View file

@ -21,25 +21,28 @@ const useHooks = () => {
// Persistent accessors.
const [ invoicePrefix, setInvoicePrefix ] =
usePersistent( 'invoicePrefix' );
const [ brandName, setBrandName ] = usePersistent( 'brandName' );
const [ softDescriptor, setSoftDescriptor ] =
usePersistent( 'softDescriptor' );
const [ subtotalAdjustment, setSubtotalAdjustment ] =
usePersistent( 'subtotalAdjustment' );
const [ landingPage, setLandingPage ] = usePersistent( 'landingPage' );
const [ buttonLanguage, setButtonLanguage ] =
usePersistent( 'buttonLanguage' );
const [ authorizeOnly, setAuthorizeOnly ] =
usePersistent( 'authorizeOnly' );
const [ captureVirtualOnlyOrders, setCaptureVirtualOnlyOrders ] =
usePersistent( 'captureVirtualOnlyOrders' );
usePersistent( 'captureVirtualOrders' );
const [ savePaypalAndVenmo, setSavePaypalAndVenmo ] =
usePersistent( 'savePaypalAndVenmo' );
const [ saveCardDetails, setSaveCardDetails ] =
usePersistent( 'saveCardDetails' );
const [ payNowExperience, setPayNowExperience ] =
usePersistent( 'payNowExperience' );
const [ logging, setLogging ] = usePersistent( 'logging' );
const [ subtotalAdjustment, setSubtotalAdjustment ] =
usePersistent( 'subtotalAdjustment' );
const [ brandName, setBrandName ] = usePersistent( 'brandName' );
const [ softDescriptor, setSoftDescriptor ] =
usePersistent( 'softDescriptor' );
const [ landingPage, setLandingPage ] = usePersistent( 'landingPage' );
const [ buttonLanguage, setButtonLanguage ] =
usePersistent( 'buttonLanguage' );
usePersistent( 'enablePayNow' );
const [ logging, setLogging ] = usePersistent( 'enableLogging' );
const [ disabledCards, setDisabledCards ] =
usePersistent( 'disabledCards' );

View file

@ -25,18 +25,25 @@ const defaultTransient = Object.freeze( {
* These represent the core PayPal payment settings configuration.
*/
const defaultPersistent = Object.freeze( {
// String values.
invoicePrefix: '', // Prefix for PayPal invoice IDs
authorizeOnly: false, // Whether to only authorize payments initially
captureVirtualOnlyOrders: false, // Auto-capture virtual-only orders
savePaypalAndVenmo: false, // Enable PayPal & Venmo vaulting
saveCardDetails: false, // Enable card vaulting
payNowExperience: false, // Enable Pay Now experience
logging: false, // Enable debug logging
subtotalAdjustment: 'skip_details', // Handling for subtotal mismatches
brandName: '', // Merchant brand name for PayPal
softDescriptor: '', // Payment descriptor on statements
landingPage: 'any', // PayPal checkout landing page
// Limited value strings.
subtotalAdjustment: 'no_details', // [correction|no_details] Handling for subtotal mismatches
landingPage: 'any', // [any|login|guest_checkout] PayPal checkout landing page
buttonLanguage: '', // Language for PayPal buttons
// Boolean flags.
authorizeOnly: false, // Whether to only authorize payments initially
captureVirtualOrders: false, // Auto-capture virtual-only orders
savePaypalAndVenmo: false, // Enable PayPal & Venmo vaulting
saveCardDetails: false, // Enable card vaulting
enablePayNow: false, // Enable Pay Now experience
enableLogging: false, // Enable debug logging
// String arrays.
disabledCards: [], // Disabled credit card types
} );