diff options
author | Stas Vilchik <vilchiks@gmail.com> | 2016-06-27 16:08:44 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-06-27 16:08:44 +0200 |
commit | 4ecb6fc3ec08ee37a1bb5c19d8ca44e2b832b5a7 (patch) | |
tree | 763ce4b5a9d7b37982ab54fc64a0597167c2bc3d /server/sonar-web/src/main/js/apps/quality-profiles/components | |
parent | c467e84eed1a60bbe8c0fe800e2cada2bb26bb4c (diff) | |
download | sonarqube-4ecb6fc3ec08ee37a1bb5c19d8ca44e2b832b5a7.tar.gz sonarqube-4ecb6fc3ec08ee37a1bb5c19d8ca44e2b832b5a7.zip |
refactor quality profiles page (#1056)
Diffstat (limited to 'server/sonar-web/src/main/js/apps/quality-profiles/components')
6 files changed, 384 insertions, 0 deletions
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/App.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/App.js new file mode 100644 index 00000000000..30d59329a05 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/App.js @@ -0,0 +1,97 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import { getLanguages } from '../../../api/languages'; +import { + getQualityProfiles, + getExporters +} from '../../../api/quality-profiles'; +import { getCurrentUser } from '../../../api/users'; +import '../styles.css'; +import { sortProfiles } from '../utils'; + +export default class App extends React.Component { + state = { loading: true }; + + componentWillMount () { + this.updateProfiles = this.updateProfiles.bind(this); + } + + componentDidMount () { + this.mounted = true; + this.loadData(); + } + + componentWillUnmount () { + this.mounted = false; + } + + loadData () { + this.setState({ loading: true }); + Promise.all([ + getCurrentUser(), + getLanguages(), + getExporters(), + getQualityProfiles() + ]).then(responses => { + if (this.mounted) { + const [user, languages, exporters, profiles] = responses; + const canAdmin = user.permissions.global.includes('profileadmin'); + this.setState({ + languages, + exporters, + canAdmin, + profiles: sortProfiles(profiles), + loading: false + }); + } + }); + } + + updateProfiles () { + return getQualityProfiles().then(profiles => { + if (this.mounted) { + this.setState({ profiles: sortProfiles(profiles) }); + } + }); + } + + renderChild () { + if (this.state.loading) { + return <i className="spinner"/>; + } + + return React.cloneElement(this.props.children, { + profiles: this.state.profiles, + languages: this.state.languages, + exporters: this.state.exporters, + canAdmin: this.state.canAdmin, + updateProfiles: this.updateProfiles + }); + } + + render () { + return ( + <div className="page page-limited-small"> + {this.renderChild()} + </div> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js new file mode 100644 index 00000000000..d1fe79cfcbe --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js @@ -0,0 +1,72 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import Helmet from 'react-helmet'; +import ProfileNotFound from './ProfileNotFound'; +import ProfileHeader from '../details/ProfileHeader'; +import { translate } from '../../../helpers/l10n'; +import { ProfilesListType } from '../propTypes'; + +export default class ProfileContainer extends React.Component { + static propTypes = { + location: React.PropTypes.object, + profiles: ProfilesListType, + canAdmin: React.PropTypes.bool, + updateProfiles: React.PropTypes.func + }; + + componentWillMount () { + document.querySelector('html').classList.add('dashboard-page'); + } + + componentWillUnmount () { + document.querySelector('html').classList.remove('dashboard-page'); + } + + render () { + const { profiles, location, ...other } = this.props; + const { key } = location.query; + const profile = profiles.find(profile => profile.key === key); + + if (!profile) { + return <ProfileNotFound/>; + } + + const child = React.cloneElement( + this.props.children, + { profile, profiles, ...other }); + + const title = translate('quality_profiles.page') + ' - ' + profile.name; + + return ( + <div> + <Helmet + title={title} + titleTemplate="SonarQube - %s"/> + + <ProfileHeader + profile={profile} + canAdmin={this.props.canAdmin} + updateProfiles={this.props.updateProfiles}/> + {child} + </div> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileDate.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileDate.js new file mode 100644 index 00000000000..974f2d80f96 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileDate.js @@ -0,0 +1,49 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import moment from 'moment'; +import shallowCompare from 'react-addons-shallow-compare'; +import { translate } from '../../../helpers/l10n'; + +export default class ProfileDate extends React.Component { + static propTypes = { + date: React.PropTypes.string + }; + + shouldComponentUpdate (nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } + + render () { + const { date } = this.props; + + if (!date) { + return ( + <span className="note">{translate('never')}</span> + ); + } + + return ( + <span title={moment(date).format('LLL')} data-toggle="tooltip"> + {moment(date).fromNow()} + </span> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileLink.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileLink.js new file mode 100644 index 00000000000..eda61f67fe0 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileLink.js @@ -0,0 +1,37 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import { Link } from 'react-router'; + +export default class ProfileLink extends React.Component { + static propTypes = { + profileKey: React.PropTypes.string.isRequired + }; + + render () { + const { profileKey, children, ...other } = this.props; + const query = { key: profileKey }; + return ( + <Link to={{ pathname: '/show', query }} {...other}> + {children} + </Link> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileNotFound.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileNotFound.js new file mode 100644 index 00000000000..9c37677df44 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileNotFound.js @@ -0,0 +1,40 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import { IndexLink } from 'react-router'; +import { translate } from '../../../helpers/l10n'; + +export default class ProfileNotFound extends React.Component { + render () { + return ( + <div className="quality-profile-not-found"> + <div className="note spacer-bottom"> + <IndexLink to="/" className="text-muted"> + {translate('quality_profiles.page')} + </IndexLink> + </div> + + <div> + {translate('quality_profiles.not_found')} + </div> + </div> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileContainer-test.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileContainer-test.js new file mode 100644 index 00000000000..87dacc78d87 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileContainer-test.js @@ -0,0 +1,89 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { expect } from 'chai'; +import { shallow } from 'enzyme'; +import sinon from 'sinon'; +import React from 'react'; +import Helmet from 'react-helmet'; +import ProfileContainer from '../ProfileContainer'; +import ProfileNotFound from '../ProfileNotFound'; +import ProfileHeader from '../../details/ProfileHeader'; +import { createFakeProfile } from '../../utils'; + +describe('Quality Profiles :: ProfileContainer', () => { + it('should render ProfileHeader', () => { + const targetProfile = createFakeProfile({ key: 'profile1' }); + const profiles = [ + targetProfile, + createFakeProfile({ key: 'profile2' }) + ]; + const updateProfiles = sinon.spy(); + const output = shallow( + <ProfileContainer + location={{ query: { key: 'profile1' } }} + profiles={profiles} + canAdmin={false} + updateProfiles={updateProfiles}> + <div/> + </ProfileContainer> + ); + const header = output.find(ProfileHeader); + expect(header).to.have.length(1); + expect(header.prop('profile')).to.equal(targetProfile); + expect(header.prop('canAdmin')).to.equal(false); + expect(header.prop('updateProfiles')).to.equal(updateProfiles); + }); + + it('should render ProfileNotFound', () => { + const profiles = [ + createFakeProfile({ key: 'profile1' }), + createFakeProfile({ key: 'profile2' }) + ]; + const output = shallow( + <ProfileContainer + location={{ query: { key: 'random' } }} + profiles={profiles} + canAdmin={false} + updateProfiles={() => true}> + <div/> + </ProfileContainer> + ); + expect(output.is(ProfileNotFound)).to.equal(true); + }); + + it('should render Helmet', () => { + const profiles = [ + createFakeProfile({ key: 'profile1', name: 'First Profile' }) + ]; + const updateProfiles = sinon.spy(); + const output = shallow( + <ProfileContainer + location={{ query: { key: 'profile1' } }} + profiles={profiles} + canAdmin={false} + updateProfiles={updateProfiles}> + <div/> + </ProfileContainer> + ); + const helmet = output.find(Helmet); + expect(helmet).to.have.length(1); + expect(helmet.prop('title')).to.include('First Profile'); + }); +}); |