diff --git a/.storybook/storybook.requires.js b/.storybook/storybook.requires.js
index a1ca7774ac8..c08c085d9a9 100644
--- a/.storybook/storybook.requires.js
+++ b/.storybook/storybook.requires.js
@@ -126,7 +126,7 @@ const getStories = () => {
"./app/components/Views/confirmations/components/UI/InfoRow/InfoRow.stories.tsx": require("../app/components/Views/confirmations/components/UI/InfoRow/InfoRow.stories.tsx"),
"./app/components/Views/confirmations/components/UI/ExpandableSection/ExpandableSection.stories.tsx": require("../app/components/Views/confirmations/components/UI/ExpandableSection/ExpandableSection.stories.tsx"),
"./app/components/Views/confirmations/components/UI/Tooltip/Tooltip.stories.tsx": require("../app/components/Views/confirmations/components/UI/Tooltip/Tooltip.stories.tsx"),
- "./app/components/Views/confirmations/components/UI/CopyButton/CopyButton.stories.tsx": require("../app/components/Views/confirmations/components/UI/CopyButton/CopyButton.stories.tsx"),
+ "./app/component-library/components/Texts/SensitiveText/SensitiveText.stories.tsx": require("../app/component-library/components/Texts/SensitiveText/SensitiveText.stories.tsx"),
};
};
diff --git a/app/component-library/components/Texts/SensitiveText/README.md b/app/component-library/components/Texts/SensitiveText/README.md
new file mode 100644
index 00000000000..dd81879e398
--- /dev/null
+++ b/app/component-library/components/Texts/SensitiveText/README.md
@@ -0,0 +1,57 @@
+# SensitiveText
+
+SensitiveText is a component that extends the Text component to handle sensitive information. It provides the ability to hide or show the text content, replacing it with dots when hidden.
+
+## Props
+
+This component extends all props from the [Text](../Text/README.md) component and adds the following:
+
+### `isHidden`
+
+Boolean to determine whether the text should be hidden or visible.
+
+| TYPE | REQUIRED | DEFAULT |
+| :-------------------------------------------------- | :------------------------------------------------------ | :----------------------------------------------------- |
+| boolean | Yes | false |
+
+### `length`
+
+Determines the length of the hidden text (number of dots). Can be a predefined SensitiveTextLength or a custom string number.
+
+| TYPE | REQUIRED | DEFAULT |
+| :-------------------------------------------------- | :------------------------------------------------------ | :----------------------------------------------------- |
+| [SensitiveTextLengthType](./SensitiveText.types.ts#L14) \| [CustomLength](./SensitiveText.types.ts#L19) | No | SensitiveTextLength.Short |
+
+### `children`
+
+The text content to be displayed or hidden.
+
+| TYPE | REQUIRED | DEFAULT |
+| :-------------------------------------------------- | :------------------------------------------------------ | :----------------------------------------------------- |
+| string | Yes | - |
+
+## Usage
+
+```javascript
+import SensitiveText from 'app/component-library/components/Texts/SensitiveText';
+import { TextVariant } from 'app/component-library/components/Texts/Text';
+import { SensitiveTextLength } from 'app/component-library/components/Texts/SensitiveText/SensitiveText.types';
+
+
+ Sensitive Information
+
+
+
+ Custom Length Hidden Text
+
+```
+
+This will render a Text component with dots instead of the actual text when `isHidden` is true, and the original text when `isHidden` is false. The number of asterisks is determined by the `length` prop.
diff --git a/app/component-library/components/Texts/SensitiveText/SensitiveText.stories.tsx b/app/component-library/components/Texts/SensitiveText/SensitiveText.stories.tsx
new file mode 100644
index 00000000000..c881014ed43
--- /dev/null
+++ b/app/component-library/components/Texts/SensitiveText/SensitiveText.stories.tsx
@@ -0,0 +1,89 @@
+// Third party dependencies
+import React from 'react';
+
+// External dependencies
+import { TextVariant, TextColor } from '../Text/Text.types';
+
+// Internal dependencies
+import SensitiveText from './SensitiveText';
+import { SensitiveTextProps, SensitiveTextLength } from './SensitiveText.types';
+
+const SensitiveTextMeta = {
+ title: 'Component Library / Texts',
+ component: SensitiveText,
+ argTypes: {
+ isHidden: {
+ control: 'boolean',
+ },
+ length: {
+ options: SensitiveTextLength,
+ control: {
+ type: 'select',
+ },
+ },
+ variant: {
+ options: TextVariant,
+ control: {
+ type: 'select',
+ },
+ },
+ color: {
+ options: TextColor,
+ control: {
+ type: 'select',
+ },
+ },
+ children: {
+ control: { type: 'text' },
+ },
+ },
+};
+export default SensitiveTextMeta;
+
+export const SensitiveTextExample = {
+ args: {
+ isHidden: false,
+ length: SensitiveTextLength.Short,
+ variant: TextVariant.BodyMD,
+ color: TextColor.Default,
+ children: 'Sensitive Information',
+ },
+};
+
+export const SensitiveTextVariants = (
+ args: React.JSX.IntrinsicAttributes &
+ SensitiveTextProps & { children?: React.ReactNode | undefined },
+) => (
+ <>
+
+ Visible Sensitive Text
+
+ {Object.values(SensitiveTextLength).map((length) => (
+
+ {`Hidden (${length})`}
+
+ ))}
+ >
+);
+SensitiveTextVariants.argTypes = {
+ isHidden: { control: false },
+ length: { control: false },
+ children: { control: false },
+};
+SensitiveTextVariants.args = {
+ variant: TextVariant.BodyMD,
+ color: TextColor.Default,
+};
diff --git a/app/component-library/components/Texts/SensitiveText/SensitiveText.test.tsx b/app/component-library/components/Texts/SensitiveText/SensitiveText.test.tsx
new file mode 100644
index 00000000000..2e5b158263c
--- /dev/null
+++ b/app/component-library/components/Texts/SensitiveText/SensitiveText.test.tsx
@@ -0,0 +1,116 @@
+// Third party dependencies
+import React from 'react';
+import { render } from '@testing-library/react-native';
+
+// External dependencies
+import { mockTheme } from '../../../../util/theme';
+
+// Internal dependencies
+import SensitiveText from './SensitiveText';
+import { SensitiveTextLength } from './SensitiveText.types';
+import { TextVariant, TextColor } from '../Text/Text.types';
+
+describe('SensitiveText', () => {
+ const testProps = {
+ isHidden: false,
+ length: SensitiveTextLength.Short,
+ variant: TextVariant.BodyMD,
+ color: TextColor.Default,
+ children: 'Sensitive Information',
+ };
+
+ it('should render correctly', () => {
+ const wrapper = render();
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ it('should display the text when isHidden is false', () => {
+ const { getByText } = render();
+ expect(getByText('Sensitive Information')).toBeTruthy();
+ });
+
+ it('should hide the text when isHidden is true', () => {
+ const { queryByText, getByText } = render(
+ ,
+ );
+ expect(queryByText('Sensitive Information')).toBeNull();
+ expect(getByText('••••••')).toBeTruthy();
+ });
+
+ it('should render the correct number of asterisks for different lengths', () => {
+ const { getByText: getShort } = render(
+ ,
+ );
+ expect(getShort('••••••')).toBeTruthy();
+
+ const { getByText: getMedium } = render(
+ ,
+ );
+ expect(getMedium('•••••••••')).toBeTruthy();
+
+ const { getByText: getLong } = render(
+ ,
+ );
+ expect(getLong('••••••••••••')).toBeTruthy();
+
+ const { getByText: getExtraLong } = render(
+ ,
+ );
+ expect(getExtraLong('••••••••••••••••••••')).toBeTruthy();
+ });
+
+ it('should apply the correct text color', () => {
+ const { getByText } = render(
+ ,
+ );
+ const textElement = getByText('Sensitive Information');
+ expect(textElement.props.style.color).toBe(mockTheme.colors.text.default);
+ });
+ it('should handle all predefined SensitiveTextLength values', () => {
+ Object.entries(SensitiveTextLength).forEach(([_, value]) => {
+ const { getByText } = render(
+ ,
+ );
+ expect(getByText('•'.repeat(Number(value)))).toBeTruthy();
+ });
+ });
+
+ it('should handle custom length as a string', () => {
+ const { getByText } = render(
+ ,
+ );
+ expect(getByText('•••••••••••••••')).toBeTruthy();
+ });
+
+ it('should fall back to Short length for invalid custom length', () => {
+ const { getByText } = render(
+ ,
+ );
+ expect(getByText('••••••')).toBeTruthy();
+ });
+
+ it('should log a warning for invalid custom length', () => {
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
+ render();
+ expect(consoleSpy).toHaveBeenCalledWith(
+ 'Invalid length provided: abc. Falling back to Short.',
+ );
+ consoleSpy.mockRestore();
+ });
+});
diff --git a/app/component-library/components/Texts/SensitiveText/SensitiveText.tsx b/app/component-library/components/Texts/SensitiveText/SensitiveText.tsx
new file mode 100644
index 00000000000..6e1f512c35b
--- /dev/null
+++ b/app/component-library/components/Texts/SensitiveText/SensitiveText.tsx
@@ -0,0 +1,39 @@
+// external dependencies
+import React, { useMemo } from 'react';
+import Text from '../Text/Text';
+
+// internal dependencies
+import { SensitiveTextProps, SensitiveTextLength } from './SensitiveText.types';
+
+const SensitiveText: React.FC = ({
+ isHidden = false,
+ children = '',
+ length = SensitiveTextLength.Short,
+ ...props
+}) => {
+ const getFallbackLength = useMemo(
+ () => (len: string) => {
+ const numLength = Number(len);
+ return Number.isNaN(numLength) ? 0 : numLength;
+ },
+ [],
+ );
+
+ const isValidCustomLength = (value: string): boolean => {
+ const num = Number(value);
+ return !Number.isNaN(num) && num > 0;
+ };
+
+ if (!(length in SensitiveTextLength) && !isValidCustomLength(length)) {
+ console.warn(`Invalid length provided: ${length}. Falling back to Short.`);
+ length = SensitiveTextLength.Short;
+ }
+
+ const fallback = useMemo(
+ () => '•'.repeat(getFallbackLength(length)),
+ [length, getFallbackLength],
+ );
+ return {isHidden ? fallback : children};
+};
+
+export default SensitiveText;
diff --git a/app/component-library/components/Texts/SensitiveText/SensitiveText.types.ts b/app/component-library/components/Texts/SensitiveText/SensitiveText.types.ts
new file mode 100644
index 00000000000..1c6f4688b78
--- /dev/null
+++ b/app/component-library/components/Texts/SensitiveText/SensitiveText.types.ts
@@ -0,0 +1,46 @@
+// External dependencies.
+import { TextProps } from '../Text/Text.types';
+
+/**
+ * SensitiveText length options.
+ */
+export const SensitiveTextLength = {
+ Short: '6',
+ Medium: '9',
+ Long: '12',
+ ExtraLong: '20',
+} as const;
+
+/**
+ * Type for SensitiveTextLength values.
+ */
+export type SensitiveTextLengthType =
+ (typeof SensitiveTextLength)[keyof typeof SensitiveTextLength];
+
+/**
+ * Type for custom length values.
+ */
+export type CustomLength = string;
+
+/**
+ * SensitiveText component props.
+ */
+export interface SensitiveTextProps extends TextProps {
+ /**
+ * Boolean to determine whether the text should be hidden or visible.
+ *
+ * @default false
+ */
+ isHidden?: boolean;
+ /**
+ * Determines the length of the hidden text (number of asterisks).
+ * Can be a predefined SensitiveTextLength or a custom string number.
+ *
+ * @default SensitiveTextLength.Short
+ */
+ length?: SensitiveTextLengthType | CustomLength;
+ /**
+ * The text content to be displayed or hidden.
+ */
+ children: string;
+}
diff --git a/app/component-library/components/Texts/SensitiveText/__snapshots__/SensitiveText.test.tsx.snap b/app/component-library/components/Texts/SensitiveText/__snapshots__/SensitiveText.test.tsx.snap
new file mode 100644
index 00000000000..baa2e5148bf
--- /dev/null
+++ b/app/component-library/components/Texts/SensitiveText/__snapshots__/SensitiveText.test.tsx.snap
@@ -0,0 +1,19 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`SensitiveText should render correctly 1`] = `
+
+ Sensitive Information
+
+`;
diff --git a/app/component-library/components/Texts/SensitiveText/index.ts b/app/component-library/components/Texts/SensitiveText/index.ts
new file mode 100644
index 00000000000..4ea8f25dad4
--- /dev/null
+++ b/app/component-library/components/Texts/SensitiveText/index.ts
@@ -0,0 +1,3 @@
+export { default } from './SensitiveText';
+export { SensitiveTextLength } from './SensitiveText.types';
+export type { SensitiveTextProps } from './SensitiveText.types';