diff --git a/src/components/PRAnalyticsDashboard/ReviewsInsight/PRQualityGraph.jsx b/src/components/PRAnalyticsDashboard/ReviewsInsight/PRQualityGraph.jsx
index a2b9810496..f58355e04e 100644
--- a/src/components/PRAnalyticsDashboard/ReviewsInsight/PRQualityGraph.jsx
+++ b/src/components/PRAnalyticsDashboard/ReviewsInsight/PRQualityGraph.jsx
@@ -7,7 +7,13 @@ import ChartDataLabels from 'chartjs-plugin-datalabels';
Chart.register(ChartDataLabels);
-function PRQualityGraph({ selectedTeams, qualityData, isDataViewActive, orderedTeamIds }) {
+function PRQualityGraph({
+ selectedTeams,
+ qualityData,
+ isDataViewActive,
+ orderedTeamIds,
+ teamData,
+}) {
const darkMode = useSelector(state => state.theme.darkMode);
if (!selectedTeams || selectedTeams.length === 0) {
@@ -67,9 +73,8 @@ function PRQualityGraph({ selectedTeams, qualityData, isDataViewActive, orderedT
displayColors: false,
enabled: true,
callbacks: {
- title: items =>
- items && items[0] ? `${items[0].label}: ${items[0].formattedValue}` : '',
- label: ctx => (isDataViewActive ? `${ctx.raw.toFixed(1)}%` : ctx.raw),
+ title: () => '',
+ label: ctx => `${ctx.label}: ${isDataViewActive ? `${ctx.raw.toFixed(1)}%` : ctx.raw}`,
},
},
datalabels: {
@@ -101,6 +106,14 @@ function PRQualityGraph({ selectedTeams, qualityData, isDataViewActive, orderedT
}`}
>
{team}
+
+ {' '}
+ ({teamData[team]?.memberCount || 0} members)
+
@@ -127,6 +140,11 @@ PRQualityGraph.propTypes = {
),
isDataViewActive: PropTypes.bool,
orderedTeamIds: PropTypes.arrayOf(PropTypes.string),
+ teamData: PropTypes.objectOf(
+ PropTypes.shape({
+ memberCount: PropTypes.number,
+ }),
+ ),
};
PRQualityGraph.defaultProps = {
@@ -134,6 +152,7 @@ PRQualityGraph.defaultProps = {
qualityData: {},
isDataViewActive: false,
orderedTeamIds: [],
+ teamData: {},
};
export default PRQualityGraph;
diff --git a/src/components/PRAnalyticsDashboard/ReviewsInsight/ReviewsInsight.jsx b/src/components/PRAnalyticsDashboard/ReviewsInsight/ReviewsInsight.jsx
index 3086749795..107dc1de9a 100644
--- a/src/components/PRAnalyticsDashboard/ReviewsInsight/ReviewsInsight.jsx
+++ b/src/components/PRAnalyticsDashboard/ReviewsInsight/ReviewsInsight.jsx
@@ -247,6 +247,7 @@ function ReviewsInsight() {
qualityData={qualityData}
isDataViewActive={dataViewActive}
orderedTeamIds={orderedTeamIds}
+ teamData={teamData}
/>
)}
diff --git a/src/components/PRAnalyticsDashboard/ReviewsInsight/ReviewsInsight.module.css b/src/components/PRAnalyticsDashboard/ReviewsInsight/ReviewsInsight.module.css
index fafa0b329c..f8ca15659f 100644
--- a/src/components/PRAnalyticsDashboard/ReviewsInsight/ReviewsInsight.module.css
+++ b/src/components/PRAnalyticsDashboard/ReviewsInsight/ReviewsInsight.module.css
@@ -237,6 +237,11 @@
border: none;
}
+.riTeamMemberCount {
+ font-size: 0.8em;
+ font-weight: normal;
+}
+
/* Shared */
.riGraph {
border: 1px solid #ccc;
diff --git a/src/components/PRAnalyticsDashboard/ReviewsInsight/__tests__/PRQualityGraph.test.jsx b/src/components/PRAnalyticsDashboard/ReviewsInsight/__tests__/PRQualityGraph.test.jsx
new file mode 100644
index 0000000000..e4edde762b
--- /dev/null
+++ b/src/components/PRAnalyticsDashboard/ReviewsInsight/__tests__/PRQualityGraph.test.jsx
@@ -0,0 +1,102 @@
+import { vi } from 'vitest';
+
+vi.mock('react-redux', async importOriginal => {
+ const actual = await importOriginal();
+ return {
+ ...actual,
+ useSelector: vi.fn(() => false),
+ };
+});
+
+let lastPieOptions;
+vi.mock('react-chartjs-2', () => ({
+ Pie: ({ options }) => {
+ lastPieOptions = options;
+ return
;
+ },
+}));
+
+import { render, screen } from '@testing-library/react';
+import PRQualityGraph from '../PRQualityGraph';
+
+const renderWithStore = ui => render(ui);
+
+const selectedTeams = [{ value: 'Team A', label: 'Team A' }];
+const qualityData = {
+ 'Team A': {
+ NotApproved: 0,
+ LowQuality: 1,
+ Sufficient: 1,
+ Exceptional: 0,
+ },
+};
+const teamData = {
+ 'Team A': {
+ memberCount: 5,
+ },
+};
+
+describe('PRQualityGraph', () => {
+ it('labels every category in the tooltip in Number mode', () => {
+ renderWithStore(
+ ,
+ );
+
+ const { tooltip } = lastPieOptions.plugins;
+ expect(tooltip.callbacks.title()).toBe('');
+ expect(tooltip.callbacks.label({ label: 'Not Approved', raw: 0 })).toBe('Not Approved: 0');
+ expect(tooltip.callbacks.label({ label: 'Low Quality', raw: 1 })).toBe('Low Quality: 1');
+ expect(tooltip.callbacks.label({ label: 'Sufficient', raw: 1 })).toBe('Sufficient: 1');
+ expect(tooltip.callbacks.label({ label: 'Exceptional', raw: 0 })).toBe('Exceptional: 0');
+ });
+
+ it('labels every category in the tooltip in Data View (percentage) mode', () => {
+ renderWithStore(
+ ,
+ );
+
+ const { tooltip } = lastPieOptions.plugins;
+ expect(tooltip.callbacks.label({ label: 'Not Approved', raw: 0 })).toBe('Not Approved: 0.0%');
+ expect(tooltip.callbacks.label({ label: 'Low Quality', raw: 50 })).toBe('Low Quality: 50.0%');
+ });
+
+ it('renders the team member count from teamData', () => {
+ renderWithStore(
+ ,
+ );
+
+ expect(screen.getByText(/5 members/)).toBeInTheDocument();
+ });
+
+ it('defaults to 0 members when teamData is missing for a team', () => {
+ renderWithStore(
+ ,
+ );
+
+ expect(screen.getByText(/0 members/)).toBeInTheDocument();
+ });
+});