summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2018-04-11 17:30:58 +0200
committerAndrej Mihajlov <and@mullvad.net>2018-04-11 17:30:58 +0200
commit0c6c767e265e186495dbaabc859c5844aeb15b41 (patch)
tree93882e8ff46ba96c89a00441a8846ecb49b08dc6
parentc1cb78f55c8e346c1d2c097df4bf368c56e91abf (diff)
parentf146d3e0eb3c74107c8bda124ccfae4048000d33 (diff)
downloadmullvadvpn-0c6c767e265e186495dbaabc859c5844aeb15b41.tar.xz
mullvadvpn-0c6c767e265e186495dbaabc859c5844aeb15b41.zip
Merge branch 'xp-connect-select'
-rw-r--r--app/assets/css/buttons.css131
-rw-r--r--app/assets/css/style.css3
-rw-r--r--app/components/Accordion.js4
-rw-r--r--app/components/Connect.css190
-rw-r--r--app/components/Connect.js191
-rw-r--r--app/components/ConnectStyles.js146
-rw-r--r--app/components/SelectLocation.css146
-rw-r--r--app/components/SelectLocation.js140
-rw-r--r--app/components/SelectLocationStyles.js119
-rw-r--r--app/components/styled/AppButton.js77
-rw-r--r--app/components/styled/AppButtonStyles.js68
-rw-r--r--app/components/styled/BlurAppButtonStyles.android.js20
-rw-r--r--app/components/styled/BlurAppButtonStyles.js24
-rw-r--r--app/components/styled/CellButton.js6
-rw-r--r--app/components/styled/index.js3
-rw-r--r--test/components/Connect.spec.js65
-rw-r--r--test/components/SelectLocation.spec.js27
17 files changed, 589 insertions, 771 deletions
diff --git a/app/assets/css/buttons.css b/app/assets/css/buttons.css
deleted file mode 100644
index 805d9e9a74..0000000000
--- a/app/assets/css/buttons.css
+++ /dev/null
@@ -1,131 +0,0 @@
-.button {
- display: flex;
- width: 100%;
- padding: 7px 12px 9px;
- border-radius: 4px;
- border: 0;
- font-family: DINPro;
- font-size: 20px;
- font-weight: 900;
- line-height: 26px;
- justify-content: center;
- align-items: center;
- transition: 0.25s opacity;
-}
-
-.button:disabled {
- opacity: 0.5;
-}
-
-.button-label {
- margin: 0 auto;
-}
-
-/* make negative margin to center label within button */
-.button-label + .button-icon--16 {
- margin-left: -16px;
-}
-
-.button--blur {
- backdrop-filter: blur(4px);
-}
-
-.button--primary {
- background-color: rgba(41,71,115,1);
- color: rgba(255,255,255,0.8);
-}
-
-.button--primary .button-icon path {
- fill: rgba(255,255,255,0.8);
-}
-
-.button--primary:not(:disabled):hover {
- background-color: rgba(41,71,115,0.9);
- color: rgba(255,255,255,1);
-}
-
-.button--primary:not(:disabled):hover .button-icon path {
- fill: rgba(255,255,255,1);
-}
-
-.button--primary:active {
- background-color: rgba(41,71,115,1);
-}
-
-.button--secondary {
- background-color: rgba(41,71,115,0.4);
- color: rgba(255,255,255,0.6);
-}
-
-.button--secondary:not(:disabled):hover {
- background-color: rgba(41,71,115,0.5);
- color: rgba(255,255,255,0.8);
-}
-
-.button--secondary:active {
- background-color: rgba(41,71,115,0.4);
-}
-
-.button--negative {
- background-color: rgba(208,2,27,1);
- color: rgba(255,255,255,0.8);
-}
-
-.button--negative:not(:disabled):hover {
- background-color: rgba(208,2,27,0.95);
- color: rgba(255,255,255,1);
-}
-
-.button--negative:active {
- background-color: rgba(208,2,27,1);
-}
-
-.button--negative-light {
- background-color: rgba(208,2,27,0.4);
- color: rgba(255,255,255,0.6);
-}
-
-.button--negative-light:not(:disabled):hover {
- background-color: rgba(208,2,27,0.45);
- color: rgba(255,255,255,0.8);
-}
-
-.button--negative-light:active {
- background-color: rgba(208,2,27,0.4);
-}
-
-.button--positive {
- background-color: rgba(63,173,77,1);
- color: rgba(255,255,255,0.8);
-}
-
-.button--positive .button-icon path {
- fill: rgba(255,255,255,0.8);
-}
-
-.button--positive:not(:disabled):hover {
- background-color: rgba(63,173,77,0.9);
- color: rgba(255,255,255,1);
-}
-
-.button--positive:not(:disabled):hover .button-icon path {
- fill: rgba(255,255,255,1);
-}
-
-.button--positive:active {
- background-color: rgba(63,173,77,1);
-}
-
-.button--neutral {
- background-color: rgba(255,255,255,0.2);
- color: rgba(255,255,255,0.8);
-}
-
-.button--neutral:not(:disabled):hover {
- background-color: rgba(255,255,255,0.25);
- color: rgba(255,255,255,1);
-}
-
-.button--neutral:active {
- background-color: rgba(255,255,255,0.2);
-}
diff --git a/app/assets/css/style.css b/app/assets/css/style.css
index 27403aaedf..619e40daf3 100644
--- a/app/assets/css/style.css
+++ b/app/assets/css/style.css
@@ -2,12 +2,9 @@
@import 'reset.css';
@import 'fonts.css';
@import 'global.css';
-@import 'buttons.css';
/* app */
@import '../../components/PlatformWindow.css';
@import '../../components/CustomScrollbars.css';
-@import '../../components/Connect.css';
-@import '../../components/SelectLocation.css';
@import '../../components/Layout.css';
@import '../../components/Switch.css';
diff --git a/app/components/Accordion.js b/app/components/Accordion.js
index 0b2f2929d3..116d257a40 100644
--- a/app/components/Accordion.js
+++ b/app/components/Accordion.js
@@ -59,8 +59,8 @@ export default class Accordion extends Component<AccordionProps, AccordionState>
}
render() {
- const { height: _height, children, animationDuration: _animationDuration, ...otherProps } = this.props;
- const containerStyles = [];
+ const { style: style, height: _height, children, animationDuration: _animationDuration, ...otherProps } = this.props;
+ const containerStyles = [style];
if(this.state.animatedValue !== null) {
const animatedStyle = Styles.createAnimatedViewStyle({
diff --git a/app/components/Connect.css b/app/components/Connect.css
deleted file mode 100644
index 592e824ea2..0000000000
--- a/app/components/Connect.css
+++ /dev/null
@@ -1,190 +0,0 @@
-.connect {
- height: 100%;
- position: relative;
-}
-
-.connect__map {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- z-index: 0;
-}
-
-.connect__container {
- display: flex;
- flex-direction: column;
- height: 100%;
- position: relative; /* need this for z-index to work to cover map */
- z-index: 1;
-}
-
-.connect__footer {
- display: flex;
- flex-direction: column;
- padding: 42px 24px 24px;
-}
-
-.connect__row + .connect__row{
- margin-top: 16px;
-}
-
-.connect__blocking-container {
- color: rgba(255,255,255,0.8);
- max-height: 0px;
- height: 100%;
- width: 100%;
- overflow: hidden;
-
- text-transform: uppercase;
-
- position: absolute;
- border-bottom: 1px solid rgba(255,255,255,0.8);
- transition: max-height 0.5s;
-}
-.connect__blocking-container.show {
- max-height: 36px;
-}
-.connect__blocking-container.hide {
- max-height: 0px;
- border-bottom: 0px solid rgb(208, 2, 27);
- transition: max-height 0.5s, border-bottom 0.1s 0.4s;
-}
-
-.connect__blocking-message {
- display: flex;
- flex-direction: row;
- margin: 8px 20px;
- font-family: "Open Sans";
- font-size: 12px;
- font-weight: 800;
- line-height: 17px;
-}
-
-.connect__blocking-icon {
- width: 10px;
- height: 10px;
- border-radius: 5px;
- margin-top: 4px;
- margin-right: 8px;
-
- background-color: rgb(208, 2, 27);
-}
-
-.connect__server {
- padding: 7px 12px 9px;
- background-color: rgba(255,255,255,0.2);
- border-radius: 4px;
- display: flex;
- flex-direction: row;
- align-items: center;
- backdrop-filter: blur(4px);
-}
-
-.connect__server-label {
- flex: 1 1 auto;
- text-align: center;
-}
-
-.connect__server-chevron {
- flex: 0 0 auto;
- width: 7px;
- margin-left: -7px; /* let .connect__server-label extend to occupy the entire space */
-}
-
-.connect__footer-button {
- display: block;
- width:100%;
- border: 0;
- padding: 7px 12px 9px;
- border-radius: 4px;
- font-family: DINPro;
- font-size: 20px;
- font-weight: 900;
- text-align: center;
- line-height: 26px;
- color: #FFFFFF;
-}
-
-.connect__status {
- padding: 0 24px;
- margin-top: 94px;
- margin-bottom: auto;
-}
-
-.connect__error-title {
- font-family: DINPro;
- font-size: 32px;
- font-weight: 900;
- line-height: 1.25;
- color: #fff;
- margin-bottom: 8px;
-}
-
-.connect__error-message {
- font-family: "Open Sans";
- font-size: 13px;
- font-weight: 600;
- line-height: normal;
- color: #fff;
- margin-bottom: 24px;
-}
-
-.connect__status-security {
- font-family: "Open Sans";
- font-size: 16px;
- font-weight: 800;
- line-height: 22px;
- margin-bottom: 4px;
- color: #FFFFFF;
- text-transform: uppercase;
-}
-
-.connect__status-security--unsecured {
- color: #D0021B;
-}
-
-.connect__status-security--secure {
- color: #44AD4D;
-}
-
-.connect__status-location {
- font-family: DINPro;
- font-size: 38px;
- font-weight: 900;
- line-height: 1.16em;
- max-height: calc(1.16em * 2);
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- letter-spacing: -0.9px;
- color: #FFFFFF;
- margin-bottom: 4px;
-}
-
-.connect__status-location-icon {
- display: inline-block;
- margin-right: 8px;
-}
-
-.connect__status-ipaddress {
- font-family: "Open Sans";
- font-size: 16px;
- font-weight: 800;
- line-height: normal;
- color: #FFFFFF;
-}
-
-.connect__status-ipaddress--invisible {
- visibility: hidden;
-}
-
-.connect__status-icon {
- text-align: center;
- margin-bottom: 32px;
-}
-
-.connect__status-icon--hidden {
- visibility: hidden;
-}
diff --git a/app/components/Connect.js b/app/components/Connect.js
index 3408e40b8d..9f8b2b993f 100644
--- a/app/components/Connect.js
+++ b/app/components/Connect.js
@@ -3,12 +3,15 @@
import moment from 'moment';
import * as React from 'react';
import { Layout, Container, Header } from './Layout';
+import { Component, Text, View, Types } from 'reactxp';
+import Img from './Img';
+import { TransparentButton, RedTransparentButton, GreenButton, Label } from './styled';
+import Accordion from './Accordion';
+import styles from './ConnectStyles';
+
import { BackendError } from '../lib/backend';
import Map from './Map';
-import ExternalLinkSVG from '../assets/images/icon-extLink.svg';
-import ChevronRightSVG from '../assets/images/icon-chevron.svg';
-
import type { HeaderBarStyle } from './HeaderBar';
import type { ConnectionReduxState } from '../redux/connection/reducers';
@@ -29,7 +32,7 @@ type ConnectState = {
mapOffset: [number, number],
};
-export default class Connect extends React.Component<ConnectProps, ConnectState> {
+export default class Connect extends Component<ConnectProps, ConnectState> {
state = {
showCopyIPMessage: false,
mapOffset: [0, 0],
@@ -66,7 +69,7 @@ export default class Connect extends React.Component<ConnectProps, ConnectState>
return (
<Layout>
- <Header style={ this.headerStyle() } showSettings={ true } onSettings={ this.props.onSettings } />
+ <Header style={ this.headerStyle() } showSettings={ true } onSettings={ this.props.onSettings } testName='header'/>
<Container>
{ child }
</Container>
@@ -76,28 +79,28 @@ export default class Connect extends React.Component<ConnectProps, ConnectState>
renderError(error: BackendError) {
return (
- <div className="connect">
- <div className="connect__status">
- <div className="connect__status-icon">
- <img src="./assets/images/icon-fail.svg" alt="" />
- </div>
- <div className="connect__error-title">
+ <View style={styles.connect}>
+ <View style={styles.status}>
+ <View style={styles.status_icon}>
+ <Img source="icon-fail" height="60" width="60" alt="" />
+ </View>
+ <View style={styles.error_title}>
{ error.title }
- </div>
- <div className="connect__error-message">
+ </View>
+ <View style={styles.error_message}>
{ error.message }
- </div>
+ </View>
{ error.type === 'NO_CREDIT' ?
- <div>
- <button className="button button--positive" onClick={ this.onExternalLink.bind(this, 'purchase') }>
- <span className="button-label">Buy more time</span>
- <ExternalLinkSVG className="button-icon button-icon--16" />
- </button>
- </div>
+ <View>
+ <GreenButton onPress={ this.onExternalLink.bind(this, 'purchase') }>
+ <Label>Buy more time</Label>
+ <Img source='icon-extLink' height='16' width='16' />
+ </GreenButton>
+ </View>
: null
}
- </div>
- </div>
+ </View>
+ </View>
);
}
@@ -150,20 +153,25 @@ export default class Connect extends React.Component<ConnectProps, ConnectState>
}
return (
- <div className="connect">
- <div className="connect__map">
+ <View style={styles.connect}>
+ <View style={styles.map}>
<Map style={{ width: '100%', height: '100%' }} { ...this._getMapProps() } />
- </div>
- <div className="connect__container">
+ </View>
+ <View style={styles.container}>
{ this._renderIsBlockingInternetMessage() }
- <div className="connect__status">
- { /* show spinner when connecting */ }
- <div className={ this.spinnerClass() }>
- <img src="./assets/images/icon-spinner.svg" alt="" ref={ this._updateMapOffset } />
- </div>
- <div className={ this.networkSecurityClass() }>{ this.networkSecurityMessage() }</div>
+ { /* show spinner when connecting */ }
+ { isConnecting ?
+ <View style={ styles.status_icon }>
+ <Img source="icon-spinner" height="60" width="60" alt="" ref={ this._updateMapOffset } />
+ </View>
+ : null
+ }
+
+ <View style={styles.status}>
+
+ <View style={ this.networkSecurityStyle() } testName='networkSecurityMessage'>{ this.networkSecurityMessage() }</View>
{ /*
**********************************
@@ -173,19 +181,19 @@ export default class Connect extends React.Component<ConnectProps, ConnectState>
{ /* location when connecting or disconnected */ }
{ isConnecting || isDisconnected ?
- <div className="connect__status-location">
- <span>{ this.props.connection.country }</span>
- </div>
+ <Text style={styles.status_location} testName='location'>
+ { this.props.connection.country }
+ </Text>
: null
}
{ /* location when connected */ }
{ isConnected ?
- <div className="connect__status-location">
+ <Text style={styles.status_location} testName='location'>
{ this.props.connection.city }
{ this.props.connection.city && <br/> }
{ this.props.connection.country }
- </div>
+ </Text>
:null
}
@@ -195,15 +203,15 @@ export default class Connect extends React.Component<ConnectProps, ConnectState>
**********************************
*/ }
- <div className={ this.ipAddressClass() } onClick={ this.onIPAddressClick.bind(this) }>
+ <Text style={ this.ipAddressStyle() } onPress={ this.onIPAddressClick.bind(this) }>
{ (isConnected || isDisconnected) ? (
- <span>{
+ <Text testName='ipAddress'>{
this.state.showCopyIPMessage ?
'IP copied to clipboard!' :
this.props.connection.ip
- }</span>) : null }
- </div>
- </div>
+ }</Text>) : null }
+ </Text>
+ </View>
{ /*
@@ -214,46 +222,31 @@ export default class Connect extends React.Component<ConnectProps, ConnectState>
{ /* footer when disconnected */ }
{ isDisconnected ?
- <div className="connect__footer">
- <div className="connect__row">
- <button className="connect__server button button--neutral button--blur" onClick={ this.props.onSelectLocation }>
- <div className="connect__server-label">{ this.props.selectedRelayName }</div>
- <div className="connect__server-chevron"><ChevronRightSVG /></div>
- </button>
- </div>
-
- <div className="connect__row">
- <button className="button button--positive" onClick={ this.props.onConnect }>Secure my connection</button>
- </div>
- </div>
+ <View style={styles.footer}>
+ <TransparentButton onPress={ this.props.onSelectLocation }>
+ <Label>{ this.props.selectedRelayName }</Label>
+ <Img height='12' width='7' source='icon-chevron' />
+ </TransparentButton>
+ <GreenButton onPress={ this.props.onConnect } testName='secureConnection'>Secure my connection</GreenButton>
+ </View>
: null
}
{ /* footer when connecting */ }
{ isConnecting ?
- <div className="connect__footer">
- <div className="connect__row">
- <button className="button button--neutral button--blur" onClick={ this.props.onSelectLocation }>Switch location</button>
- </div>
-
- <div className="connect__row">
- <button className="button button--negative-light button--blur" onClick={ this.props.onDisconnect }>Cancel</button>
- </div>
- </div>
+ <View style={styles.footer}>
+ <TransparentButton onPress={ this.props.onSelectLocation }>Switch location</TransparentButton>
+ <RedTransparentButton onPress={ this.props.onDisconnect }>Cancel</RedTransparentButton>
+ </View>
: null
}
{ /* footer when connected */ }
{ isConnected ?
- <div className="connect__footer">
- <div className="connect__row">
- <button className="button button--neutral button--blur" onClick={ this.props.onSelectLocation }>Switch location</button>
- </div>
-
- <div className="connect__row">
- <button className="button button--negative-light button--blur" onClick={ this.props.onDisconnect }>Disconnect</button>
- </div>
- </div>
+ <View style={styles.footer}>
+ <TransparentButton onPress={ this.props.onSelectLocation }>Switch location</TransparentButton>
+ <RedTransparentButton onPress={ this.props.onDisconnect } testName='disconnect'>Disconnect</RedTransparentButton>
+ </View>
: null
}
@@ -263,23 +256,18 @@ export default class Connect extends React.Component<ConnectProps, ConnectState>
**********************************
*/ }
- </div>
- </div>
+ </View>
+ </View>
);
}
_renderIsBlockingInternetMessage() {
- let animationClass = 'hide';
- if (this.props.connection.status === 'connecting') {
- animationClass = 'show';
- }
-
- return <div className={`connect__blocking-container ${animationClass}`}>
- <div className="connect__blocking-message">
- <div className="connect__blocking-icon">&nbsp;</div>
- blocking internet
- </div>
- </div>;
+ return <Accordion style={styles.blocking_container} height={ (this.props.connection.status === 'connecting') ? 'auto' : 0 }>
+ <Text style={styles.blocking_message}>
+ <Text style={styles.blocking_icon}>&nbsp;</Text>
+ <Text>BLOCKING INTERNET</Text>
+ </Text>
+ </Accordion>;
}
// Handlers
@@ -308,39 +296,30 @@ export default class Connect extends React.Component<ConnectProps, ConnectState>
throw new Error('Invalid ConnectionState');
}
- networkSecurityClass(): string {
- let classes = ['connect__status-security'];
+ networkSecurityStyle(): Types.Style {
+ let classes = [styles.status_security];
if(this.props.connection.status === 'connected') {
- classes.push('connect__status-security--secure');
+ classes.push(styles.status_security__secure);
} else if(this.props.connection.status === 'disconnected') {
- classes.push('connect__status-security--unsecured');
+ classes.push(styles.status_security__unsecured);
}
-
- return classes.join(' ');
+ return classes;
}
networkSecurityMessage(): string {
switch(this.props.connection.status) {
- case 'connected': return 'Secure connection';
- case 'connecting': return 'Creating secure connection';
- default: return 'Unsecured connection';
- }
- }
-
- spinnerClass(): string {
- var classes = ['connect__status-icon'];
- if(this.props.connection.status !== 'connecting') {
- classes.push('connect__status-icon--hidden');
+ case 'connected': return 'SECURE CONNECTION';
+ case 'connecting': return 'CREATING SECURE CONNECTION';
+ default: return 'UNSECURED CONNECTION';
}
- return classes.join(' ');
}
- ipAddressClass(): string {
- var classes = ['connect__status-ipaddress'];
+ ipAddressStyle(): Types.Style {
+ var classes = [styles.status_ipaddress];
if(this.props.connection.status === 'connecting') {
- classes.push('connect__status-ipaddress--invisible');
+ classes.push(styles.status_ipaddress__invisible);
}
- return classes.join(' ');
+ return classes;
}
displayError(): ?BackendError {
diff --git a/app/components/ConnectStyles.js b/app/components/ConnectStyles.js
new file mode 100644
index 0000000000..413d2f0c1d
--- /dev/null
+++ b/app/components/ConnectStyles.js
@@ -0,0 +1,146 @@
+// @flow
+import { createViewStyles, createTextStyles } from '../lib/styles';
+import { colors } from '../config';
+
+export default {
+ ...createViewStyles({
+ connect: {
+ height: '100%',
+ flex: 1,
+ },
+ map: {
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ zIndex: 0,
+ height: '100%',
+ width: '100%',
+ },
+ container: {
+ flexDirection: 'column',
+ flex: 1,
+ position: 'relative', /* need this for z-index to work to cover map */
+ zIndex: 1,
+
+ },
+ footer:{
+ flex: 0,
+ marginBottom: 16,
+ },
+ blocking_container: {
+ width: '100%',
+ position: 'absolute',
+ },
+ blocking_icon: {
+ width: 10,
+ height: 10,
+ flex: 0,
+ display: 'flex',
+ borderRadius: 5,
+ marginTop: 4,
+ marginRight: 8,
+ backgroundColor: colors.red,
+ },
+ server: {
+ paddingTop: 7,
+ paddingLeft: 12,
+ paddingRight: 12,
+ paddingBottom: 9,
+ backgroundColor: colors.white20,
+ borderRadius: 4,
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ status: {
+ paddingTop: 0,
+ paddingLeft: 24,
+ paddingRight: 24,
+ paddingBottom: 0,
+ marginTop: 186,
+ flex: 1,
+ },
+ status_icon: {
+ position:'absolute',
+ alignSelf: 'center',
+ width: 60,
+ height: 60,
+ marginTop: 94,
+ },
+ }),
+ ...createTextStyles({
+ blocking_message: {
+ display: 'flex',
+ flexDirection: 'row',
+ fontFamily: 'Open Sans',
+ fontSize: 12,
+ fontWeight: '800',
+ lineHeight: 17,
+ paddingTop: 8,
+ paddingLeft: 20,
+ paddingRight: 20,
+ paddingBottom: 8,
+ color: colors.white60,
+ backgroundColor: colors.blue,
+ },
+ server_label: {
+ fontFamily: 'DINPro',
+ fontSize: 32,
+ fontWeight: '900',
+ lineHeight: 44,
+ letterSpacing: -0.7,
+ color: colors.white,
+ marginBottom: 7,
+ flex:0,
+ },
+ error_title: {
+ fontFamily: 'DINPro',
+ fontSize: 32,
+ fontWeight: '900',
+ lineHeight: 40,
+ color: colors.white,
+ marginBottom: 8,
+ },
+ error_message: {
+ fontFamily: 'Open Sans',
+ fontSize: 13,
+ fontWeight: '600',
+ color: colors.white,
+ marginBottom: 24,
+ },
+ status_security: {
+ fontFamily: 'Open Sans',
+ fontSize: 16,
+ fontWeight: '800',
+ lineHeight: 22,
+ marginBottom: 4,
+ color: colors.white,
+ },
+ status_security__secure: {
+ color: colors.green,
+ },
+ status_security__unsecured: {
+ color: colors.red,
+ },
+ status_ipaddress: {
+ fontFamily: 'Open Sans',
+ fontSize: 16,
+ fontWeight: '800',
+ color: colors.white,
+ },
+ status_ipaddress__invisible: {
+ opacity: 0,
+ },
+ status_location: {
+ fontFamily: 'DINPro',
+ fontSize: 38,
+ fontWeight: '900',
+ lineHeight: 40,
+ overflow: 'hidden',
+ letterSpacing: -0.9,
+ color: colors.white,
+ marginBottom: 4,
+ },
+ }),
+}; \ No newline at end of file
diff --git a/app/components/SelectLocation.css b/app/components/SelectLocation.css
deleted file mode 100644
index 7e107f237f..0000000000
--- a/app/components/SelectLocation.css
+++ /dev/null
@@ -1,146 +0,0 @@
-.select-location {
- background: #192E45;
- height: 100%;
-}
-
-.select-location__container {
- display: flex;
- flex-direction: column;
- height: 100%;
-}
-
-.select-location__header {
- flex: 0 0 auto;
- padding: 40px 24px 16px;
- position: relative; /* anchor for close button */
-}
-
-.select-location__close {
- position: absolute;
- display: block;
- border: 0;
- padding: 0;
- margin: 0;
- width: 24px;
- height: 24px;
- top: 24px;
- left: 12px;
- background-color: transparent;
- background-image: url(../assets/images/icon-close.svg);
- opacity: 0.6;
- z-index: 1; /* part of .select-location__container covers the button */
-}
-
-.select-location__title {
- font-family: DINPro;
- font-size: 32px;
- font-weight: 900;
- line-height: 1.25em;
- color: #FFFFFF;
-}
-
-.select-location__subtitle {
- font-family: "Open Sans";
- font-size: 13px;
- font-weight: 600;
- line-height: normal;
- color: rgba(255,255,255,0.8);
- padding: 0 24px 24px;
-}
-
-.select-location__cell {
- background-color: rgba(41,71,115,1);
- display: flex;
- flex-direction: row;
- align-items: stretch;
-}
-
-.select-location__cell-content {
- padding: 15px 24px;
- display: flex;
- flex: 1 1 auto;
- flex-direction: row;
- align-items: center;
-}
-
-.select-location__cell--selectable:hover {
- background-color:rgba(41,71,115,0.9);
-}
-
-.select-location__cell--selected,
-.select-location__cell--selected:hover {
- background-color: #44AD4D;
-}
-
-.select-location__country + .select-location__country {
- margin-top: 1px;
-}
-
-.select-location__cell-label {
- font-family: DINPro;
- font-size: 20px;
- font-weight: 900;
- line-height: 26px;
- color: #FFFFFF;
-}
-
-.select-location__cell-label--inactive {
- color: rgba(255, 255, 255, 0.2);
-}
-
-.select-location__cell-icon {
- width: 24px;
- height: 24px;
- flex: 0 0 auto;
- margin-right: 8px;
- align-items: center;
- justify-content: center;
- display: flex;
- color: #fff;
-}
-
-.select-location__collapse-button {
- border: 0;
- background: transparent;
- padding: 0;
- margin: 0 0 0 auto;
- display: flex;
- align-items: stretch;
- padding: 12px;
-}
-
-.select-location__collapse-icon {
- color: #fff;
-}
-
-.select-location__sub-cell {
- background-color: rgb(36, 57, 84);
- padding: 15px 24px 15px 40px;
- display: flex;
- flex-direction: row;
- align-items: center;
- margin-top: 1px;
-}
-
-.select-location__sub-cell--selectable:hover {
- background-color: rgba(41,71,115,0.9);
-}
-
-.select-location__sub-cell--selected,
-.select-location__sub-cell--selected:hover {
- background-color: #44AD4D;
-}
-
-.select-location-relay-status {
- width: 16px;
- height: 16px;
- border-radius: 8px;
-}
-
-.select-location-relay-status--inactive {
- background: rgba(208, 2, 27, 0.95);
-}
-
-.select-location-relay-status--active {
- background: rgba(68, 173, 77, 0.9);
-} \ No newline at end of file
diff --git a/app/components/SelectLocation.js b/app/components/SelectLocation.js
index 88f1415b01..5a0b8c1101 100644
--- a/app/components/SelectLocation.js
+++ b/app/components/SelectLocation.js
@@ -1,12 +1,13 @@
// @flow
import * as React from 'react';
-import { Layout, Container, Header } from './Layout';
+import { Layout, Container } from './Layout';
import CustomScrollbars from './CustomScrollbars';
+import { Text, View } from 'reactxp';
+import { Button, CellButton, Label } from './styled';
+import styles from './SelectLocationStyles';
+import Img from './Img';
import Accordion from './Accordion';
-import ChevronDownSVG from '../assets/images/icon-chevron-down.svg';
-import ChevronUpSVG from '../assets/images/icon-chevron-up.svg';
-import TickSVG from '../assets/images/icon-tick.svg';
import type { SettingsReduxState, RelayLocationRedux, RelayLocationCityRedux } from '../redux/settings/reducers';
import type { RelayLocation } from '../lib/ipc-facade';
@@ -22,7 +23,7 @@ type State = {
};
export default class SelectLocation extends React.Component<SelectLocationProps, State> {
- _selectedCell: ?HTMLElement;
+ _selectedCell: ?CellButton;
_scrollView: ?CustomScrollbars;
state = {
@@ -52,36 +53,38 @@ export default class SelectLocation extends React.Component<SelectLocationProps,
const scrollView = this._scrollView;
if(scrollView && cell) {
- scrollView.scrollToElement(cell, 'middle');
+ //TODO: fix this when repairing the auto-scroll in customscrollbars.
+ //scrollView.scrollToElement(cell, 'middle');
}
}
render() {
return (
<Layout>
- <Header hidden={ true } style={ 'defaultDark' } />
<Container>
- <div className="select-location">
- <button className="select-location__close" onClick={ this.props.onClose } />
- <div className="select-location__container">
- <div className="select-location__header">
- <h2 className="select-location__title">Select location</h2>
- </div>
+ <View style={styles.select_location}>
+ <Button style={styles.close} onPress={ this.props.onClose } testName='close'>
+ <Img style={styles.close_icon} source='icon-close'/>
+ </Button>
+ <View style={styles.container}>
+ <View style={styles.header}>
+ <Text style={styles.title}>Select location</Text>
+ </View>
- <CustomScrollbars autoHide={ true } ref={ (ref) => this._scrollView = ref }>
- <div>
- <div className="select-location__subtitle">
+ <CustomScrollbars autoHide={ true } ref={ (ref) => this._scrollView = ref }>
+ <View>
+ <Text style={styles.subtitle}>
While connected, your real location is masked with a private and secure location in the selected region
- </div>
+ </Text>
{ this.props.settings.relayLocations.map((relayCountry) => {
return this._renderCountry(relayCountry);
}) }
- </div>
+ </View>
</CustomScrollbars>
- </div>
- </div>
+ </View>
+ </View>
</Container>
</Layout>
);
@@ -121,10 +124,12 @@ export default class SelectLocation extends React.Component<SelectLocationProps,
});
}
- _relayStatusIndicator(active: boolean) {
- const statusClass = active ? 'select-location-relay-status--active' : 'select-location-relay-status--inactive';
+ _relayStatusIndicator(active: boolean, isSelected: boolean) {
+ const statusClass = active ? styles.relay_status__active : styles.relay_status__inactive;
- return (<div className={ 'select-location-relay-status ' + statusClass }></div>);
+ return ( isSelected ?
+ <Img style={ styles.tick_icon } source='icon-tick' height={24} width={24} /> :
+ <View style={[ styles.relay_status, statusClass ]}></View>);
}
_renderCountry(relayCountry: RelayLocationRedux) {
@@ -142,47 +147,37 @@ export default class SelectLocation extends React.Component<SelectLocationProps,
e.stopPropagation();
};
- const countryClass = 'select-location__cell' +
- (isSelected ? ' select-location__cell--selected' : '') +
- (relayCountry.hasActiveRelays ? ' select-location__cell--selectable' : '');
-
- const onRef = isSelected ? (element) => {
- this._selectedCell = element;
- } : undefined;
-
return (
- <div key={ relayCountry.code } className="select-location__country">
- <div className={ countryClass }
- onClick={ handleSelect }
- ref={ onRef }>
- <div className="select-location__cell-content">
-
- <div className="select-location__cell-icon">
- { isSelected ?
- <TickSVG /> :
- this._relayStatusIndicator(relayCountry.hasActiveRelays) }
- </div>
+ <View key={ relayCountry.code } style={styles.country}>
+ <CellButton
+ cellHoverStyle={ isSelected ? styles.cell_selected : null }
+ style={ isSelected ? styles.cell_selected : styles.cell }
+ onPress={ handleSelect }
+ disabled={!relayCountry.hasActiveRelays}
+ testName='country'>
- <div className={ 'select-location__cell-label' +
- (relayCountry.hasActiveRelays ? '' : ' select-location__cell-label--inactive') }>
- { relayCountry.name }
- </div>
- </div>
+ { this._relayStatusIndicator(relayCountry.hasActiveRelays, isSelected) }
- { relayCountry.cities.length > 1 && <button type="button" className="select-location__collapse-button" onClick={ handleCollapse }>
- { isExpanded ?
- <ChevronUpSVG className="select-location__collapse-icon" /> :
- <ChevronDownSVG className="select-location__collapse-icon" /> }
- </button> }
+ <Label>
+ { relayCountry.name }
+ </Label>
- </div>
+ { relayCountry.cities.length > 1 ?
+ <Img style={styles.collapse_button}
+ hoverStyle={styles.expand_chevron_hover}
+ onPress={ handleCollapse }
+ source={isExpanded ? 'icon-chevron-up' : 'icon-chevron-down'}
+ height={24}
+ width={24} />
+ : null }
+ </CellButton>
{ relayCountry.cities.length > 1 &&
- (<Accordion className="select-location__cities" height={ isExpanded ? 'auto' : 0 }>
+ (<Accordion height={ isExpanded ? 'auto' : 0 }>
{ relayCountry.cities.map((relayCity) => this._renderCity(relayCountry.code, relayCity)) }
</Accordion>)
}
- </div>
+ </View>
);
}
@@ -191,36 +186,29 @@ export default class SelectLocation extends React.Component<SelectLocationProps,
const isSelected = this._isSelected(relayLocation);
- const cityClass = 'select-location__sub-cell' +
- (isSelected ? ' select-location__sub-cell--selected' : '') +
- (relayCity.hasActiveRelays ? ' select-location__sub-cell--selectable' : '');
+ const onRef = isSelected ? (element) => {
+ this._selectedCell = element;
+ } : undefined;
const handleSelect = (relayCity.hasActiveRelays && !isSelected) ? () => {
this.props.onSelect(relayLocation);
} : undefined;
- const onRef = isSelected ? (element) => {
- this._selectedCell = element;
- } : undefined;
-
return (
- <div key={ `${countryCode}_${relayCity.code}` }
- className={ cityClass }
- onClick={ handleSelect }
- ref={ onRef }>
+ <CellButton key={ `${countryCode}_${relayCity.code}` }
+ onPress={ handleSelect }
+ disabled={!relayCity.hasActiveRelays}
+ cellHoverStyle={isSelected ? styles.sub_cell__selected : null}
+ style={isSelected ? styles.sub_cell__selected : styles.sub_cell}
+ testName='city'
+ ref={onRef}>
- <div className="select-location__cell-icon">
- { isSelected ?
- <TickSVG /> :
- this._relayStatusIndicator(relayCity.hasActiveRelays) }
- </div>
+ { this._relayStatusIndicator(relayCity.hasActiveRelays, isSelected) }
- <div className={ 'select-location__cell-label' +
- (relayCity.hasActiveRelays ? '' : ' select-location__cell-label--inactive') }>
+ <Label>
{ relayCity.name }
- </div>
- </div>
+ </Label>
+ </CellButton>
);
}
-
}
diff --git a/app/components/SelectLocationStyles.js b/app/components/SelectLocationStyles.js
new file mode 100644
index 0000000000..079e28dd93
--- /dev/null
+++ b/app/components/SelectLocationStyles.js
@@ -0,0 +1,119 @@
+// @flow
+import { createViewStyles, createTextStyles } from '../lib/styles';
+import { colors } from '../config';
+
+export default {
+ ...createViewStyles({
+ select_location: {
+ backgroundColor: colors.darkBlue,
+ height: '100%',
+ },
+ container: {
+ flexDirection: 'column',
+ flex: 1,
+ },
+ header:{
+ flex: 0,
+ marginBottom: 16,
+ },
+ close: {
+ marginLeft: 12,
+ marginTop: 24,
+ cursor: 'default',
+ },
+ close_icon:{
+ width: 24,
+ height: 24,
+ flex: 0,
+ opacity: 0.6,
+ },
+ relay_status: {
+ width: 16,
+ height: 16,
+ borderRadius: 8,
+ marginLeft: 4,
+ marginRight: 4,
+ marginTop: 19.5,
+ marginBottom: 19.5,
+ },
+ relay_status__inactive: {
+ backgroundColor: colors.red95,
+ },
+ relay_status__active: {
+ backgroundColor: colors.green90,
+ },
+ tick_icon: {
+ color: colors.white,
+ marginLeft: 0,
+ marginRight: 0,
+ marginTop: 15.5,
+ marginBottom: 15.5,
+ },
+ country: {
+ flexDirection: 'column',
+ flex: 0,
+ },
+ collapse_button: {
+ flex: 0,
+ alignSelf: 'stretch',
+ justifyContent: 'center',
+ paddingRight: 16,
+ paddingLeft: 16,
+ },
+ cell: {
+ paddingTop: 0,
+ paddingBottom: 0,
+ paddingLeft: 20,
+ paddingRight: 0,
+ },
+ sub_cell: {
+ paddingTop: 0,
+ paddingBottom: 0,
+ paddingRight: 0,
+ paddingLeft: 40,
+ backgroundColor: colors.blue40,
+ },
+ sub_cell__selected: {
+ paddingTop: 0,
+ paddingBottom: 0,
+ paddingRight: 0,
+ paddingLeft: 40,
+ backgroundColor: colors.green,
+ },
+ cell_selected: {
+ paddingTop: 0,
+ paddingBottom: 0,
+ paddingLeft: 20,
+ paddingRight: 0,
+ backgroundColor: colors.green,
+ },
+ expand_chevron_hover: {
+ color: colors.white,
+ },
+ }),
+ ...createTextStyles({
+ title: {
+ fontFamily: 'DINPro',
+ fontSize: 32,
+ fontWeight: '900',
+ lineHeight: 40,
+ color: colors.white,
+ paddingTop: 16,
+ paddingLeft: 24,
+ paddingRight: 24,
+ },
+ subtitle: {
+ fontFamily: 'Open Sans',
+ fontSize: 13,
+ fontWeight: '600',
+ lineHeight: 20,
+ color: colors.white60,
+ letterSpacing: -0.2,
+ paddingTop: 0,
+ paddingLeft: 24,
+ paddingRight: 24,
+ paddingBottom: 24,
+ flex: 0,
+ },
+ }),
+}; \ No newline at end of file
diff --git a/app/components/styled/AppButton.js b/app/components/styled/AppButton.js
index fdb6d35dfa..59cba4eefb 100644
--- a/app/components/styled/AppButton.js
+++ b/app/components/styled/AppButton.js
@@ -2,75 +2,8 @@
import * as React from 'react';
import { Text, Component } from 'reactxp';
import { Button } from './Button';
-import { colors } from '../../config';
-
-import { createViewStyles, createTextStyles } from '../../lib/styles';
-
-const styles = {
- ...createViewStyles({
- red:{
- backgroundColor: colors.red95,
- },
- redHover: {
- backgroundColor: colors.red,
- },
- green:{
- backgroundColor: colors.green,
- },
- greenHover:{
- backgroundColor: colors.green90,
- },
- blue:{
- backgroundColor: colors.blue80,
- },
- blueHover:{
- backgroundColor: colors.blue60,
- },
- transparent:{
- backgroundColor: colors.white20,
- },
- transparentHover:{
- backgroundColor: colors.white40,
- },
- white80:{
- color: colors.white80,
- },
- white: {
- color: colors.white,
- },
- icon:{
- position: 'absolute',
- alignSelf: 'flex-end',
- right: 8,
- marginLeft: 8,
- },
- common:{
- paddingTop: 9,
- paddingLeft: 9,
- paddingRight: 9,
- paddingBottom: 9,
- marginTop: 8,
- marginBottom: 8,
- marginLeft: 24,
- marginRight: 24,
- borderRadius: 4,
- flex: 1,
- flexDirection: 'column',
- alignContent: 'center',
- justifyContent: 'center',
- },
- }),
- ...createTextStyles({
- label:{
- alignSelf: 'center',
- fontFamily: 'DINPro',
- fontSize: 20,
- fontWeight: '900',
- lineHeight: 26,
- flex: 1,
- },
- }),
-};
+import styles from './AppButtonStyles';
+import blurStyles from './BlurAppButtonStyles';
export class Label extends Text {}
@@ -132,5 +65,9 @@ export class BlueButton extends BaseButton {
}
export class TransparentButton extends BaseButton {
- backgroundStyle = () => this.state.hovered ? styles.transparentHover : styles.transparent;
+ backgroundStyle = () => this.state.hovered ? blurStyles.transparentHover : blurStyles.transparent;
+}
+
+export class RedTransparentButton extends BaseButton {
+ backgroundStyle = () => this.state.hovered ? blurStyles.redTransparentHover : blurStyles.redTransparent;
} \ No newline at end of file
diff --git a/app/components/styled/AppButtonStyles.js b/app/components/styled/AppButtonStyles.js
new file mode 100644
index 0000000000..477a0cc54a
--- /dev/null
+++ b/app/components/styled/AppButtonStyles.js
@@ -0,0 +1,68 @@
+import { colors } from '../../config';
+
+import { createViewStyles, createTextStyles } from '../../lib/styles';
+
+export default {
+ ...createViewStyles({
+ red:{
+ backgroundColor: colors.red95,
+ },
+ redHover: {
+ backgroundColor: colors.red,
+ },
+ green:{
+ backgroundColor: colors.green,
+ },
+ greenHover:{
+ backgroundColor: colors.green90,
+ },
+ blue:{
+ backgroundColor: colors.blue80,
+ },
+ blueHover:{
+ backgroundColor: colors.blue60,
+ },
+ white80:{
+ color: colors.white80,
+ },
+ white: {
+ color: colors.white,
+ },
+ icon:{
+ position: 'absolute',
+ alignSelf: 'flex-end',
+ right: 8,
+ marginLeft: 8,
+ },
+ iconTransparent:{
+ position: 'absolute',
+ alignSelf: 'flex-end',
+ right: 42,
+ },
+ common:{
+ paddingTop: 9,
+ paddingLeft: 9,
+ paddingRight: 9,
+ paddingBottom: 9,
+ marginTop: 8,
+ marginBottom: 8,
+ marginLeft: 24,
+ marginRight: 24,
+ borderRadius: 4,
+ flex: 1,
+ flexDirection: 'column',
+ alignContent: 'center',
+ justifyContent: 'center',
+ },
+ }),
+ ...createTextStyles({
+ label:{
+ alignSelf: 'center',
+ fontFamily: 'DINPro',
+ fontSize: 20,
+ fontWeight: '900',
+ lineHeight: 26,
+ flex: 1,
+ },
+ }),
+}; \ No newline at end of file
diff --git a/app/components/styled/BlurAppButtonStyles.android.js b/app/components/styled/BlurAppButtonStyles.android.js
new file mode 100644
index 0000000000..9243a28729
--- /dev/null
+++ b/app/components/styled/BlurAppButtonStyles.android.js
@@ -0,0 +1,20 @@
+import { colors } from '../../config';
+
+import { createViewStyles } from '../../lib/styles';
+
+export default {
+ ...createViewStyles({
+ transparent:{
+ backgroundColor: colors.white20,
+ },
+ transparentHover:{
+ backgroundColor: colors.white40,
+ },
+ redTransparent:{
+ backgroundColor: colors.red40,
+ },
+ redTransparentHover:{
+ backgroundColor: colors.red45,
+ },
+ })
+}; \ No newline at end of file
diff --git a/app/components/styled/BlurAppButtonStyles.js b/app/components/styled/BlurAppButtonStyles.js
new file mode 100644
index 0000000000..9e7b213754
--- /dev/null
+++ b/app/components/styled/BlurAppButtonStyles.js
@@ -0,0 +1,24 @@
+import { colors } from '../../config';
+
+import { createViewStyles } from '../../lib/styles';
+
+export default {
+ ...createViewStyles({
+ transparent:{
+ backgroundColor: colors.white20,
+ backdropFilter: 'blur(4px)',
+ },
+ transparentHover:{
+ backgroundColor: colors.white40,
+ backdropFilter: 'blur(4px)',
+ },
+ redTransparent:{
+ backgroundColor: colors.red40,
+ backdropFilter: 'blur(4px)',
+ },
+ redTransparentHover:{
+ backgroundColor: colors.red45,
+ backdropFilter: 'blur(4px)',
+ },
+ })
+}; \ No newline at end of file
diff --git a/app/components/styled/CellButton.js b/app/components/styled/CellButton.js
index 400a7ac6de..1a0e055b01 100644
--- a/app/components/styled/CellButton.js
+++ b/app/components/styled/CellButton.js
@@ -82,9 +82,9 @@ type State = { hovered: boolean };
export default class CellButton extends Component<CellButtonProps, State> {
state = { hovered: false };
- textStyle = (cellHoverStyle?: Types.ViewStyle) => this.state.hovered ? cellHoverStyle || styles.white80 : null;
- iconStyle = (cellHoverStyle?: Types.ViewStyle) => this.state.hovered ? cellHoverStyle || styles.white40 : null;
- subtextStyle = (cellHoverStyle?: Types.ViewStyle) => this.state.hovered ? cellHoverStyle || styles.white40 : null;
+ textStyle = (cellHoverStyle?: Types.ViewStyle) => this.state.hovered ? cellHoverStyle : null;
+ iconStyle = (cellHoverStyle?: Types.ViewStyle) => this.state.hovered ? cellHoverStyle : null;
+ subtextStyle = (cellHoverStyle?: Types.ViewStyle) => this.state.hovered ? cellHoverStyle : null;
backgroundStyle = (cellHoverStyle?: Types.ViewStyle) => this.state.hovered ? cellHoverStyle || styles.blueHover : null;
onHoverStart = () => !this.props.disabled ? this.setState({ hovered: true }) : null;
diff --git a/app/components/styled/index.js b/app/components/styled/index.js
index de18529cac..7efeb28389 100644
--- a/app/components/styled/index.js
+++ b/app/components/styled/index.js
@@ -2,7 +2,7 @@
import { Button } from './Button';
import CellButton, { Label, SubText } from './CellButton';
-import { RedButton, GreenButton, BlueButton, TransparentButton } from './AppButton';
+import { RedButton, GreenButton, BlueButton, TransparentButton, RedTransparentButton } from './AppButton';
export {
Button,
@@ -11,6 +11,7 @@ export {
GreenButton,
BlueButton,
TransparentButton,
+ RedTransparentButton,
Label,
SubText,
};
diff --git a/test/components/Connect.spec.js b/test/components/Connect.spec.js
index 9c6ed0d109..59079082c5 100644
--- a/test/components/Connect.spec.js
+++ b/test/components/Connect.spec.js
@@ -2,10 +2,9 @@
import { expect } from 'chai';
import React from 'react';
-import { mount } from 'enzyme';
+import { shallow } from 'enzyme';
import Connect from '../../app/components/Connect';
-import Header from '../../app/components/HeaderBar';
import type { ConnectProps } from '../../app/components/Connect';
@@ -19,13 +18,12 @@ describe('components/Connect', () => {
}
});
- const header = component.find(Header);
- const securityMessage = component.find('.connect__status-security--unsecured');
- const connectButton = component.find('.button .button--positive');
-
+ const header = getComponent(component, 'header');
+ const securityMessage = getComponent(component, 'networkSecurityMessage');
+ const connectButton = getComponent(component, 'secureConnection');
expect(header.prop('style')).to.equal('error');
- expect(securityMessage.text().toLowerCase()).to.contain('unsecured');
- expect(connectButton.text()).to.equal('Secure my connection');
+ expect(securityMessage.html()).to.contain('UNSECURED');
+ expect(connectButton.html()).to.contain('Secure my connection');
});
it('shows secured hints when connected', () => {
@@ -36,13 +34,12 @@ describe('components/Connect', () => {
}
});
- const header = component.find(Header);
- const securityMessage = component.find('.connect__status-security--secure');
- const disconnectButton = component.find('.button .button--negative-light');
-
+ const header = getComponent(component, 'header');
+ const securityMessage = getComponent(component, 'networkSecurityMessage');
+ const disconnectButton = getComponent(component, 'disconnect');
expect(header.prop('style')).to.equal('success');
- expect(securityMessage.text().toLowerCase()).to.contain('secure');
- expect(disconnectButton.text()).to.equal('Disconnect');
+ expect(securityMessage.html()).to.contain('SECURE ');
+ expect(disconnectButton.html()).to.contain('Disconnect');
});
it('shows the connection location when connecting', () => {
@@ -54,12 +51,12 @@ describe('components/Connect', () => {
city: 'Oslo',
}
});
- const countryAndCity = component.find('.connect__status-location');
- const ipAddr = component.find('.connect__status-ipaddress');
+ const countryAndCity = getComponent(component, 'location');
+ const ipAddr = getComponent(component, 'ipAddress');
- expect(countryAndCity.text()).to.contain('Norway');
- expect(countryAndCity.text()).not.to.contain('Oslo');
- expect(ipAddr.text()).to.be.empty;
+ expect(countryAndCity.html()).to.contain('Norway');
+ expect(countryAndCity.html()).not.to.contain('Oslo');
+ expect(ipAddr.length).to.equal(0);
});
it('shows the connection location when connected', () => {
@@ -72,12 +69,12 @@ describe('components/Connect', () => {
ip: '4.3.2.1',
}
});
- const countryAndCity = component.find('.connect__status-location');
- const ipAddr = component.find('.connect__status-ipaddress');
+ const countryAndCity = getComponent(component, 'location');
+ const ipAddr = getComponent(component, 'ipAddress');
- expect(countryAndCity.text()).to.contain('Norway');
- expect(countryAndCity.text()).to.contain('Oslo');
- expect(ipAddr.text()).to.contain('4.3.2.1');
+ expect(countryAndCity.html()).to.contain('Norway');
+ expect(countryAndCity.html()).to.contain('Oslo');
+ expect(ipAddr.html()).to.contain('4.3.2.1');
});
it('shows the connection location when disconnected', () => {
@@ -90,12 +87,12 @@ describe('components/Connect', () => {
ip: '4.3.2.1',
}
});
- const countryAndCity = component.find('.connect__status-location');
- const ipAddr = component.find('.connect__status-ipaddress');
+ const countryAndCity = getComponent(component, 'location');
+ const ipAddr = getComponent(component, 'ipAddress');
- expect(countryAndCity.text()).to.contain('Norway');
- expect(countryAndCity.text()).to.not.contain('Oslo');
- expect(ipAddr.text()).to.contain('4.3.2.1');
+ expect(countryAndCity.html()).to.contain('Norway');
+ expect(countryAndCity.html()).to.not.contain('Oslo');
+ expect(ipAddr.html()).to.contain('4.3.2.1');
});
it('invokes the onConnect prop', (done) => {
@@ -106,9 +103,9 @@ describe('components/Connect', () => {
status: 'disconnected',
}
});
- const connectButton = component.find('.button .button--positive');
+ const connectButton = getComponent(component, 'secureConnection');
- connectButton.simulate('click');
+ connectButton.prop('onPress')();
});
});
@@ -134,5 +131,9 @@ const defaultProps: ConnectProps = {
function renderWithProps(customProps: $Shape<ConnectProps>) {
const props = { ...defaultProps, ...customProps };
- return mount( <Connect { ...props } /> );
+ return shallow( <Connect { ...props } /> );
}
+
+function getComponent(container, testName) {
+ return container.findWhere( n => n.prop('testName') === testName);
+} \ No newline at end of file
diff --git a/test/components/SelectLocation.spec.js b/test/components/SelectLocation.spec.js
index 59a8dc9770..4e2265c6dd 100644
--- a/test/components/SelectLocation.spec.js
+++ b/test/components/SelectLocation.spec.js
@@ -2,7 +2,7 @@
import { expect } from 'chai';
import * as React from 'react';
-import ReactTestUtils, { Simulate } from 'react-dom/test-utils';
+import { shallow } from 'enzyme';
import SelectLocation from '../../app/components/SelectLocation';
import type { SettingsReduxState } from '../../app/redux/settings/reducers';
@@ -48,7 +48,7 @@ describe('components/SelectLocation', () => {
};
const render = (props: SelectLocationProps) => {
- return ReactTestUtils.renderIntoDocument(
+ return shallow(
<SelectLocation { ...props } />
);
};
@@ -57,11 +57,8 @@ describe('components/SelectLocation', () => {
const props = makeProps(state, {
onClose: () => done()
});
- const domNode = ReactTestUtils.findRenderedDOMComponentWithClass(render(props), 'select-location__close');
- // See: https://github.com/facebook/flow/pull/5841
- if(domNode) {
- Simulate.click(domNode);
- }
+ const node = getComponent(render(props), 'close');
+ click(node);
});
it('should call select callback for country', (done) => {
@@ -77,9 +74,9 @@ describe('components/SelectLocation', () => {
}
}
});
- const elements = ReactTestUtils.scryRenderedDOMComponentsWithClass(render(props), 'select-location__cell');
+ const elements = getComponent(render(props), 'country');
expect(elements).to.have.length(1);
- Simulate.click(elements[0]);
+ click(elements.at(0));
});
it('should call select callback for city', (done) => {
@@ -95,9 +92,17 @@ describe('components/SelectLocation', () => {
}
}
});
- const elements = ReactTestUtils.scryRenderedDOMComponentsWithClass(render(props), 'select-location__sub-cell');
+ const elements = getComponent(render(props), 'city');
expect(elements).to.have.length(2);
- Simulate.click(elements[0]);
+ click(elements.at(0));
});
});
+
+function getComponent(container, testName) {
+ return container.findWhere( n => n.prop('testName') === testName);
+}
+
+function click(component) {
+ component.prop('onPress')();
+}