Skip to content

Commit

Permalink
Merge pull request #1299 from openedx/ammar/translate-hovertemplate-a…
Browse files Browse the repository at this point in the history
…nd-legends

feat: translate chart hover template and legend
  • Loading branch information
muhammad-ammar authored Sep 19, 2024
2 parents 039039e + 566a9e3 commit 46203b8
Show file tree
Hide file tree
Showing 14 changed files with 163 additions and 20 deletions.
13 changes: 12 additions & 1 deletion src/components/AdvanceAnalyticsV2/charts/ChartWrapper.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,18 @@ ChartWrapper.propTypes = {
isFetching: PropTypes.bool.isRequired,
isError: PropTypes.bool.isRequired,
chartType: PropTypes.oneOf(['ScatterChart', 'LineChart', 'BarChart']).isRequired,
chartProps: PropTypes.shape({ data: PropTypes.shape({}) }).isRequired,
chartProps: PropTypes.shape({
data: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
xKey: PropTypes.string.isRequired,
yKey: PropTypes.string.isRequired,
colorKey: PropTypes.string.isRequired,
colorMap: PropTypes.objectOf(PropTypes.string).isRequired,
hovertemplate: PropTypes.string.isRequired,
xAxisTitle: PropTypes.string,
yAxisTitle: PropTypes.string,
markerSizeKey: PropTypes.string,
customDataKeys: PropTypes.arrayOf(PropTypes.string),
}).isRequired,
loadingMessage: PropTypes.string.isRequired,
};

Expand Down
7 changes: 5 additions & 2 deletions src/components/AdvanceAnalyticsV2/charts/ScatterChart.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { useMemo } from 'react';
import Plot from 'react-plotly.js';
import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
import messages from '../messages';

/**
* ScatterChart component renders a scatter chart using Plotly.js.
Expand All @@ -21,6 +23,7 @@ import PropTypes from 'prop-types';
const ScatterChart = ({
data, xKey, yKey, colorKey, colorMap, hovertemplate, xAxisTitle, yAxisTitle, markerSizeKey, customDataKeys,
}) => {
const intl = useIntl();
const categories = Object.keys(colorMap);

const traces = useMemo(() => categories.map(category => {
Expand All @@ -30,15 +33,15 @@ const ScatterChart = ({
y: filteredData.map(item => item[yKey]),
type: 'scatter',
mode: 'markers',
name: category,
name: messages[category] ? intl.formatMessage(messages[category]) : category,
marker: {
color: colorMap[category],
size: filteredData.map(item => item[markerSizeKey] * 0.015).map(size => (size < 5 ? size + 6 : size)),
},
customdata: customDataKeys.length ? filteredData.map(item => customDataKeys.map(key => item[key])) : [],
hovertemplate,
};
}), [data, xKey, yKey, colorKey, colorMap, hovertemplate, categories, markerSizeKey, customDataKeys]);
}), [data, xKey, yKey, colorKey, colorMap, hovertemplate, categories, markerSizeKey, customDataKeys, intl]);

const layout = {
margin: { t: 0 },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import { shallow } from 'enzyme';
import { mount } from 'enzyme';
import Plot from 'react-plotly.js';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import ScatterChart from './ScatterChart';

describe('ScatterChart', () => {
Expand Down Expand Up @@ -28,8 +29,10 @@ describe('ScatterChart', () => {
};

it('renders correctly', () => {
const wrapper = shallow(
<ScatterChart {...props} />,
const wrapper = mount(
<IntlProvider locale="en">
<ScatterChart {...props} />,
</IntlProvider>,
);
const plotComponent = wrapper.find(Plot);
const traces = plotComponent.prop('data');
Expand Down
14 changes: 14 additions & 0 deletions src/components/AdvanceAnalyticsV2/data/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { sum } from 'lodash';
import utc from 'dayjs/plugin/utc';
import quarterOfYear from 'dayjs/plugin/quarterOfYear';
import { CHART_TYPES, CALCULATION } from './constants';
import messages from '../messages';

dayjs.extend(utc);
dayjs.extend(quarterOfYear);
Expand All @@ -14,6 +15,19 @@ const simulateURL = (activeTab, chartType) => {
return `${activeTab}/stats`;
};

/**
* Constructs a chart hover template.
*
* @param {Object} intl - Internationalization object.
* @param {Object} hoverInfo - Object containing hover information to show over chart data points.
* @returns {string} The constructed chart hover template.
*/
export function constructChartHoverTemplate(intl, hoverInfo) {
return Object.entries(hoverInfo)
.map(([key, value]) => `${intl.formatMessage(messages[key])}: ${value}`)
.join('<br>');
}

export default simulateURL;

/**
Expand Down
16 changes: 15 additions & 1 deletion src/components/AdvanceAnalyticsV2/data/utils.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Jest test for utils.js

import { applyCalculation, applyGranularity } from './utils';
import { createIntl } from '@edx/frontend-platform/i18n';
import { applyCalculation, applyGranularity, constructChartHoverTemplate } from './utils';
import { CALCULATION, GRANULARITY } from './constants';

describe('utils', () => {
Expand Down Expand Up @@ -201,3 +202,16 @@ describe('utils', () => {
});
});
});

describe('constructChartHoverTemplate', () => {
const intl = createIntl({
locale: 'en',
messages: {},
});

it('should construct a hover template', () => {
const hoverInfo = { skill: 'value1', enrollments: 'value2' };
const result = constructChartHoverTemplate(intl, hoverInfo);
expect(result).toBe('Skill: value1<br>Enrollments: value2');
});
});
54 changes: 54 additions & 0 deletions src/components/AdvanceAnalyticsV2/messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { defineMessages } from '@edx/frontend-platform/i18n';

const messages = defineMessages({
skill: {
id: 'advance.analytics.skill.label',
defaultMessage: 'Skill',
},
enrollments: {
id: 'advance.analytics.enrollments.label',
defaultMessage: 'Enrollments',
},
completions: {
id: 'advance.analytics.completions.label',
defaultMessage: 'Completions',
},
date: {
id: 'advance.analytics.date.label',
defaultMessage: 'Date',
},
course: {
id: 'advance.analytics.course.label',
defaultMessage: 'Course',
},
subject: {
id: 'advance.analytics.subject.label',
defaultMessage: 'Subject',
},
learningHours: {
id: 'advance.analytics.learning.hours.label',
defaultMessage: 'Learning Hours',
},
'Common Skill': {
id: 'advance.analytics.common.skill.label',
defaultMessage: 'Common Skill',
},
'Specialized Skill': {
id: 'advance.analytics.specialized.skill.label',
defaultMessage: 'Specialized Skill',
},
'Hard Skill': {
id: 'advance.analytics.hard.skill.label',
defaultMessage: 'Hard Skill',
},
'Soft Skill': {
id: 'advance.analytics.soft.skill.label',
defaultMessage: 'Soft Skill',
},
Certification: {
id: 'advance.analytics.certification.label',
defaultMessage: 'Certification',
},
});

export default messages;
16 changes: 13 additions & 3 deletions src/components/AdvanceAnalyticsV2/tabs/Completions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import AnalyticsTable from './AnalyticsTable';
import ChartWrapper from '../charts/ChartWrapper';
import { useEnterpriseAnalyticsData } from '../data/hooks';
import DownloadCSV from '../DownloadCSV';
import { constructChartHoverTemplate } from '../data/utils';

const Completions = ({
startDate, endDate, granularity, calculation, enterpriseId,
Expand Down Expand Up @@ -62,7 +63,10 @@ const Completions = ({
colorMap: chartColorMap,
xAxisTitle: '',
yAxisTitle: 'Number of Completions',
hovertemplate: 'Date: %{x}<br>Number of Completions: %{y}',
hovertemplate: constructChartHoverTemplate(intl, {
date: '%{x}',
completions: '%{y}',
}),
}}
loadingMessage={intl.formatMessage({
id: 'advance.analytics.completions.tab.chart.top.courses.by.completions.loading.message',
Expand Down Expand Up @@ -110,7 +114,10 @@ const Completions = ({
defaultMessage: 'Number of Completions',
description: 'Y-axis title for the top courses by completions chart.',
}),
hovertemplate: 'Course: %{x}<br>Number of Completions: %{y}',
hovertemplate: constructChartHoverTemplate(intl, {
course: '%{x}',
completions: '%{y}',
}),
}}
loadingMessage={intl.formatMessage({
id: 'advance.analytics.completions.tab.chart.top.10.courses.by.completions.loading.message',
Expand Down Expand Up @@ -158,7 +165,10 @@ const Completions = ({
defaultMessage: 'Number of Completions',
description: 'Y-axis title for the top subjects by completions chart.',
}),
hovertemplate: 'Subject: %{x}<br>Number of Completions: %{y}',
hovertemplate: constructChartHoverTemplate(intl, {
subject: '%{x}',
completions: '%{y}',
}),
}}
loadingMessage={intl.formatMessage({
id: 'advance.analytics.completions.tab.chart.top.subjects.by.completions.loading.message',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable import/no-extraneous-dependencies */
import {
render, screen, waitFor, within,
} from '@testing-library/react';
Expand Down
16 changes: 13 additions & 3 deletions src/components/AdvanceAnalyticsV2/tabs/Engagements.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import AnalyticsTable from './AnalyticsTable';
import ChartWrapper from '../charts/ChartWrapper';
import { useEnterpriseAnalyticsData } from '../data/hooks';
import DownloadCSV from '../DownloadCSV';
import { constructChartHoverTemplate } from '../data/utils';

const Engagements = ({
startDate, endDate, granularity, calculation, enterpriseId,
Expand Down Expand Up @@ -61,7 +62,10 @@ const Engagements = ({
colorMap: chartColorMap,
xAxisTitle: '',
yAxisTitle: 'Number of Learning Hours',
hovertemplate: 'Date: %{x}<br>Learning Hours: %{y}',
hovertemplate: constructChartHoverTemplate(intl, {
date: '%{x}',
learningHours: '%{y}',
}),
}}
loadingMessage={intl.formatMessage({
id: 'advance.analytics.engagements.tab.chart.learning.hours.over.time.loading.message',
Expand Down Expand Up @@ -109,7 +113,10 @@ const Engagements = ({
defaultMessage: 'Number of Learning Hours',
description: 'Y-axis title for the top 10 courses by learning hours chart.',
}),
hovertemplate: 'Course: %{x}<br>Learning Hours: %{y}',
hovertemplate: constructChartHoverTemplate(intl, {
course: '%{x}',
learningHours: '%{y}',
}),
}}
loadingMessage={intl.formatMessage({
id: 'advance.analytics.engagements.tab.chart.top.10.courses.by.learning.hours.loading.message',
Expand Down Expand Up @@ -157,7 +164,10 @@ const Engagements = ({
defaultMessage: 'Number of Learning Hours',
description: 'Y-axis title for the top 10 subjects by learning hours chart.',
}),
hovertemplate: 'Subject: %{x}<br>Learning Hours: %{y}',
hovertemplate: constructChartHoverTemplate(intl, {
subject: '%{x}',
learningHours: '%{y}',
}),
}}
loadingMessage={intl.formatMessage({
id: 'advance.analytics.engagements.tab.chart.top.10.subjects.by.learning.hours.loading.message',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable import/no-extraneous-dependencies */
import {
render, screen, waitFor, within,
} from '@testing-library/react';
Expand Down
17 changes: 13 additions & 4 deletions src/components/AdvanceAnalyticsV2/tabs/Enrollments.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import AnalyticsTable from './AnalyticsTable';
import ChartWrapper from '../charts/ChartWrapper';
import { useEnterpriseEnrollmentsData } from '../data/hooks';
import DownloadCSVButton from '../DownloadCSVButton';
import { modifyDataToIntroduceEnrollTypeCount } from '../data/utils';
import { modifyDataToIntroduceEnrollTypeCount, constructChartHoverTemplate } from '../data/utils';

dayjs.extend(utc);

Expand Down Expand Up @@ -101,7 +101,10 @@ const Enrollments = ({
colorMap: chartColorMap,
xAxisTitle: '',
yAxisTitle: 'Number of Enrollments',
hovertemplate: 'Date: %{x}<br>Enrolls: %{y}',
hovertemplate: constructChartHoverTemplate(intl, {
date: '%{x}',
enrollments: '%{y}',
}),
}}
loadingMessage={intl.formatMessage({
id: 'advance.analytics.enrollments.tab.chart.enrollments.over.time.loading.message',
Expand Down Expand Up @@ -141,7 +144,10 @@ const Enrollments = ({
colorMap: chartColorMap,
xAxisTitle: '',
yAxisTitle: 'Number of Enrollments',
hovertemplate: 'Course: %{x}<br>Enrolls: %{y}',
hovertemplate: constructChartHoverTemplate(intl, {
course: '%{x}',
enrollments: '%{y}',
}),
}}
loadingMessage={intl.formatMessage({
id: 'advance.analytics.enrollments.tab.chart.top.courses.by.enrollments.loading.message',
Expand Down Expand Up @@ -181,7 +187,10 @@ const Enrollments = ({
colorMap: chartColorMap,
xAxisTitle: '',
yAxisTitle: 'Number of Enrollments',
hovertemplate: 'Subject: %{x}<br>Enrolls: %{y}',
hovertemplate: constructChartHoverTemplate(intl, {
subject: '%{x}',
enrollments: '%{y}',
}),
}}
loadingMessage={intl.formatMessage({
id: 'advance.analytics.enrollments.tab.chart.top.subjects.by.enrollments.loading.message',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable import/no-extraneous-dependencies */
import {
render, screen, waitFor, within,
} from '@testing-library/react';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable import/no-extraneous-dependencies */
import {
render, screen, waitFor, within,
} from '@testing-library/react';
Expand Down
17 changes: 14 additions & 3 deletions src/components/AdvanceAnalyticsV2/tabs/Skills.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { useEnterpriseAnalyticsData } from '../data/hooks';
import ChartWrapper from '../charts/ChartWrapper';
import DownloadCSV from '../DownloadCSV';
import { constructChartHoverTemplate } from '../data/utils';

const Skills = ({ startDate, endDate, enterpriseId }) => {
const intl = useIntl();
Expand Down Expand Up @@ -68,7 +69,11 @@ const Skills = ({ startDate, endDate, enterpriseId }) => {
}),
markerSizeKey: 'completions',
customDataKeys: ['skillName', 'skillType'],
hovertemplate: 'Skill: %{customdata[0]}<br>Enrolls: %{x}<br>Completions: %{y}',
hovertemplate: constructChartHoverTemplate(intl, {
skill: '%{customdata[0]}',
enrollments: '%{x}',
completions: '%{y}',
}),
}}
loadingMessage={intl.formatMessage({
id: 'advance.analytics.skills.tab.chart.top.skills.loading.message',
Expand Down Expand Up @@ -102,7 +107,10 @@ const Skills = ({ startDate, endDate, enterpriseId }) => {
defaultMessage: 'Number of Enrollments',
description: 'Y-axis title for the top skills by enrollment chart.',
}),
hovertemplate: 'Skill: %{x}<br>Enrolls: %{y}',
hovertemplate: constructChartHoverTemplate(intl, {
skill: '%{x}',
enrollments: '%{y}',
}),
}}
loadingMessage={intl.formatMessage({
id: 'advance.analytics.skills.tab.chart.top.skills.by.enrollment.loading.message',
Expand Down Expand Up @@ -136,7 +144,10 @@ const Skills = ({ startDate, endDate, enterpriseId }) => {
defaultMessage: 'Number of Completions',
description: 'Y-axis title for the top skills by completion chart.',
}),
hovertemplate: 'Skill: %{x}<br>Completions: %{y}',
hovertemplate: constructChartHoverTemplate(intl, {
skill: '%{x}',
completions: '%{y}',
}),
}}
loadingMessage={intl.formatMessage({
id: 'advance.analytics.skills.tab.chart.top.skills.by.completion.loading.message',
Expand Down

0 comments on commit 46203b8

Please sign in to comment.