Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Social Signup: Handle the case where users block third-party cookies/data #13983

Merged
merged 14 commits into from
May 29, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion client/components/button/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ button {
color: $gray-dark;
}
&[disabled],
&:disabled {
&:disabled,
&.disabled {
color: lighten( $gray, 30% );
background: $white;
border-color: lighten( $gray, 30% );
Expand Down
8 changes: 4 additions & 4 deletions client/components/signup-form/social.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class SocialSignupForm extends Component {
return (
<div className="signup-form__social">
<p>
{ this.props.translate( 'Or create account using social profile:' ) }
{ this.props.translate( 'Or create an account using your existing social profile:' ) }
</p>

<div className="signup-form__social-buttons">
Expand All @@ -56,10 +56,10 @@ class SocialSignupForm extends Component {
responseHandler={ this.handleFacebookResponse } />
</div>

<p>
<p>
{ this.props.translate(
"Connect to your existing social profile to get started faster. We'll never post without your permission."
) }
"Connect to your existing social profile to get started faster. We'll never post without your permission."
) }
</p>
</div>
);
Expand Down
4 changes: 0 additions & 4 deletions client/components/signup-form/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,6 @@
margin: 0 0 10px;
width: 100%;
}

svg {
margin-top: -2px;
}
}

.signup-form__notice.notice {
Expand Down
109 changes: 92 additions & 17 deletions client/components/social-buttons/google.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import React, { Component, PropTypes } from 'react';
import { loadScript } from 'lib/load-script';
import { localize } from 'i18n-calypso';

/**
* Internal dependencies
*/
import Popover from 'components/popover';
import { preventWidows } from 'lib/formatting';

class GoogleLoginButton extends Component {
static propTypes = {
clientId: PropTypes.string.isRequired,
Expand All @@ -19,11 +25,20 @@ class GoogleLoginButton extends Component {
fetchBasicProfile: true,
};

state = {
error: '',
showError: false,
errorRef: null,
};

constructor( props ) {
super( props );

this.initialized = null;

this.handleClick = this.handleClick.bind( this );
this.showError = this.showError.bind( this );
this.hideError = this.hideError.bind( this );
}

componentDidMount() {
Expand All @@ -45,6 +60,10 @@ class GoogleLoginButton extends Component {
return this.initialized;
}

this.setState( { error: '' } );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it be initialized more than once? If not then we can remove this state change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, initialize() can be called multiple times - this.initialized won't be set until the gapi instance is properly initialised.


const { translate } = this.props;

this.initialized = this.loadDependency()
.then( gapi => new Promise( resolve => gapi.load( 'client:auth2', resolve ) ).then( () => gapi ) )
.then( gapi => gapi.client.init( {
Expand All @@ -56,6 +75,11 @@ class GoogleLoginButton extends Component {
).catch( error => {
this.initialized = null;

if ( 'idpiframe_initialization_failed' === error.error ) {
// This error is caused by 3rd party cookies being blocked.
this.setState( { error: translate( 'Please enable "third-party cookies" to connect your Google account.' ) } );
}

return Promise.reject( error );
} );

Expand All @@ -65,6 +89,15 @@ class GoogleLoginButton extends Component {
handleClick( event ) {
event.preventDefault();

if ( this.state.error ) {
this.setState( {
showError: ! this.state.showError,
errorRef: event.currentTarget,
} );

return;
}

const { responseHandler } = this.props;

// Handle click async if the library is not loaded yet
Expand All @@ -74,27 +107,69 @@ class GoogleLoginButton extends Component {
this.initialize().then( gapi => gapi.auth2.getAuthInstance().signIn( { prompt: 'select_account' } ).then( responseHandler ) );
}

showError( event ) {
if ( ! this.state.error ) {
return;
}

this.setState( {
showError: true,
errorRef: event.currentTarget,
} );
}

hideError() {
this.setState( { showError: false } );
}

render() {
let classes = 'social-buttons__button button';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option is:

import classNames from 'classnames';
classNames( 'social-buttons__button', 'button', { disabled: this.state.error } );

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So many magic functions. 😁

if ( this.state.error ) {
classes += ' disabled';
}

return (
<button className="button" onClick={ this.handleClick }>
<svg className="social-buttons__logo" width="20" height="20" viewBox="0 0 20 20" xmlns="http:https://www.w3.org/2000/svg">
<div>
<button className={ classes } onMouseOver={ this.showError } onMouseOut={ this.hideError } onClick={ this.handleClick }>
{ /* eslint-disable max-len */ }
<g fill="none" fillRule="evenodd">
<path d="M19.6 10.227c0-.709-.064-1.39-.182-2.045H10v3.868h5.382a4.6 4.6 0 0 1-1.996 3.018v2.51h3.232c1.891-1.742 2.982-4.305 2.982-7.35z" fill="#4285F4" />
<path d="M10 20c2.7 0 4.964-.895 6.618-2.423l-3.232-2.509c-.895.6-2.04.955-3.386.955-2.605 0-4.81-1.76-5.595-4.123H1.064v2.59A9.996 9.996 0 0 0 10 20z" fill="#34A853" />
<path d="M4.405 11.9c-.2-.6-.314-1.24-.314-1.9 0-.66.114-1.3.314-1.9V5.51H1.064A9.996 9.996 0 0 0 0 10c0 1.614.386 3.14 1.064 4.49l3.34-2.59z" fill="#FBBC05" />
<path d="M10 3.977c1.468 0 2.786.505 3.823 1.496l2.868-2.868C14.959.99 12.695 0 10 0 6.09 0 2.71 2.24 1.064 5.51l3.34 2.59C5.192 5.736 7.396 3.977 10 3.977z" fill="#EA4335" />
<svg className="social-buttons__logo enabled" width="20" height="20" viewBox="0 0 20 20"xmlns="http:https://www.w3.org/2000/svg">
<g fill="none" fillRule="evenodd">
<path d="M19.6 10.227c0-.709-.064-1.39-.182-2.045H10v3.868h5.382a4.6 4.6 0 0 1-1.996 3.018v2.51h3.232c1.891-1.742 2.982-4.305 2.982-7.35z" fill="#4285F4" />
<path d="M10 20c2.7 0 4.964-.895 6.618-2.423l-3.232-2.509c-.895.6-2.04.955-3.386.955-2.605 0-4.81-1.76-5.595-4.123H1.064v2.59A9.996 9.996 0 0 0 10 20z" fill="#34A853" />
<path d="M4.405 11.9c-.2-.6-.314-1.24-.314-1.9 0-.66.114-1.3.314-1.9V5.51H1.064A9.996 9.996 0 0 0 0 10c0 1.614.386 3.14 1.064 4.49l3.34-2.59z" fill="#FBBC05" />
<path d="M10 3.977c1.468 0 2.786.505 3.823 1.496l2.868-2.868C14.959.99 12.695 0 10 0 6.09 0 2.71 2.24 1.064 5.51l3.34 2.59C5.192 5.736 7.396 3.977 10 3.977z" fill="#EA4335" />
</g>
</svg>

<svg className="social-buttons__logo disabled" width="20" height="20" viewBox="0 0 20 20" xmlns="http:https://www.w3.org/2000/svg">
<g fill="none" fillRule="evenodd">
<path d="M19.6 10.227c0-.709-.064-1.39-.182-2.045H10v3.868h5.382a4.6 4.6 0 0 1-1.996 3.018v2.51h3.232c1.891-1.742 2.982-4.305 2.982-7.35z" fill="#e9eff3" />
<path d="M10 20c2.7 0 4.964-.895 6.618-2.423l-3.232-2.509c-.895.6-2.04.955-3.386.955-2.605 0-4.81-1.76-5.595-4.123H1.064v2.59A9.996 9.996 0 0 0 10 20z" fill="#e9eff3" />
<path d="M4.405 11.9c-.2-.6-.314-1.24-.314-1.9 0-.66.114-1.3.314-1.9V5.51H1.064A9.996 9.996 0 0 0 0 10c0 1.614.386 3.14 1.064 4.49l3.34-2.59z" fill="#e9eff3" />
<path d="M10 3.977c1.468 0 2.786.505 3.823 1.496l2.868-2.868C14.959.99 12.695 0 10 0 6.09 0 2.71 2.24 1.064 5.51l3.34 2.59C5.192 5.736 7.396 3.977 10 3.977z" fill="#e9eff3" />
</g>
</svg>
{ /* eslint-enable max-len */ }
</g>
</svg>

<span className="social-buttons__service-name">
{ this.props.translate( 'Continue with %(service)s', {
args: { service: 'Google' },
comment: '%(service)s is the name of a Social Network, e.g. "Google", "Facebook", "Twitter" ...'
} ) }
</span>
</button>

<span className="social-buttons__service-name">
{ this.props.translate( 'Continue with %(service)s', {
args: { service: 'Google' },
comment: '%(service)s is the name of a Social Network, e.g. "Google", "Facebook", "Twitter" ...'
} ) }
</span>
</button>

<Popover
id="social-buttons__error"
className="social-buttons__error"
isVisible={ this.state.showError }
onClose={ this.hideError }
position="top"
context={ this.state.errorRef }
>
{ preventWidows( this.state.error ) }
</Popover>
</div>
);
}
}
Expand Down
27 changes: 27 additions & 0 deletions client/components/social-buttons/style.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,36 @@
/*rtl:ignore*/

.social-buttons__button {
svg {
margin-top: -2px;

&.disabled {
display: none;
}
}
}

.social-buttons__button.disabled {
svg.enabled {
display: none;
}

svg.disabled {
display: inline;
}
}

.social-buttons__logo {
vertical-align: middle;
}

.social-buttons__service-name {
margin-left: 9px;
}

.social-buttons__error {
.popover__inner {
padding: 10px;
max-width: 200px;
}
}
7 changes: 6 additions & 1 deletion client/login/wp-login/test/test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@ import { identity } from 'lodash';
/**
* Internal Dependencies
*/
import { Login } from '../index.jsx';
import useFakeDom from 'test/helpers/use-fake-dom';

describe( 'Login', () => {
let Login;

useFakeDom();

before( () => {
Login = require( '../index.jsx' ).Login;
} );

describe( 'footerLinks', () => {
context( 'when state is not loaded', () => {
it( 'should not have a return button', () => {
Expand Down