Skip to content

Commit

Permalink
Merge branch 'main' into feat-header-update
Browse files Browse the repository at this point in the history
  • Loading branch information
vinnyhoward authored Oct 24, 2024
2 parents b862976 + 6b2cf82 commit f910b8c
Show file tree
Hide file tree
Showing 44 changed files with 2,114 additions and 274 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* eslint-disable react/display-name */
/* eslint-disable react-native/no-inline-styles */
// External dependencies.
import React from 'react';
import { View, Text } from 'react-native';

// Internal dependencies.
import PickerBase from './PickerBase';
import { IconSize } from '../../Icons/Icon';

const PickerBaseMeta = {
title: 'Component Library / Pickers',
component: PickerBase,
argTypes: {
children: {
control: { type: 'text' },
defaultValue: 'Select an option',
},
iconSize: {
options: Object.values(IconSize),
control: { type: 'select' },
defaultValue: IconSize.Md,
},
},
};

export default PickerBaseMeta;

export const Default = {
render: ({
children,
iconSize,
}: {
children: string;
iconSize: IconSize;
}) => (
<View style={{ alignItems: 'flex-start' }}>
<PickerBase onPress={() => null} iconSize={iconSize}>
<Text>{children}</Text>
</PickerBase>
</View>
),
};

export const WithCustomStyles = {
render: () => (
<View style={{ alignItems: 'flex-start' }}>
<PickerBase
onPress={() => null}
style={{ width: 200 }}
dropdownIconStyle={{ marginLeft: 20 }}
>
<Text>Custom Styled Picker</Text>
</PickerBase>
</View>
),
};
Original file line number Diff line number Diff line change
@@ -1,18 +1,78 @@
// Third party dependencies.
import React from 'react';
import { View } from 'react-native';
import { render } from '@testing-library/react-native';
import { Text } from 'react-native';
import { render, fireEvent } from '@testing-library/react-native';

// Internal dependencies.
import PickerBase from './PickerBase';
import { IconName, IconSize } from '../../Icons/Icon';

describe('PickerBase', () => {
it('should render correctly', () => {
const { toJSON } = render(
<PickerBase onPress={jest.fn}>
<View />
<PickerBase onPress={jest.fn()}>
<Text>Test Content</Text>
</PickerBase>,
);
expect(toJSON()).toMatchSnapshot();
});

it('should call onPress when pressed', () => {
const onPressMock = jest.fn();
const { getByText } = render(
<PickerBase onPress={onPressMock}>
<Text>Test Content</Text>
</PickerBase>,
);

fireEvent.press(getByText('Test Content'));
expect(onPressMock).toHaveBeenCalledTimes(1);
});

it('should render children correctly', () => {
const { getByText } = render(
<PickerBase onPress={jest.fn()}>
<Text>Child Component</Text>
</PickerBase>,
);

expect(getByText('Child Component')).toBeTruthy();
});

it('should render dropdown icon', () => {
const { UNSAFE_getByProps } = render(
<PickerBase onPress={jest.fn()}>
<Text>Test Content</Text>
</PickerBase>,
);

const icon = UNSAFE_getByProps({ name: IconName.ArrowDown });
expect(icon).toBeTruthy();
});

it('should apply custom icon size', () => {
const { UNSAFE_getByProps } = render(
<PickerBase onPress={jest.fn()} iconSize={IconSize.Lg}>
<Text>Test Content</Text>
</PickerBase>,
);

const icon = UNSAFE_getByProps({
name: IconName.ArrowDown,
size: IconSize.Lg,
});
expect(icon).toBeTruthy();
});

it('should apply custom dropdown icon style', () => {
const customStyle = { marginLeft: 20 };
const { UNSAFE_getByProps } = render(
<PickerBase onPress={jest.fn()} dropdownIconStyle={customStyle}>
<Text>Test Content</Text>
</PickerBase>,
);

const icon = UNSAFE_getByProps({ name: IconName.ArrowDown });
expect(icon.props.style).toEqual(expect.objectContaining(customStyle));
});
});
56 changes: 50 additions & 6 deletions app/component-library/components/Pickers/PickerBase/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# PickerBase

PickerBase is a **wrapper** component used for providing a dropdown icon next to wrapped content.
PickerBase is a **wrapper** component used for providing a dropdown icon next to wrapped content. It's designed to be a flexible base for various picker-style components.

## Props

This component extends `TouchableOpacityProps` from React Native's [TouchableOpacityProps](https://reactnative.dev/docs/touchableOpacity) opacity.
This component extends `TouchableOpacityProps` from React Native's [TouchableOpacity](https://reactnative.dev/docs/touchableopacity).

### `onPress`

Expand All @@ -22,11 +22,55 @@ Content to wrap in PickerBase.
| :-------------------------------------------------- | :------------------------------------------------------ |
| ReactNode | Yes |

### `iconSize`

Size of the dropdown icon.

| <span style="color:gray;font-size:14px">TYPE</span> | <span style="color:gray;font-size:14px">REQUIRED</span> | <span style="color:gray;font-size:14px">DEFAULT</span> |
| :-------------------------------------------------- | :------------------------------------------------------ | :----------------------------------------------------- |
| IconSize | No | IconSize.Md |

### `dropdownIconStyle`

Custom styles for the dropdown icon.

| <span style="color:gray;font-size:14px">TYPE</span> | <span style="color:gray;font-size:14px">REQUIRED</span> |
| :-------------------------------------------------- | :------------------------------------------------------ |
| ViewStyle | No |

### `style`

Custom styles for the main container.

| <span style="color:gray;font-size:14px">TYPE</span> | <span style="color:gray;font-size:14px">REQUIRED</span> |
| :-------------------------------------------------- | :------------------------------------------------------ |
| ViewStyle | No |

## Usage

```javascript
// Replace import with relative path.
import React from 'react';
import { Text } from 'react-native';
import PickerBase from 'app/component-library/components/Pickers/PickerBase';
import { IconSize } from 'app/component-library/components/Icons/Icon';

const ExampleComponent = () => (
<PickerBase
onPress={() => console.log('Picker pressed')}
iconSize={IconSize.Lg}
dropdownIconStyle={{ marginLeft: 20 }}
style={{ backgroundColor: 'lightgray' }}
>
<Text>Select an option</Text>
</PickerBase>
);

<PickerBase onPress={ONPRESS_CALLBACK}>
<SampleContent />
</PickerBase>;
export default ExampleComponent;
```

## Notes

- The component uses a `TouchableOpacity` as its base, providing press feedback.
- It automatically includes a dropdown icon (ArrowDown) to the right of the content.
- The component is designed to be flexible and can be customized using the `style` and `dropdownIconStyle` props.
- The dropdown icon color is determined by the theme's `colors.icon.default`.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

exports[`PickerBase should render correctly 1`] = `
<TouchableOpacity
onPress={[Function]}
onPress={[MockFunction]}
style={
{
"alignItems": "center",
Expand All @@ -15,7 +15,9 @@ exports[`PickerBase should render correctly 1`] = `
}
}
>
<View />
<Text>
Test Content
</Text>
<SvgMock
color="#141618"
height={20}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useRef } from 'react';
import { View, Image, Platform } from 'react-native';
import { View, Image } from 'react-native';
import { createStyles } from './styles';
import { strings } from '../../../../locales/i18n';
import Text, {
Expand All @@ -23,12 +23,7 @@ import {
import { MetaMetricsEvents } from '../../../core/Analytics';

import { ScrollView } from 'react-native-gesture-handler';
import {
ENABLE_AUTOMATIC_SECURITY_CHECK_CONTAINER_ID,
ENABLE_AUTOMATIC_SECURITY_CHECK_NO_THANKS_BUTTON_ID,
} from '../../../../wdio/screen-objects/testIDs/Screens/EnableAutomaticSecurityChecksScreen.testIds';

import generateTestId from '../../../../wdio/utils/generateTestId';
import { EnableAutomaticSecurityChecksIDs } from '../../../../e2e/selectors/Modals/EnableAutomaticSecurityChecks.selectors';
import generateDeviceAnalyticsMetaData from '../../../util/metrics';
import { useMetrics } from '../../../components/hooks/useMetrics';

Expand Down Expand Up @@ -92,10 +87,7 @@ const EnableAutomaticSecurityChecksModal = () => {
<ScrollView contentContainerStyle={styles.content}>
<View
style={styles.images}
{...generateTestId(
Platform,
ENABLE_AUTOMATIC_SECURITY_CHECK_CONTAINER_ID,
)}
testID={EnableAutomaticSecurityChecksIDs.CONTAINER}
>
<Image source={onboardingDeviceImage} />
</View>
Expand All @@ -122,10 +114,7 @@ const EnableAutomaticSecurityChecksModal = () => {
label={strings(
'enable_automatic_security_check_modal.secondary_action',
)}
{...generateTestId(
Platform,
ENABLE_AUTOMATIC_SECURITY_CHECK_NO_THANKS_BUTTON_ID,
)}
testID={EnableAutomaticSecurityChecksIDs.NO_THANKS_BUTTON}
size={ButtonSize.Md}
onPress={triggerCloseAndDisableAutomaticSecurityChecks}
/>
Expand Down
62 changes: 61 additions & 1 deletion app/components/Views/AccountActions/AccountActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ import { protectWalletModalVisible } from '../../../actions/user';
import Routes from '../../../constants/navigation/Routes';
import { AccountActionsModalSelectorsIDs } from '../../../../e2e/selectors/Modals/AccountActionsModal.selectors';
import { useMetrics } from '../../../components/hooks/useMetrics';
import { isHardwareAccount } from '../../../util/address';
import {
isHardwareAccount,
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
isSnapAccount,
///: END:ONLY_INCLUDE_IF
} from '../../../util/address';
import { removeAccountsFromPermissions } from '../../../core/Permissions';
import ExtendedKeyringTypes, {
HardwareDeviceTypes,
Expand Down Expand Up @@ -189,6 +194,49 @@ const AccountActions = () => {
}
}, [controllers.KeyringController]);

///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)

/**
* Remove the snap account from the keyring
*/
const removeSnapAccount = useCallback(async () => {
if (selectedAddress) {
await controllers.KeyringController.removeAccount(selectedAddress as Hex);
await removeAccountsFromPermissions([selectedAddress]);
trackEvent(MetaMetricsEvents.ACCOUNT_REMOVED, {
accountType: keyring?.type,
selectedAddress,
});
}
}, [
controllers.KeyringController,
keyring?.type,
selectedAddress,
trackEvent,
]);

const showRemoveSnapAccountAlert = useCallback(() => {
Alert.alert(
strings('accounts.remove_snap_account'),
strings('accounts.remove_snap_account_alert_description'),
[
{
text: strings('accounts.remove_account_alert_cancel_btn'),
style: 'cancel',
},
{
text: strings('accounts.remove_account_alert_remove_btn'),
onPress: async () => {
sheetRef.current?.onCloseBottomSheet(async () => {
await removeSnapAccount();
});
},
},
],
);
}, [removeSnapAccount]);
///: END:ONLY_INCLUDE_IF

/**
* Forget the device if there are no more accounts in the keyring
* @param keyringType - The keyring type
Expand Down Expand Up @@ -306,6 +354,18 @@ const AccountActions = () => {
testID={AccountActionsModalSelectorsIDs.REMOVE_HARDWARE_ACCOUNT}
/>
)}
{
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
selectedAddress && isSnapAccount(selectedAddress) && (
<AccountAction
actionTitle={strings('accounts.remove_snap_account')}
iconName={IconName.Close}
onPress={showRemoveSnapAccountAlert}
testID={AccountActionsModalSelectorsIDs.REMOVE_SNAP_ACCOUNT}
/>
)
///: END:ONLY_INCLUDE_IF
}
</View>
<BlockingActionModal
modalVisible={blockingModalVisible}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
export const KEYRING_SNAP_REMOVAL_WARNING = 'keyring-snap-removal-warning';
export const KEYRING_SNAP_REMOVAL_WARNING_CONTINUE =
'keyring-snap-removal-warning-continue';
export const KEYRING_SNAP_REMOVAL_WARNING_CANCEL =
'keyring-snap-removal-warning-cancel';
export const KEYRING_SNAP_REMOVAL_WARNING_TEXT_INPUT =
'keyring-snap-removal-warning-text-input';
///: END:ONLY_INCLUDE_IF
Loading

0 comments on commit f910b8c

Please sign in to comment.