Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add copy button component #11736

Merged
merged 3 commits into from
Oct 10, 2024
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
1 change: 1 addition & 0 deletions .storybook/storybook.requires.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +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"),
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { View } from 'react-native';
import { useSelector } from 'react-redux';

import { strings } from '../../../../../../../../locales/i18n';
import { selectRpcUrl } from '../../../../../../../selectors/networkController';
import { selectProviderConfig } from '../../../../../../../selectors/networkController';
import { selectNetworkName } from '../../../../../../../selectors/networkInfos';
import useAccountInfo from '../../../../hooks/useAccountInfo';
import useApprovalRequest from '../../../../hooks/useApprovalRequest';
Expand All @@ -15,7 +15,8 @@ import InfoURL from '../../../UI/InfoRow/InfoValue/InfoURL';
const AccountNetworkInfoExpanded = () => {
const { approvalRequest } = useApprovalRequest();
const networkName = useSelector(selectNetworkName);
const networkRpcUrl = useSelector(selectRpcUrl);
const { rpcUrl: networkRpcUrl, type: networkType } =
useSelector(selectProviderConfig);
const fromAddress = approvalRequest?.requestData?.from;
const { accountAddress, accountBalance } = useAccountInfo(fromAddress);

Expand All @@ -34,7 +35,9 @@ const AccountNetworkInfoExpanded = () => {
{networkName}
</InfoRow>
<InfoRow label={strings('confirm.rpc_url')}>
<InfoURL url={networkRpcUrl} />
<InfoURL
url={networkRpcUrl ?? `https://${networkType}.infura.io/v3/`}
/>
</InfoRow>
</InfoSection>
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,9 @@ exports[`AccountNetworkInfoExpanded should match snapshot for personal sign 1`]
"marginTop": 8,
}
}
/>
>
mainnet.infura.io/v3/
</Text>
</View>
</View>
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const styleSheet = (params: { theme: Theme }) => {
fontSize: 14,
fontWeight: '400',
},
copyButton: {
copyButtonContainer: {
position: 'absolute',
top: -40,
right: 0,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,24 @@
import React, { useCallback, useMemo, useState } from 'react';
import React, { useMemo } from 'react';
import { Text, View } from 'react-native';
import { hexToText } from '@metamask/controller-utils';

import ButtonIcon, {
ButtonIconSizes,
} from '../../../../../../../../component-library/components/Buttons/ButtonIcon';
import ClipboardManager from '../../../../../../../../core/ClipboardManager';
import {
IconColor,
IconName,
} from '../../../../../../../../component-library/components/Icons/Icon';
import { sanitizeString } from '../../../../../../../../util/string';
import { strings } from '../../../../../../../../../locales/i18n';
import { useStyles } from '../../../../../../../../component-library/hooks';
import useApprovalRequest from '../../../../../hooks/useApprovalRequest';
import ExpandableSection from '../../../../UI/ExpandableSection';
import styleSheet from './Message.styles';
import CopyButton from '../../../../UI/CopyButton';

const Message = () => {
const { approvalRequest } = useApprovalRequest();
const [copied, setCopied] = useState(false);
const { styles } = useStyles(styleSheet, {});

const message = useMemo(
() => sanitizeString(hexToText(approvalRequest?.requestData?.data)),
[approvalRequest?.requestData?.data],
);

const copyMessage = useCallback(async () => {
await ClipboardManager.setString(message);
setCopied(true);
}, [message, setCopied]);

return (
<ExpandableSection
collapsedContent={
Expand All @@ -44,14 +31,9 @@ const Message = () => {
}
expandedContent={
<View style={styles.messageContainer}>
<ButtonIcon
iconColor={IconColor.Muted}
size={ButtonIconSizes.Sm}
onPress={copyMessage}
iconName={copied ? IconName.CopySuccess : IconName.Copy}
style={styles.copyButton}
testID="copyButtonTestId"
/>
<View style={styles.copyButtonContainer}>
<CopyButton copyText={message} />
</View>
<Text style={styles.messageExpanded}>{message}</Text>
</View>
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from 'react';
import { storiesOf } from '@storybook/react-native';

import CopyButton from './CopyButton';

storiesOf('Confirmations / CopyButton', module)
.addDecorator((getStory) => getStory())
.add('Default', () => <CopyButton copyText="DUMMY" />);
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import { fireEvent, render } from '@testing-library/react-native';

import ClipboardManager from '../../../../../../core/ClipboardManager';
import CopyButton from './CopyButton';

jest.mock('../../../../../../core/ClipboardManager');

describe('CopyButton', () => {
it('should match snapshot', async () => {
const container = render(<CopyButton copyText={'DUMMY'} />);
expect(container).toMatchSnapshot();
});

it('should copy text to clipboard when pressed', async () => {
const { getByTestId } = render(<CopyButton copyText={'DUMMY'} />);
fireEvent.press(getByTestId('copyButtonTestId'));
expect(ClipboardManager.setString).toHaveBeenCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React, { useCallback, useState } from 'react';

import ButtonIcon, {
ButtonIconSizes,
} from '../../../../../../component-library/components/Buttons/ButtonIcon';
import ClipboardManager from '../../../../../../core/ClipboardManager';
import {
IconColor,
IconName,
} from '../../../../../../component-library/components/Icons/Icon';

interface CopyButtonProps {
copyText: string;
testID?: string;
}

const CopyButton = ({ copyText, testID }: CopyButtonProps) => {
const [copied, setCopied] = useState(false);

const copyMessage = useCallback(async () => {
await ClipboardManager.setString(copyText);
setCopied(true);
}, [copyText, setCopied]);

return (
<ButtonIcon
iconColor={IconColor.Alternative}
size={ButtonIconSizes.Sm}
onPress={copyMessage}
iconName={copied ? IconName.CopySuccess : IconName.Copy}
testID={testID ?? 'copyButtonTestId'}
/>
);
};

export default CopyButton;
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`CopyButton should match snapshot 1`] = `
<TouchableOpacity
accessible={true}
activeOpacity={1}
disabled={false}
onPress={[Function]}
onPressIn={[Function]}
onPressOut={[Function]}
style={
{
"alignItems": "center",
"borderRadius": 8,
"height": 24,
"justifyContent": "center",
"opacity": 1,
"width": 24,
}
}
testID="copyButtonTestId"
>
<SvgMock
color="#6a737d"
height={16}
name="Copy"
style={
{
"height": 16,
"width": 16,
}
}
width={16}
/>
</TouchableOpacity>
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './CopyButton';
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { StyleSheet } from 'react-native';

import { Colors, Theme } from '../../../../../../util/theme/models';
import { Theme } from '../../../../../../util/theme/models';
import { fontStyles } from '../../../../../../styles/common';

const createStyles = (colors: Colors, shadows: Theme['shadows']) =>
StyleSheet.create({
const styleSheet = (params: { theme: Theme }) => {
const { theme } = params;

return StyleSheet.create({
modal: {
margin: 0,
},
modalView: {
backgroundColor: colors.background.default,
backgroundColor: theme.colors.background.default,
justifyContent: 'center',
alignItems: 'center',
marginHorizontal: 16,
borderRadius: 8,
...shadows.size.sm,
...theme.shadows.size.sm,
elevation: 11,
paddingHorizontal: 16,
paddingVertical: 24,
Expand All @@ -25,17 +27,18 @@ const createStyles = (colors: Colors, shadows: Theme['shadows']) =>
right: 10,
},
modalTitle: {
color: colors.text.default,
color: theme.colors.text.default,
...fontStyles.bold,
fontSize: 16,
fontWeight: '700',
marginBottom: 16,
},
modalContent: {
color: colors.text.default,
color: theme.colors.text.default,
...fontStyles.normal,
fontSize: 14,
}
},
});
};

export default createStyles;
export default styleSheet;
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import {
IconColor,
IconName,
} from '../../../../../../component-library/components/Icons/Icon';
import { useStyles } from '../../../../../../component-library/hooks';
import { useTheme } from '../../../../../../util/theme';
import createStyles from './style';
import styleSheet from './Tooltip.styles';

interface TooltipProps {
content: ReactNode;
Expand All @@ -20,8 +21,8 @@ interface TooltipProps {

const Tooltip = ({ content, title, tooltipTestId }: TooltipProps) => {
const [open, setOpen] = useState(false);
const { colors, shadows } = useTheme();
const styles = createStyles(colors, shadows);
const { colors } = useTheme();
const { styles } = useStyles(styleSheet, {});

return (
<View>
Expand Down Expand Up @@ -52,8 +53,8 @@ const Tooltip = ({ content, title, tooltipTestId }: TooltipProps) => {
style={styles.closeModalBtn}
testID={tooltipTestId ?? 'tooltipTestId'}
/>
{title && <Text style={styles.modalTitle}>{title}</Text>}
<Text>{content}</Text>
{title && <Text style={styles.modalTitle}>{title}</Text>}
<Text>{content}</Text>
</View>
</Modal>
</View>
Expand Down
Loading