diff options
author | Stas Vilchik <stas-vilchik@users.noreply.github.com> | 2017-03-28 15:25:50 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-03-28 15:25:50 +0200 |
commit | c9846f5d48118eca3273de2cf065bd007f5dff45 (patch) | |
tree | 70a9009eabfd4a01ecdd97f44441871bb3e65c6f /server/sonar-web/src/main/js/apps | |
parent | aac1e32b731df7358e2ecda8968a5db802c1be46 (diff) | |
download | sonarqube-c9846f5d48118eca3273de2cf065bd007f5dff45.tar.gz sonarqube-c9846f5d48118eca3273de2cf065bd007f5dff45.zip |
MMF-721 apply feedback (#1858)
* add real links to the bubble chart
* do not display org names if feature is not enabled
* display bubbles with zero measure
* display color legend
* add charts descriptions
Diffstat (limited to 'server/sonar-web/src/main/js/apps')
6 files changed, 70 insertions, 49 deletions
diff --git a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js index 8c0a54dca6c..ec5299b31a8 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js +++ b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js @@ -24,7 +24,6 @@ import ProjectsListFooterContainer from './ProjectsListFooterContainer'; import PageSidebar from './PageSidebar'; import VisualizationsContainer from '../visualizations/VisualizationsContainer'; import { parseUrlQuery } from '../store/utils'; -import { getProjectUrl } from '../../../helpers/urls'; import '../styles.css'; export default class AllProjects extends React.Component { @@ -86,10 +85,6 @@ export default class AllProjects extends React.Component { }); }; - handleProjectOpen = projectKey => { - this.props.router.push(getProjectUrl(projectKey)); - }; - render() { const { query } = this.state; const isFiltered = Object.keys(query).some(key => query[key] != null); @@ -126,7 +121,6 @@ export default class AllProjects extends React.Component { />} {view === 'visualizations' && <VisualizationsContainer - onProjectOpen={this.handleProjectOpen} onVisualizationChange={this.handleVisualizationChange} sort={query.sort} visualization={visualization} diff --git a/server/sonar-web/src/main/js/apps/projects/styles.css b/server/sonar-web/src/main/js/apps/projects/styles.css index 87101df7544..1f9a2fc8477 100644 --- a/server/sonar-web/src/main/js/apps/projects/styles.css +++ b/server/sonar-web/src/main/js/apps/projects/styles.css @@ -198,12 +198,17 @@ } .projects-visualizations-footer { + margin-top: 8px; padding: 15px 0; - color: #777; + border-top: 1px solid #e6e6e6; font-size: 12px; text-align: center; } +.projects-visualizations-footer .note { + font-style: italic; +} + .measure-details-bubble-chart-axis { position: absolute; color: #777; diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/QualityModel.js b/server/sonar-web/src/main/js/apps/projects/visualizations/QualityModel.js index a9c9392940b..0f6e7ac43d1 100644 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/QualityModel.js +++ b/server/sonar-web/src/main/js/apps/projects/visualizations/QualityModel.js @@ -23,6 +23,7 @@ import BubbleChart from '../../../components/charts/BubbleChart'; import { formatMeasure } from '../../../helpers/measures'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { RATING_COLORS } from '../../../helpers/constants'; +import { getProjectUrl } from '../../../helpers/urls'; type Project = { key: string, @@ -43,17 +44,25 @@ const COLOR_METRIC_TYPE = 'RATING'; export default class QualityModel extends React.PureComponent { props: { - onProjectOpen: (?string) => void, + displayOrganizations: boolean, projects: Array<Project> }; - getMetricTooltip(metric: { key: string, type: string }, value: number) { + getMetricTooltip(metric: { key: string, type: string }, value: ?number) { const name = translate('metric', metric.key, 'name'); - return `<div>${name}: ${formatMeasure(value, metric.type)}</div>`; + const formattedValue = value != null ? formatMeasure(value, metric.type) : '–'; + return `<div>${name}: ${formattedValue}</div>`; } - getTooltip(project: Project, x: number, y: number, size: number, color1: number, color2: number) { - const fullProjectName = project.organization + getTooltip( + project: Project, + x: number, + y: ?number, + size: number, + color1: number, + color2: number + ) { + const fullProjectName = this.props.displayOrganizations && project.organization ? `<div class="little-spacer-bottom">${project.organization.name} / <strong>${project.name}</strong></div>` : `<div class="little-spacer-bottom"><strong>${project.name}</strong></div>`; const inner = [ @@ -64,7 +73,6 @@ export default class QualityModel extends React.PureComponent { this.getMetricTooltip({ key: X_METRIC, type: X_METRIC_TYPE }, x), this.getMetricTooltip({ key: SIZE_METRIC, type: SIZE_METRIC_TYPE }, size) ].join(''); - return `<div class="text-left">${inner}</div>`; } @@ -73,31 +81,28 @@ export default class QualityModel extends React.PureComponent { .filter( ({ measures }) => measures[X_METRIC] != null && - measures[Y_METRIC] != null && measures[SIZE_METRIC] != null && measures[COLOR_METRIC_1] != null && measures[COLOR_METRIC_2] != null ) .map(project => { const x = Number(project.measures[X_METRIC]); - const y = Number(project.measures[Y_METRIC]); + const y = project.measures[Y_METRIC] != null ? Number(project.measures[Y_METRIC]) : null; const size = Number(project.measures[SIZE_METRIC]); const color1 = Number(project.measures[COLOR_METRIC_1]); const color2 = Number(project.measures[COLOR_METRIC_2]); return { x, - y, + y: y || 0, size, color: RATING_COLORS[Math.max(color1, color2) - 1], key: project.key, tooltip: this.getTooltip(project, x, y, size, color1, color2), - link: project.key + link: getProjectUrl(project.key) }; }); - const formatXTick = tick => formatMeasure(tick, X_METRIC_TYPE); const formatYTick = tick => formatMeasure(tick, Y_METRIC_TYPE); - return ( <div> <BubbleChart @@ -106,7 +111,6 @@ export default class QualityModel extends React.PureComponent { height={600} items={items} padding={[40, 20, 60, 100]} - onBubbleClick={this.props.onProjectOpen} yDomain={[100, 0]} /> <div className="measure-details-bubble-chart-axis x"> diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.js b/server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.js index a44fbf96d7a..76e704ae57a 100644 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.js +++ b/server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.js @@ -23,6 +23,7 @@ import BubbleChart from '../../../components/charts/BubbleChart'; import { formatMeasure } from '../../../helpers/measures'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { RATING_COLORS } from '../../../helpers/constants'; +import { getProjectUrl } from '../../../helpers/urls'; type Metric = { key: string, type: string }; @@ -35,7 +36,7 @@ type Project = { export default class SimpleBubbleChart extends React.PureComponent { props: { - onProjectOpen: (?string) => void, + displayOrganizations: boolean, projects: Array<Project>, sizeMetric: Metric, xMetric: Metric, @@ -44,13 +45,14 @@ export default class SimpleBubbleChart extends React.PureComponent { colorMetric?: string }; - getMetricTooltip(metric: Metric, value: number) { + getMetricTooltip(metric: Metric, value: ?number) { const name = translate('metric', metric.key, 'name'); - return `<div>${name}: ${formatMeasure(value, metric.type)}</div>`; + const formattedValue = value != null ? formatMeasure(value, metric.type) : '–'; + return `<div>${name}: ${formattedValue}</div>`; } - getTooltip(project: Project, x: number, y: number, size: number, color?: number) { - const fullProjectName = project.organization + getTooltip(project: Project, x: ?number, y: ?number, size: ?number, color?: number) { + const fullProjectName = this.props.displayOrganizations && project.organization ? `<div class="little-spacer-bottom">${project.organization.name} / <strong>${project.name}</strong></div>` : `<div class="little-spacer-bottom"><strong>${project.name}</strong></div>`; @@ -73,23 +75,26 @@ export default class SimpleBubbleChart extends React.PureComponent { const { xMetric, yMetric, sizeMetric, colorMetric } = this.props; const items = this.props.projects - .filter(project => project.measures[xMetric.key] != null) - .filter(project => project.measures[yMetric.key] != null) - .filter(project => project.measures[sizeMetric.key] != null) .filter(project => colorMetric == null || project.measures[colorMetric] !== null) .map(project => { - const x = Number(project.measures[xMetric.key]); - const y = Number(project.measures[yMetric.key]); - const size = Number(project.measures[sizeMetric.key]); + const x = project.measures[xMetric.key] != null + ? Number(project.measures[xMetric.key]) + : null; + const y = project.measures[yMetric.key] != null + ? Number(project.measures[yMetric.key]) + : null; + const size = project.measures[sizeMetric.key] != null + ? Number(project.measures[sizeMetric.key]) + : null; const color = colorMetric ? Number(project.measures[colorMetric]) : undefined; return { - x, - y, - size, + x: x || 0, + y: y || 0, + size: size || 0, color: color ? RATING_COLORS[color - 1] : undefined, key: project.key, tooltip: this.getTooltip(project, x, y, size, color), - link: project.key + link: getProjectUrl(project.key) }; }); @@ -103,7 +108,6 @@ export default class SimpleBubbleChart extends React.PureComponent { formatYTick={formatYTick} height={600} items={items} - onBubbleClick={this.props.onProjectOpen} padding={[40, 20, 60, 100]} yDomain={this.props.yDomain} /> @@ -114,6 +118,12 @@ export default class SimpleBubbleChart extends React.PureComponent { {translate('metric', yMetric.key, 'name')} </div> <div className="measure-details-bubble-chart-axis size"> + {colorMetric != null && <span className="spacer-right"> + {translateWithParameters( + 'component_measures.legend.color_x', + translate('metric', colorMetric, 'name') + )} + </span>} {translateWithParameters( 'component_measures.legend.size_x', translate('metric', sizeMetric.key, 'name') diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/Visualizations.js b/server/sonar-web/src/main/js/apps/projects/visualizations/Visualizations.js index 5803e5bc13b..633aadb6ab9 100644 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/Visualizations.js +++ b/server/sonar-web/src/main/js/apps/projects/visualizations/Visualizations.js @@ -27,11 +27,11 @@ import CodeSmells from './CodeSmells'; import UncoveredLines from './UncoveredLines'; import DuplicatedBlocks from './DuplicatedBlocks'; import { localizeSorting } from '../utils'; -import { translateWithParameters } from '../../../helpers/l10n'; +import { translate, translateWithParameters } from '../../../helpers/l10n'; export default class Visualizations extends React.PureComponent { props: { - onProjectOpen: (string) => void, + displayOrganizations: boolean, onVisualizationChange: (string) => void, projects?: Array<*>, sort?: string, @@ -51,24 +51,27 @@ export default class Visualizations extends React.PureComponent { const Component = visualizationToComponent[this.props.visualization]; return Component - ? <Component onProjectOpen={this.props.onProjectOpen} projects={projects} /> + ? <Component displayOrganizations={this.props.displayOrganizations} projects={projects} /> : null; } renderFooter() { const { projects, total, sort } = this.props; - if (projects == null || total == null || projects.length >= total) { - return null; - } + const limitReached = projects != null && total != null && projects.length < total; return ( <footer className="projects-visualizations-footer"> - {translateWithParameters( - 'projects.limited_set_of_projects', - projects.length, - localizeSorting(sort) - )} + <p>{translate('projects.visualization', this.props.visualization, 'description')}</p> + {limitReached && + <p className="note spacer-top"> + {translateWithParameters( + 'projects.limited_set_of_projects', + // $FlowFixMe + projects.length, + localizeSorting(sort) + )} + </p>} </footer> ); } diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/VisualizationsContainer.js b/server/sonar-web/src/main/js/apps/projects/visualizations/VisualizationsContainer.js index 5ee27e1f90a..2de9e7bebb7 100644 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/VisualizationsContainer.js +++ b/server/sonar-web/src/main/js/apps/projects/visualizations/VisualizationsContainer.js @@ -24,7 +24,8 @@ import { getComponent, getComponentMeasures, getOrganizationByKey, - getProjectsAppState + getProjectsAppState, + areThereCustomOrganizations } from '../../../store/rootReducer'; const mapStateToProps = state => { @@ -38,7 +39,11 @@ const mapStateToProps = state => { }; }); const appState = getProjectsAppState(state); - return { projects, total: appState.total }; + return { + projects, + total: appState.total, + displayOrganizations: areThereCustomOrganizations(state) + }; }; export default connect(mapStateToProps)(Visualizations); |