aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/quality-profiles/components
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2016-06-27 16:08:44 +0200
committerGitHub <noreply@github.com>2016-06-27 16:08:44 +0200
commit4ecb6fc3ec08ee37a1bb5c19d8ca44e2b832b5a7 (patch)
tree763ce4b5a9d7b37982ab54fc64a0597167c2bc3d /server/sonar-web/src/main/js/apps/quality-profiles/components
parentc467e84eed1a60bbe8c0fe800e2cada2bb26bb4c (diff)
downloadsonarqube-4ecb6fc3ec08ee37a1bb5c19d8ca44e2b832b5a7.tar.gz
sonarqube-4ecb6fc3ec08ee37a1bb5c19d8ca44e2b832b5a7.zip
refactor quality profiles page (#1056)
Diffstat (limited to 'server/sonar-web/src/main/js/apps/quality-profiles/components')
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/App.js97
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js72
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileDate.js49
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileLink.js37
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileNotFound.js40
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileContainer-test.js89
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');
+ });
+});