diff --git a/packages/netlify-cms-backend-git-gateway/src/AuthenticationPage.js b/packages/netlify-cms-backend-git-gateway/src/AuthenticationPage.js index 60fc72d8..c7f89842 100644 --- a/packages/netlify-cms-backend-git-gateway/src/AuthenticationPage.js +++ b/packages/netlify-cms-backend-git-gateway/src/AuthenticationPage.js @@ -60,6 +60,9 @@ if (window.netlifyIdentity) { window.netlifyIdentity.on('logout', () => { component && component.handleIdentityLogout(); }); + window.netlifyIdentity.on('error', err => { + component && component.handleIdentityError(err); + }); } export default class GitGatewayAuthenticationPage extends React.Component { @@ -88,6 +91,15 @@ export default class GitGatewayAuthenticationPage extends React.Component { window.netlifyIdentity.open(); }; + handleIdentityError = err => { + if (err?.message?.match(/^Failed to load settings from.+\.netlify\/identity$/)) { + window.netlifyIdentity.close(); + this.setState({ + errors: { identity: this.props.t('auth.errors.identitySettings') }, + }); + } + }; + handleIdentity = () => { const user = window.netlifyIdentity.currentUser(); if (user) { @@ -102,6 +114,7 @@ export default class GitGatewayAuthenticationPage extends React.Component { inProgress: PropTypes.bool.isRequired, error: PropTypes.node, config: ImmutablePropTypes.map, + t: PropTypes.func.isRequired, }; state = { email: '', password: '', errors: {} }; @@ -114,12 +127,13 @@ export default class GitGatewayAuthenticationPage extends React.Component { e.preventDefault(); const { email, password } = this.state; + const { t } = this.props; const errors = {}; if (!email) { - errors.email = 'Make sure to enter your email.'; + errors.email = t('auth.errors.email'); } if (!password) { - errors.password = 'Please enter your password.'; + errors.password = t('auth.errors.password'); } if (Object.keys(errors).length > 0) { @@ -142,16 +156,34 @@ export default class GitGatewayAuthenticationPage extends React.Component { render() { const { errors } = this.state; - const { error, inProgress, config } = this.props; + const { error, inProgress, config, t } = this.props; if (window.netlifyIdentity) { - return ( - 'Login with Netlify Identity'} - /> - ); + if (errors.identity) { + return ( + ( + + {errors.identity} + + )} + /> + ); + } else { + return ( + t('auth.loginWithNetlifyIdentity')} + /> + ); + } } return ( @@ -179,7 +211,7 @@ export default class GitGatewayAuthenticationPage extends React.Component { onChange={partial(this.handleChange, 'password')} /> - {inProgress ? 'Logging in...' : 'Login'} + {inProgress ? t('auth.loggingIn') : t('auth.login')} )} diff --git a/packages/netlify-cms-backend-git-gateway/src/__tests__/AuthenticationPage.spec.js b/packages/netlify-cms-backend-git-gateway/src/__tests__/AuthenticationPage.spec.js new file mode 100644 index 00000000..5d56f3a4 --- /dev/null +++ b/packages/netlify-cms-backend-git-gateway/src/__tests__/AuthenticationPage.spec.js @@ -0,0 +1,42 @@ +import React from 'react'; +import { Map } from 'immutable'; +import { render } from '@testing-library/react'; + +window.netlifyIdentity = { + currentUser: jest.fn(), + on: jest.fn(), + close: jest.fn(), +}; + +describe('GitGatewayAuthenticationPage', () => { + const props = { + config: Map({ logo_url: 'logo_url' }), + t: jest.fn(key => key), + onLogin: jest.fn(), + inProgress: false, + }; + + beforeEach(() => { + jest.clearAllMocks(); + jest.resetModules(); + }); + + it('should render with identity error', () => { + const { default: GitGatewayAuthenticationPage } = require('../AuthenticationPage'); + const { asFragment } = render(); + + const errorCallback = window.netlifyIdentity.on.mock.calls.find(call => call[0] === 'error')[1]; + + errorCallback( + new Error('Failed to load settings from https://site.netlify.com/.netlify/identity'), + ); + + expect(asFragment()).toMatchSnapshot(); + }); + + it('should render with no identity error', () => { + const { default: GitGatewayAuthenticationPage } = require('../AuthenticationPage'); + const { asFragment } = render(); + expect(asFragment()).toMatchSnapshot(); + }); +}); diff --git a/packages/netlify-cms-backend-git-gateway/src/__tests__/__snapshots__/AuthenticationPage.spec.js.snap b/packages/netlify-cms-backend-git-gateway/src/__tests__/__snapshots__/AuthenticationPage.spec.js.snap new file mode 100644 index 00000000..b0817a5c --- /dev/null +++ b/packages/netlify-cms-backend-git-gateway/src/__tests__/__snapshots__/AuthenticationPage.spec.js.snap @@ -0,0 +1,331 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`GitGatewayAuthenticationPage should render with identity error 1`] = ` + + .emotion-5 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-flow: column nowrap; + -ms-flex-flow: column nowrap; + flex-flow: column nowrap; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + height: 100vh; +} + +.emotion-0 { + width: 300px; + height: 200px; + margin-top: -150px; +} + +.emotion-3 { + display: inline-block; + line-height: 0; + width: 100px; + height: 100px; + -webkit-transform: rotate(0deg); + -ms-transform: rotate(0deg); + transform: rotate(0deg); + color: #c4c6d2; + position: absolute; + bottom: 10px; +} + +.emotion-3 path:not(.no-fill), +.emotion-3 circle:not(.no-fill), +.emotion-3 polygon:not(.no-fill), +.emotion-3 rect:not(.no-fill) { + fill: currentColor; +} + +.emotion-3 path.clipped { + fill: transparent; +} + +.emotion-3 svg { + width: 100%; + height: 100%; +} + +
+ + Logo + + + auth.errors.identitySettings + + + + + + + + + + + +
+
+`; + +exports[`GitGatewayAuthenticationPage should render with no identity error 1`] = ` + + .emotion-7 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-flow: column nowrap; + -ms-flex-flow: column nowrap; + flex-flow: column nowrap; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + height: 100vh; +} + +.emotion-0 { + width: 300px; + height: 200px; + margin-top: -150px; +} + +.emotion-2 { + border: 0; + border-radius: 5px; + cursor: pointer; + box-shadow: 0 4px 12px 0 rgba(68,74,87,0.15),0 1px 3px 0 rgba(68,74,87,0.25); + height: 36px; + line-height: 36px; + font-weight: 500; + padding: 0 15px; + background-color: #798291; + color: #fff; + background-color: #798291; + color: #fff; + padding: 0 12px; + margin-top: -40px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + position: relative; +} + +.emotion-2:focus, +.emotion-2:hover { + color: #fff; + background-color: #555a65; +} + +.emotion-2[disabled] { + background-color: #eff0f4; + color: #798291; +} + +.emotion-5 { + display: inline-block; + line-height: 0; + width: 100px; + height: 100px; + -webkit-transform: rotate(0deg); + -ms-transform: rotate(0deg); + transform: rotate(0deg); + color: #c4c6d2; + position: absolute; + bottom: 10px; +} + +.emotion-5 path:not(.no-fill), +.emotion-5 circle:not(.no-fill), +.emotion-5 polygon:not(.no-fill), +.emotion-5 rect:not(.no-fill) { + fill: currentColor; +} + +.emotion-5 path.clipped { + fill: transparent; +} + +.emotion-5 svg { + width: 100%; + height: 100%; +} + +.emotion-7 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-flow: column nowrap; + -ms-flex-flow: column nowrap; + flex-flow: column nowrap; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + height: 100vh; +} + +.emotion-0 { + width: 300px; + height: 200px; + margin-top: -150px; +} + +.emotion-2 { + border: 0; + border-radius: 5px; + cursor: pointer; + box-shadow: 0 4px 12px 0 rgba(68,74,87,0.15),0 1px 3px 0 rgba(68,74,87,0.25); + height: 36px; + line-height: 36px; + font-weight: 500; + padding: 0 15px; + background-color: #798291; + color: #fff; + background-color: #798291; + color: #fff; + padding: 0 12px; + margin-top: -40px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + position: relative; +} + +.emotion-2:focus, +.emotion-2:hover { + color: #fff; + background-color: #555a65; +} + +.emotion-2[disabled] { + background-color: #eff0f4; + color: #798291; +} + +.emotion-5 { + display: inline-block; + line-height: 0; + width: 100px; + height: 100px; + -webkit-transform: rotate(0deg); + -ms-transform: rotate(0deg); + transform: rotate(0deg); + color: #c4c6d2; + position: absolute; + bottom: 10px; +} + +.emotion-5 path:not(.no-fill), +.emotion-5 circle:not(.no-fill), +.emotion-5 polygon:not(.no-fill), +.emotion-5 rect:not(.no-fill) { + fill: currentColor; +} + +.emotion-5 path.clipped { + fill: transparent; +} + +.emotion-5 svg { + width: 100%; + height: 100%; +} + +
+ + Logo + + + + + + + + + + + + +
+
+`; diff --git a/packages/netlify-cms-core/src/components/App/App.js b/packages/netlify-cms-core/src/components/App/App.js index da598a48..278d0c13 100644 --- a/packages/netlify-cms-core/src/components/App/App.js +++ b/packages/netlify-cms-core/src/components/App/App.js @@ -132,6 +132,7 @@ class App extends React.Component { authEndpoint: this.props.config.getIn(['backend', 'auth_endpoint']), config: this.props.config, clearHash: () => history.replace('/'), + t, })} ); diff --git a/packages/netlify-cms-locales/src/en/index.js b/packages/netlify-cms-locales/src/en/index.js index 65975533..2428e36e 100644 --- a/packages/netlify-cms-locales/src/en/index.js +++ b/packages/netlify-cms-locales/src/en/index.js @@ -1,4 +1,15 @@ const en = { + auth: { + login: 'Login', + loggingIn: 'Logging in...', + loginWithNetlifyIdentity: 'Login with Netlify Identity', + errors: { + email: 'Make sure to enter your email.', + password: 'Please enter your password.', + identitySettings: + 'Unable to access identity settings. When using git-gateway backend make sure to enable Identity service and Git Gateway.', + }, + }, app: { header: { content: 'Contents',