diff --git a/src/components/ImportedFromAccountingSoftware.tsx b/src/components/ImportedFromAccountingSoftware.tsx
new file mode 100644
index 000000000000..48c44573702e
--- /dev/null
+++ b/src/components/ImportedFromAccountingSoftware.tsx
@@ -0,0 +1,66 @@
+import React from 'react';
+import {View} from 'react-native';
+import useEnvironment from '@hooks/useEnvironment';
+import useLocalize from '@hooks/useLocalize';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
+import {getIntegrationIcon} from '@libs/ReportUtils';
+import variables from '@styles/variables';
+import CONST from '@src/CONST';
+import ROUTES from '@src/ROUTES';
+import type {ConnectionName} from '@src/types/onyx/Policy';
+import Icon from './Icon';
+import Text from './Text';
+import TextBlock from './TextBlock';
+import TextLinkBlock from './TextLinkBlock';
+
+type ImportedFromAccountingSoftwareProps = {
+ /** The policy ID to link to */
+ policyID: string;
+
+ /** The name of the current connection */
+ currentConnectionName: string;
+
+ /** The connected integration */
+ connectedIntegration: ConnectionName | undefined;
+
+ /** The translated text for the "imported from" message */
+ translatedText: string;
+};
+
+function ImportedFromAccountingSoftware({policyID, currentConnectionName, translatedText, connectedIntegration}: ImportedFromAccountingSoftwareProps) {
+ const styles = useThemeStyles();
+ const StyleUtils = useStyleUtils();
+ const {translate} = useLocalize();
+ const {environmentURL} = useEnvironment();
+ const icon = getIntegrationIcon(connectedIntegration);
+
+ return (
+
+
+
+ ) : undefined
+ }
+ />
+ .
+
+ );
+}
+
+ImportedFromAccountingSoftware.displayName = 'ImportedFromAccountingSoftware';
+
+export default ImportedFromAccountingSoftware;
diff --git a/src/components/TextLink.tsx b/src/components/TextLink.tsx
index 2d390f9811e6..715d25e040a9 100644
--- a/src/components/TextLink.tsx
+++ b/src/components/TextLink.tsx
@@ -4,7 +4,7 @@ import React, {forwardRef} from 'react';
import type {GestureResponderEvent, Text as RNText, StyleProp, TextStyle} from 'react-native';
import useEnvironment from '@hooks/useEnvironment';
import useThemeStyles from '@hooks/useThemeStyles';
-import * as Link from '@userActions/Link';
+import {openLink as openLinkUtil} from '@userActions/Link';
import CONST from '@src/CONST';
import type {TextProps} from './Text';
import Text from './Text';
@@ -40,7 +40,7 @@ function TextLink({href, onPress, children, style, onMouseDown = (event) => even
if (onPress) {
onPress(event);
} else {
- Link.openLink(href, environmentURL);
+ openLinkUtil(href, environmentURL);
}
};
@@ -79,6 +79,6 @@ function TextLink({href, onPress, children, style, onMouseDown = (event) => even
TextLink.displayName = 'TextLink';
-export type {LinkProps, PressProps};
+export type {LinkProps, PressProps, TextLinkProps};
export default forwardRef(TextLink);
diff --git a/src/components/TextLinkBlock.tsx b/src/components/TextLinkBlock.tsx
new file mode 100644
index 000000000000..ce0454b0ae26
--- /dev/null
+++ b/src/components/TextLinkBlock.tsx
@@ -0,0 +1,72 @@
+/**
+ * TextLinkBlock component splits a given text into individual words and displays
+ * each word within a TextLink component so the link text wraps naturally.
+ */
+import React, {memo, useMemo} from 'react';
+import type {StyleProp, TextStyle} from 'react-native';
+import {View} from 'react-native';
+import useEnvironment from '@hooks/useEnvironment';
+import useThemeStyles from '@hooks/useThemeStyles';
+import {openLink as openLinkUtil} from '@userActions/Link';
+import CONST from '@src/CONST';
+import {PressableWithoutFeedback} from './Pressable';
+import Text from './Text';
+import TextLink from './TextLink';
+import type {LinkProps, PressProps} from './TextLink';
+
+type TextLinkBlockProps = (LinkProps | PressProps) & {
+ /** Styles to apply to each word */
+ style?: StyleProp;
+
+ /** The full text to be split into words */
+ text: string;
+
+ /** The full text to be split into words */
+ prefixIcon?: React.JSX.Element;
+};
+
+function TextLinkBlock({text, style, prefixIcon, ...rest}: TextLinkBlockProps) {
+ const words = useMemo(() => text.match(/(\S+\s*)/g) ?? [], [text]);
+
+ const {environmentURL} = useEnvironment();
+ const styles = useThemeStyles();
+
+ const openLink = () => {
+ if (!rest.href) {
+ return;
+ }
+ openLinkUtil(rest.href, environmentURL);
+ };
+
+ return (
+
+ {words.map((word, index) => (
+
+ {prefixIcon && index === 0 && prefixIcon}
+ {!!prefixIcon && index === 0 && }
+
+ {word}
+
+
+ ))}
+
+ );
+}
+
+TextLinkBlock.displayName = 'TextLinkBlock';
+
+export default memo(TextLinkBlock);
diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
index ecbdfc03ae11..f4ecc1c4fe45 100644
--- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
+++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx
@@ -9,6 +9,7 @@ import EmptyStateComponent from '@components/EmptyStateComponent';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Expensicons from '@components/Icon/Expensicons';
import * as Illustrations from '@components/Icon/Illustrations';
+import ImportedFromAccountingSoftware from '@components/ImportedFromAccountingSoftware';
import LottieAnimations from '@components/LottieAnimations';
import RenderHTML from '@components/RenderHTML';
import ScreenWrapper from '@components/ScreenWrapper';
@@ -21,7 +22,6 @@ import CustomListHeader from '@components/SelectionListWithModal/CustomListHeade
import TableListItemSkeleton from '@components/Skeletons/TableRowSkeleton';
import Switch from '@components/Switch';
import Text from '@components/Text';
-import TextLink from '@components/TextLink';
import useAutoTurnSelectionModeOffWhenHasNoActiveOption from '@hooks/useAutoTurnSelectionModeOffWhenHasNoActiveOption';
import useCleanupSelectedOptions from '@hooks/useCleanupSelectedOptions';
import useEnvironment from '@hooks/useEnvironment';
@@ -440,17 +440,13 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) {
const headerContent = (
<>
- {!hasSyncError && isConnectionVerified ? (
-
- {`${translate('workspace.categories.importedFromAccountingSoftware')} `}
-
- {`${currentConnectionName} ${translate('workspace.accounting.settings')}`}
-
- .
-
+ {!hasSyncError && isConnectionVerified && currentConnectionName ? (
+
) : (
{translate('workspace.categories.subtitle')}
)}
diff --git a/src/pages/workspace/reports/WorkspaceReportsPage.tsx b/src/pages/workspace/reports/WorkspaceReportsPage.tsx
index 868af9d3d7d9..90015b13e7cb 100644
--- a/src/pages/workspace/reports/WorkspaceReportsPage.tsx
+++ b/src/pages/workspace/reports/WorkspaceReportsPage.tsx
@@ -7,6 +7,7 @@ import ConfirmModal from '@components/ConfirmModal';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import {Plus} from '@components/Icon/Expensicons';
import {ReportReceipt} from '@components/Icon/Illustrations';
+import ImportedFromAccountingSoftware from '@components/ImportedFromAccountingSoftware';
import MenuItem from '@components/MenuItem';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
@@ -16,8 +17,6 @@ import ScrollView from '@components/ScrollView';
import Section from '@components/Section';
import type {ListItem} from '@components/SelectionList/types';
import Text from '@components/Text';
-import TextLink from '@components/TextLink';
-import useEnvironment from '@hooks/useEnvironment';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import useOnyx from '@hooks/useOnyx';
@@ -69,7 +68,6 @@ function WorkspaceReportFieldsPage({
const [isReportFieldsWarningModalOpen, setIsReportFieldsWarningModalOpen] = useState(false);
const policy = usePolicy(policyID);
const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policyID}`, {canBeMissing: true});
- const {environmentURL} = useEnvironment();
const isSyncInProgress = isConnectionInProgress(connectionSyncProgress, policy);
const hasSyncError = shouldShowSyncError(policy, isSyncInProgress);
const connectedIntegration = getConnectedIntegration(policy) ?? connectionSyncProgress?.connectionName;
@@ -127,16 +125,14 @@ function WorkspaceReportFieldsPage({
);
const getHeaderText = () =>
- !hasSyncError && isConnectionVerified ? (
+ !hasSyncError && isConnectionVerified && currentConnectionName ? (
- {`${translate('workspace.reportFields.importedFromAccountingSoftware')} `}
-
- {`${currentConnectionName} ${translate('workspace.accounting.settings')}`}
-
- .
+
) : (
{translate('workspace.reportFields.subtitle')}
diff --git a/src/pages/workspace/tags/WorkspaceTagsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsPage.tsx
index ebac3e9729ce..558c8effac58 100644
--- a/src/pages/workspace/tags/WorkspaceTagsPage.tsx
+++ b/src/pages/workspace/tags/WorkspaceTagsPage.tsx
@@ -9,6 +9,7 @@ import EmptyStateComponent from '@components/EmptyStateComponent';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Expensicons from '@components/Icon/Expensicons';
import * as Illustrations from '@components/Icon/Illustrations';
+import ImportedFromAccountingSoftware from '@components/ImportedFromAccountingSoftware';
import LottieAnimations from '@components/LottieAnimations';
import RenderHTML from '@components/RenderHTML';
import ScreenWrapper from '@components/ScreenWrapper';
@@ -604,17 +605,13 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) {
const headerContent = (
<>
- {!hasSyncError && isConnectionVerified ? (
-
- {`${translate('workspace.tags.importedFromAccountingSoftware')} `}
-
- {`${currentConnectionName} ${translate('workspace.accounting.settings')}`}
-
- .
-
+ {!hasSyncError && isConnectionVerified && currentConnectionName ? (
+
) : (
{translate('workspace.tags.subtitle')}
diff --git a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx
index ce446a3b81ba..7e9d0d3d1ada 100644
--- a/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx
+++ b/src/pages/workspace/taxes/WorkspaceTaxesPage.tsx
@@ -7,6 +7,7 @@ import ConfirmModal from '@components/ConfirmModal';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Expensicons from '@components/Icon/Expensicons';
import * as Illustrations from '@components/Icon/Illustrations';
+import ImportedFromAccountingSoftware from '@components/ImportedFromAccountingSoftware';
import ScreenWrapper from '@components/ScreenWrapper';
import SearchBar from '@components/SearchBar';
import TableListItem from '@components/SelectionList/TableListItem';
@@ -15,9 +16,7 @@ import SelectionListWithModal from '@components/SelectionListWithModal';
import CustomListHeader from '@components/SelectionListWithModal/CustomListHeader';
import Switch from '@components/Switch';
import Text from '@components/Text';
-import TextLink from '@components/TextLink';
import useCleanupSelectedOptions from '@hooks/useCleanupSelectedOptions';
-import useEnvironment from '@hooks/useEnvironment';
import useLocalize from '@hooks/useLocalize';
import useMobileSelectionMode from '@hooks/useMobileSelectionMode';
import useNetwork from '@hooks/useNetwork';
@@ -66,7 +65,6 @@ function WorkspaceTaxesPage({
const styles = useThemeStyles();
const theme = useTheme();
const {translate, localeCompare} = useLocalize();
- const {environmentURL} = useEnvironment();
const [selectedTaxesIDs, setSelectedTaxesIDs] = useState([]);
const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false);
const isMobileSelectionModeEnabled = useMobileSelectionMode();
@@ -362,17 +360,13 @@ function WorkspaceTaxesPage({
const headerContent = (
<>
- {!hasSyncError && isConnectionVerified ? (
-
- {`${translate('workspace.taxes.importedFromAccountingSoftware')} `}
-
- {`${currentConnectionName} ${translate('workspace.accounting.settings')}`}
-
- .
-
+ {!hasSyncError && isConnectionVerified && currentConnectionName ? (
+
) : (
{translate('workspace.taxes.subtitle')}
)}
diff --git a/src/styles/utils/display.ts b/src/styles/utils/display.ts
index 0f0166e135c9..dc2ee88fe68c 100644
--- a/src/styles/utils/display.ts
+++ b/src/styles/utils/display.ts
@@ -19,6 +19,14 @@ export default {
display: 'none',
},
+ /**
+ * display: 'contents' - Supported in React Native 0.77+
+ * Ref: https://reactnative.dev/blog/2025/01/21/version-0.77#simpler-layouts-with-display-contents
+ */
+ dContents: {
+ display: 'contents',
+ },
+
/**
* Web-only style.
*/