diff options
Diffstat (limited to 'web-ui')
-rw-r--r-- | web-ui/src/common/flat_button/flat_button.js | 58 | ||||
-rw-r--r-- | web-ui/src/common/flat_button/flat_button.spec.js | 32 | ||||
-rw-r--r-- | web-ui/src/common/header/header.js | 15 | ||||
-rw-r--r-- | web-ui/src/common/header/header.scss | 5 | ||||
-rw-r--r-- | web-ui/src/common/header/header.spec.js | 12 | ||||
-rw-r--r-- | web-ui/src/common/logout/logout.js | 48 | ||||
-rw-r--r-- | web-ui/src/common/logout/logout.spec.js | 49 | ||||
-rw-r--r-- | web-ui/test/integration/backup_account.spec.js | 6 |
8 files changed, 205 insertions, 20 deletions
diff --git a/web-ui/src/common/flat_button/flat_button.js b/web-ui/src/common/flat_button/flat_button.js new file mode 100644 index 00000000..b8c6c2c7 --- /dev/null +++ b/web-ui/src/common/flat_button/flat_button.js @@ -0,0 +1,58 @@ +/* + * 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 React from 'react'; +import FlatButton from 'material-ui/FlatButton'; +import FontIcon from 'material-ui/FontIcon'; +import { grey500 } from 'material-ui/styles/colors'; + +const labelStyle = { + textTransform: 'none', + verticalAlign: 'middle' +}; + +const iconStyle = { + marginRight: 0 +}; + +const flatButtonStyle = { + minWidth: 0, + verticalAlign: 'top' +}; + +const SubmitFlatButton = ({ name, buttonText, fontIconClass }) => ( + <FlatButton + name={name} + type='submit' + hoverColor='transparent' + style={flatButtonStyle} + labelPosition='before' + label={buttonText} + labelStyle={labelStyle} + aria-label={buttonText} + title={buttonText} + icon={<FontIcon className={fontIconClass} color={grey500} style={iconStyle} />} + /> +); + +SubmitFlatButton.propTypes = { + name: React.PropTypes.string.isRequired, + buttonText: React.PropTypes.string.isRequired, + fontIconClass: React.PropTypes.string.isRequired +}; + +export default SubmitFlatButton; diff --git a/web-ui/src/common/flat_button/flat_button.spec.js b/web-ui/src/common/flat_button/flat_button.spec.js new file mode 100644 index 00000000..16f03acb --- /dev/null +++ b/web-ui/src/common/flat_button/flat_button.spec.js @@ -0,0 +1,32 @@ +import { shallow } from 'enzyme'; +import expect from 'expect'; +import React from 'react'; +import FlatButton from 'src/common/flat_button/flat_button'; + +describe('FlatButton', () => { + let flatButton; + + beforeEach(() => { + flatButton = shallow(<FlatButton name='logout' buttonText='Logout' fontIconClass='fa fa-sign-out' />); + }); + + it('renders a FlatButton of type submit with name logout', () => { + expect(flatButton.find('FlatButton').props().name).toEqual('logout'); + }); + + it('renders a FlatButton of type submit with text logout', () => { + expect(flatButton.find('FlatButton').props().label).toEqual('Logout'); + }); + + it('renders a FlatButton of type submit with title logout', () => { + expect(flatButton.find('FlatButton').props().title).toEqual('Logout'); + }); + + it('renders a FlatButton of type submit with aria-label logout', () => { + expect(flatButton.find('FlatButton').props()['aria-label']).toEqual('Logout'); + }); + + it('renders a FlatButton with given fontIcon class', () => { + expect(flatButton.find('FlatButton').props().icon.props.className).toEqual('fa fa-sign-out'); + }); +}); diff --git a/web-ui/src/common/header/header.js b/web-ui/src/common/header/header.js index 50c863b5..715d54c6 100644 --- a/web-ui/src/common/header/header.js +++ b/web-ui/src/common/header/header.js @@ -16,10 +16,10 @@ */ import React from 'react'; -import { translate } from 'react-i18next'; +import Logout from 'src/common/logout/logout'; import './header.scss'; -export const Header = ({ t }) => ( +export const Header = () => ( <header className='header-wrapper'> <div className='header-content'> <a href='/'> @@ -30,17 +30,10 @@ export const Header = ({ t }) => ( /> </a> <div className='header-icons'> - <a href='/'> - <span>{t('logout')}</span> - <i className='fa fa-sign-out' aria-hidden='true' /> - </a> + <Logout /> </div> </div> </header> ); -Header.propTypes = { - t: React.PropTypes.func.isRequired -}; - -export default translate('', { wait: true })(Header); +export default Header; diff --git a/web-ui/src/common/header/header.scss b/web-ui/src/common/header/header.scss index d56629bf..3e10ba39 100644 --- a/web-ui/src/common/header/header.scss +++ b/web-ui/src/common/header/header.scss @@ -28,7 +28,6 @@ .header-content { display: flex; - align-items: center; } .header-logo { @@ -41,7 +40,7 @@ position: absolute; right: 6%; - top: 13px; + line-height: 0; span { display: none; @@ -51,6 +50,7 @@ font-size: 1.3em; margin-left: 0.4em; color: $medium_light_grey; + overflow: visible; } } @@ -78,6 +78,7 @@ font-style: normal; font-size: 0.7em; padding-bottom: 0.1em; + line-height: 36px; } .fa { diff --git a/web-ui/src/common/header/header.spec.js b/web-ui/src/common/header/header.spec.js index 82e29e1c..81a952c7 100644 --- a/web-ui/src/common/header/header.spec.js +++ b/web-ui/src/common/header/header.spec.js @@ -2,16 +2,20 @@ import { shallow } from 'enzyme'; import expect from 'expect'; import React from 'react'; import { Header } from 'src/common/header/header'; +import Logout from 'src/common/logout/logout'; describe('Header', () => { let header; beforeEach(() => { - const mockTranslations = key => key; - header = shallow(<Header t={mockTranslations} />); + header = shallow(<Header />); }); - it('renders the header content', () => { - expect(header.find('header').text()).toContain('logout'); + it('renders the header containing the logout button', () => { + expect(header.find('header').find(Logout)).toExist(); + }); + + it('renders the header pixelated logo', () => { + expect(header.find('header').find('img').props().alt).toEqual('Pixelated'); }); }); diff --git a/web-ui/src/common/logout/logout.js b/web-ui/src/common/logout/logout.js new file mode 100644 index 00000000..259b448e --- /dev/null +++ b/web-ui/src/common/logout/logout.js @@ -0,0 +1,48 @@ +/* + * 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 React from 'react'; +import { translate } from 'react-i18next'; +import browser from 'helpers/browser'; + +import SubmitFlatButton from 'src/common/flat_button/flat_button'; + +export class Logout extends React.Component { + + constructor(props) { + super(props); + this.state = { csrf_token: browser.getCookie('XSRF-TOKEN') }; + } + + render() { + const t = this.props.t; + return ( + <div className='logout-container'> + <form id='logout-form' method='POST' action='logout'> + <input type='hidden' name='csrftoken' value={this.state.csrf_token} /> + <SubmitFlatButton name='logout' buttonText={t('logout')} fontIconClass='fa fa-sign-out' /> + </form> + </div> + ); + } +} + +Logout.propTypes = { + t: React.PropTypes.func.isRequired +}; + +export default translate('', { wait: true })(Logout); diff --git a/web-ui/src/common/logout/logout.spec.js b/web-ui/src/common/logout/logout.spec.js new file mode 100644 index 00000000..ba228e61 --- /dev/null +++ b/web-ui/src/common/logout/logout.spec.js @@ -0,0 +1,49 @@ +import { shallow } from 'enzyme'; +import expect from 'expect'; +import React from 'react'; +import { Logout } from 'src/common/logout/logout'; + +describe('Logout', () => { + let logout; + + beforeEach(() => { + const mockTranslations = key => key; + logout = shallow(<Logout t={mockTranslations} fontIconClass='fa fa-sign-out' />); + }); + + it('renders the logout container', () => { + expect(logout.find('div.logout-container')).toExist(); + }); + + describe('logout form', () => { + let logoutForm; + + beforeEach(() => { + logoutForm = logout.find('form#logout-form'); + }); + + it('renders logout form', () => { + expect(logoutForm).toExist(); + }); + + it('renders logout form with POST method', () => { + expect(logoutForm.props().method).toEqual('POST'); + }); + + it('renders logout form with action as logout', () => { + expect(logoutForm.props().action).toEqual('logout'); + }); + + it('renders csrf hidden input', () => { + expect(logoutForm.find('input[name="csrftoken"]')).toExist(); + }); + + it('renders SubmitFlatButton for logout', () => { + expect(logoutForm.find('SubmitFlatButton').props().buttonText).toEqual('logout'); + }); + + it('renders SubmitFlatButton for logout with fontIcon', () => { + expect(logoutForm.find('SubmitFlatButton').props().fontIconClass).toEqual('fa fa-sign-out'); + }); + }); +}); diff --git a/web-ui/test/integration/backup_account.spec.js b/web-ui/test/integration/backup_account.spec.js index b44c3b2c..b9667a41 100644 --- a/web-ui/test/integration/backup_account.spec.js +++ b/web-ui/test/integration/backup_account.spec.js @@ -16,7 +16,7 @@ describe('Backup account email validation', () => { context('with valid email', () => { beforeEach(() => { - backupAccountPage.find('input').simulate('change', {target: {value: 'test@test.com'}}); + backupAccountPage.find('input[name="email"]').simulate('change', {target: {value: 'test@test.com'}}); }); it('shows no validation error', () => { @@ -30,7 +30,7 @@ describe('Backup account email validation', () => { context('with invalid email', () => { beforeEach(() => { - backupAccountPage.find('input').simulate('change', {target: {value: 'test'}}); + backupAccountPage.find('input[name="email"]').simulate('change', {target: {value: 'test'}}); }); it('shows validation error', () => { @@ -44,7 +44,7 @@ describe('Backup account email validation', () => { context('with empty email', () => { beforeEach(() => { - backupAccountPage.find('input').simulate('change', {target: {value: ''}}); + backupAccountPage.find('input[name="email"]').simulate('change', {target: {value: ''}}); }); it('shows no validation error', () => { |