diff options
author | Anike Arni <anikarni@gmail.com> | 2017-02-21 18:39:06 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-21 18:39:06 -0300 |
commit | 92c6a9dbc39318df48b4b3d5fae1a3888f201343 (patch) | |
tree | 548fb92a6ae19d34945c19b2c704cc8c2c97382a /web-ui | |
parent | d5d7c8607138c8f39b55cdaa6ef3231c98d6af8a (diff) | |
parent | db1db55b806953ff93950b724fc96c8db388bbcf (diff) |
Merge pull request #986 from pixelated/login-errors
Translate and make login responsive
Diffstat (limited to 'web-ui')
-rw-r--r-- | web-ui/app/index.html | 10 | ||||
-rw-r--r-- | web-ui/app/locales/en_US/translation.json | 10 | ||||
-rw-r--r-- | web-ui/app/locales/pt_BR/translation.json | 10 | ||||
-rw-r--r-- | web-ui/src/backup_account/backup_account.html | 2 | ||||
-rw-r--r-- | web-ui/src/common/input_field/input_field.js | 14 | ||||
-rw-r--r-- | web-ui/src/login/app.js | 36 | ||||
-rw-r--r-- | web-ui/src/login/app.scss | 70 | ||||
-rw-r--r-- | web-ui/src/login/login.css | 143 | ||||
-rw-r--r-- | web-ui/src/login/login.html | 12 | ||||
-rw-r--r-- | web-ui/src/login/login.js | 5 | ||||
-rw-r--r-- | web-ui/src/login/opensans.css | 20 | ||||
-rw-r--r-- | web-ui/src/util.js | 7 | ||||
-rw-r--r-- | web-ui/test/unit/login/app.spec.js | 26 | ||||
-rw-r--r-- | web-ui/test/unit/util.spec.js | 20 |
14 files changed, 228 insertions, 157 deletions
diff --git a/web-ui/app/index.html b/web-ui/app/index.html index 72b04c83..ac4189e1 100644 --- a/web-ui/app/index.html +++ b/web-ui/app/index.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <head> -<link rel="icon" type="image/png" href="public/images/Favicon.png"> +<link rel="icon" type="image/png" href="public/images/favicon.png"> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <title>$account_email - Pixelated Mail</title> @@ -25,15 +25,15 @@ <path fill="#3E3B38" d="M233.8,678.3h-24v71.2h16.2v-26.5h7.8c14,0,24.3-8,24.3-22.9C258.1,685.6,247.6,678.3,233.8,678.3z M230.6,710.2h-4.6v-18.8h4.6c6.5,0,12.5,2.2,12.5,9.5C243,708.1,237.1,710.2,230.6,710.2z"/> <rect x="263.5" y="678.3" fill="#3E3B38" width="16.2" height="71.2"/> - <polygon fill="#3E3B38" points="350.4,678.3 330.1,678.3 316.9,697.7 303.7,678.3 284.6,678.3 307,711.1 282.6,749.5 302.9,749.5 + <polygon fill="#3E3B38" points="350.4,678.3 330.1,678.3 316.9,697.7 303.7,678.3 284.6,678.3 307,711.1 282.6,749.5 302.9,749.5 316.9,725.3 331,749.5 352.1,749.5 326.9,711.1 "/> - <polygon fill="#3E3B38" points="354.7,749.5 395.5,749.5 395.5,735.2 370.9,735.2 370.9,721 394.4,721 394.4,706.6 370.9,706.6 + <polygon fill="#3E3B38" points="354.7,749.5 395.5,749.5 395.5,735.2 370.9,735.2 370.9,721 394.4,721 394.4,706.6 370.9,706.6 370.9,692.5 395.5,692.5 395.5,678.3 354.7,678.3 "/> <path fill="#3E3B38" d="M456.1,678.3l-22.9,57h-15.9v-57h-16.2v71.2h26.5h14.3h3.2l5.4-14.3h27l5.4,14.3h17.5l-28.9-71.2H456.1z M455.7,721l7.8-20.7h0.2l7.8,20.7H455.7z"/> - <polygon fill="#3E3B38" points="486.4,692.5 503.4,692.5 503.4,749.5 519.6,749.5 519.6,692.5 536.6,692.5 536.6,678.3 + <polygon fill="#3E3B38" points="486.4,692.5 503.4,692.5 503.4,749.5 519.6,749.5 519.6,692.5 536.6,692.5 536.6,678.3 486.4,678.3 "/> - <polygon fill="#3E3B38" points="542,749.5 582.8,749.5 582.8,735.2 558.4,735.2 558.4,721 581.9,721 581.9,706.6 558.4,706.6 + <polygon fill="#3E3B38" points="542,749.5 582.8,749.5 582.8,735.2 558.4,735.2 558.4,721 581.9,721 581.9,706.6 558.4,706.6 558.4,692.5 582.8,692.5 582.8,678.3 542,678.3 "/> <path fill="#3E3B38" d="M606.5,678.3h-17.9v71.2h17.9c19.7,0,35.9-14.9,35.9-35.6C642.4,693.1,625.9,678.3,606.5,678.3z M607,735 h-2.4v-42.1h2.4c12.1,0,20.3,9.1,20.3,21.1C627.3,725.8,619.1,735,607,735z"/> diff --git a/web-ui/app/locales/en_US/translation.json b/web-ui/app/locales/en_US/translation.json index 7252d1d7..7aad4fe8 100644 --- a/web-ui/app/locales/en_US/translation.json +++ b/web-ui/app/locales/en_US/translation.json @@ -64,7 +64,8 @@ "error": { "timeout": "A timeout occurred", "general": "Problems talking to server", - "parse": "Got invalid response from server" + "parse": "Got invalid response from server", + "auth": "Invalid email or password" }, "tags": { "inbox": "Inbox", @@ -84,5 +85,10 @@ "button": "Add Account" }, "back-to-inbox": "Back to my inbox", - "footer-text": "Product in development. Feedback and issues to" + "footer-text": "Product in development. Feedback and issues to", + "login": { + "email": "Your email", + "password": "Your password", + "submit": "Login" + } } diff --git a/web-ui/app/locales/pt_BR/translation.json b/web-ui/app/locales/pt_BR/translation.json index 9a4393fa..b38b7535 100644 --- a/web-ui/app/locales/pt_BR/translation.json +++ b/web-ui/app/locales/pt_BR/translation.json @@ -64,7 +64,8 @@ "error": { "timeout": "A operação excedeu o limite de tempo", "general": "Problemas ao se comunicar com o servidor", - "parse": "Obteve uma resposta inválida do servidor" + "parse": "Obteve uma resposta inválida do servidor", + "auth": "E-mail ou senha inválidos" }, "tags": { "inbox": "Caixa de Entrada", @@ -84,5 +85,10 @@ "button": "Adicionar e-mail" }, "back-to-inbox": "Voltar", - "footer-text": "Produto em desenvolvimento. Reporte problemas através do" + "footer-text": "Produto em desenvolvimento. Reporte problemas através do", + "login": { + "email": "Seu e-mail", + "password": "Sua senha", + "submit": "Entrar" + } } diff --git a/web-ui/src/backup_account/backup_account.html b/web-ui/src/backup_account/backup_account.html index 55881444..084824f2 100644 --- a/web-ui/src/backup_account/backup_account.html +++ b/web-ui/src/backup_account/backup_account.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <head> - <link rel="icon" type="image/png" href="public/images/Favicon.png" /> + <link rel="icon" type="image/png" href="public/images/favicon.png" /> <meta charset="utf-8"/> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0"/> diff --git a/web-ui/src/common/input_field/input_field.js b/web-ui/src/common/input_field/input_field.js index 1378ba74..d4876d9f 100644 --- a/web-ui/src/common/input_field/input_field.js +++ b/web-ui/src/common/input_field/input_field.js @@ -19,16 +19,24 @@ import React from 'react'; import './input-field.scss'; -const InputField = ({ label, name }) => ( +const InputField = ({ label, name, type = 'text' }) => ( <div className='input-field-group'> - <input type='text' name={name} className='input-field' required /> + <input + type={type} name={name} className='input-field' + autoFocus='' required + /> <label className='input-field-label' htmlFor={name}>{label}</label> </div> ); InputField.propTypes = { label: React.PropTypes.string.isRequired, - name: React.PropTypes.string.isRequired + name: React.PropTypes.string.isRequired, + type: React.PropTypes.string +}; + +InputField.defaultProps = { + type: 'text' }; export default InputField; diff --git a/web-ui/src/login/app.js b/web-ui/src/login/app.js index e6ac3192..07099c60 100644 --- a/web-ui/src/login/app.js +++ b/web-ui/src/login/app.js @@ -17,19 +17,31 @@ import React from 'react'; import { translate } from 'react-i18next'; +import InputField from 'src/common/input_field/input_field'; +import SubmitButton from 'src/common/submit_button/submit_button'; -const App = () => ( - <form className='standard' id='login_form' action='/login' method='post'> - <input - type='text' name='username' id='email' className='text-field' - placeholder='username' autoFocus='' - /> - <input - type='password' name='password' id='password' className='text-field' - placeholder='password' autoComplete='off' - /> - <input type='submit' name='login' value='Login' className='button' /> - </form> +import './app.scss'; + +const errorMessage = (t, authError) => { + if (authError) return <p className='error'>{t('error.auth')}</p>; + return <div />; +}; + +export const App = ({ t, authError }) => ( + <div className='login'> + <img className='logo' src='/public/images/logo-orange.svg' alt='Pixelated logo' /> + {errorMessage(t, authError)} + <form className='standard' id='login_form' action='/login' method='post'> + <InputField name='username' label={t('login.email')} /> + <InputField type='password' name='password' label={t('login.password')} /> + <SubmitButton buttonText={t('login.submit')} /> + </form> + </div> ); +App.propTypes = { + t: React.PropTypes.func.isRequired, + authError: React.PropTypes.bool +}; + export default translate('', { wait: true })(App); diff --git a/web-ui/src/login/app.scss b/web-ui/src/login/app.scss new file mode 100644 index 00000000..f6e6bc11 --- /dev/null +++ b/web-ui/src/login/app.scss @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see <http://www.gnu.org/licenses/>. + */ + +@import "~scss/base/colors"; + +.error { + color: $error; + margin: 10px 0 0 0; +} + +.login { + display: block; + width: 90%; + margin: auto; + max-width: 400px; + padding: 2em 0; + margin-top: 3%; + margin-bottom: 3%; + background-color: $white; + display: flex; + flex-direction: column; + align-items: center; +} + +#login_form { + padding: 20px 0; + width: 70%; + + .input-field-group { + width: 100%; + } + + .submit-button { + width: 100%; + } +} + +.logo { + width: 70%; +} + +@media only screen and (min-width : 500px) { + #login_form .input-field-group { + margin-top: 1em; + } + + .login { + width: 60%; + } +} + +@media only screen and (min-width : 960px) { + .login { + width: 40%; + } +} diff --git a/web-ui/src/login/login.css b/web-ui/src/login/login.css index 51ab2046..d1206a39 100644 --- a/web-ui/src/login/login.css +++ b/web-ui/src/login/login.css @@ -16,130 +16,47 @@ */ body { - font-family: "Open Sans", "Microsoft YaHei", "Hiragino Sans GB", "Hiragino Sans GB W3", "微软雅黑", "Helvetica Neue", Arial, sans-serif; - background-color: #EAEAEA; - height: 100vh; - color: #3E3A37; + font-family: "Open Sans", "Microsoft YaHei", "Hiragino Sans GB", "Hiragino Sans GB W3", "微软雅黑", "Helvetica Neue", Arial, sans-serif; + background-color: #EAEAEA; + height: 100vh; + color: #3E3A37; - background-image: url("/public/images/hive-bg.png"); - background-repeat: repeat; -} - -.content { - height: 100vh; - width: 100%; -} - -.error { - color: #D72A25; - margin: 10px 0 0 0; -} - -.message-panel { - width: 100%; - margin: 10px auto; - z-index: 10000; - text-align: center; - } - -.message-panel span{ - background: #F7E8AF; - padding: 5px 60px; - border: 1px solid #f2db81; - color: #987b0f; - box-shadow: 1px 1px 3px #69560b; -} - -.message-panel.message-panel-small span{ - padding: 5px 0px; - display: inline-block; - width: 100%; -} - -.login { - display: block; - width: 240px; - margin: auto; - padding: 45px 40px 35px 40px; - background-color: #FFF; - margin-top: 2%; - margin-bottom: 2%; -} - -form#login_form { - padding: 10px 0; + background-image: url("/public/images/hive-bg.png"); + background-repeat: repeat; } .disclaimer { - display: block; - margin-top: 10%; - width: 50%; - margin: auto; - background-color: #2BA6CB; - color: #FFFFFF; - font-weight: 300; - font-size: 0.8rem; - padding: 1em; - margin-bottom: 20px; + display: block; + width: 90%; + margin: auto; + max-width: 400px; + background-color: #2BA6CB; + color: #FFFFFF; + font-weight: 300; + font-size: 0.8rem; + margin-bottom: 20px; } -.disclaimer li { - margin-top: 1em; -} - -.logo { - width: 100%; - height: auto; -} - -input { - display: block; - margin: 10px 0; - padding-left: 5px; -} - -input.text-field { - width: 97%; +.disclaimer-content { + padding: 1em; } -button, .button, input[type=button] { - cursor: pointer; - margin: 0 0 1.25rem; - border: none; - position: relative; - text-decoration: none; - text-align: center; - -webkit-appearance: none; - display: inline-block; - padding: 0.4rem 1.1rem; - font-size: 0.9rem; - background-color: #2ba6cb; - border-color: #2285a2; - color: white; - -webkit-border-radius: 2px; - -moz-border-radius: 2px; - -ms-border-radius: 2px; - -o-border-radius: 2px; - border-radius: 2px; -} - -button:hover, button:focus, .button:hover, .button:focus, input[type=button]:hover, input[type=button]:focus { - background-color: #2285a2; - outline: none; - color: white; +.disclaimer li { + margin-top: 1em; } -ul.accounts { - margin-bottom: 5%; -} +@media only screen and (min-width : 500px) { + body { + font-size: 1.2em; + } -ul.accounts li { - display: inline-block; - list-style: none; - margin-right: 35px; - margin-top: 0px; + .disclaimer { + width: 60%; + } } -ul.accounts li span { - font-weight: bold; +@media only screen and (min-width : 960px) { + .disclaimer { + width: 40%; + } } diff --git a/web-ui/src/login/login.html b/web-ui/src/login/login.html index b8c45180..3cebf6f4 100644 --- a/web-ui/src/login/login.html +++ b/web-ui/src/login/login.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html xmlns:t="http://twistedmatrix.com/ns/twisted.web.template/0.1"> <head> - <link rel="icon" type="image/png" href="/public/images/Favicon.png" /> + <link rel="icon" type="image/png" href="/public/images/favicon.png" /> <meta charset="utf-8"/> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0"/> @@ -12,13 +12,11 @@ </head> <body> <div class="content"> - <div class="login"> - <img class="logo" src="/public/images/logo-orange.svg" alt="Pixelated logo"/> - <p t:render="error_msg" class="error"></p> - <div id="root"/> - </div> + <div id="root"/> <div class="disclaimer"> - <div t:render="disclaimer"></div> + <div class="disclaimer-content"> + <div t:render="disclaimer"></div> + </div> </div> </div> <script type="text/javascript" src="/public/login.js"></script> diff --git a/web-ui/src/login/login.js b/web-ui/src/login/login.js index ddbe1943..74e5a14e 100644 --- a/web-ui/src/login/login.js +++ b/web-ui/src/login/login.js @@ -20,14 +20,15 @@ import { render } from 'react-dom'; import a11y from 'react-a11y'; import { I18nextProvider } from 'react-i18next'; -import App from './app'; +import AppWrapper from './app'; import i18n from '../i18n'; +import { hasQueryParameter } from '../util'; if (process.env.NODE_ENV === 'development') a11y(React); render( <I18nextProvider i18n={i18n}> - <App /> + <AppWrapper authError={hasQueryParameter('auth-error')} /> </I18nextProvider>, document.getElementById('root') ); diff --git a/web-ui/src/login/opensans.css b/web-ui/src/login/opensans.css index a42f346c..8795bdf7 100644 --- a/web-ui/src/login/opensans.css +++ b/web-ui/src/login/opensans.css @@ -2,68 +2,68 @@ font-family: 'Open Sans'; font-style: normal; font-weight: 300; - src: local("Open Sans Light"), local("OpenSans-Light"), url("/fonts/OpenSans-Light.woff") format("woff"); + src: local("Open Sans Light"), local("OpenSans-Light"), url("/assets/fonts/OpenSans-Light.woff") format("woff"); } @font-face { font-family: 'Open Sans'; font-style: normal; font-weight: 400; - src: local("Open Sans"), local("OpenSans"), url("/fonts/OpenSans.woff") format("woff"); + src: local("Open Sans"), local("OpenSans"), url("/assets/fonts/OpenSans.woff") format("woff"); } @font-face { font-family: 'Open Sans'; font-style: normal; font-weight: 600; - src: local("Open Sans Semibold"), local("OpenSans-Semibold"), url("/fonts/OpenSans-Semibold.woff") format("woff"); + src: local("Open Sans Semibold"), local("OpenSans-Semibold"), url("/assets/fonts/OpenSans-Semibold.woff") format("woff"); } @font-face { font-family: 'Open Sans'; font-style: normal; font-weight: 700; - src: local("Open Sans Bold"), local("OpenSans-Bold"), url("/fonts/OpenSans-Bold.woff") format("woff"); + src: local("Open Sans Bold"), local("OpenSans-Bold"), url("/assets/fonts/OpenSans-Bold.woff") format("woff"); } @font-face { font-family: 'Open Sans'; font-style: normal; font-weight: 800; - src: local("Open Sans Extrabold"), local("OpenSans-Extrabold"), url("/fonts/OpenSans-Extrabold.woff") format("woff"); + src: local("Open Sans Extrabold"), local("OpenSans-Extrabold"), url("/assets/fonts/OpenSans-Extrabold.woff") format("woff"); } @font-face { font-family: 'Open Sans'; font-style: italic; font-weight: 300; - src: local("Open Sans Light Italic"), local("OpenSansLight-Italic"), url("/fonts/OpenSansLight-Italic.woff") format("woff"); + src: local("Open Sans Light Italic"), local("OpenSansLight-Italic"), url("/assets/fonts/OpenSansLight-Italic.woff") format("woff"); } @font-face { font-family: 'Open Sans'; font-style: italic; font-weight: 400; - src: local("Open Sans Italic"), local("OpenSans-Italic"), url("/fonts/OpenSans-Italic.woff") format("woff"); + src: local("Open Sans Italic"), local("OpenSans-Italic"), url("/assets/fonts/OpenSans-Italic.woff") format("woff"); } @font-face { font-family: 'Open Sans'; font-style: italic; font-weight: 600; - src: local("Open Sans Semibold Italic"), local("OpenSans-SemiboldItalic"), url("/fonts/OpenSans-SemiboldItalic.woff ") format("woff"); + src: local("Open Sans Semibold Italic"), local("OpenSans-SemiboldItalic"), url("/assets/fonts/OpenSans-SemiboldItalic.woff ") format("woff"); } @font-face { font-family: 'Open Sans'; font-style: italic; font-weight: 700; - src: local("Open Sans Bold Italic"), local("OpenSans-BoldItalic"), url("/fonts/OpenSans-BoldItalic.woff") format("woff"); + src: local("Open Sans Bold Italic"), local("OpenSans-BoldItalic"), url("/assets/fonts/OpenSans-BoldItalic.woff") format("woff"); } @font-face { font-family: 'Open Sans'; font-style: italic; font-weight: 800; - src: local("Open Sans Extrabold Italic"), local("OpenSans-ExtraboldItalic"), url("/fonts/OpenSans-ExtraboldItalic.woff") format("woff"); + src: local("Open Sans Extrabold Italic"), local("OpenSans-ExtraboldItalic"), url("/assets/fonts/OpenSans-ExtraboldItalic.woff") format("woff"); } diff --git a/web-ui/src/util.js b/web-ui/src/util.js new file mode 100644 index 00000000..1b244458 --- /dev/null +++ b/web-ui/src/util.js @@ -0,0 +1,7 @@ +export const hasQueryParameter = param => ( + decodeURIComponent(window.location.search.substring(1)).includes(param) +); + +export default { + hasQueryParameter +}; diff --git a/web-ui/test/unit/login/app.spec.js b/web-ui/test/unit/login/app.spec.js new file mode 100644 index 00000000..347e2b19 --- /dev/null +++ b/web-ui/test/unit/login/app.spec.js @@ -0,0 +1,26 @@ +import { shallow } from 'enzyme'; +import expect from 'expect'; +import React from 'react'; +import { App } from 'src/login/app'; + +describe('App', () => { + let app; + const mockTranslations = key => key; + + beforeEach(() => { + app = shallow(<App t={mockTranslations} />); + }); + + it('renders login form', () => { + expect(app.find('form').props().action).toEqual('/login'); + }); + + it('renders auth error message', () => { + app = shallow(<App t={mockTranslations} authError />); + expect(app.find('.error').length).toEqual(1); + }); + + it('does not render auth error message', () => { + expect(app.find('.error').length).toEqual(0); + }); +}); diff --git a/web-ui/test/unit/util.spec.js b/web-ui/test/unit/util.spec.js new file mode 100644 index 00000000..84decf6f --- /dev/null +++ b/web-ui/test/unit/util.spec.js @@ -0,0 +1,20 @@ +import expect from 'expect'; +import Util from 'src/util'; + +describe('Utils', () => { + describe('.hasQueryParameter', () => { + global.window = { + location: { + search: '?auth&lng=pt-BR' + } + }; + + it('checks if param included in query parameters', () => { + expect(Util.hasQueryParameter('auth')).toBe(true); + }); + + it('checks if param not included in query parameters', () => { + expect(Util.hasQueryParameter('error')).toBe(false); + }); + }); +}); |