aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/component-measures/components/App.tsx
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2018-08-20 17:28:52 +0200
committersonartech <sonartech@sonarsource.com>2018-09-19 10:51:41 +0200
commit0ba8c56c9a7cddf225720f224e66b267f9bbf528 (patch)
tree70bde7296671e7a6fe172b7c1160314eedec9ce8 /server/sonar-web/src/main/js/apps/component-measures/components/App.tsx
parent19107a98d5248a1556ac76b3d9263f1e69fbcefc (diff)
downloadsonarqube-0ba8c56c9a7cddf225720f224e66b267f9bbf528.tar.gz
sonarqube-0ba8c56c9a7cddf225720f224e66b267f9bbf528.zip
SONAR-11165 Migrate rest of component measures page to TS
Diffstat (limited to 'server/sonar-web/src/main/js/apps/component-measures/components/App.tsx')
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/App.tsx233
1 files changed, 233 insertions, 0 deletions
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/App.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/App.tsx
new file mode 100644
index 00000000000..ff78d3a724a
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/App.tsx
@@ -0,0 +1,233 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info 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 * as React from 'react';
+import * as key from 'keymaster';
+import { InjectedRouter } from 'react-router';
+import Helmet from 'react-helmet';
+import MeasureContentContainer from './MeasureContentContainer';
+import MeasureOverviewContainer from './MeasureOverviewContainer';
+import Sidebar from '../sidebar/Sidebar';
+import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
+import { isProjectOverview, hasBubbleChart, parseQuery, serializeQuery, Query } from '../utils';
+import { isSameBranchLike, getBranchLikeQuery } from '../../../helpers/branches';
+import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
+import {
+ getLocalizedMetricDomain,
+ translateWithParameters,
+ translate
+} from '../../../helpers/l10n';
+import { getDisplayMetrics } from '../../../helpers/measures';
+import { RawQuery } from '../../../helpers/query';
+import {
+ BranchLike,
+ ComponentMeasure,
+ MeasureEnhanced,
+ Metric,
+ CurrentUser,
+ Period
+} from '../../../app/types';
+import '../../../components/search-navigator.css';
+import '../style.css';
+
+interface Props {
+ branchLike?: BranchLike;
+ component: ComponentMeasure;
+ currentUser: CurrentUser;
+ location: { pathname: string; query: RawQuery };
+ fetchMeasures: (
+ component: string,
+ metricsKey: string[],
+ branchLike?: BranchLike
+ ) => Promise<{ component: ComponentMeasure; measures: MeasureEnhanced[]; leakPeriod?: Period }>;
+ fetchMetrics: () => void;
+ metrics: { [metric: string]: Metric };
+ metricsKey: string[];
+ router: InjectedRouter;
+}
+
+interface State {
+ loading: boolean;
+ measures: MeasureEnhanced[];
+ leakPeriod?: Period;
+}
+
+export default class App extends React.PureComponent<Props, State> {
+ mounted = false;
+
+ constructor(props: Props) {
+ super(props);
+ this.state = { loading: true, measures: [] };
+ }
+
+ componentDidMount() {
+ this.mounted = true;
+
+ document.body.classList.add('white-page');
+ document.documentElement.classList.add('white-page');
+ const footer = document.getElementById('footer');
+ if (footer) {
+ footer.classList.add('page-footer-with-sidebar');
+ }
+
+ key.setScope('measures-files');
+ this.props.fetchMetrics();
+ this.fetchMeasures(this.props);
+ }
+
+ componentWillReceiveProps(nextProps: Props) {
+ if (
+ !isSameBranchLike(nextProps.branchLike, this.props.branchLike) ||
+ nextProps.component.key !== this.props.component.key ||
+ nextProps.metrics !== this.props.metrics
+ ) {
+ this.fetchMeasures(nextProps);
+ }
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+
+ document.body.classList.remove('white-page');
+ document.documentElement.classList.remove('white-page');
+
+ const footer = document.getElementById('footer');
+ if (footer) {
+ footer.classList.remove('page-footer-with-sidebar');
+ }
+
+ key.deleteScope('measures-files');
+ }
+
+ fetchMeasures = ({ branchLike, component, fetchMeasures, metrics }: Props) => {
+ this.setState({ loading: true });
+ const filteredKeys = getDisplayMetrics(Object.values(metrics)).map(metric => metric.key);
+
+ fetchMeasures(component.key, filteredKeys, branchLike).then(
+ ({ measures, leakPeriod }) => {
+ if (this.mounted) {
+ this.setState({
+ loading: false,
+ leakPeriod,
+ measures: measures.filter(
+ measure => measure.value !== undefined || measure.leak !== undefined
+ )
+ });
+ }
+ },
+ () => {
+ if (this.mounted) {
+ this.setState({ loading: false });
+ }
+ }
+ );
+ };
+
+ updateQuery = (newQuery: Partial<Query>) => {
+ const query = serializeQuery({
+ ...parseQuery(this.props.location.query),
+ ...newQuery
+ });
+ this.props.router.push({
+ pathname: this.props.location.pathname,
+ query: {
+ ...query,
+ ...getBranchLikeQuery(this.props.branchLike),
+ id: this.props.component.key
+ }
+ });
+ };
+
+ getHelmetTitle = (metric?: Metric) => {
+ if (metric && hasBubbleChart(metric.key)) {
+ return isProjectOverview(metric.key)
+ ? translate('component_measures.overview.project_overview.facet')
+ : translateWithParameters(
+ 'component_measures.domain_x_overview',
+ getLocalizedMetricDomain(metric.key)
+ );
+ }
+ return metric ? metric.name : translate('layout.measures');
+ };
+
+ render() {
+ const isLoading = this.state.loading || this.props.metricsKey.length <= 0;
+ if (isLoading) {
+ return <i className="spinner spinner-margin" />;
+ }
+ const { branchLike, component, fetchMeasures, metrics } = this.props;
+ const { leakPeriod } = this.state;
+ const query = parseQuery(this.props.location.query);
+ const metric = metrics[query.metric];
+ return (
+ <div className="layout-page" id="component-measures">
+ <Suggestions suggestions="component_measures" />
+ <Helmet title={this.getHelmetTitle(metric)} />
+
+ <ScreenPositionHelper className="layout-page-side-outer">
+ {({ top }) => (
+ <div className="layout-page-side" style={{ top }}>
+ <div className="layout-page-side-inner">
+ <div className="layout-page-filters">
+ <Sidebar
+ measures={this.state.measures}
+ selectedMetric={query.metric}
+ updateQuery={this.updateQuery}
+ />
+ </div>
+ </div>
+ </div>
+ )}
+ </ScreenPositionHelper>
+
+ {metric && (
+ <MeasureContentContainer
+ branchLike={branchLike}
+ className="layout-page-main"
+ currentUser={this.props.currentUser}
+ fetchMeasures={fetchMeasures}
+ leakPeriod={leakPeriod}
+ metric={metric}
+ metrics={metrics}
+ rootComponent={component}
+ router={this.props.router}
+ selected={query.selected}
+ updateQuery={this.updateQuery}
+ view={query.view}
+ />
+ )}
+ {!metric &&
+ hasBubbleChart(query.metric) && (
+ <MeasureOverviewContainer
+ branchLike={branchLike}
+ className="layout-page-main"
+ currentUser={this.props.currentUser}
+ domain={query.metric}
+ leakPeriod={leakPeriod}
+ metrics={metrics}
+ rootComponent={component}
+ router={this.props.router}
+ selected={query.selected}
+ updateQuery={this.updateQuery}
+ />
+ )}
+ </div>
+ );
+ }
+}