diff --git a/src/containers/LearnerDashboardHeader/AuthenticatedUserDropdown.jsx b/src/containers/LearnerDashboardHeader/AuthenticatedUserDropdown.jsx index c0ba261..34e4e5d 100644 --- a/src/containers/LearnerDashboardHeader/AuthenticatedUserDropdown.jsx +++ b/src/containers/LearnerDashboardHeader/AuthenticatedUserDropdown.jsx @@ -9,7 +9,7 @@ import { AvatarButton, Dropdown } from '@edx/paragon'; import { hooks as appHooks } from 'data/redux'; import urls from 'data/services/lms/urls'; -import { useIsCollapsed } from './hooks'; +import { useIsCollapsed, findCoursesNavDropdownClicked } from './hooks'; import messages from './messages'; export const AuthenticatedUserDropdown = ({ username }) => { @@ -17,6 +17,7 @@ export const AuthenticatedUserDropdown = ({ username }) => { const { authenticatedUser } = React.useContext(AppContext); const { profileImage } = authenticatedUser; const dashboard = appHooks.useEnterpriseDashboardData(); + const { courseSearchUrl } = appHooks.usePlatformSettingsData(); const isCollapsed = useIsCollapsed(); return ( @@ -48,9 +49,14 @@ export const AuthenticatedUserDropdown = ({ username }) => { {formatMessage(messages.profile)} {isCollapsed && ( - - {formatMessage(messages.viewPrograms)} - + <> + + {formatMessage(messages.viewPrograms)} + + + {formatMessage(messages.exploreCourses)} + + )} {formatMessage(messages.account)} diff --git a/src/containers/LearnerDashboardHeader/AuthenticatedUserDropdown.test.jsx b/src/containers/LearnerDashboardHeader/AuthenticatedUserDropdown.test.jsx index 0a8a9e8..fe76384 100644 --- a/src/containers/LearnerDashboardHeader/AuthenticatedUserDropdown.test.jsx +++ b/src/containers/LearnerDashboardHeader/AuthenticatedUserDropdown.test.jsx @@ -14,10 +14,14 @@ jest.mock('@edx/frontend-platform/react', () => ({ jest.mock('data/redux', () => ({ hooks: { useEnterpriseDashboardData: jest.fn(), + usePlatformSettingsData: jest.fn(() => ({ + courseSearchUrl: 'test-course-search-url', + })), }, })); jest.mock('containers/LearnerDashboardHeader/hooks', () => ({ useIsCollapsed: jest.fn(), + findCoursesNavDropdownClicked: (href) => jest.fn().mockName(`findCoursesNavDropdownClicked('${href}')`), })); describe('AuthenticatedUserDropdown', () => { diff --git a/src/containers/LearnerDashboardHeader/__snapshots__/AuthenticatedUserDropdown.test.jsx.snap b/src/containers/LearnerDashboardHeader/__snapshots__/AuthenticatedUserDropdown.test.jsx.snap index 76e01aa..e4c3d5b 100644 --- a/src/containers/LearnerDashboardHeader/__snapshots__/AuthenticatedUserDropdown.test.jsx.snap +++ b/src/containers/LearnerDashboardHeader/__snapshots__/AuthenticatedUserDropdown.test.jsx.snap @@ -50,6 +50,12 @@ exports[`AuthenticatedUserDropdown snapshots with enterprise dashboard 1`] = ` > View Programs + + Explore courses + diff --git a/src/containers/LearnerDashboardHeader/__snapshots__/index.test.jsx.snap b/src/containers/LearnerDashboardHeader/__snapshots__/index.test.jsx.snap index 6fee45f..65337f2 100644 --- a/src/containers/LearnerDashboardHeader/__snapshots__/index.test.jsx.snap +++ b/src/containers/LearnerDashboardHeader/__snapshots__/index.test.jsx.snap @@ -28,8 +28,16 @@ exports[`LearnerDashboardHeader snapshots with collapsed 1`] = ` />
+
@@ -66,6 +74,14 @@ exports[`LearnerDashboardHeader snapshots without collapsed 1`] = `
+
diff --git a/src/containers/LearnerDashboardHeader/hooks.js b/src/containers/LearnerDashboardHeader/hooks.js index fa52e5d..3e683fd 100644 --- a/src/containers/LearnerDashboardHeader/hooks.js +++ b/src/containers/LearnerDashboardHeader/hooks.js @@ -1,5 +1,7 @@ import React from 'react'; import { useWindowSize, breakpoints } from '@edx/paragon'; +import track from 'tracking'; +import { linkNames } from 'tracking/constants'; export const useIsCollapsed = () => { const { width } = useWindowSize(); @@ -7,4 +9,16 @@ export const useIsCollapsed = () => { return isCollapsed; }; -export default { useIsCollapsed }; +export const findCoursesNavClicked = (href) => track.findCourses.findCoursesClicked(href, { + linkName: linkNames.learnerHomeNavExplore, +}); + +export const findCoursesNavDropdownClicked = (href) => track.findCourses.findCoursesClicked(href, { + linkName: linkNames.learnerHomeNavDropdownExplore, +}); + +export default { + useIsCollapsed, + findCoursesNavClicked, + findCoursesNavDropdownClicked, +}; diff --git a/src/containers/LearnerDashboardHeader/hooks.test.js b/src/containers/LearnerDashboardHeader/hooks.test.js index 296deb7..79ea527 100644 --- a/src/containers/LearnerDashboardHeader/hooks.test.js +++ b/src/containers/LearnerDashboardHeader/hooks.test.js @@ -1,5 +1,15 @@ import { useWindowSize, breakpoints } from '@edx/paragon'; -import { useIsCollapsed } from './hooks'; +import track from 'tracking'; +import { linkNames } from 'tracking/constants'; +import { useIsCollapsed, findCoursesNavClicked, findCoursesNavDropdownClicked } from './hooks'; + +jest.mock('tracking', () => ({ + findCourses: { + findCoursesClicked: jest.fn(), + }, +})); + +const url = 'http://example.com'; describe('LearnerDashboardHeader hooks', () => { describe('useIsCollapsed', () => { @@ -12,4 +22,22 @@ describe('LearnerDashboardHeader hooks', () => { expect(useIsCollapsed()).toEqual(true); }); }); + + describe('findCoursesNavClicked', () => { + test('calls tracking with nav link name', () => { + findCoursesNavClicked(url); + expect(track.findCourses.findCoursesClicked).toHaveBeenCalledWith(url, { + linkName: linkNames.learnerHomeNavExplore, + }); + }); + }); + + describe('findCoursesNavDropdownClicked', () => { + test('calls tracking with dropdown link name', () => { + findCoursesNavDropdownClicked(url); + expect(track.findCourses.findCoursesClicked).toHaveBeenCalledWith(url, { + linkName: linkNames.learnerHomeNavDropdownExplore, + }); + }); + }); }); diff --git a/src/containers/LearnerDashboardHeader/index.jsx b/src/containers/LearnerDashboardHeader/index.jsx index 42ceec7..60f35ab 100644 --- a/src/containers/LearnerDashboardHeader/index.jsx +++ b/src/containers/LearnerDashboardHeader/index.jsx @@ -2,18 +2,21 @@ import React, { useContext } from 'react'; import { useIntl } from '@edx/frontend-platform/i18n'; import { AppContext } from '@edx/frontend-platform/react'; -import { Program } from '@edx/paragon/icons'; -import { Button, Image } from '@edx/paragon'; +import { Program, Search } from '@edx/paragon/icons'; +import { + Button, Image, IconButton, Icon, +} from '@edx/paragon'; import topBanner from 'assets/top_stripe.svg'; import MasqueradeBar from 'containers/MasqueradeBar'; import urls from 'data/services/lms/urls'; +import { hooks as appHooks } from 'data/redux'; import AuthenticatedUserDropdown from './AuthenticatedUserDropdown'; import GreetingBanner from './GreetingBanner'; import ConfirmEmailBanner from './ConfirmEmailBanner'; -import { useIsCollapsed } from './hooks'; +import { useIsCollapsed, findCoursesNavClicked } from './hooks'; import messages from './messages'; import './index.scss'; @@ -25,6 +28,9 @@ export const UserMenu = () => { export const LearnerDashboardHeader = () => { const { formatMessage } = useIntl(); const isCollapsed = useIsCollapsed(); + const { courseSearchUrl } = appHooks.usePlatformSettingsData(); + + const exploreCoursesClick = findCoursesNavClicked(courseSearchUrl); return ( <> @@ -43,9 +49,33 @@ export const LearnerDashboardHeader = () => {
{isCollapsed && }
- {isCollapsed - ? (
) - : ()} + {isCollapsed ? ( +
+ + +
+ ) : ( + <> + + + + )} {!isCollapsed && } diff --git a/src/containers/LearnerDashboardHeader/index.test.jsx b/src/containers/LearnerDashboardHeader/index.test.jsx index 4fbab24..7208a31 100644 --- a/src/containers/LearnerDashboardHeader/index.test.jsx +++ b/src/containers/LearnerDashboardHeader/index.test.jsx @@ -14,6 +14,14 @@ jest.mock('@edx/frontend-platform/react', () => ({ })); jest.mock('./hooks', () => ({ useIsCollapsed: jest.fn(), + findCoursesNavClicked: (href) => jest.fn().mockName(`findCoursesNavClicked('${href}')`), +})); +jest.mock('data/redux', () => ({ + hooks: { + usePlatformSettingsData: jest.fn(() => ({ + courseSearchUrl: 'test-course-search-url', + })), + }, })); jest.mock('containers/MasqueradeBar', () => 'MasqueradeBar'); diff --git a/src/containers/LearnerDashboardHeader/messages.js b/src/containers/LearnerDashboardHeader/messages.js index 117d6d5..3105055 100644 --- a/src/containers/LearnerDashboardHeader/messages.js +++ b/src/containers/LearnerDashboardHeader/messages.js @@ -57,6 +57,11 @@ const messages = defineMessages({ defaultMessage: 'Switch to Programs', description: 'Header link for switching to program page.', }, + exploreCourses: { + id: 'leanerDashboard.exploreCourses', + defaultMessage: 'Explore courses', + description: 'Header link for switching to course page.', + }, }); export default messages; diff --git a/src/tracking/constants.js b/src/tracking/constants.js index b561af4..32bbc5c 100644 --- a/src/tracking/constants.js +++ b/src/tracking/constants.js @@ -4,6 +4,7 @@ export const categories = StrictDict({ dashboard: 'dashboard', upgrade: 'upgrade', userEngagement: 'user-engagement', + searchButton: 'search_button', }); export const events = StrictDict({ @@ -46,6 +47,12 @@ export const eventNames = StrictDict({ enterpriseDashboardModalOpened: `${learnerPortal}.opened`, enterpriseDashboardModalCTAClicked: `${learnerPortal}.dashboard_cta.clicked`, enterpriseDashboardModalClosed: `${learnerPortal}.closed`, + findCoursesClicked: 'edx.bi.dashboard.find_courses_button.clicked', +}); + +export const linkNames = StrictDict({ + learnerHomeNavExplore: 'learner_home_nav_explore', + learnerHomeNavDropdownExplore: 'learner_home_nav_dropdown_explore', }); export const appName = 'learner-home'; diff --git a/src/tracking/index.js b/src/tracking/index.js index cccd169..30f5557 100644 --- a/src/tracking/index.js +++ b/src/tracking/index.js @@ -3,6 +3,7 @@ import engagement from './trackers/engagement'; import enterpriseDashboard from './trackers/enterpriseDashboard'; import entitlements from './trackers/entitlements'; import socialShare from './trackers/socialShare'; +import findCourses from './trackers/findCourses'; export default { course, @@ -10,4 +11,5 @@ export default { enterpriseDashboard, entitlements, socialShare, + findCourses, }; diff --git a/src/tracking/trackers/findCourses.js b/src/tracking/trackers/findCourses.js new file mode 100644 index 0000000..495f32a --- /dev/null +++ b/src/tracking/trackers/findCourses.js @@ -0,0 +1,16 @@ +import { createLinkTracker, createEventTracker } from 'data/services/segment/utils'; +import { categories, eventNames } from '../constants'; + +export const findCoursesClicked = (href, args = {}) => createLinkTracker( + createEventTracker(eventNames.findCoursesClicked, { + pageName: 'learner_home', + linkType: 'button', + linkCategory: categories.searchButton, + ...args, + }), + href, +); + +export default { + findCoursesClicked, +}; diff --git a/src/tracking/trackers/findCourses.test.js b/src/tracking/trackers/findCourses.test.js new file mode 100644 index 0000000..24b5719 --- /dev/null +++ b/src/tracking/trackers/findCourses.test.js @@ -0,0 +1,44 @@ +import { createLinkTracker, createEventTracker } from 'data/services/segment/utils'; +import { findCoursesClicked } from './findCourses'; +import { categories, eventNames } from '../constants'; + +jest.mock('data/services/segment/utils', () => ({ + createEventTracker: jest.fn((args) => ({ createEventTracker: args })), + createLinkTracker: jest.fn((args) => ({ createLinkTracker: args })), +})); + +describe('find courses trackers', () => { + const url = 'http://example.com'; + const defaultProps = { + pageName: 'learner_home', + linkType: 'button', + linkCategory: categories.searchButton, + }; + beforeEach(() => { + createEventTracker.mockClear(); + createLinkTracker.mockClear(); + }); + test('no args', () => { + findCoursesClicked(url); + expect(createEventTracker).toHaveBeenCalledWith(eventNames.findCoursesClicked, defaultProps); + expect(createLinkTracker).toHaveBeenCalledWith( + createEventTracker(eventNames.findCoursesClicked, defaultProps), url, + ); + }); + + test('with args', () => { + const args = { linkName: 'foo' }; + findCoursesClicked(url, args); + expect(createEventTracker).toHaveBeenCalledWith(eventNames.findCoursesClicked, { + ...defaultProps, + ...args, + }); + expect(createLinkTracker).toHaveBeenCalledWith( + createEventTracker(eventNames.findCoursesClicked, { + ...defaultProps, + ...args, + }), + url, + ); + }); +}); diff --git a/src/widgets/LookingForChallengeWidget/__snapshots__/index.test.jsx.snap b/src/widgets/LookingForChallengeWidget/__snapshots__/index.test.jsx.snap index f13ad67..191f37b 100644 --- a/src/widgets/LookingForChallengeWidget/__snapshots__/index.test.jsx.snap +++ b/src/widgets/LookingForChallengeWidget/__snapshots__/index.test.jsx.snap @@ -19,6 +19,7 @@ exports[`LookingForChallengeWidget snapshots default 1`] = ` { {formatMessage(messages.findCoursesButton, { arrow: arrowIcon })} diff --git a/src/widgets/LookingForChallengeWidget/index.test.jsx b/src/widgets/LookingForChallengeWidget/index.test.jsx index 6114679..94df1b8 100644 --- a/src/widgets/LookingForChallengeWidget/index.test.jsx +++ b/src/widgets/LookingForChallengeWidget/index.test.jsx @@ -11,7 +11,7 @@ jest.mock('data/redux', () => ({ })); jest.mock('../RecommendationsPanel/track', () => ({ - findCoursesClicked: jest.fn().mockName('track.findCoursesClicked'), + findCoursesWidgetClicked: (href) => jest.fn().mockName(`track.findCoursesWidgetClicked('${href}')`), })); describe('LookingForChallengeWidget', () => { diff --git a/src/widgets/RecommendationsPanel/LoadedView.jsx b/src/widgets/RecommendationsPanel/LoadedView.jsx index 20f1c3a..e5e157a 100644 --- a/src/widgets/RecommendationsPanel/LoadedView.jsx +++ b/src/widgets/RecommendationsPanel/LoadedView.jsx @@ -39,7 +39,7 @@ export const LoadedView = ({ iconBefore={Search} as="a" href={courseSearchUrl} - onClick={track.findCoursesClicked(courseSearchUrl)} + onClick={track.findCoursesWidgetClicked(courseSearchUrl)} > {formatMessage(messages.exploreCoursesButton)} diff --git a/src/widgets/RecommendationsPanel/LoadedView.test.jsx b/src/widgets/RecommendationsPanel/LoadedView.test.jsx index 2f5cb14..c2fdb46 100644 --- a/src/widgets/RecommendationsPanel/LoadedView.test.jsx +++ b/src/widgets/RecommendationsPanel/LoadedView.test.jsx @@ -14,7 +14,7 @@ jest.mock('data/redux', () => ({ }, })); jest.mock('./track', () => ({ - findCoursesClicked: () => 'find-courses-clicked', + findCoursesWidgetClicked: (href) => jest.fn().mockName(`track.findCoursesWidgetClicked('${href}')`), })); describe('RecommendationsPanel LoadedView', () => { diff --git a/src/widgets/RecommendationsPanel/__snapshots__/LoadedView.test.jsx.snap b/src/widgets/RecommendationsPanel/__snapshots__/LoadedView.test.jsx.snap index d074753..08eaaa5 100644 --- a/src/widgets/RecommendationsPanel/__snapshots__/LoadedView.test.jsx.snap +++ b/src/widgets/RecommendationsPanel/__snapshots__/LoadedView.test.jsx.snap @@ -65,7 +65,7 @@ exports[`RecommendationsPanel LoadedView snapshot with personalize recommendatio