From c15b4c68b06a132df196004a8443504b6bc2bae5 Mon Sep 17 00:00:00 2001 From: Lucian Marica Date: Sat, 19 Oct 2019 15:13:33 +0300 Subject: [PATCH 1/4] Add theme support to components --- src/components/account-card/styles.ts | 5 +- .../coin-balance-card/coin-balance-card.tsx | 18 +++-- src/components/coin-balance-card/styles.ts | 33 ++++---- .../coin-dashboard/coin-dashboard.tsx | 15 ++-- src/components/coin-dashboard/styles.ts | 57 +++++++------- .../conversion-card/conversion-card.tsx | 12 +-- src/components/conversion-card/styles.ts | 45 +++++------ .../currency-format/currency-format.tsx | 6 +- src/components/currency-format/styles.ts | 9 --- src/core/theme/itheme.ts | 13 ++++ src/library/text.tsx | 19 +++-- src/screens/dashboard/dashboard.tsx | 24 ++++-- src/screens/dashboard/styles.ts | 75 ++++++++++--------- src/styles/colors.ts | 6 +- src/styles/themes/dark-theme.ts | 15 +++- 15 files changed, 191 insertions(+), 161 deletions(-) delete mode 100644 src/components/currency-format/styles.ts diff --git a/src/components/account-card/styles.ts b/src/components/account-card/styles.ts index b91a2548b..538b6826e 100644 --- a/src/components/account-card/styles.ts +++ b/src/components/account-card/styles.ts @@ -1,11 +1,10 @@ import { StyleSheet } from 'react-native'; -import { COLORS } from '../../styles/colors'; import { ITheme } from '../../core/theme/itheme'; export default (theme: ITheme) => StyleSheet.create({ container: { - backgroundColor: COLORS.cardBackground, + backgroundColor: theme.colors.cardBackground, borderRadius: 6, height: 80, display: 'flex', @@ -23,6 +22,6 @@ export default (theme: ITheme) => marginHorizontal: 8 }, icon: { - color: theme.colors.primary + color: theme.colors.accent } }); diff --git a/src/components/coin-balance-card/coin-balance-card.tsx b/src/components/coin-balance-card/coin-balance-card.tsx index f7991a827..083c19b8a 100644 --- a/src/components/coin-balance-card/coin-balance-card.tsx +++ b/src/components/coin-balance-card/coin-balance-card.tsx @@ -3,7 +3,8 @@ import { View } from 'react-native'; import Convert from '../convert/convert'; import { Text } from '../../library/text'; -import styles from './styles'; +import stylesProvider from './styles'; +import { withTheme } from '../../core/theme/with-theme'; interface IProps { currency: string; @@ -11,25 +12,28 @@ interface IProps { toCurrency: string; width: number; active: boolean; + styles: ReturnType; } -export const CoinBalanceCard = (props: IProps) => ( - +export const CoinBalanceCardComponent = (props: IProps) => ( + - + {props.balance} - {props.currency} + {props.currency} {props.balance} - {props.toCurrency} + {props.toCurrency} ); + +export const CoinBalanceCard = withTheme(CoinBalanceCardComponent, stylesProvider); diff --git a/src/components/coin-balance-card/styles.ts b/src/components/coin-balance-card/styles.ts index 139a188ed..fc5871757 100644 --- a/src/components/coin-balance-card/styles.ts +++ b/src/components/coin-balance-card/styles.ts @@ -1,19 +1,18 @@ import { StyleSheet } from 'react-native'; -import { COLORS } from '../../styles/colors'; +import { ITheme } from '../../core/theme/itheme'; -export default StyleSheet.create({ - container: { - padding: 0, - paddingTop: 10, - paddingBottom: 10, - alignItems: 'center', - color: COLORS.WHITE - // width: 200 - }, - darkerText: { - color: COLORS.LIGHT_GRAY - }, - mainText: { - fontSize: 34 - } -}); +export default (theme: ITheme) => + StyleSheet.create({ + container: { + padding: 0, + paddingTop: 10, + paddingBottom: 10, + alignItems: 'center' + }, + darkerText: { + color: theme.colors.textDarker + }, + mainText: { + fontSize: 34 + } + }); diff --git a/src/components/coin-dashboard/coin-dashboard.tsx b/src/components/coin-dashboard/coin-dashboard.tsx index b785fa6e2..37ad50b51 100644 --- a/src/components/coin-dashboard/coin-dashboard.tsx +++ b/src/components/coin-dashboard/coin-dashboard.tsx @@ -7,18 +7,20 @@ import { Blockchain } from '../../core/blockchain/types'; import { BLOCKCHAIN_INFO } from '../../core/constants/blockchain'; import { Icon } from '../icon'; -import styles from './styles'; +import stylesProvider from './styles'; +import { withTheme } from '../../core/theme/with-theme'; interface IProps { blockchain: Blockchain; accounts?: IAccountState[]; + styles: ReturnType; } const conversionCards = ['USD', 'BTC', 'ETH']; -export const CoinDashboard = (props: IProps) => ( - - +export const CoinDashboardComponent = (props: IProps) => ( + + {conversionCards.map( (toCurrency, i) => BLOCKCHAIN_INFO[props.blockchain].coin !== toCurrency && ( @@ -30,8 +32,8 @@ export const CoinDashboard = (props: IProps) => ( ) )} - - + + {props.accounts && @@ -41,3 +43,4 @@ export const CoinDashboard = (props: IProps) => ( ); +export const CoinDashboard = withTheme(CoinDashboardComponent, stylesProvider); diff --git a/src/components/coin-dashboard/styles.ts b/src/components/coin-dashboard/styles.ts index 15feffa44..844ea0529 100644 --- a/src/components/coin-dashboard/styles.ts +++ b/src/components/coin-dashboard/styles.ts @@ -1,33 +1,28 @@ import { StyleSheet } from 'react-native'; -import { COLORS } from '../../styles/colors'; +import { ITheme } from '../../core/theme/itheme'; -export default StyleSheet.create({ - container: { - padding: 0, - paddingTop: 10, - alignItems: 'center', - alignSelf: 'stretch', - flex: 1, - margin: 12, - marginBottom: 4 - }, - exchangeCardContainer: { - display: 'flex', - flexDirection: 'row' - }, - text: { - color: COLORS.WHITE - }, - mainText: { - fontSize: 34 - }, - icon: { - color: COLORS.AQUA - }, - addButton: { - marginTop: 20, - marginBottom: 12, - alignSelf: 'flex-end', - marginRight: 12 - } -}); +export default (theme: ITheme) => + StyleSheet.create({ + container: { + padding: 0, + paddingTop: 10, + alignItems: 'center', + alignSelf: 'stretch', + flex: 1, + margin: 12, + marginBottom: 4 + }, + exchangeCardContainer: { + display: 'flex', + flexDirection: 'row' + }, + icon: { + color: theme.colors.accent + }, + addButton: { + marginTop: 20, + marginBottom: 12, + alignSelf: 'flex-end', + marginRight: 12 + } + }); diff --git a/src/components/conversion-card/conversion-card.tsx b/src/components/conversion-card/conversion-card.tsx index 23b25f4b7..96f180840 100644 --- a/src/components/conversion-card/conversion-card.tsx +++ b/src/components/conversion-card/conversion-card.tsx @@ -4,12 +4,14 @@ import { TextSmall } from '../../library/text'; import { connect } from 'react-redux'; import Convert from '../convert/convert'; -import styles from './styles'; +import stylesProvider from './styles'; +import { withTheme } from '../../core/theme/with-theme'; interface IProps { fromCurrency: string; toCurrency: string; change: any; + styles: ReturnType; } const mapStateToProps = (state: any) => ({ @@ -20,15 +22,15 @@ export const ConversionCardComponent = (props: IProps) => { const change = props.change[props.fromCurrency][props.toCurrency] || 0; return ( - - + + {props.fromCurrency} {props.toCurrency} 1 - = 0 ? styles.changeUp : styles.changeDown]}> + = 0 ? props.styles.changeUp : props.styles.changeDown]}> {change} @@ -38,4 +40,4 @@ export const ConversionCardComponent = (props: IProps) => { export const ConversionCard = connect( mapStateToProps, null -)(ConversionCardComponent); +)(withTheme(ConversionCardComponent, stylesProvider)); diff --git a/src/components/conversion-card/styles.ts b/src/components/conversion-card/styles.ts index 33ea0550e..e63d5f478 100644 --- a/src/components/conversion-card/styles.ts +++ b/src/components/conversion-card/styles.ts @@ -1,24 +1,25 @@ import { StyleSheet } from 'react-native'; -import { COLORS } from '../../styles/colors'; +import { ITheme } from '../../core/theme/itheme'; -export default StyleSheet.create({ - container: { - backgroundColor: COLORS.cardBackground, - borderRadius: 6, - height: 60, - display: 'flex', - justifyContent: 'center', - marginHorizontal: 4, - alignItems: 'center', - flex: 1 - }, - conversionLabel: { - color: COLORS.LIGHT_GRAY - }, - changeUp: { - color: COLORS.HOT_GREEN - }, - changeDown: { - color: COLORS.HOT_RED - } -}); +export default (theme: ITheme) => + StyleSheet.create({ + container: { + backgroundColor: theme.colors.cardBackground, + borderRadius: 6, + height: 60, + display: 'flex', + justifyContent: 'center', + marginHorizontal: 4, + alignItems: 'center', + flex: 1 + }, + conversionLabel: { + color: theme.colors.textDarker + }, + changeUp: { + color: theme.colors.positive + }, + changeDown: { + color: theme.colors.negative + } + }); diff --git a/src/components/currency-format/currency-format.tsx b/src/components/currency-format/currency-format.tsx index 0409f2546..57c3689d8 100644 --- a/src/components/currency-format/currency-format.tsx +++ b/src/components/currency-format/currency-format.tsx @@ -1,7 +1,5 @@ import React from 'react'; -import { Text } from 'react-native'; - -import styles from './styles'; +import { Text } from '../../library/text'; const formatter = /(\d)(?=(\d{3})+(?!\d))/g; @@ -16,7 +14,7 @@ const CurrencyFormat = (props: IProps) => { let output = props.decimals ? value.toFixed(2) : '' + value; output = output.replace(formatter, '$1,'); - return {output}; + return {output}; }; export default CurrencyFormat; diff --git a/src/components/currency-format/styles.ts b/src/components/currency-format/styles.ts deleted file mode 100644 index 821d4c70f..000000000 --- a/src/components/currency-format/styles.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { StyleSheet } from 'react-native'; -import { COLORS } from '../../styles/colors'; - -export default StyleSheet.create({ - container: {}, - text: { - color: COLORS.WHITE - } -}); diff --git a/src/core/theme/itheme.ts b/src/core/theme/itheme.ts index 20ddd18ea..230631048 100644 --- a/src/core/theme/itheme.ts +++ b/src/core/theme/itheme.ts @@ -1,7 +1,20 @@ export interface ITheme { dark: boolean; + fontSize: { + small: number; + regular: number; + large: number; + }; + colors: { primary: string; + accent: string; + text: string; + textDarker: string; + positive: string; + negative: string; + + cardBackground: string; }; } diff --git a/src/library/text.tsx b/src/library/text.tsx index 45da657d0..61b0da7a4 100644 --- a/src/library/text.tsx +++ b/src/library/text.tsx @@ -1,11 +1,16 @@ import React from 'react'; import ReactNative from 'react-native'; -const defaultStyle = { - color: '#FFFFFF', +import { withTheme } from '../core/theme/with-theme'; +import { ITheme } from '../core/theme/itheme'; + +// TODO refactor this + +const defaultStyleProvider = (theme: ITheme) => ({ + color: theme.colors.text, fontFamily: 'System', - fontSize: 16 -}; + fontSize: theme.fontSize.regular +}); const mergeStyle = (baseStyle: any, newStyle: any) => { let style; @@ -23,8 +28,8 @@ const getProps = (props: any) => ({ children: props.children }); -const Text = (props: any) => { - return ; +const TextComponent = (props: any) => { + return ; }; const TextSmall = (props: any) => { @@ -43,4 +48,6 @@ const TextSmall = (props: any) => { ); }; +const Text = withTheme(TextComponent, defaultStyleProvider); + export { Text, TextSmall }; diff --git a/src/screens/dashboard/dashboard.tsx b/src/screens/dashboard/dashboard.tsx index 3c7175011..d1d10891e 100644 --- a/src/screens/dashboard/dashboard.tsx +++ b/src/screens/dashboard/dashboard.tsx @@ -4,18 +4,20 @@ import { Text } from '../../library/text'; import { NavigationParams, NavigationScreenProp, NavigationState } from 'react-navigation'; import { CoinBalanceCard } from '../../components/coin-balance-card/coin-balance-card'; import { CoinDashboard } from '../../components/coin-dashboard/coin-dashboard'; -import { mapStateToProps } from '../../redux/utils/redux-decorators'; import { IReduxState } from '../../redux/state'; +import { connect } from 'react-redux'; import { IWalletState, IAccountState } from '../../redux/wallets/state'; import { Blockchain } from '../../core/blockchain/types'; import { BLOCKCHAIN_INFO } from '../../core/constants/blockchain'; -import styles from './styles'; +import stylesProvider from './styles'; +import { withTheme } from '../../core/theme/with-theme'; interface IProps { navigation: NavigationScreenProp; money: number; ethusd: number; + styles: ReturnType; } interface IReduxProps { @@ -49,12 +51,11 @@ const calculateBalances = (accounts: IAccountState[]) => { coins: [], balance: {} } ); -@mapStateToProps( - (state: IReduxState): IReduxProps => ({ - wallet: state.wallets[state.app.currentWalletIndex] - }) -) -export class DashboardScreen extends React.Component { +const mapStateToProps = (state: IReduxState) => ({ + wallet: state.wallets[state.app.currentWalletIndex] +}); + +export class DashboardScreenComponent extends React.Component { public initialIndex = 0; public dashboardOpacity = new Animated.Value(1); public balancesScrollView: any; @@ -112,6 +113,8 @@ export class DashboardScreen extends React.Component @@ -186,3 +189,8 @@ export class DashboardScreen extends React.Component + StyleSheet.create({ + container: { + padding: 0, + paddingTop: 40, + alignItems: 'center', + justifyContent: 'flex-start', + backgroundColor: '#121212', + ...StyleSheet.absoluteFillObject + }, + balancesContainer: { + height: 160 + }, + blockchainSelectorContainer: { + backgroundColor: theme.colors.cardBackground, + borderRadius: 20, + height: 40, + alignSelf: 'stretch', + margin: 12, + marginTop: 8, + flexDirection: 'row' + }, + blockchainButton: { + flex: 1, + flexBasis: 0, + justifyContent: 'center', + alignItems: 'center', + borderRadius: 16, + margin: 4 + }, + blockchainButtonActive: { + backgroundColor: theme.colors.primary + }, + blockchainButtonTextActive: { + color: theme.colors.accent + } + }); diff --git a/src/styles/colors.ts b/src/styles/colors.ts index 2772f7650..2d3b7b6d9 100644 --- a/src/styles/colors.ts +++ b/src/styles/colors.ts @@ -1,14 +1,10 @@ export const COLORS = { WHITE: '#FFF', BLACK: '#000', - COD_GRAY: '#121212', DARK_GRAY: '#2f2f31', GRAY: '#4E4E53', LIGHT_GRAY: '#9A99A2', HOT_GREEN: '#00E676', HOT_RED: '#E91E63', - AQUA: '#00DAFF', - - // TODO these should be move un themes - cardBackground: '#2f2f31' + AQUA: '#00DAFF' }; diff --git a/src/styles/themes/dark-theme.ts b/src/styles/themes/dark-theme.ts index b075cebae..a29797c22 100644 --- a/src/styles/themes/dark-theme.ts +++ b/src/styles/themes/dark-theme.ts @@ -4,7 +4,20 @@ import { COLORS } from '../colors'; export const darkTheme: ITheme = { dark: true, + fontSize: { + small: 12, + regular: 16, + large: 34 + }, + colors: { - primary: COLORS.AQUA + primary: COLORS.GRAY, + accent: COLORS.AQUA, + text: COLORS.WHITE, + textDarker: COLORS.LIGHT_GRAY, + positive: COLORS.HOT_GREEN, + negative: COLORS.HOT_RED, + + cardBackground: COLORS.DARK_GRAY } }; From c508e3058cf76f500a99c01c9fcd272cd42899b3 Mon Sep 17 00:00:00 2001 From: Lucian Marica Date: Sun, 20 Oct 2019 00:04:06 +0300 Subject: [PATCH 2/4] Add dashboard tests, formatting improvements --- .../__snapshots__/account-card.test.tsx.snap | 24 ++++ .../__tests__/account-card.test.tsx | 26 ++++ src/components/account-card/account-card.tsx | 17 +-- .../coin-balance-card.test.tsx.snap | 18 +++ .../__tests__/coin-balance-card.test.tsx | 23 +++ .../coin-balance-card/coin-balance-card.tsx | 13 +- .../coin-dashboard.test.tsx.snap | 17 +++ .../__tests__/coin-dashboard.test.tsx | 29 ++++ .../coin-dashboard/coin-dashboard.tsx | 2 +- .../conversion-card.test.tsx.snap | 27 ++++ .../__tests__/conversion-card.test.tsx | 39 +++++ .../conversion-card/conversion-card.tsx | 17 +-- .../__snapshots__/convert.test.tsx.snap | 13 ++ .../convert/__tests__/convert.test.tsx | 29 ++++ src/components/convert/convert.tsx | 36 +++-- .../utils/__tests__/format-number.test.ts | 51 +++++++ src/core/utils/format-number.ts | 34 +++++ .../__snapshots__/text.test.tsx.snap | 19 +++ src/library/__tests__/text.test.tsx | 32 +++++ src/library/text.tsx | 27 ++-- .../__snapshots__/dashboard.test.tsx.snap | 136 ++++++++++++++++++ .../dashboard/__tests__/dashboard.test.tsx | 117 +++++++++++++++ src/screens/dashboard/dashboard.tsx | 8 +- .../__snapshots__/settings.test.tsx.snap | 9 ++ .../settings/__tests__/settings.test.tsx | 12 ++ tslint.json | 3 +- 26 files changed, 722 insertions(+), 56 deletions(-) create mode 100644 src/components/account-card/__tests__/__snapshots__/account-card.test.tsx.snap create mode 100644 src/components/account-card/__tests__/account-card.test.tsx create mode 100644 src/components/coin-balance-card/__tests__/__snapshots__/coin-balance-card.test.tsx.snap create mode 100644 src/components/coin-balance-card/__tests__/coin-balance-card.test.tsx create mode 100644 src/components/coin-dashboard/__tests__/__snapshots__/coin-dashboard.test.tsx.snap create mode 100644 src/components/coin-dashboard/__tests__/coin-dashboard.test.tsx create mode 100644 src/components/conversion-card/__tests__/__snapshots__/conversion-card.test.tsx.snap create mode 100644 src/components/conversion-card/__tests__/conversion-card.test.tsx create mode 100644 src/components/convert/__tests__/__snapshots__/convert.test.tsx.snap create mode 100644 src/components/convert/__tests__/convert.test.tsx create mode 100644 src/core/utils/__tests__/format-number.test.ts create mode 100644 src/core/utils/format-number.ts create mode 100644 src/library/__tests__/__snapshots__/text.test.tsx.snap create mode 100644 src/library/__tests__/text.test.tsx create mode 100644 src/screens/dashboard/__tests__/__snapshots__/dashboard.test.tsx.snap create mode 100644 src/screens/dashboard/__tests__/dashboard.test.tsx create mode 100644 src/screens/settings/__tests__/__snapshots__/settings.test.tsx.snap create mode 100644 src/screens/settings/__tests__/settings.test.tsx diff --git a/src/components/account-card/__tests__/__snapshots__/account-card.test.tsx.snap b/src/components/account-card/__tests__/__snapshots__/account-card.test.tsx.snap new file mode 100644 index 000000000..600cc0e5b --- /dev/null +++ b/src/components/account-card/__tests__/__snapshots__/account-card.test.tsx.snap @@ -0,0 +1,24 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`account card component renders correctly 1`] = ` +" + + + + + 12332 + + + + $ + + + + + zil1vs74hw5k21233h432kj321l3k21b + + + + +" +`; diff --git a/src/components/account-card/__tests__/account-card.test.tsx b/src/components/account-card/__tests__/account-card.test.tsx new file mode 100644 index 000000000..14dfae726 --- /dev/null +++ b/src/components/account-card/__tests__/account-card.test.tsx @@ -0,0 +1,26 @@ +import 'react-native'; +import React from 'react'; +import { AccountCardComponent, IProps } from '../account-card'; +import { Blockchain } from '../../../core/blockchain/types'; +import { darkTheme } from '../../../styles/themes/dark-theme'; +import styleProvider from '../styles'; + +import { shallow } from 'enzyme'; + +const props: IProps = { + account: { + index: 1, + blockchain: Blockchain.ZILLIQA, + address: 'zil1vs74hw5k21233h432kj321l3k21b', + publicKey: '1', + balance: 12332 + }, + blockchain: Blockchain.ZILLIQA, + styles: styleProvider(darkTheme) +}; +describe('account card component', () => { + it('renders correctly', () => { + const wrapper = shallow(); + expect(wrapper.debug()).toMatchSnapshot(); + }); +}); diff --git a/src/components/account-card/account-card.tsx b/src/components/account-card/account-card.tsx index 880c157db..f0ab7537b 100644 --- a/src/components/account-card/account-card.tsx +++ b/src/components/account-card/account-card.tsx @@ -5,12 +5,12 @@ import { IAccountState } from '../../redux/wallets/state'; import { Blockchain } from '../../core/blockchain/types'; import { BLOCKCHAIN_INFO } from '../../core/constants/blockchain'; import { Icon } from '../icon'; -import Convert from '../convert/convert'; +import { Convert } from '../convert/convert'; import stylesProvider from './styles'; import { withTheme } from '../../core/theme/with-theme'; -interface IProps { +export interface IProps { account: IAccountState; blockchain: Blockchain; styles: ReturnType; @@ -22,19 +22,20 @@ export const AccountCardComponent = (props: IProps) => { - - {props.account.balance} {BLOCKCHAIN_INFO[props.account.blockchain].coin} + + + {props.account.balance} + {' '} $ - {props.account.balance} - + amount={props.account.balance} + /> - + {props.account.address} diff --git a/src/components/coin-balance-card/__tests__/__snapshots__/coin-balance-card.test.tsx.snap b/src/components/coin-balance-card/__tests__/__snapshots__/coin-balance-card.test.tsx.snap new file mode 100644 index 000000000..177db0ac1 --- /dev/null +++ b/src/components/coin-balance-card/__tests__/__snapshots__/coin-balance-card.test.tsx.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`coin balance component renders correctly 1`] = ` +" + + + 100 + + + + ZIL + + + + + +" +`; diff --git a/src/components/coin-balance-card/__tests__/coin-balance-card.test.tsx b/src/components/coin-balance-card/__tests__/coin-balance-card.test.tsx new file mode 100644 index 000000000..9de190175 --- /dev/null +++ b/src/components/coin-balance-card/__tests__/coin-balance-card.test.tsx @@ -0,0 +1,23 @@ +import 'react-native'; +import React from 'react'; +import { CoinBalanceCardComponent, IProps } from '../coin-balance-card'; +import { darkTheme } from '../../../styles/themes/dark-theme'; +import styleProvider from '../styles'; + +import { shallow } from 'enzyme'; + +const props: IProps = { + currency: 'ZIL', + balance: 100, + toCurrency: 'ETH', + width: 100, + active: true, + styles: styleProvider(darkTheme) +}; + +describe('coin balance component', () => { + it('renders correctly', () => { + const wrapper = shallow(); + expect(wrapper.debug()).toMatchSnapshot(); + }); +}); diff --git a/src/components/coin-balance-card/coin-balance-card.tsx b/src/components/coin-balance-card/coin-balance-card.tsx index 083c19b8a..0b91144c3 100644 --- a/src/components/coin-balance-card/coin-balance-card.tsx +++ b/src/components/coin-balance-card/coin-balance-card.tsx @@ -1,12 +1,12 @@ import React from 'react'; import { View } from 'react-native'; -import Convert from '../convert/convert'; +import { Convert } from '../convert/convert'; import { Text } from '../../library/text'; import stylesProvider from './styles'; import { withTheme } from '../../core/theme/with-theme'; -interface IProps { +export interface IProps { currency: string; balance: number; toCurrency: string; @@ -18,7 +18,7 @@ interface IProps { export const CoinBalanceCardComponent = (props: IProps) => ( - + {props.balance} {props.currency} @@ -28,10 +28,9 @@ export const CoinBalanceCardComponent = (props: IProps) => ( from={props.currency} to={props.toCurrency} style={!props.active && props.styles.darkerText} - > - {props.balance} - - {props.toCurrency} + amount={props.balance} + displayCurrency={true} + /> ); diff --git a/src/components/coin-dashboard/__tests__/__snapshots__/coin-dashboard.test.tsx.snap b/src/components/coin-dashboard/__tests__/__snapshots__/coin-dashboard.test.tsx.snap new file mode 100644 index 000000000..cb94cc386 --- /dev/null +++ b/src/components/coin-dashboard/__tests__/__snapshots__/coin-dashboard.test.tsx.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`coin dashboard component renders correctly 1`] = ` +" + + + + + + + + + + + +" +`; diff --git a/src/components/coin-dashboard/__tests__/coin-dashboard.test.tsx b/src/components/coin-dashboard/__tests__/coin-dashboard.test.tsx new file mode 100644 index 000000000..1011f565e --- /dev/null +++ b/src/components/coin-dashboard/__tests__/coin-dashboard.test.tsx @@ -0,0 +1,29 @@ +import 'react-native'; +import React from 'react'; +import { CoinDashboardComponent, IProps } from '../coin-dashboard'; +import { darkTheme } from '../../../styles/themes/dark-theme'; +import { Blockchain } from '../../../core/blockchain/types'; +import styleProvider from '../styles'; + +import { shallow } from 'enzyme'; + +const props: IProps = { + accounts: [ + { + index: 1, + blockchain: Blockchain.ZILLIQA, + address: 'zil1vs74hw5k21233h432kj321l3k21b', + publicKey: '1', + balance: 12332 + } + ], + blockchain: Blockchain.ZILLIQA, + styles: styleProvider(darkTheme) +}; + +describe('coin dashboard component', () => { + it('renders correctly', () => { + const wrapper = shallow(); + expect(wrapper.debug()).toMatchSnapshot(); + }); +}); diff --git a/src/components/coin-dashboard/coin-dashboard.tsx b/src/components/coin-dashboard/coin-dashboard.tsx index 37ad50b51..b1f7bfbc6 100644 --- a/src/components/coin-dashboard/coin-dashboard.tsx +++ b/src/components/coin-dashboard/coin-dashboard.tsx @@ -10,7 +10,7 @@ import { Icon } from '../icon'; import stylesProvider from './styles'; import { withTheme } from '../../core/theme/with-theme'; -interface IProps { +export interface IProps { blockchain: Blockchain; accounts?: IAccountState[]; styles: ReturnType; diff --git a/src/components/conversion-card/__tests__/__snapshots__/conversion-card.test.tsx.snap b/src/components/conversion-card/__tests__/__snapshots__/conversion-card.test.tsx.snap new file mode 100644 index 000000000..e1bddb9f8 --- /dev/null +++ b/src/components/conversion-card/__tests__/__snapshots__/conversion-card.test.tsx.snap @@ -0,0 +1,27 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`conversion card component renders correctly 1`] = ` +" + + ZIL + ETH + + + + 0.0216 + +" +`; + +exports[`conversion card component renders correctly negative values 1`] = ` +" + + ZIL + ETH + + + + -0.0216 + +" +`; diff --git a/src/components/conversion-card/__tests__/conversion-card.test.tsx b/src/components/conversion-card/__tests__/conversion-card.test.tsx new file mode 100644 index 000000000..6ebf13edb --- /dev/null +++ b/src/components/conversion-card/__tests__/conversion-card.test.tsx @@ -0,0 +1,39 @@ +import 'react-native'; +import React from 'react'; +import { ConversionCardComponent, IProps, IReduxProps } from '../conversion-card'; +import { darkTheme } from '../../../styles/themes/dark-theme'; +import styleProvider from '../styles'; + +import { shallow } from 'enzyme'; + +const props: IProps & IReduxProps = { + fromCurrency: 'ZIL', + toCurrency: 'ETH', + change: { + ZIL: { + ETH: 0.0216 + } + }, + styles: styleProvider(darkTheme) +}; + +describe('conversion card component', () => { + it('renders correctly', () => { + const wrapper = shallow(); + expect(wrapper.debug()).toMatchSnapshot(); + }); + + it('renders correctly negative values', () => { + const wrapper = shallow( + + ); + expect(wrapper.debug()).toMatchSnapshot(); + }); +}); diff --git a/src/components/conversion-card/conversion-card.tsx b/src/components/conversion-card/conversion-card.tsx index 96f180840..764f23554 100644 --- a/src/components/conversion-card/conversion-card.tsx +++ b/src/components/conversion-card/conversion-card.tsx @@ -2,15 +2,18 @@ import React from 'react'; import { View } from 'react-native'; import { TextSmall } from '../../library/text'; import { connect } from 'react-redux'; -import Convert from '../convert/convert'; +import { Convert } from '../convert/convert'; import stylesProvider from './styles'; import { withTheme } from '../../core/theme/with-theme'; -interface IProps { +export interface IReduxProps { + change: any; +} + +export interface IProps { fromCurrency: string; toCurrency: string; - change: any; styles: ReturnType; } @@ -18,8 +21,8 @@ const mapStateToProps = (state: any) => ({ change: state.market.change.daily }); -export const ConversionCardComponent = (props: IProps) => { - const change = props.change[props.fromCurrency][props.toCurrency] || 0; +export const ConversionCardComponent = (props: IProps & IReduxProps) => { + const change = props.change[props.fromCurrency][props.toCurrency]; return ( @@ -27,9 +30,7 @@ export const ConversionCardComponent = (props: IProps) => { {props.fromCurrency} {props.toCurrency} - - 1 - + = 0 ? props.styles.changeUp : props.styles.changeDown]}> {change} diff --git a/src/components/convert/__tests__/__snapshots__/convert.test.tsx.snap b/src/components/convert/__tests__/__snapshots__/convert.test.tsx.snap new file mode 100644 index 000000000..5a7bc9788 --- /dev/null +++ b/src/components/convert/__tests__/__snapshots__/convert.test.tsx.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`convert component doesnt throw when props missing 1`] = ` +" + 0 +" +`; + +exports[`convert component renders correctly 1`] = ` +" + 3130905.3427246176 +" +`; diff --git a/src/components/convert/__tests__/convert.test.tsx b/src/components/convert/__tests__/convert.test.tsx new file mode 100644 index 000000000..2fa3a4711 --- /dev/null +++ b/src/components/convert/__tests__/convert.test.tsx @@ -0,0 +1,29 @@ +import 'react-native'; +import React from 'react'; +import { ConvertComponent, IProps, IReduxProps } from '../convert'; + +import { shallow } from 'enzyme'; + +const props: IProps & IReduxProps = { + amount: 100, + from: 'ETH', + to: 'ZIL', + usdPrices: { + ETH: 182.25, + ZIL: 0.005821 + }, + displayCurrency: true +}; + +describe('convert component', () => { + it('renders correctly', () => { + const wrapper = shallow(); + expect(wrapper.debug()).toMatchSnapshot(); + }); + + it('doesnt throw when props missing', () => { + // @ts-ignore + const wrapper = shallow(); + expect(wrapper.debug()).toMatchSnapshot(); + }); +}); diff --git a/src/components/convert/convert.tsx b/src/components/convert/convert.tsx index cef68843c..2df184823 100644 --- a/src/components/convert/convert.tsx +++ b/src/components/convert/convert.tsx @@ -2,34 +2,42 @@ import React from 'react'; import { Text } from '../../library/text'; import { connect } from 'react-redux'; -// import styles from './styles'; - -interface IProps { +export interface IProps { + amount: number; from: string; to: string; - children: any; usdPrices: any; + displayCurrency?: boolean; decimals?: number; style?: any; } +export interface IReduxProps { + usdPrices: any; +} + const mapStateToProps = (state: any) => ({ usdPrices: state.market.usdPrices }); -// TODO: add currecy formatter -const roundToDecimals = (value: number, decimals: number) => - Math.round(value * Math.pow(10, decimals)) / Math.pow(10, decimals); - -const Convert = (props: IProps) => { +export const ConvertComponent = (props: IProps & IReduxProps) => { const conversion = props.usdPrices[props.from] / props.usdPrices[props.to] || 0; - const amount = (parseFloat(props.children) || 0) * conversion; - const decimals = props.decimals ? props.decimals : amount > 1 ? 2 : 6; + const amount = (props.amount || 0) * conversion; + + const formatOptions = props.displayCurrency + ? { + currency: props.to + } + : {}; - return {roundToDecimals(amount, decimals)}; + return ( + + {amount} + + ); }; -export default connect( +export const Convert = connect( mapStateToProps, null -)(Convert); +)(ConvertComponent); diff --git a/src/core/utils/__tests__/format-number.test.ts b/src/core/utils/__tests__/format-number.test.ts new file mode 100644 index 000000000..a1a97f57a --- /dev/null +++ b/src/core/utils/__tests__/format-number.test.ts @@ -0,0 +1,51 @@ +import { NativeModules } from 'react-native'; +import { formatNumber } from '../format-number'; + +const tests = [ + { + amount: 1000.3243132, + result: '1,000.32' + }, + { + amount: 0.16, + result: '$0.16', + options: { + currency: 'USD' + } + }, + { + amount: 0.00000016312332, + result: '0.00000016 ATOM', + options: { + currency: 'ATOM' + } + }, + { + amount: 0.1, + result: '0.100 ETH', + options: { + minimumFractionDigits: 3, + maximumFractionDigits: 3, + currency: 'ETH' + } + } +]; + +let settingsManager: any; + +describe('format number function', () => { + beforeAll(() => { + settingsManager = NativeModules.SettingsManager; + NativeModules.SettingsManager = { settings: { AppleLocale: 'en-US' } }; + }); + + afterAll(() => { + NativeModules.SettingsManager = settingsManager; + }); + + tests.forEach(t => { + it('should format number correctly', () => { + expect(formatNumber(t.amount, t.options)).toBe(t.result); + }); + }); +}); diff --git a/src/core/utils/format-number.ts b/src/core/utils/format-number.ts new file mode 100644 index 000000000..4152f6338 --- /dev/null +++ b/src/core/utils/format-number.ts @@ -0,0 +1,34 @@ +import { Platform, NativeModules } from 'react-native'; + +interface INumberFormatOptions { + locale?: string; + currency?: string; + minimumFractionDigits?: number; + maximumFractionDigits?: number; +} + +const significantDecimalsNumber = (amount: number) => (amount > 1 ? 2 : amount > 0.00001 ? 6 : 8); + +// list of currencies to be displayed using js formatter +const formattedCurrencies = ['USD', 'EUR']; + +export const formatNumber = (amount: number, options: INumberFormatOptions = {}) => { + const locale = + Platform.OS === 'ios' + ? NativeModules.SettingsManager.settings.AppleLocale + : NativeModules.I18nManager.localeIdentifier; + + const displayFormatCurrency = + options.currency && formattedCurrencies.indexOf(options.currency) !== -1; + + const formattedNumber = new Intl.NumberFormat(options.locale || locale || 'en-US', { + style: displayFormatCurrency ? 'currency' : 'decimal', + currency: displayFormatCurrency ? options.currency : undefined, + minimumFractionDigits: options.minimumFractionDigits || 0, + maximumFractionDigits: options.maximumFractionDigits || significantDecimalsNumber(amount) + }).format(amount); + + return options.currency && !displayFormatCurrency + ? formattedNumber + ' ' + options.currency + : formattedNumber; +}; diff --git a/src/library/__tests__/__snapshots__/text.test.tsx.snap b/src/library/__tests__/__snapshots__/text.test.tsx.snap new file mode 100644 index 000000000..2fb3aebf4 --- /dev/null +++ b/src/library/__tests__/__snapshots__/text.test.tsx.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`text library components renders correctly formated text 1`] = ` +" + 100.13 +" +`; + +exports[`text library components renders correctly regular text 1`] = ` +" + dummy +" +`; + +exports[`text library components renders correctly small text 1`] = ` +" + dummy +" +`; diff --git a/src/library/__tests__/text.test.tsx b/src/library/__tests__/text.test.tsx new file mode 100644 index 000000000..78e70bf3a --- /dev/null +++ b/src/library/__tests__/text.test.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { NativeModules } from 'react-native'; +import { TextComponent, TextSmall, defaultStyleProvider } from '../text'; +import { darkTheme } from '../../styles/themes/dark-theme'; + +const props = defaultStyleProvider(darkTheme); + +import { shallow } from 'enzyme'; + +describe('text library components', () => { + it('renders correctly regular text', () => { + const wrapper = shallow(dummy); + expect(wrapper.debug()).toMatchSnapshot(); + }); + it('renders correctly small text', () => { + const wrapper = shallow(dummy); + expect(wrapper.debug()).toMatchSnapshot(); + }); + it('renders correctly formated text', () => { + const settingsManager = NativeModules.SettingsManager; + NativeModules.SettingsManager = { settings: { AppleLocale: 'en-US' } }; + + const wrapper = shallow( + + 100.133333333 + + ); + expect(wrapper.debug()).toMatchSnapshot(); + + NativeModules.SettingsManager = settingsManager; + }); +}); diff --git a/src/library/text.tsx b/src/library/text.tsx index 61b0da7a4..cf35cdab4 100644 --- a/src/library/text.tsx +++ b/src/library/text.tsx @@ -3,10 +3,11 @@ import ReactNative from 'react-native'; import { withTheme } from '../core/theme/with-theme'; import { ITheme } from '../core/theme/itheme'; +import { formatNumber } from '../core/utils/format-number'; -// TODO refactor this +// TODO reconsider this -const defaultStyleProvider = (theme: ITheme) => ({ +export const defaultStyleProvider = (theme: ITheme) => ({ color: theme.colors.text, fontFamily: 'System', fontSize: theme.fontSize.regular @@ -24,20 +25,24 @@ const mergeStyle = (baseStyle: any, newStyle: any) => { return style; }; -const getProps = (props: any) => ({ - children: props.children -}); +export const TextComponent = (props: any) => { + let text = props.children; -const TextComponent = (props: any) => { - return ; + if (props.format) { + const amount = parseFloat(text); + text = formatNumber(amount, props.format); + } + return ( + {text} + ); }; -const TextSmall = (props: any) => { +export const TextSmall = (props: any) => { delete props.style; return ( { ); }; -const Text = withTheme(TextComponent, defaultStyleProvider); - -export { Text, TextSmall }; +export const Text = withTheme(TextComponent, defaultStyleProvider); diff --git a/src/screens/dashboard/__tests__/__snapshots__/dashboard.test.tsx.snap b/src/screens/dashboard/__tests__/__snapshots__/dashboard.test.tsx.snap new file mode 100644 index 000000000..ba0640007 --- /dev/null +++ b/src/screens/dashboard/__tests__/__snapshots__/dashboard.test.tsx.snap @@ -0,0 +1,136 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`dashboard screen component handles scroll correctly 1`] = ` +" + + + some header + + + + + + + + + + + + + + + + + + + ZIL + + + + + ETH + + + + + ATOM + + + + + XLM + + + +" +`; + +exports[`dashboard screen component renders correctly 1`] = ` +" + + + some header + + + + + + + + + + + + + + + + + + + ZIL + + + + + ETH + + + + + ATOM + + + + + XLM + + + +" +`; + +exports[`dashboard screen component swtiches to correct coin on press 1`] = ` +" + + + some header + + + + + + + + + + + + + + + + + + + ZIL + + + + + ETH + + + + + ATOM + + + + + XLM + + + +" +`; diff --git a/src/screens/dashboard/__tests__/dashboard.test.tsx b/src/screens/dashboard/__tests__/dashboard.test.tsx new file mode 100644 index 000000000..858b21658 --- /dev/null +++ b/src/screens/dashboard/__tests__/dashboard.test.tsx @@ -0,0 +1,117 @@ +import React from 'react'; +import { Dimensions } from 'react-native'; +import { DashboardScreenComponent, IProps, IReduxProps } from '../dashboard'; +import { WalletType } from '../../../core/wallet/types'; +import { Blockchain } from '../../../core/blockchain/types'; +import { darkTheme } from '../../../styles/themes/dark-theme'; +import styleProvider from '../styles'; + +import { shallow } from 'enzyme'; + +const props: IProps & IReduxProps = { + // @ts-ignore + navigation: { + navigate: jest.fn() + }, + wallet: { + id: 'walletId', + type: WalletType.HD, + accounts: [ + { + index: 1, + blockchain: Blockchain.ZILLIQA, + address: 'zil1vs74hw5k21233h432kj321l3k21b', + publicKey: '1', + balance: 12332 + }, + { + index: 2, + blockchain: Blockchain.ZILLIQA, + address: 'zil1vs74hw5k21233h432kj321l3k21b', + publicKey: '1', + balance: 3432 + }, + { + index: 8, + blockchain: Blockchain.ETHEREUM, + address: '0xeAE3Dcc2E37AD412312ASds2d4a6065A831eF89E', + publicKey: '1', + balance: 3.5 + }, + { + index: 9, + blockchain: Blockchain.COSMOS, + address: 'cosmos123ksdadasda', + publicKey: '1', + balance: 220 + }, + { + index: 8, + blockchain: Blockchain.STELLAR, + address: 'STLsadlij23lj313', + publicKey: '1', + balance: 1234 + } + ] + }, + styles: styleProvider(darkTheme) +}; + +jest.useFakeTimers(); + +describe('dashboard screen component', () => { + beforeAll(() => { + jest.mock('react-native/Libraries/Animated/src/Animated.js', () => { + const ActualAnimated = jest.requireActual( + 'react-native/Libraries/Animated/src/Animated.js' + ); + return { + ...ActualAnimated, + timing: (value: any, config: any) => { + return { + start: (callback: any) => { + value.setValue(config.toValue); + callback && callback(); + } + }; + } + }; + }); + }); + + it('renders correctly', () => { + const wrapper = shallow(); + expect(wrapper.debug()).toMatchSnapshot(); + }); + + it('handles scroll correctly', () => { + const screenWidth = Dimensions.get('window').width; + const spySetState = jest.spyOn(DashboardScreenComponent.prototype, 'setState'); + + const wrapper = shallow(); + const instance = wrapper.instance(); + // @ts-ignore + instance.handleScrollEnd({ nativeEvent: { contentOffset: { x: 0 } } }); + + // dont call set state when its the same coin index + expect(spySetState).not.toHaveBeenCalled(); + + // @ts-ignore + instance.handleScrollEnd({ + nativeEvent: { contentOffset: { x: Math.round(screenWidth * 0.5) } } + }); + expect(spySetState).toHaveBeenCalledWith({ + coinIndex: 1 + }); + expect(wrapper.debug()).toMatchSnapshot(); + }); + + it('swtiches to correct coin on press', () => { + const wrapper = shallow(); + const selectionButton = wrapper.find('[testID="blockchainSelector"]').childAt(2); + selectionButton.simulate('press'); + // @ts-ignore + expect(wrapper.state().coinIndex).toBe(2); + expect(wrapper.debug()).toMatchSnapshot(); + }); +}); diff --git a/src/screens/dashboard/dashboard.tsx b/src/screens/dashboard/dashboard.tsx index d1d10891e..03be865a7 100644 --- a/src/screens/dashboard/dashboard.tsx +++ b/src/screens/dashboard/dashboard.tsx @@ -13,14 +13,12 @@ import { BLOCKCHAIN_INFO } from '../../core/constants/blockchain'; import stylesProvider from './styles'; import { withTheme } from '../../core/theme/with-theme'; -interface IProps { +export interface IProps { navigation: NavigationScreenProp; - money: number; - ethusd: number; styles: ReturnType; } -interface IReduxProps { +export interface IReduxProps { wallet: IWalletState; } @@ -165,7 +163,7 @@ export class DashboardScreenComponent extends React.Component - + {this.state.coins.map((coin, i) => ( + + Settings Screen + +" +`; diff --git a/src/screens/settings/__tests__/settings.test.tsx b/src/screens/settings/__tests__/settings.test.tsx new file mode 100644 index 000000000..a408f7841 --- /dev/null +++ b/src/screens/settings/__tests__/settings.test.tsx @@ -0,0 +1,12 @@ +import 'react-native'; +import React from 'react'; +import { SettingsScreen } from '../settings'; + +import { shallow } from 'enzyme'; + +describe('settings screen component', () => { + it('renders correctly', () => { + const wrapper = shallow(); + expect(wrapper.debug()).toMatchSnapshot(); + }); +}); diff --git a/tslint.json b/tslint.json index b899202c7..57fba8b68 100644 --- a/tslint.json +++ b/tslint.json @@ -4,7 +4,8 @@ "ordered-imports": false, "object-literal-sort-keys": false, "no-submodule-imports": false, - "react-no-dangerous-html": [true] + "react-no-dangerous-html": [true], + "no-unused-expression": false }, "rulesDirectory": ["node_modules/tslint-microsoft-contrib"] } From 1db6a9d52c3f0fe745f1c60d5fc6c16f71018963 Mon Sep 17 00:00:00 2001 From: Lucian Marica Date: Sun, 20 Oct 2019 01:14:52 +0300 Subject: [PATCH 3/4] Temporary fix for android scrollview background bleeding --- src/components/account-card/styles.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/account-card/styles.ts b/src/components/account-card/styles.ts index 538b6826e..ad6005e5a 100644 --- a/src/components/account-card/styles.ts +++ b/src/components/account-card/styles.ts @@ -1,11 +1,11 @@ -import { StyleSheet } from 'react-native'; +import { StyleSheet, Platform } from 'react-native'; import { ITheme } from '../../core/theme/itheme'; export default (theme: ITheme) => StyleSheet.create({ container: { backgroundColor: theme.colors.cardBackground, - borderRadius: 6, + borderRadius: Platform.OS === 'ios' ? 6 : 0, height: 80, display: 'flex', justifyContent: 'space-between', From 63d3fcbdd16540b809deb685d9058ec7c5ec19c0 Mon Sep 17 00:00:00 2001 From: Lucian Marica Date: Sun, 20 Oct 2019 01:36:15 +0300 Subject: [PATCH 4/4] Update tslint config --- tslint.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tslint.json b/tslint.json index 57fba8b68..0c892e898 100644 --- a/tslint.json +++ b/tslint.json @@ -5,7 +5,14 @@ "object-literal-sort-keys": false, "no-submodule-imports": false, "react-no-dangerous-html": [true], - "no-unused-expression": false + "no-unused-expression": [ + "error", + { + "allowShortCircuit": true, + "allowTernary": true, + "allowTaggedTemplates": true + } + ] }, "rulesDirectory": ["node_modules/tslint-microsoft-contrib"] }