diff --git a/src/components/Accordion/index.tsx b/src/components/Accordion/index.tsx new file mode 100644 index 000000000000..40f51a45343d --- /dev/null +++ b/src/components/Accordion/index.tsx @@ -0,0 +1,60 @@ +import type {ReactNode} from 'react'; +import React from 'react'; +import type {StyleProp, ViewStyle} from 'react-native'; +import {View} from 'react-native'; +import type {SharedValue} from 'react-native-reanimated'; +import Animated, {Easing, useAnimatedStyle, useDerivedValue, useSharedValue, withTiming} from 'react-native-reanimated'; +import useThemeStyles from '@hooks/useThemeStyles'; + +type AccordionProps = { + /** Giving information whether the component is open */ + isExpanded: SharedValue; + + /** Element that is inside Accordion */ + children: ReactNode; + + /** Duration of expansion animation */ + duration?: number; + + /** Additional external style */ + style?: StyleProp; +}; + +function Accordion({isExpanded, children, duration = 300, style}: AccordionProps) { + const height = useSharedValue(0); + const styles = useThemeStyles(); + + const derivedHeight = useDerivedValue(() => + withTiming(height.get() * Number(isExpanded.get()), { + duration, + easing: Easing.inOut(Easing.quad), + }), + ); + + const derivedOpacity = useDerivedValue(() => + withTiming(isExpanded.get() ? 1 : 0, { + duration, + easing: Easing.inOut(Easing.quad), + }), + ); + + const bodyStyle = useAnimatedStyle(() => ({ + height: derivedHeight.get(), + opacity: derivedOpacity.get(), + })); + + return ( + + { + height.set(e.nativeEvent.layout.height); + }} + style={[styles.pAbsolute, styles.l0, styles.r0, styles.t0]} + > + {children} + + + ); +} + +export default Accordion; diff --git a/src/pages/workspace/accounting/intacct/advanced/SageIntacctAdvancedPage.tsx b/src/pages/workspace/accounting/intacct/advanced/SageIntacctAdvancedPage.tsx index 2d2480835f6e..36656b3ef357 100644 --- a/src/pages/workspace/accounting/intacct/advanced/SageIntacctAdvancedPage.tsx +++ b/src/pages/workspace/accounting/intacct/advanced/SageIntacctAdvancedPage.tsx @@ -1,4 +1,6 @@ -import React, {useMemo} from 'react'; +import React, {useEffect, useMemo} from 'react'; +import {useSharedValue} from 'react-native-reanimated'; +import Accordion from '@components/Accordion'; import ConnectionLayout from '@components/ConnectionLayout'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -33,6 +35,11 @@ function SageIntacctAdvancedPage({policy}: WithPolicyProps) { const {importEmployees, autoSync, sync, pendingFields, errorFields} = policy?.connections?.intacct?.config ?? {}; const {data, config} = policy?.connections?.intacct ?? {}; + const isAccordionExpanded = useSharedValue(!!sync?.syncReimbursedReports); + + useEffect(() => { + isAccordionExpanded.set(!!sync?.syncReimbursedReports); + }, [isAccordionExpanded, sync?.syncReimbursedReports]); const toggleSections = useMemo( () => [ @@ -113,20 +120,23 @@ function SageIntacctAdvancedPage({policy}: WithPolicyProps) { /> ))} - {!!sync?.syncReimbursedReports && ( + Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_PAYMENT_ACCOUNT.getRoute(policyID))} brickRoadIndicator={areSettingsInErrorFields([CONST.SAGE_INTACCT_CONFIG.REIMBURSEMENT_ACCOUNT_ID], errorFields) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} /> - )} + ); } diff --git a/src/pages/workspace/accounting/intacct/import/SageIntacctToggleMappingsPage.tsx b/src/pages/workspace/accounting/intacct/import/SageIntacctToggleMappingsPage.tsx index af85a65a39c9..aa89cf2f5097 100644 --- a/src/pages/workspace/accounting/intacct/import/SageIntacctToggleMappingsPage.tsx +++ b/src/pages/workspace/accounting/intacct/import/SageIntacctToggleMappingsPage.tsx @@ -1,5 +1,7 @@ import {Str} from 'expensify-common'; -import React from 'react'; +import React, {useEffect, useState} from 'react'; +import {useSharedValue} from 'react-native-reanimated'; +import Accordion from '@components/Accordion'; import ConnectionLayout from '@components/ConnectionLayout'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -51,10 +53,22 @@ function SageIntacctToggleMappingsPage({route}: SageIntacctToggleMappingsPagePro const policy = usePolicy(route.params.policyID); const mappingName: SageIntacctMappingName = route.params.mapping; const policyID: string = policy?.id ?? '-1'; - const config = policy?.connections?.intacct?.config; - const translationKeys = getDisplayTypeTranslationKeys(config?.mappings?.[mappingName]); const isImportMappingEnable = config?.mappings?.[mappingName] !== CONST.SAGE_INTACCT_MAPPING_VALUE.NONE; + const isAccordionExpanded = useSharedValue(isImportMappingEnable); + + // We are storing translation keys in the local state for animation purposes. + // Otherwise, the values change to undefined immediately after clicking, before the closing animation finishes, + // resulting in a janky animation effect. + const [translationKeys, setTranslationKey] = useState(undefined); + + useEffect(() => { + if (!isImportMappingEnable) { + return; + } + setTranslationKey(getDisplayTypeTranslationKeys(config?.mappings?.[mappingName])); + }, [isImportMappingEnable, config?.mappings, mappingName]); + return ( { const mappingValue = enabled ? CONST.SAGE_INTACCT_MAPPING_VALUE.TAG : CONST.SAGE_INTACCT_MAPPING_VALUE.NONE; updateSageIntacctMappingValue(policyID, mappingName, mappingValue, config?.mappings?.[mappingName]); + isAccordionExpanded.set(enabled); }} pendingAction={settingsPendingAction([mappingName], config?.pendingFields)} errors={ErrorUtils.getLatestErrorField(config ?? {}, mappingName)} onCloseError={() => clearSageIntacctErrorField(policyID, mappingName)} /> - {isImportMappingEnable && ( + Navigation.navigate(ROUTES.POLICY_ACCOUNTING_SAGE_INTACCT_MAPPINGS_TYPE.getRoute(policyID, mappingName))} brickRoadIndicator={areSettingsInErrorFields([mappingName], config?.errorFields) ? 'error' : undefined} + hintText={translationKeys?.descriptionKey ? translate(translationKeys?.descriptionKey) : undefined} /> - - {translationKeys?.descriptionKey ? translate(translationKeys?.descriptionKey) : undefined} - - )} + ); } diff --git a/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAutoSyncPage.tsx b/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAutoSyncPage.tsx index 992b42dd9478..b79bae45065e 100644 --- a/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAutoSyncPage.tsx +++ b/src/pages/workspace/accounting/netsuite/advanced/NetSuiteAutoSyncPage.tsx @@ -1,5 +1,7 @@ import {CONST as COMMON_CONST} from 'expensify-common'; -import React from 'react'; +import React, {useEffect} from 'react'; +import {useSharedValue} from 'react-native-reanimated'; +import Accordion from '@components/Accordion'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -29,6 +31,12 @@ function NetSuiteAutoSyncPage({policy, route}: WithPolicyConnectionsProps) { const pendingAction = settingsPendingAction([CONST.NETSUITE_CONFIG.AUTO_SYNC], autoSyncConfig?.pendingFields) ?? settingsPendingAction([CONST.NETSUITE_CONFIG.ACCOUNTING_METHOD], config?.pendingFields); + const isAccordionExpanded = useSharedValue(!!autoSyncConfig?.autoSync?.enabled); + + useEffect(() => { + isAccordionExpanded.set(!!autoSyncConfig?.autoSync?.enabled); + }, [isAccordionExpanded, autoSyncConfig?.autoSync?.enabled]); + return ( - {!!autoSyncConfig?.autoSync?.enabled && ( + + Navigation.navigate(ROUTES.POLICY_ACCOUNTING_NETSUITE_ACCOUNTING_METHOD.getRoute(policyID))} /> - )} + ); diff --git a/src/pages/workspace/accounting/qbd/export/QuickbooksDesktopCompanyCardExpenseAccountPage.tsx b/src/pages/workspace/accounting/qbd/export/QuickbooksDesktopCompanyCardExpenseAccountPage.tsx index 3c4d781804fb..7cf0d52ce8e2 100644 --- a/src/pages/workspace/accounting/qbd/export/QuickbooksDesktopCompanyCardExpenseAccountPage.tsx +++ b/src/pages/workspace/accounting/qbd/export/QuickbooksDesktopCompanyCardExpenseAccountPage.tsx @@ -1,4 +1,6 @@ -import React, {useMemo} from 'react'; +import React, {useEffect, useMemo} from 'react'; +import {useSharedValue} from 'react-native-reanimated'; +import Accordion from '@components/Accordion'; import ConnectionLayout from '@components/ConnectionLayout'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -34,6 +36,12 @@ function QuickbooksDesktopCompanyCardExpenseAccountPage({policy}: WithPolicyConn return qbdReimbursableAccounts.find(({id}) => nonReimbursableAccount === id)?.name || qbdReimbursableAccounts.at(0)?.name || translate('workspace.qbd.notConfigured'); }, [policy?.connections?.quickbooksDesktop, nonReimbursable, translate, nonReimbursableAccount]); + const isAccordionExpanded = useSharedValue(!!qbdConfig?.shouldAutoCreateVendor); + + useEffect(() => { + isAccordionExpanded.set(!!qbdConfig?.shouldAutoCreateVendor); + }, [isAccordionExpanded, qbdConfig?.shouldAutoCreateVendor]); + const sections = [ { title: nonReimbursable ? translate(`workspace.qbd.accounts.${nonReimbursable}`) : undefined, @@ -96,7 +104,11 @@ function QuickbooksDesktopCompanyCardExpenseAccountPage({policy}: WithPolicyConn onToggle={(isOn) => QuickbooksDesktop.updateQuickbooksDesktopShouldAutoCreateVendor(policyID, isOn)} onCloseError={() => clearQBDErrorField(policyID, CONST.QUICKBOOKS_DESKTOP_CONFIG.SHOULD_AUTO_CREATE_VENDOR)} /> - {!!qbdConfig?.shouldAutoCreateVendor && ( + + - )} + )} diff --git a/src/pages/workspace/accounting/qbd/import/QuickbooksDesktopClassesPage.tsx b/src/pages/workspace/accounting/qbd/import/QuickbooksDesktopClassesPage.tsx index 6ce56c44c680..e8b825697928 100644 --- a/src/pages/workspace/accounting/qbd/import/QuickbooksDesktopClassesPage.tsx +++ b/src/pages/workspace/accounting/qbd/import/QuickbooksDesktopClassesPage.tsx @@ -1,4 +1,6 @@ -import React from 'react'; +import React, {useEffect} from 'react'; +import {useSharedValue} from 'react-native-reanimated'; +import Accordion from '@components/Accordion'; import ConnectionLayout from '@components/ConnectionLayout'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -23,6 +25,12 @@ function QuickbooksDesktopClassesPage({policy}: WithPolicyProps) { const isSwitchOn = !!(qbdConfig?.mappings?.classes && qbdConfig.mappings.classes !== CONST.INTEGRATION_ENTITY_MAP_TYPES.NONE); const isReportFieldsSelected = qbdConfig?.mappings?.classes === CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD; + const isAccordionExpanded = useSharedValue(isSwitchOn); + + useEffect(() => { + isAccordionExpanded.set(isSwitchOn); + }, [isAccordionExpanded, isSwitchOn]); + return ( clearQBDErrorField(policyID, CONST.QUICKBOOKS_DESKTOP_CONFIG.MAPPINGS.CLASSES)} /> - {isSwitchOn && ( + - )} + ); } diff --git a/src/pages/workspace/accounting/qbd/import/QuickbooksDesktopCustomersPage.tsx b/src/pages/workspace/accounting/qbd/import/QuickbooksDesktopCustomersPage.tsx index 3f0300dc2bf4..7cdf39e5517b 100644 --- a/src/pages/workspace/accounting/qbd/import/QuickbooksDesktopCustomersPage.tsx +++ b/src/pages/workspace/accounting/qbd/import/QuickbooksDesktopCustomersPage.tsx @@ -1,4 +1,6 @@ -import React from 'react'; +import React, {useEffect} from 'react'; +import {useSharedValue} from 'react-native-reanimated'; +import Accordion from '@components/Accordion'; import ConnectionLayout from '@components/ConnectionLayout'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -23,6 +25,12 @@ function QuickbooksDesktopCustomersPage({policy}: WithPolicyProps) { const isSwitchOn = !!(qbdConfig?.mappings?.customers && qbdConfig.mappings.customers !== CONST.INTEGRATION_ENTITY_MAP_TYPES.NONE); const isReportFieldsSelected = qbdConfig?.mappings?.customers === CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD; + const isAccordionExpanded = useSharedValue(isSwitchOn); + + useEffect(() => { + isAccordionExpanded.set(isSwitchOn); + }, [isAccordionExpanded, isSwitchOn]); + return ( clearQBDErrorField(policyID, CONST.QUICKBOOKS_DESKTOP_CONFIG.MAPPINGS.CUSTOMERS)} /> - {isSwitchOn && ( + - )} + ); } diff --git a/src/pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountPage.tsx b/src/pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountPage.tsx index 8d59c13e528a..29e152496789 100644 --- a/src/pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountPage.tsx +++ b/src/pages/workspace/accounting/qbo/export/QuickbooksCompanyCardExpenseAccountPage.tsx @@ -1,4 +1,6 @@ -import React from 'react'; +import React, {useEffect} from 'react'; +import {useSharedValue} from 'react-native-reanimated'; +import Accordion from '@components/Accordion'; import ConnectionLayout from '@components/ConnectionLayout'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -24,6 +26,12 @@ function QuickbooksCompanyCardExpenseAccountPage({policy}: WithPolicyConnections const {vendors} = policy?.connections?.quickbooksOnline?.data ?? {}; const nonReimbursableBillDefaultVendorObject = vendors?.find((vendor) => vendor.id === qboConfig?.nonReimbursableBillDefaultVendor); + const isAccordionExpanded = useSharedValue(!!qboConfig?.autoCreateVendor); + + useEffect(() => { + isAccordionExpanded.set(!!qboConfig?.autoCreateVendor); + }, [isAccordionExpanded, qboConfig?.autoCreateVendor]); + const sections = [ { title: qboConfig?.nonReimbursableExpensesExportDestination ? translate(`workspace.qbo.accounts.${qboConfig?.nonReimbursableExpensesExportDestination}`) : undefined, @@ -93,7 +101,10 @@ function QuickbooksCompanyCardExpenseAccountPage({policy}: WithPolicyConnections } onCloseError={() => clearQBOErrorField(policyID, CONST.QUICKBOOKS_CONFIG.AUTO_CREATE_VENDOR)} /> - {qboConfig?.autoCreateVendor && ( + - )} + )} diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx index ae72999ad975..5b076300e88f 100644 --- a/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx +++ b/src/pages/workspace/accounting/qbo/import/QuickbooksClassesPage.tsx @@ -1,4 +1,6 @@ -import React from 'react'; +import React, {useEffect} from 'react'; +import {useSharedValue} from 'react-native-reanimated'; +import Accordion from '@components/Accordion'; import ConnectionLayout from '@components/ConnectionLayout'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -23,6 +25,12 @@ function QuickbooksClassesPage({policy}: WithPolicyProps) { const isSwitchOn = !!(qboConfig?.syncClasses && qboConfig.syncClasses !== CONST.INTEGRATION_ENTITY_MAP_TYPES.NONE); const isReportFieldsSelected = qboConfig?.syncClasses === CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD; + const isAccordionExpanded = useSharedValue(isSwitchOn); + + useEffect(() => { + isAccordionExpanded.set(isSwitchOn); + }, [isAccordionExpanded, isSwitchOn]); + return ( clearQBOErrorField(policyID, CONST.QUICKBOOKS_CONFIG.SYNC_CLASSES)} /> - {isSwitchOn && ( + - )} + ); } diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx index 8b842e737de6..914aabf5dbd9 100644 --- a/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx +++ b/src/pages/workspace/accounting/qbo/import/QuickbooksCustomersPage.tsx @@ -1,4 +1,6 @@ -import React from 'react'; +import React, {useEffect} from 'react'; +import {useSharedValue} from 'react-native-reanimated'; +import Accordion from '@components/Accordion'; import ConnectionLayout from '@components/ConnectionLayout'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -22,6 +24,13 @@ function QuickbooksCustomersPage({policy}: WithPolicyProps) { const qboConfig = policy?.connections?.quickbooksOnline?.config; const isSwitchOn = !!(qboConfig?.syncCustomers && qboConfig?.syncCustomers !== CONST.INTEGRATION_ENTITY_MAP_TYPES.NONE); const isReportFieldsSelected = qboConfig?.syncCustomers === CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD; + + const isAccordionExpanded = useSharedValue(isSwitchOn); + + useEffect(() => { + isAccordionExpanded.set(isSwitchOn); + }, [isAccordionExpanded, isSwitchOn]); + return ( clearQBOErrorField(policyID, CONST.QUICKBOOKS_CONFIG.SYNC_CUSTOMERS)} /> - {isSwitchOn && ( + + - )} + ); } diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsPage.tsx index 3f405f8a3f8f..e93de6b12584 100644 --- a/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsPage.tsx +++ b/src/pages/workspace/accounting/qbo/import/QuickbooksLocationsPage.tsx @@ -1,9 +1,9 @@ import React, {useCallback, useEffect} from 'react'; -import {View} from 'react-native'; +import {useSharedValue} from 'react-native-reanimated'; +import Accordion from '@components/Accordion'; import ConnectionLayout from '@components/ConnectionLayout'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; -import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as QuickbooksOnline from '@libs/actions/connections/QuickbooksOnline'; @@ -27,6 +27,7 @@ function QuickbooksLocationsPage({policy}: WithPolicyProps) { const isSwitchOn = !!(qboConfig?.syncLocations && qboConfig?.syncLocations !== CONST.INTEGRATION_ENTITY_MAP_TYPES.NONE); const isTagsSelected = qboConfig?.syncLocations === CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG; const shouldShowLineItemsRestriction = shouldShowLocationsLineItemsRestriction(qboConfig); + const isAccordionExpanded = useSharedValue(isSwitchOn); const updateQuickbooksOnlineSyncLocations = useCallback( (settingValue: IntegrationEntityMap) => { @@ -48,6 +49,10 @@ function QuickbooksLocationsPage({policy}: WithPolicyProps) { updateQuickbooksOnlineSyncLocations(CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD); }, [qboConfig, updateQuickbooksOnlineSyncLocations]); + useEffect(() => { + isAccordionExpanded.set(isSwitchOn); + }, [isAccordionExpanded, isSwitchOn]); + return ( clearQBOErrorField(policyID, CONST.QUICKBOOKS_CONFIG.SYNC_LOCATIONS)} pendingAction={PolicyUtils.settingsPendingAction([CONST.QUICKBOOKS_CONFIG.SYNC_LOCATIONS], qboConfig?.pendingFields)} /> - {isSwitchOn && ( + - )} - - {shouldShowLineItemsRestriction && isSwitchOn && ( - - {translate('workspace.qbo.locationsLineItemsRestrictionDescription')} - - )} + ); } diff --git a/src/pages/workspace/accounting/xero/XeroTrackingCategoryConfigurationPage.tsx b/src/pages/workspace/accounting/xero/XeroTrackingCategoryConfigurationPage.tsx index 91096880c938..655817ff7232 100644 --- a/src/pages/workspace/accounting/xero/XeroTrackingCategoryConfigurationPage.tsx +++ b/src/pages/workspace/accounting/xero/XeroTrackingCategoryConfigurationPage.tsx @@ -1,5 +1,7 @@ -import React, {useMemo} from 'react'; +import React, {useEffect, useMemo} from 'react'; import {View} from 'react-native'; +import {useSharedValue} from 'react-native-reanimated'; +import Accordion from '@components/Accordion'; import ConnectionLayout from '@components/ConnectionLayout'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -26,6 +28,12 @@ function XeroTrackingCategoryConfigurationPage({policy}: WithPolicyProps) { const xeroConfig = policy?.connections?.xero?.config; const isSwitchOn = !!xeroConfig?.importTrackingCategories; + const isAccordionExpanded = useSharedValue(!!xeroConfig?.importTrackingCategories); + + useEffect(() => { + isAccordionExpanded.set(!!xeroConfig?.importTrackingCategories); + }, [isAccordionExpanded, xeroConfig?.importTrackingCategories]); + const menuItems = useMemo(() => { const trackingCategories = Xero.getTrackingCategories(policy); return trackingCategories.map((category: XeroTrackingCategory & {value: string}) => ({ @@ -61,7 +69,10 @@ function XeroTrackingCategoryConfigurationPage({policy}: WithPolicyProps) { errors={ErrorUtils.getLatestErrorField(xeroConfig ?? {}, CONST.XERO_CONFIG.IMPORT_TRACKING_CATEGORIES)} onCloseError={() => Policy.clearXeroErrorField(policyID, CONST.XERO_CONFIG.IMPORT_TRACKING_CATEGORIES)} /> - {!!xeroConfig?.importTrackingCategories && ( + {menuItems.map((menuItem) => ( ))} - )} + ); } diff --git a/src/pages/workspace/accounting/xero/advanced/XeroAdvancedPage.tsx b/src/pages/workspace/accounting/xero/advanced/XeroAdvancedPage.tsx index bcf24942047d..ed845a0c8572 100644 --- a/src/pages/workspace/accounting/xero/advanced/XeroAdvancedPage.tsx +++ b/src/pages/workspace/accounting/xero/advanced/XeroAdvancedPage.tsx @@ -1,4 +1,6 @@ -import React, {useMemo} from 'react'; +import React, {useEffect, useMemo} from 'react'; +import {useSharedValue} from 'react-native-reanimated'; +import Accordion from '@components/Accordion'; import ConnectionLayout from '@components/ConnectionLayout'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; @@ -39,6 +41,12 @@ function XeroAdvancedPage({policy}: WithPolicyConnectionsProps) { const currentXeroOrganizationName = useMemo(() => getCurrentXeroOrganizationName(policy ?? undefined), [policy]); + const isAccordionExpanded = useSharedValue(!!sync?.syncReimbursedReports); + + useEffect(() => { + isAccordionExpanded.set(!!sync?.syncReimbursedReports); + }, [isAccordionExpanded, sync?.syncReimbursedReports]); + return ( Policy.clearXeroErrorField(policyID, CONST.XERO_CONFIG.SYNC_REIMBURSED_REPORTS)} /> - {!!sync?.syncReimbursedReports && ( + <> - )} + ); } diff --git a/src/pages/workspace/workflows/ToggleSettingsOptionRow.tsx b/src/pages/workspace/workflows/ToggleSettingsOptionRow.tsx index 26c6275e197f..005970a0647e 100644 --- a/src/pages/workspace/workflows/ToggleSettingsOptionRow.tsx +++ b/src/pages/workspace/workflows/ToggleSettingsOptionRow.tsx @@ -1,7 +1,9 @@ import type {ReactNode} from 'react'; -import React, {useMemo} from 'react'; +import React, {useEffect, useMemo} from 'react'; import {View} from 'react-native'; import type {StyleProp, TextStyle, ViewStyle} from 'react-native'; +import {useSharedValue} from 'react-native-reanimated'; +import Accordion from '@components/Accordion'; import Icon from '@components/Icon'; import OfflineWithFeedback from '@components/OfflineWithFeedback'; import RenderHTML from '@components/RenderHTML'; @@ -98,6 +100,11 @@ function ToggleSettingOptionRow({ showLockIcon = false, }: ToggleSettingOptionRowProps) { const styles = useThemeStyles(); + const isExpanded = useSharedValue(isActive); + + useEffect(() => { + isExpanded.set(isActive); + }, [isExpanded, isActive]); const subtitleHtml = useMemo(() => { if (!subtitle || !shouldParseSubtitle || typeof subtitle !== 'string') { @@ -178,7 +185,7 @@ function ToggleSettingOptionRow({ /> {shouldPlaceSubtitleBelowSwitch && subtitle && subTitleView} - {isActive && subMenuItems} + {subMenuItems} );