From 01588f21687cd29a33189fdba00bddc761af7578 Mon Sep 17 00:00:00 2001 From: Joel Thiessen <444632+joelclimbsthings@users.noreply.github.com> Date: Thu, 10 Jun 2021 10:10:42 -0700 Subject: [PATCH] Fixing local WCPay payment task and refactoring for dumber components (https://github.com/woocommerce/woocommerce-admin/pull/7151) --- .../task-list/tasks/payments/LocalPayments.js | 15 +- .../components/List/Item.js | 31 +-- .../components/List/List.js | 14 +- .../components/Setup/Connect.js | 14 +- .../components/Setup/Setup.js | 73 ++++---- .../PaymentGatewaySuggestions/index.js | 177 ++++++++++-------- ...{WCPayMethodCard.js => WCPaySuggestion.js} | 17 +- .../task-list/tasks/payments/methods/index.js | 2 +- .../WooPaymentGatewayConnect/README.md | 1 + .../WooPaymentGatewaySetup/README.md | 1 + plugins/woocommerce-admin/readme.txt | 1 + .../PaymentGatewaySuggestions/Init.php | 20 -- 12 files changed, 191 insertions(+), 175 deletions(-) rename plugins/woocommerce-admin/client/task-list/tasks/payments/components/{WCPayMethodCard.js => WCPaySuggestion.js} (79%) diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/LocalPayments.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/LocalPayments.js index ed449dfa04f..d29e94ef68b 100644 --- a/plugins/woocommerce-admin/client/task-list/tasks/payments/LocalPayments.js +++ b/plugins/woocommerce-admin/client/task-list/tasks/payments/LocalPayments.js @@ -17,7 +17,7 @@ import { useMemo, useState } from '@wordpress/element'; * Internal dependencies */ import { PaymentMethodList } from './components/PaymentMethodList'; -import { WCPayMethodCard } from './components/WCPayMethodCard'; +import { WCPaySuggestion } from './components/WCPaySuggestion'; import { getCountryCode } from '../../../dashboard/utils'; import { getPaymentMethods } from './methods'; import { PaymentSetup } from './components/PaymentSetup'; @@ -219,12 +219,21 @@ export const LocalPayments = ( { query } ) => { const wcPayMethod = wcPayIndex === -1 ? null - : additionalCardMethods.splice( wcPayIndex, 1 ); + : additionalCardMethods.splice( wcPayIndex, 1 )[ 0 ]; return (
{ !! wcPayMethod && ( - + ) } { !! enabledCardMethods.length && ( diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/PaymentGatewaySuggestions/components/List/Item.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/PaymentGatewaySuggestions/components/List/Item.js index 90c1bcb006a..c0910e33295 100644 --- a/plugins/woocommerce-admin/client/task-list/tasks/payments/PaymentGatewaySuggestions/components/List/Item.js +++ b/plugins/woocommerce-admin/client/task-list/tasks/payments/PaymentGatewaySuggestions/components/List/Item.js @@ -18,31 +18,32 @@ import './List.scss'; export const Item = ( { isRecommended, markConfigured, - paymentGateways, - suggestion, - suggestionKeys, + paymentGateway, + gatewayIds, } ) => { - const { image, content, id, plugins = [], title, loading } = suggestion; + const { + image, + content, + id, + plugins = [], + title, + loading, + enabled: isEnabled = false, + needsSetup = false, + requiredSettings, + settingsUrl: manageUrl, + } = paymentGateway; const connectSlot = useSlot( `woocommerce_payment_gateway_connect_${ id }` ); const setupSlot = useSlot( `woocommerce_payment_gateway_setup_${ id }` ); - const paymentGateway = paymentGateways[ id ] || {}; - - const { - enabled: isEnabled = false, - needs_setup: needsSetup = false, - required_settings_keys: requiredSettingsKeys = [], - settings_url: manageUrl, - } = paymentGateway; - const hasFills = Boolean( connectSlot?.fills?.length ) || Boolean( setupSlot?.fills?.length ); const hasSetup = Boolean( - plugins.length || requiredSettingsKeys.length || hasFills + plugins.length || requiredSettings.length || hasFills ); const showRecommendedRibbon = isRecommended && needsSetup; @@ -84,7 +85,7 @@ export const Item = ( { markConfigured={ markConfigured } onSetup={ () => recordEvent( 'tasklist_payment_setup', { - options: suggestionKeys, + options: gatewayIds, selected: id, } ) } diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/PaymentGatewaySuggestions/components/List/List.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/PaymentGatewaySuggestions/components/List/List.js index ff166ffa538..87a78eb7a97 100644 --- a/plugins/woocommerce-admin/client/task-list/tasks/payments/PaymentGatewaySuggestions/components/List/List.js +++ b/plugins/woocommerce-admin/client/task-list/tasks/payments/PaymentGatewaySuggestions/components/List/List.js @@ -13,23 +13,23 @@ import './List.scss'; export const List = ( { heading, markConfigured, - paymentGateways, recommendation, - suggestions, + paymentGateways, } ) => { return ( { heading } - { Array.from( suggestions.values() ).map( ( suggestion ) => { - const { id } = suggestion; + { paymentGateways.map( ( paymentGateway ) => { + const { id } = paymentGateway; return ( gateway.id + ) } /> ); } ) } diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/PaymentGatewaySuggestions/components/Setup/Connect.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/PaymentGatewaySuggestions/components/Setup/Connect.js index 53f72cd7167..5bcd862079b 100644 --- a/plugins/woocommerce-admin/client/task-list/tasks/payments/PaymentGatewaySuggestions/components/Setup/Connect.js +++ b/plugins/woocommerce-admin/client/task-list/tasks/payments/PaymentGatewaySuggestions/components/Setup/Connect.js @@ -21,23 +21,17 @@ export const Connect = ( { } ) => { const { id, - connection_url: connectionUrl, - setup_help_text: setupHelpText, - required_settings_keys: settingKeys, - settings, - settings_url: settingsUrl, + connectionUrl, + setupHelpText, + settingsUrl, title, + requiredSettings: fields, } = paymentGateway; const { createNotice } = useDispatch( 'core/notices' ); const { updatePaymentGateway } = useDispatch( PAYMENT_GATEWAYS_STORE_NAME ); const slot = useSlot( `woocommerce_payment_gateway_connect_${ id }` ); const hasFills = Boolean( slot?.fills?.length ); - const fields = settingKeys - ? settingKeys - .map( ( settingKey ) => settings[ settingKey ] ) - .filter( Boolean ) - : []; const { isUpdating } = useSelect( ( select ) => { const { isPaymentGatewayUpdating } = select( diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/PaymentGatewaySuggestions/components/Setup/Setup.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/PaymentGatewaySuggestions/components/Setup/Setup.js index c7fa96669d5..5fb62478a84 100644 --- a/plugins/woocommerce-admin/client/task-list/tasks/payments/PaymentGatewaySuggestions/components/Setup/Setup.js +++ b/plugins/woocommerce-admin/client/task-list/tasks/payments/PaymentGatewaySuggestions/components/Setup/Setup.js @@ -14,7 +14,7 @@ import { Plugins, Stepper } from '@woocommerce/components'; import { WooPaymentGatewaySetup } from '@woocommerce/onboarding'; import { recordEvent } from '@woocommerce/tracks'; import { useEffect, useState, useMemo, useCallback } from '@wordpress/element'; -import { useSelect } from '@wordpress/data'; +import { useSelect, useDispatch } from '@wordpress/data'; import { useSlot } from '@woocommerce/experimental'; /** @@ -26,10 +26,16 @@ import './Setup.scss'; export const Setup = ( { markConfigured, - suggestion, + paymentGateway, recordConnectStartEvent, } ) => { - const { id, plugins = [], title } = suggestion; + const { + id, + plugins = [], + title, + postInstallScripts, + installed: gatewayInstalled, + } = paymentGateway; const slot = useSlot( `woocommerce_payment_gateway_setup_${ id }` ); const hasFills = Boolean( slot?.fills?.length ); const [ isPluginLoaded, setIsPluginLoaded ] = useState( false ); @@ -40,16 +46,17 @@ export const Setup = ( { } ); }, [] ); + const { invalidateResolutionForStoreSelector } = useDispatch( + PAYMENT_GATEWAYS_STORE_NAME + ); + const { isOptionUpdating, isPaymentGatewayResolving, needsPluginInstall, - paymentGateway, } = useSelect( ( select ) => { const { isOptionsUpdating } = select( OPTIONS_STORE_NAME ); - const { getPaymentGateway, isResolving } = select( - PAYMENT_GATEWAYS_STORE_NAME - ); + const { isResolving } = select( PAYMENT_GATEWAYS_STORE_NAME ); const activePlugins = select( PLUGINS_STORE_NAME ).getActivePlugins(); const pluginsToInstall = plugins.filter( ( m ) => ! activePlugins.includes( m ) @@ -57,22 +64,16 @@ export const Setup = ( { return { isOptionUpdating: isOptionsUpdating(), - isPaymentGatewayResolving: isResolving( 'getPaymentGateway', [ - id, - ] ), - paymentGateway: ! pluginsToInstall.length - ? getPaymentGateway( id ) - : null, + isPaymentGatewayResolving: isResolving( 'getPaymentGateways' ), needsPluginInstall: !! pluginsToInstall.length, }; } ); useEffect( () => { - if ( ! paymentGateway ) { + if ( needsPluginInstall ) { return; } - const { post_install_scripts: postInstallScripts } = paymentGateway; if ( postInstallScripts && postInstallScripts.length ) { const scriptPromises = postInstallScripts.map( ( script ) => enqueueScript( script ) @@ -84,7 +85,7 @@ export const Setup = ( { } setIsPluginLoaded( true ); - }, [ paymentGateway ] ); + }, [ postInstallScripts, needsPluginInstall ] ); const pluginNamesString = plugins .map( ( pluginSlug ) => pluginNames[ pluginSlug ] ) @@ -103,6 +104,9 @@ export const Setup = ( { { createNoticesFromResponse( response ); + invalidateResolutionForStoreSelector( + 'getPaymentGateways' + ); recordEvent( 'tasklist_payment_install_method', { @@ -122,22 +126,25 @@ export const Setup = ( { : null; }, [ needsPluginInstall ] ); - const connectStep = { - key: 'connect', - label: sprintf( - __( 'Connect your %(title)s account', 'woocommerce-admin' ), - { - title, - } - ), - content: paymentGateway ? ( - - ) : null, - }; + const connectStep = useMemo( + () => ( { + key: 'connect', + label: sprintf( + __( 'Connect your %(title)s account', 'woocommerce-admin' ), + { + title, + } + ), + content: gatewayInstalled ? ( + + ) : null, + } ), + [ gatewayInstalled ] + ); const stepperPending = ! installStep?.isComplete || @@ -155,7 +162,7 @@ export const Setup = ( { { ...props } /> ), - [ stepperPending, installStep ] + [ stepperPending, installStep, connectStep ] ); return ( diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/PaymentGatewaySuggestions/index.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/PaymentGatewaySuggestions/index.js index d9ef39e900f..07c4491e827 100644 --- a/plugins/woocommerce-admin/client/task-list/tasks/payments/PaymentGatewaySuggestions/index.js +++ b/plugins/woocommerce-admin/client/task-list/tasks/payments/PaymentGatewaySuggestions/index.js @@ -17,7 +17,7 @@ import { useMemo, useCallback } from '@wordpress/element'; */ import { List, Placeholder as ListPlaceholder } from './components/List'; import { Setup, Placeholder as SetupPlaceholder } from './components/Setup'; -import { WCPayMethodCard } from '../components/WCPayMethodCard'; +import { WCPaySuggestion } from '../components/WCPaySuggestion'; import './plugins/Bacs'; const RECOMMENDED_GATEWAY_IDS = [ @@ -28,63 +28,65 @@ const RECOMMENDED_GATEWAY_IDS = [ export const PaymentGatewaySuggestions = ( { query } ) => { const { updatePaymentGateway } = useDispatch( PAYMENT_GATEWAYS_STORE_NAME ); - const { - additionalSuggestions, - enabledSuggestions, - getPaymentGateway, - paymentGateways, - suggestions, - isResolving, - wcPaySuggestion, - } = useSelect( ( select ) => { - const gateways = select( PAYMENT_GATEWAYS_STORE_NAME ) - .getPaymentGateways() - .reduce( ( map, gateway ) => { - map[ gateway.id ] = gateway; - return map; - }, {} ); - - const enabled = new Map(); - const additional = new Map(); - let wcPay = null; - const mappedSuggestions = select( ONBOARDING_STORE_NAME ) - .getPaymentGatewaySuggestions() - .reduce( ( map, suggestion ) => { - const { id } = suggestion; - map.set( id, suggestion ); - - // WCPay is handled separately when not installed and configured - if ( - id === 'woocommerce_payments' && - ! ( gateways[ id ] && ! gateways[ id ].needs_setup ) - ) { - wcPay = suggestion; + const { getPaymentGateway, paymentGateways, isResolving } = useSelect( + ( select ) => { + const installedPaymentGateways = select( + PAYMENT_GATEWAYS_STORE_NAME + ) + .getPaymentGateways() + .reduce( ( map, gateway ) => { + map[ gateway.id ] = gateway; return map; - } + }, {} ); - if ( gateways[ id ] && gateways[ id ].enabled ) { - enabled.set( id, suggestion ); - } else { - additional.set( id, suggestion ); - } + const mappedSuggestions = select( ONBOARDING_STORE_NAME ) + .getPaymentGatewaySuggestions() + .reduce( ( map, suggestion ) => { + const { id } = suggestion; + const installedGateway = installedPaymentGateways[ + suggestion.id + ] + ? installedPaymentGateways[ id ] + : {}; - return map; - }, new Map() ); + const enrichedSuggestion = { + installed: !! installedPaymentGateways[ id ], + postInstallScripts: + installedGateway.post_install_scripts, + enabled: installedGateway.enabled, + needsSetup: installedGateway.needs_setup, + settingsUrl: installedGateway.settings_url, + connectionUrl: installedGateway.connection_url, + setupHelpText: installedGateway.setup_help_text, + title: installedGateway.title, + requiredSettings: installedGateway.required_settings_keys + ? installedGateway.required_settings_keys + .map( + ( settingKey ) => + installedGateway.settings[ + settingKey + ] + ) + .filter( Boolean ) + : [], + ...suggestion, + }; - return { - additionalSuggestions: additional, - enabledSuggestions: enabled, - getPaymentGateway: select( PAYMENT_GATEWAYS_STORE_NAME ) - .getPaymentGateway, - getOption: select( OPTIONS_STORE_NAME ).getOption, - isResolving: select( ONBOARDING_STORE_NAME ).isResolving( - 'getPaymentGatewaySuggestions' - ), - paymentGateways: gateways, - suggestions: mappedSuggestions, - wcPaySuggestion: wcPay, - }; - } ); + map.set( id, enrichedSuggestion ); + return map; + }, new Map() ); + + return { + getPaymentGateway: select( PAYMENT_GATEWAYS_STORE_NAME ) + .getPaymentGateway, + getOption: select( OPTIONS_STORE_NAME ).getOption, + isResolving: select( ONBOARDING_STORE_NAME ).isResolving( + 'getPaymentGatewaySuggestions' + ), + paymentGateways: mappedSuggestions, + }; + } + ); const enablePaymentGateway = ( id ) => { if ( ! id ) { @@ -104,7 +106,7 @@ export const PaymentGatewaySuggestions = ( { query } ) => { const markConfigured = useCallback( async ( id, queryParams = {} ) => { - if ( ! suggestions.get( id ) ) { + if ( ! paymentGateways.get( id ) ) { throw `Payment gateway ${ id } not found in available gateways list`; } @@ -118,47 +120,72 @@ export const PaymentGatewaySuggestions = ( { query } ) => { getNewPath( { ...queryParams, task: 'payments' }, '/', {} ) ); }, - [ paymentGateways, suggestions ] + [ paymentGateways ] ); - const recordConnectStartEvent = useCallback( ( gatewayKey ) => { + const recordConnectStartEvent = useCallback( ( gatewayId ) => { recordEvent( 'tasklist_payment_connect_start', { - payment_method: gatewayKey, + payment_method: gatewayId, } ); }, [] ); const recommendation = useMemo( () => { for ( const id in RECOMMENDED_GATEWAY_IDS ) { - const gateway = suggestions.get( id ); + const gateway = paymentGateways.get( id ); if ( gateway ) { return gateway; } } return null; - }, [ suggestions ] ); + }, [ paymentGateways ] ); - const currentSuggestion = useMemo( () => { - if ( ! query.id || isResolving || ! suggestions.size ) { + const currentGateway = useMemo( () => { + if ( ! query.id || isResolving || ! paymentGateways.size ) { return null; } - const gateway = suggestions.get( query.id ); + const gateway = paymentGateways.get( query.id ); if ( ! gateway ) { throw `Current gateway ${ query.id } not found in available gateways list`; } return gateway; - }, [ isResolving, query, suggestions ] ); + }, [ isResolving, query, paymentGateways ] ); - if ( query.id && ! currentSuggestion ) { + const [ wcPayGateway, enabledGateways, additionalGateways ] = useMemo( + () => + Array.from( paymentGateways.values() ).reduce( + ( all, gateway ) => { + const [ wcPay, enabled, additional ] = all; + + // WCPay is handled separately when not installed and configured + if ( + gateway.id === 'woocommerce_payments' && + ! ( gateway.installed && ! gateway.needsSetup ) + ) { + wcPay.push( gateway ); + } else if ( gateway.enabled ) { + enabled.push( gateway ); + } else { + additional.push( gateway ); + } + + return all; + }, + [ [], [], [] ] + ), + [ paymentGateways ] + ); + + if ( query.id && ! currentGateway ) { return ; } - if ( currentSuggestion ) { + if ( currentGateway ) { return ( @@ -167,33 +194,31 @@ export const PaymentGatewaySuggestions = ( { query } ) => { return (
- { ! suggestions.size && } + { ! paymentGateways.size && } - { !! wcPaySuggestion && ( - + { !! wcPayGateway.length && ( + ) } - { !! enabledSuggestions.size && ( + { !! enabledGateways.length && ( ) } - { !! additionalSuggestions.size && ( + { !! additionalGateways.length && ( ) } diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/components/WCPayMethodCard.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/components/WCPaySuggestion.js similarity index 79% rename from plugins/woocommerce-admin/client/task-list/tasks/payments/components/WCPayMethodCard.js rename to plugins/woocommerce-admin/client/task-list/tasks/payments/components/WCPaySuggestion.js index c0079f28ee4..172e0a95a23 100644 --- a/plugins/woocommerce-admin/client/task-list/tasks/payments/components/WCPayMethodCard.js +++ b/plugins/woocommerce-admin/client/task-list/tasks/payments/components/WCPaySuggestion.js @@ -4,10 +4,8 @@ import { __ } from '@wordpress/i18n'; import interpolateComponents from 'interpolate-components'; import { Link, Pill } from '@woocommerce/components'; -import { PAYMENT_GATEWAYS_STORE_NAME } from '@woocommerce/data'; import { recordEvent } from '@woocommerce/tracks'; import { Text } from '@woocommerce/experimental'; -import { useSelect } from '@wordpress/data'; import { WCPayCard, WCPayCardHeader, @@ -39,14 +37,11 @@ const TosPrompt = () => }, } ); -export const WCPayMethodCard = ( { suggestion } ) => { - const { description, id } = suggestion; - const paymentGateway = useSelect( ( select ) => { - return ( - select( PAYMENT_GATEWAYS_STORE_NAME ).getPaymentGateway( id ) || {} - ); - } ); - const { enabled: isEnabled, needs_setup: needsSetup } = paymentGateway; +export const WCPaySuggestion = ( { + paymentGateway, + onSetupCallback = null, +} ) => { + const { description, id, needsSetup, isEnabled } = paymentGateway; return ( @@ -64,6 +59,7 @@ export const WCPayMethodCard = ( { suggestion } ) => { recordEvent( 'tasklist_payment_learn_more' ); } } /> + <> @@ -79,6 +75,7 @@ export const WCPayMethodCard = ( { suggestion } ) => { 'Get started', 'woocommerce-admin' ) } + onSetupCallback={ onSetupCallback } /> diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/index.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/index.js index 992747f62be..e8b272ff2f7 100644 --- a/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/index.js +++ b/plugins/woocommerce-admin/client/task-list/tasks/payments/methods/index.js @@ -578,7 +578,7 @@ export function getPaymentMethods( { ), before: ( { ), diff --git a/plugins/woocommerce-admin/packages/onboarding/src/components/WooPaymentGatewayConnect/README.md b/plugins/woocommerce-admin/packages/onboarding/src/components/WooPaymentGatewayConnect/README.md index 7a19d0b7549..e3bcc82cf55 100644 --- a/plugins/woocommerce-admin/packages/onboarding/src/components/WooPaymentGatewayConnect/README.md +++ b/plugins/woocommerce-admin/packages/onboarding/src/components/WooPaymentGatewayConnect/README.md @@ -22,6 +22,7 @@ This is the fill component. You must provide the `id` prop to identify the slot | `defaultSubmit` | Function | The default submit handler that is provided to the
component | | `defaultFields` | Array | An array of the field configuration objects provided by the API | | `markConfigured` | Function | A helper function that will mark your gateway as configured | +| `paymentGateway` | Object | An object describing all of the relevant data pertaining to this payment gateway | ### WooPaymentGatewayConnect.Slot (slot) diff --git a/plugins/woocommerce-admin/packages/onboarding/src/components/WooPaymentGatewaySetup/README.md b/plugins/woocommerce-admin/packages/onboarding/src/components/WooPaymentGatewaySetup/README.md index 229039c7ecf..50313ed7ab6 100644 --- a/plugins/woocommerce-admin/packages/onboarding/src/components/WooPaymentGatewaySetup/README.md +++ b/plugins/woocommerce-admin/packages/onboarding/src/components/WooPaymentGatewaySetup/README.md @@ -21,6 +21,7 @@ This is the fill component. You must provide the `id` prop to identify the slot | `defaultStepper` | Component | The default instance of the component. Any provided props will override the given defaults | | `defaultInstallStep` | Object | The object that describes the default step configuration for installation of the gateway | | `defaultConnectStep` | Object | The object that describes the default step configuration for configuration of the gateway | +| `paymentGateway` | Object | An object describing all of the relevant data pertaining to this payment gateway | ### WooPaymentGatewaySetup.Slot (slot) diff --git a/plugins/woocommerce-admin/readme.txt b/plugins/woocommerce-admin/readme.txt index 564f6c0aa42..12562333c46 100644 --- a/plugins/woocommerce-admin/readme.txt +++ b/plugins/woocommerce-admin/readme.txt @@ -75,6 +75,7 @@ Release and roadmap notes are available on the [WooCommerce Developers Blog](htt == Unreleased == +- Fix: WCPay not working in local payments task #7151 - Dev: Add Jetpack Backup admin note #6738 - Add: Adding WCPay payment configuration defaults. #7097 - Fix: Transformer casing is incorrect and creates an error on case-sensitive systems #7104 diff --git a/plugins/woocommerce-admin/src/Features/PaymentGatewaySuggestions/Init.php b/plugins/woocommerce-admin/src/Features/PaymentGatewaySuggestions/Init.php index 3e5a87f3fb7..36e15ebc9b6 100644 --- a/plugins/woocommerce-admin/src/Features/PaymentGatewaySuggestions/Init.php +++ b/plugins/woocommerce-admin/src/Features/PaymentGatewaySuggestions/Init.php @@ -106,26 +106,6 @@ class Init { $data = (object) array_merge( (array) $locale, (array) $spec ); unset( $data->locales ); - $data->fields = array(); - - // Loop over and localize fields. - foreach ( $spec->fields as $field ) { - if ( ! isset( $field->locales ) ) { - continue; - } - - $locale = SpecRunner::get_locale( $field->locales ); - - if ( ! $locale ) { - continue; - } - - $field_data = (object) array_merge( (array) $field, (array) $locale ); - unset( $field_data->locale ); - unset( $field_data->locales ); - $data->fields[] = $field_data; - } - $localized_specs[] = $data; }