Browse Source

SONAR-21797 Add info message about chart gap for app/portfolio

tags/10.5.0.89998
stanislavh 1 month ago
parent
commit
8d83292a4f

+ 48
- 0
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx View File

* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import { FlagMessage } from 'design-system';
import { debounce, findLast, maxBy, minBy, sortBy } from 'lodash'; import { debounce, findLast, maxBy, minBy, sortBy } from 'lodash';
import * as React from 'react'; import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import GraphsHeader from '../../../components/activity-graph/GraphsHeader'; import GraphsHeader from '../../../components/activity-graph/GraphsHeader';
import GraphsHistory from '../../../components/activity-graph/GraphsHistory'; import GraphsHistory from '../../../components/activity-graph/GraphsHistory';
import GraphsZoom from '../../../components/activity-graph/GraphsZoom'; import GraphsZoom from '../../../components/activity-graph/GraphsZoom';
saveActivityGraph, saveActivityGraph,
splitSeriesInGraphs, splitSeriesInGraphs,
} from '../../../components/activity-graph/utils'; } from '../../../components/activity-graph/utils';
import DocumentationLink from '../../../components/common/DocumentationLink';
import { CCT_SOFTWARE_QUALITY_METRICS } from '../../../helpers/constants';
import { translate } from '../../../helpers/l10n';
import { import {
GraphType, GraphType,
MeasureHistory, MeasureHistory,
} }
}; };


renderQualitiesMetricInfoMessage = () => {
const { measuresHistory } = this.props;

const qualityMeasuresHistory = measuresHistory.find((history) =>
CCT_SOFTWARE_QUALITY_METRICS.includes(history.metric),
);

const indexOfFirstMeasureWithValue = qualityMeasuresHistory?.history.findIndex(
(item) => item.value,
);

const hasGaps =
indexOfFirstMeasureWithValue === -1
? false
: qualityMeasuresHistory?.history
.slice(indexOfFirstMeasureWithValue)
.some((item) => item.value === undefined);

if (hasGaps) {
return (
<FlagMessage variant="info">
<FormattedMessage
id="project_activity.graphs.data_table.data_gap"
tagName="div"
values={{
learn_more: (
<DocumentationLink
className="sw-whitespace-nowrap"
to="/user-guide/clean-code/code-analysis/"
>
{translate('learn_more')}
</DocumentationLink>
),
}}
/>
</FlagMessage>
);
}

return null;
};

render() { render() {
const { analyses, leakPeriodDate, loading, measuresHistory, metrics, query } = this.props; const { analyses, leakPeriodDate, loading, measuresHistory, metrics, query } = this.props;
const { graphEndDate, graphStartDate, series } = this.state; const { graphEndDate, graphStartDate, series } = this.state;
selectedMetrics={query.customMetrics} selectedMetrics={query.customMetrics}
onUpdateGraph={this.handleUpdateGraph} onUpdateGraph={this.handleUpdateGraph}
/> />
{this.renderQualitiesMetricInfoMessage()}
<GraphsHistory <GraphsHistory
analyses={analyses} analyses={analyses}
graph={query.graph} graph={query.graph}

+ 53
- 3
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityApp-it.tsx View File

MetricKey.sqale_rating, MetricKey.sqale_rating,
MetricKey.security_hotspots_reviewed, MetricKey.security_hotspots_reviewed,
MetricKey.security_review_rating, MetricKey.security_review_rating,
MetricKey.maintainability_issues,
].map((metric) => ].map((metric) =>
mockMeasureHistory({ mockMeasureHistory({
metric, metric,
history: projectActivityHandler
.getAnalysesList()
.map(({ date }) => mockHistoryItem({ value: '3', date: parseDate(date) })),
history: projectActivityHandler.getAnalysesList().map(({ date }) =>
mockHistoryItem({
value: '3',
date: parseDate(date),
}),
),
}), }),
), ),
); );
).not.toBeInTheDocument(); ).not.toBeInTheDocument();
}, },
); );

it('should render graph gap info message', async () => {
timeMachineHandler.setMeasureHistory([
mockMeasureHistory({
metric: MetricKey.maintainability_issues,
history: projectActivityHandler.getAnalysesList().map(({ date }, index) =>
mockHistoryItem({
// eslint-disable-next-line jest/no-conditional-in-test
value: index === 0 ? '3' : undefined,
date: parseDate(date),
}),
),
}),
]);
const { ui } = getPageObject();
renderProjectActivityAppContainer(
mockComponent({
breadcrumbs: [
{ key: 'breadcrumb', name: 'breadcrumb', qualifier: ComponentQualifier.Application },
],
}),
);

await ui.changeGraphType(GraphType.custom);
await ui.openMetricsDropdown();
await ui.toggleMetric(MetricKey.maintainability_issues);
expect(ui.gapInfoMessage.get()).toBeInTheDocument();
});

it('should not render graph gap info message if no gaps', async () => {
const { ui } = getPageObject();
renderProjectActivityAppContainer(
mockComponent({
breadcrumbs: [
{ key: 'breadcrumb', name: 'breadcrumb', qualifier: ComponentQualifier.Application },
],
}),
);

await ui.changeGraphType(GraphType.custom);
await ui.openMetricsDropdown();
await ui.toggleMetric(MetricKey.maintainability_issues);
expect(ui.gapInfoMessage.query()).not.toBeInTheDocument();
});
}); });


describe('CRUD', () => { describe('CRUD', () => {
// Graphs. // Graphs.
graphs: byLabelText('project_activity.graphs.explanation_x', { exact: false }), graphs: byLabelText('project_activity.graphs.explanation_x', { exact: false }),
noDataText: byText('project_activity.graphs.custom.no_history'), noDataText: byText('project_activity.graphs.custom.no_history'),
gapInfoMessage: byText('project_activity.graphs.data_table.data_gap', { exact: false }),


// Add metrics. // Add metrics.
addMetricBtn: byRole('button', { name: 'project_activity.graphs.custom.add' }), addMetricBtn: byRole('button', { name: 'project_activity.graphs.custom.add' }),
{ {
metrics: keyBy( metrics: keyBy(
[ [
mockMetric({ key: MetricKey.maintainability_issues, type: MetricType.Data }),
mockMetric({ key: MetricKey.bugs, type: MetricType.Integer }), mockMetric({ key: MetricKey.bugs, type: MetricType.Integer }),
mockMetric({ key: MetricKey.code_smells, type: MetricType.Integer }), mockMetric({ key: MetricKey.code_smells, type: MetricType.Integer }),
mockMetric({ key: MetricKey.security_hotspots_reviewed }), mockMetric({ key: MetricKey.security_hotspots_reviewed }),

+ 1
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

project_activity.graphs.data_table.no_data_warning_check_dates_x=There is no data for the selected date range (everything after {start}). Try modifying the date filters on the main page. project_activity.graphs.data_table.no_data_warning_check_dates_x=There is no data for the selected date range (everything after {start}). Try modifying the date filters on the main page.
project_activity.graphs.data_table.no_data_warning_check_dates_y=There is no data for the selected date range (everything before {end}). Try modifying the date filters on the main page. project_activity.graphs.data_table.no_data_warning_check_dates_y=There is no data for the selected date range (everything before {end}). Try modifying the date filters on the main page.
project_activity.graphs.data_table.no_data_warning_check_dates_x_y=There is no data for the selected date range ({start} to {end}). Try modifying the date filters on the main page. project_activity.graphs.data_table.no_data_warning_check_dates_x_y=There is no data for the selected date range ({start} to {end}). Try modifying the date filters on the main page.
project_activity.graphs.data_table.data_gap=The chart history for issues related to software qualities may contain gaps while information is not available for one or more projects. {learn_more}


project_activity.custom_metric.covered_lines=Covered Lines project_activity.custom_metric.covered_lines=Covered Lines



Loading…
Cancel
Save