Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions app/component-library/components/HeaderBase/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Content to wrap to display.
### `variant`

Optional variant to control alignment and text size.

- `Compact`: center-aligned with HeadingSm text (default)
- `Display`: left-aligned with HeadingLg text

Expand Down Expand Up @@ -86,15 +87,15 @@ Optional style for the header container.

```javascript
// HeaderBase with String title and ButtonIcon props
<HeaderBase
<HeaderBase
startButtonIconProps={{ iconName: IconName.ArrowLeft, onPress: handleBack }}
endButtonIconProps={[{ iconName: IconName.Close, onPress: handleClose }]}
>
Page Title
</HeaderBase>;

// HeaderBase with multiple end icons (first item appears rightmost)
<HeaderBase
<HeaderBase
startButtonIconProps={{ iconName: IconName.ArrowLeft, onPress: handleBack }}
endButtonIconProps={[
{ iconName: IconName.Search, onPress: handleSearch },
Expand All @@ -106,17 +107,18 @@ Optional style for the header container.
</HeaderBase>;

// HeaderBase with custom accessories (legacy pattern)
<HeaderBase
<HeaderBase
startAccessory={<CustomBackButton />}
endAccessory={<CustomCloseButton />}
>
{SAMPLE_TITLE_STRING}
</HeaderBase>;

// HeaderBase with custom title content
<HeaderBase
startAccessory={SAMPLE_STARTACCESSORY}
endAccessory={SAMPLE_ENDACCESSORY}>
<HeaderBase
startAccessory={SAMPLE_STARTACCESSORY}
endAccessory={SAMPLE_ENDACCESSORY}
>
{CUSTOM_TITLE_NODE}
</HeaderBase>;

Expand Down
14 changes: 13 additions & 1 deletion app/components/UI/AddressCopy/AddressCopy.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import AddressCopy from './AddressCopy';
import { WalletViewSelectorsIDs } from '../../../../e2e/selectors/wallet/WalletView.selectors';
import renderWithProvider from '../../../util/test/renderWithProvider';
import { createMockInternalAccount } from '../../../util/test/accountsControllerTestUtils';
import { ToastContext } from '../../../component-library/components/Toast';

// Mock navigation before importing renderWithProvider
jest.mock('@react-navigation/native', () => ({
Expand All @@ -14,8 +15,18 @@ jest.mock('@react-navigation/native', () => ({
}),
}));

const mockShowToast = jest.fn();
const mockCloseToast = jest.fn();
const mockToastRef = {
current: { showToast: mockShowToast, closeToast: mockCloseToast },
};

const renderWithAddressCopy = (account: InternalAccount) =>
renderWithProvider(<AddressCopy account={account} />);
renderWithProvider(
<ToastContext.Provider value={{ toastRef: mockToastRef }}>
<AddressCopy account={account} />
</ToastContext.Provider>,
);

describe('AddressCopy', () => {
beforeEach(() => {
Expand All @@ -26,6 +37,7 @@ describe('AddressCopy', () => {
const component = renderWithAddressCopy(
createMockInternalAccount('0xaddress', 'Account 1'),
);

expect(component).toBeDefined();
expect(
component.getByTestId(WalletViewSelectorsIDs.ACCOUNT_COPY_BUTTON),
Expand Down
39 changes: 21 additions & 18 deletions app/components/UI/AddressCopy/AddressCopy.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Third parties dependencies
import React, { useCallback } from 'react';
import React, { useCallback, useContext } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { View } from 'react-native';
import { useNavigation } from '@react-navigation/native';
Expand All @@ -12,8 +12,12 @@ import {
IconName,
} from '@metamask/design-system-react-native';
import ClipboardManager from '../../../core/ClipboardManager';
import { showAlert } from '../../../actions/alert';
import { protectWalletModalVisible } from '../../../actions/user';
import {
ToastContext,
ToastVariants,
} from '../../../component-library/components/Toast';
import { IconName as ComponentLibraryIconName } from '../../../component-library/components/Icons/Icon';

import { strings } from '../../../../locales/i18n';
import { MetaMetricsEvents } from '../../../core/Analytics';
Expand All @@ -26,6 +30,7 @@ import { createAddressListNavigationDetails } from '../../Views/MultichainAccoun
// Internal dependencies
import styleSheet from './AddressCopy.styles';
import { useMetrics } from '../../../components/hooks/useMetrics';
import { useTheme } from '../../../util/theme';
import { getFormattedAddressFromInternalAccount } from '../../../core/Multichain/utils';
import type { AddressCopyProps } from './AddressCopy.types';
import {
Expand All @@ -38,25 +43,17 @@ import {
const AddressCopy = ({ account, iconColor, hitSlop }: AddressCopyProps) => {
const { styles } = useStyles(styleSheet, {});
const { navigate } = useNavigation();
const { colors } = useTheme();

const dispatch = useDispatch();
const { trackEvent, createEventBuilder } = useMetrics();
const { toastRef } = useContext(ToastContext);

const isMultichainAccountsState2Enabled = useSelector(
selectMultichainAccountsState2Enabled,
);
const selectedAccountGroupId = useSelector(selectSelectedAccountGroupId);

const handleShowAlert = useCallback(
(config: {
isVisible: boolean;
autodismiss: number;
content: string;
data: { msg: string };
}) => dispatch(showAlert(config)),
[dispatch],
);

const handleProtectWalletModalVisible = useCallback(
() => dispatch(protectWalletModalVisible()),
[dispatch],
Expand All @@ -70,11 +67,15 @@ const AddressCopy = ({ account, iconColor, hitSlop }: AddressCopyProps) => {
await ClipboardManager.setString(
getFormattedAddressFromInternalAccount(account),
);
handleShowAlert({
isVisible: true,
autodismiss: 1500,
content: 'clipboard-alert',
data: { msg: strings('account_details.account_copied_to_clipboard') },
toastRef?.current?.showToast({
variant: ToastVariants.Icon,
iconName: ComponentLibraryIconName.CheckBold,
iconColor: colors.accent03.dark,
backgroundColor: colors.accent03.normal,
labelOptions: [
{ label: strings('account_details.account_copied_to_clipboard') },
],
hasNoTimeout: false,
});
setTimeout(() => handleProtectWalletModalVisible(), 2000);

Expand All @@ -83,9 +84,11 @@ const AddressCopy = ({ account, iconColor, hitSlop }: AddressCopyProps) => {
);
}, [
account,
colors.accent03.dark,
colors.accent03.normal,
createEventBuilder,
handleProtectWalletModalVisible,
handleShowAlert,
toastRef,
trackEvent,
]);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,34 @@
import React from 'react';
import { render } from '@testing-library/react-native';
// eslint-disable-next-line import/no-namespace
import * as reactRedux from 'react-redux';
import TokenDetailsList from './';
import { ToastContext } from '../../../../../component-library/components/Toast';

jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
useDispatch: jest.fn(),
}));
const mockShowToast = jest.fn();
const mockCloseToast = jest.fn();
const mockToastRef = {
current: { showToast: mockShowToast, closeToast: mockCloseToast },
};

const mockTokenDetails = {
contractAddress: '0x935e73edb9ff52e23bac7f7e043a1ecd06d05477',
tokenDecimal: 18,
tokenList: 'Metamask, Coinmarketcap',
};

const renderComponent = () =>
render(
<ToastContext.Provider value={{ toastRef: mockToastRef }}>
<TokenDetailsList tokenDetails={mockTokenDetails} />
</ToastContext.Provider>,
);

describe('TokenDetails', () => {
beforeAll(() => {
jest.resetAllMocks();
});

it('should render correctly', () => {
const useDispatchSpy = jest.spyOn(reactRedux, 'useDispatch');
useDispatchSpy.mockImplementation(() => jest.fn());
const { toJSON, getByText } = render(
<TokenDetailsList tokenDetails={mockTokenDetails} />,
);
it('renders correctly', () => {
const { toJSON, getByText } = renderComponent();

expect(getByText('Token details')).toBeDefined();
expect(getByText('Contract address')).toBeDefined();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import React from 'react';
import React, { useContext } from 'react';
import { TouchableOpacity, View } from 'react-native';
import { useDispatch } from 'react-redux';
import { useTailwind } from '@metamask/design-system-twrnc-preset';
import { Icon, IconName, IconSize } from '@metamask/design-system-react-native';
import { showAlert } from '../../../../../actions/alert';
import {
Icon,
IconName as DesignSystemIconName,
IconSize,
} from '@metamask/design-system-react-native';
import { IconName } from '../../../../../component-library/components/Icons/Icon';
import { strings } from '../../../../../../locales/i18n';
import { useStyles } from '../../../../../component-library/hooks';
import Text, {
Expand All @@ -14,6 +17,11 @@ import ClipboardManager from '../../../../../core/ClipboardManager';
import { TokenDetails } from '../TokenDetails';
import TokenDetailsListItem from '../TokenDetailsListItem';
import { formatAddress } from '../../../../../util/address';
import {
ToastContext,
ToastVariants,
} from '../../../../../component-library/components/Toast';
import { useTheme } from '../../../../../util/theme';

interface TokenDetailsListProps {
tokenDetails: TokenDetails;
Expand All @@ -22,25 +30,23 @@ interface TokenDetailsListProps {
const TokenDetailsList: React.FC<TokenDetailsListProps> = ({
tokenDetails,
}) => {
const { styles } = useStyles(styleSheet, {});
const { toastRef } = useContext(ToastContext);
const { colors } = useTheme();
const tw = useTailwind();
const { styles } = useStyles(styleSheet);
const dispatch = useDispatch();

const handleShowAlert = (config: {
isVisible: boolean;
autodismiss: number;
content: string;
data: { msg: string };
}) => dispatch(showAlert(config));

const copyAccountToClipboard = async () => {
await ClipboardManager.setString(tokenDetails.contractAddress);

handleShowAlert({
isVisible: true,
autodismiss: 1500,
content: 'clipboard-alert',
data: { msg: strings('account_details.account_copied_to_clipboard') },
toastRef?.current?.showToast({
variant: ToastVariants.Icon,
iconName: IconName.CheckBold,
iconColor: colors.accent03.dark,
backgroundColor: colors.accent03.normal,
labelOptions: [
{ label: strings('account_details.account_copied_to_clipboard') },
],
hasNoTimeout: false,
});
};

Expand All @@ -62,7 +68,7 @@ const TokenDetailsList: React.FC<TokenDetailsListProps> = ({
<Text variant={TextVariant.BodySM}>
{formatAddress(tokenDetails.contractAddress, 'short')}
</Text>
<Icon name={IconName.Copy} size={IconSize.Sm} />
<Icon name={DesignSystemIconName.Copy} size={IconSize.Sm} />
</TouchableOpacity>
</TokenDetailsListItem>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`TokenDetails should render correctly 1`] = `
exports[`TokenDetails renders correctly 1`] = `
<View>
<Text
accessibilityRole="text"
Expand Down
Loading
Loading