@openedx/frontend-app-learner-dashboard 2.0.0-alpha.2 → 2.0.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,3 @@
1
1
  export declare const appId = "org.openedx.frontend.app.learnerDashboard";
2
2
  export declare const dashboardRole = "org.openedx.frontend.role.dashboard";
3
+ export declare const coursesRole = "org.openedx.frontend.role.courses";
package/dist/constants.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export const appId = 'org.openedx.frontend.app.learnerDashboard';
2
2
  export const dashboardRole = 'org.openedx.frontend.role.dashboard';
3
+ export const coursesRole = 'org.openedx.frontend.role.courses';
3
4
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,KAAK,GAAG,2CAA2C,CAAC;AACjE,MAAM,CAAC,MAAM,aAAa,GAAG,qCAAqC,CAAC","sourcesContent":["export const appId = 'org.openedx.frontend.app.learnerDashboard';\nexport const dashboardRole = 'org.openedx.frontend.role.dashboard';\n"]}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,KAAK,GAAG,2CAA2C,CAAC;AACjE,MAAM,CAAC,MAAM,aAAa,GAAG,qCAAqC,CAAC;AACnE,MAAM,CAAC,MAAM,WAAW,GAAG,mCAAmC,CAAC","sourcesContent":["export const appId = 'org.openedx.frontend.app.learnerDashboard';\nexport const dashboardRole = 'org.openedx.frontend.role.dashboard';\nexport const coursesRole = 'org.openedx.frontend.role.courses';\n"]}
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from 'react';
3
- import { useIntl } from '@openedx/frontend-base';
3
+ import { getLinkProps, useIntl } from '@openedx/frontend-base';
4
4
  import { Button, Image } from '@openedx/paragon';
5
5
  import { Search } from '@openedx/paragon/icons';
6
- import { baseAppUrl } from '../../../data/services/lms/urls';
6
+ import { coursesUrl } from '../../../data/services/lms/urls';
7
7
  import emptyCourseSVG from '../../../assets/empty-course.svg';
8
8
  import { useInitializeLearnerHome } from '../../../data/hooks';
9
9
  import messages from './messages';
@@ -13,7 +13,7 @@ export const NoCoursesView = () => {
13
13
  const { formatMessage } = useIntl();
14
14
  const { data: learnerData } = useInitializeLearnerHome();
15
15
  const courseSearchUrl = ((_a = learnerData === null || learnerData === void 0 ? void 0 : learnerData.platformSettings) === null || _a === void 0 ? void 0 : _a.courseSearchUrl) || '';
16
- return (_jsxs("div", { id: "no-courses-content-view", className: "d-flex align-items-center justify-content-center mb-4.5", children: [_jsx(Image, { src: emptyCourseSVG, alt: formatMessage(messages.bannerAlt) }), _jsx("h3", { className: "h1", children: formatMessage(messages.lookingForChallengePrompt) }), _jsx("p", { children: formatMessage(messages.exploreCoursesPrompt) }), _jsx(Button, { variant: "brand", as: "a", href: baseAppUrl(courseSearchUrl), iconBefore: Search, children: formatMessage(messages.exploreCoursesButton) })] }));
16
+ return (_jsxs("div", { id: "no-courses-content-view", className: "d-flex align-items-center justify-content-center mb-4.5", children: [_jsx(Image, { src: emptyCourseSVG, alt: formatMessage(messages.bannerAlt) }), _jsx("h3", { className: "h1", children: formatMessage(messages.lookingForChallengePrompt) }), _jsx("p", { children: formatMessage(messages.exploreCoursesPrompt) }), _jsx(Button, Object.assign({ variant: "brand" }, getLinkProps(coursesUrl(courseSearchUrl)), { iconBefore: Search, children: formatMessage(messages.exploreCoursesButton) }))] }));
17
17
  };
18
18
  export default NoCoursesView;
19
19
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/containers/CoursesPanel/NoCoursesView/index.jsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAEzD,OAAO,cAAc,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAE3D,OAAO,QAAQ,MAAM,YAAY,CAAC;AAClC,OAAO,cAAc,CAAC;AAEtB,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,EAAE;;IAChC,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC;IACpC,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,wBAAwB,EAAE,CAAC;IACzD,MAAM,eAAe,GAAG,CAAA,MAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,gBAAgB,0CAAE,eAAe,KAAI,EAAE,CAAC;IAC7E,OAAO,CACL,eACE,EAAE,EAAC,yBAAyB,EAC5B,SAAS,EAAC,yDAAyD,aAEnE,KAAC,KAAK,IAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,aAAa,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAI,EACtE,aAAI,SAAS,EAAC,IAAI,YACf,aAAa,CAAC,QAAQ,CAAC,yBAAyB,CAAC,GAC/C,EACL,sBACG,aAAa,CAAC,QAAQ,CAAC,oBAAoB,CAAC,GAC3C,EACJ,KAAC,MAAM,IACL,OAAO,EAAC,OAAO,EACf,EAAE,EAAC,GAAG,EACN,IAAI,EAAE,UAAU,CAAC,eAAe,CAAC,EACjC,UAAU,EAAE,MAAM,YAEjB,aAAa,CAAC,QAAQ,CAAC,oBAAoB,CAAC,GACtC,IACL,CACP,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,aAAa,CAAC","sourcesContent":["import React from 'react';\nimport { useIntl } from '@openedx/frontend-base';\nimport { Button, Image } from '@openedx/paragon';\nimport { Search } from '@openedx/paragon/icons';\nimport { baseAppUrl } from '@src/data/services/lms/urls';\n\nimport emptyCourseSVG from '@src/assets/empty-course.svg';\nimport { useInitializeLearnerHome } from '@src/data/hooks';\n\nimport messages from './messages';\nimport './index.scss';\n\nexport const NoCoursesView = () => {\n const { formatMessage } = useIntl();\n const { data: learnerData } = useInitializeLearnerHome();\n const courseSearchUrl = learnerData?.platformSettings?.courseSearchUrl || '';\n return (\n <div\n id=\"no-courses-content-view\"\n className=\"d-flex align-items-center justify-content-center mb-4.5\"\n >\n <Image src={emptyCourseSVG} alt={formatMessage(messages.bannerAlt)} />\n <h3 className=\"h1\">\n {formatMessage(messages.lookingForChallengePrompt)}\n </h3>\n <p>\n {formatMessage(messages.exploreCoursesPrompt)}\n </p>\n <Button\n variant=\"brand\"\n as=\"a\"\n href={baseAppUrl(courseSearchUrl)}\n iconBefore={Search}\n >\n {formatMessage(messages.exploreCoursesButton)}\n </Button>\n </div>\n );\n};\n\nexport default NoCoursesView;\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/containers/CoursesPanel/NoCoursesView/index.jsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAEzD,OAAO,cAAc,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAE3D,OAAO,QAAQ,MAAM,YAAY,CAAC;AAClC,OAAO,cAAc,CAAC;AAEtB,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,EAAE;;IAChC,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC;IACpC,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,wBAAwB,EAAE,CAAC;IACzD,MAAM,eAAe,GAAG,CAAA,MAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,gBAAgB,0CAAE,eAAe,KAAI,EAAE,CAAC;IAC7E,OAAO,CACL,eACE,EAAE,EAAC,yBAAyB,EAC5B,SAAS,EAAC,yDAAyD,aAEnE,KAAC,KAAK,IAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,aAAa,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAI,EACtE,aAAI,SAAS,EAAC,IAAI,YACf,aAAa,CAAC,QAAQ,CAAC,yBAAyB,CAAC,GAC/C,EACL,sBACG,aAAa,CAAC,QAAQ,CAAC,oBAAoB,CAAC,GAC3C,EACJ,KAAC,MAAM,kBACL,OAAO,EAAC,OAAO,IACX,YAAY,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC,IAC7C,UAAU,EAAE,MAAM,YAEjB,aAAa,CAAC,QAAQ,CAAC,oBAAoB,CAAC,IACtC,IACL,CACP,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,aAAa,CAAC","sourcesContent":["import React from 'react';\nimport { getLinkProps, useIntl } from '@openedx/frontend-base';\nimport { Button, Image } from '@openedx/paragon';\nimport { Search } from '@openedx/paragon/icons';\nimport { coursesUrl } from '@src/data/services/lms/urls';\n\nimport emptyCourseSVG from '@src/assets/empty-course.svg';\nimport { useInitializeLearnerHome } from '@src/data/hooks';\n\nimport messages from './messages';\nimport './index.scss';\n\nexport const NoCoursesView = () => {\n const { formatMessage } = useIntl();\n const { data: learnerData } = useInitializeLearnerHome();\n const courseSearchUrl = learnerData?.platformSettings?.courseSearchUrl || '';\n return (\n <div\n id=\"no-courses-content-view\"\n className=\"d-flex align-items-center justify-content-center mb-4.5\"\n >\n <Image src={emptyCourseSVG} alt={formatMessage(messages.bannerAlt)} />\n <h3 className=\"h1\">\n {formatMessage(messages.lookingForChallengePrompt)}\n </h3>\n <p>\n {formatMessage(messages.exploreCoursesPrompt)}\n </p>\n <Button\n variant=\"brand\"\n {...getLinkProps(coursesUrl(courseSearchUrl))}\n iconBefore={Search}\n >\n {formatMessage(messages.exploreCoursesButton)}\n </Button>\n </div>\n );\n};\n\nexport default NoCoursesView;\n"]}
@@ -1,6 +1,7 @@
1
1
  export function getApiUrl(): string;
2
2
  export function updateUrl(base: any, url: any): any;
3
3
  export function baseAppUrl(url: any): any;
4
+ export function coursesUrl(courseSearchUrl: any): any;
4
5
  export function creditPurchaseUrl(courseId: any): string | null;
5
6
  export function creditRequestUrl(providerId: any): string;
6
7
  declare const _default: any;
@@ -1,6 +1,6 @@
1
- import { appId } from '../../../constants';
1
+ import { appId, coursesRole } from '../../../constants';
2
2
  import { StrictDict } from '../../../utils';
3
- import { getAppConfig, getSiteConfig } from '@openedx/frontend-base';
3
+ import { getAppConfig, getSiteConfig, resolveRouteByRole } from '@openedx/frontend-base';
4
4
  const getBaseUrl = () => getSiteConfig().lmsBaseUrl;
5
5
  export const getApiUrl = () => (`${getSiteConfig().lmsBaseUrl}/api`);
6
6
  const getInitApiUrl = () => (`${getApiUrl()}/learner_home/init`);
@@ -13,6 +13,15 @@ export const updateUrl = (base, url) => ((url == null || url.startsWith('http://
13
13
  export const baseAppUrl = (url) => updateUrl(getBaseUrl(), url);
14
14
  // static view url
15
15
  const programsUrl = () => baseAppUrl('/dashboard/programs');
16
+ /**
17
+ * Returns the URL for browsing courses: the courses route if an app in the running site
18
+ * provides one, so navigation stays within the SPA, or otherwise the course search URL the
19
+ * LMS reports (`courseSearchUrl`), made absolute.
20
+ */
21
+ export const coursesUrl = (courseSearchUrl) => {
22
+ var _a, _b;
23
+ return ((_b = (_a = resolveRouteByRole(coursesRole)) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : baseAppUrl(courseSearchUrl));
24
+ };
16
25
  /**
17
26
  * Returns the credit purchase URL for a course, or `null` when the site
18
27
  * configures neither CREDIT_PURCHASE_URL nor ECOMMERCE_BASE_URL.
@@ -31,6 +40,7 @@ export default StrictDict({
31
40
  getApiUrl,
32
41
  baseAppUrl,
33
42
  courseUnenroll,
43
+ coursesUrl,
34
44
  creditPurchaseUrl,
35
45
  creditRequestUrl,
36
46
  entitlementEnrollment,
@@ -1 +1 @@
1
- {"version":3,"file":"urls.js","sourceRoot":"","sources":["../../../../src/data/services/lms/urls.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAErE,MAAM,UAAU,GAAG,GAAG,EAAE,CAAC,aAAa,EAAE,CAAC,UAAU,CAAC;AAEpD,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,CAAC,GAAG,aAAa,EAAE,CAAC,UAAU,MAAM,CAAC,CAAC;AAErE,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC,CAAC,GAAG,SAAS,EAAE,oBAAoB,CAAC,CAAC;AAEjE,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,GAAG,UAAU,EAAE,QAAQ,CAAC;AAC5C,MAAM,cAAc,GAAG,GAAG,EAAE,CAAC,GAAG,UAAU,EAAE,oBAAoB,CAAC;AACjE,MAAM,mBAAmB,GAAG,GAAG,EAAE,CAAC,GAAG,SAAS,EAAE,wBAAwB,CAAC;AACzE,MAAM,qBAAqB,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,SAAS,EAAE,iCAAiC,IAAI,cAAc,CAAC;AAE1G,8CAA8C;AAC9C,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,GAAG,EAAE,CAAC,CAAC;AAE3I,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,GAAG,CAAC,CAAC;AAEhE,kBAAkB;AAClB,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAC;AAE5D;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,QAAQ,EAAE,EAAE;IAC5C,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,MAAM,CAAC,mBAAmB,EAAE,CAAC;QAC/B,OAAO,GAAG,MAAM,CAAC,mBAAmB,IAAI,QAAQ,GAAG,CAAC;IACtD,CAAC;IACD,OAAO,MAAM,CAAC,kBAAkB;QAC9B,CAAC,CAAC,GAAG,MAAM,CAAC,kBAAkB,oBAAoB,QAAQ,GAAG;QAC7D,CAAC,CAAC,IAAI,CAAC;AACX,CAAC,CAAC;AACF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,UAAU,EAAE,EAAE,CAAC,GAAG,SAAS,EAAE,wBAAwB,UAAU,WAAW,CAAC;AAE5G,eAAe,UAAU,CAAC;IACxB,SAAS;IACT,UAAU;IACV,cAAc;IACd,iBAAiB;IACjB,gBAAgB;IAChB,qBAAqB;IACrB,KAAK;IACL,aAAa;IACb,WAAW;IACX,mBAAmB;CACpB,CAAC,CAAC","sourcesContent":["import { appId } from '../../../constants';\nimport { StrictDict } from '../../../utils';\n\nimport { getAppConfig, getSiteConfig } from '@openedx/frontend-base';\n\nconst getBaseUrl = () => getSiteConfig().lmsBaseUrl;\n\nexport const getApiUrl = () => (`${getSiteConfig().lmsBaseUrl}/api`);\n\nconst getInitApiUrl = () => (`${getApiUrl()}/learner_home/init`);\n\nconst event = () => `${getBaseUrl()}/event`;\nconst courseUnenroll = () => `${getBaseUrl()}/change_enrollment`;\nconst updateEmailSettings = () => `${getApiUrl()}/change_email_settings`;\nconst entitlementEnrollment = (uuid) => `${getApiUrl()}/entitlements/v1/entitlements/${uuid}/enrollments`;\n\n// if url is null or absolute, return it as is\nexport const updateUrl = (base, url) => ((url == null || url.startsWith('http://') || url.startsWith('https://')) ? url : `${base}${url}`);\n\nexport const baseAppUrl = (url) => updateUrl(getBaseUrl(), url);\n\n// static view url\nconst programsUrl = () => baseAppUrl('/dashboard/programs');\n\n/**\n * Returns the credit purchase URL for a course, or `null` when the site\n * configures neither CREDIT_PURCHASE_URL nor ECOMMERCE_BASE_URL.\n */\nexport const creditPurchaseUrl = (courseId) => {\n const config = getAppConfig(appId);\n if (config.CREDIT_PURCHASE_URL) {\n return `${config.CREDIT_PURCHASE_URL}/${courseId}/`;\n }\n return config.ECOMMERCE_BASE_URL\n ? `${config.ECOMMERCE_BASE_URL}/credit/checkout/${courseId}/`\n : null;\n};\nexport const creditRequestUrl = (providerId) => `${getApiUrl()}/credit/v1/providers/${providerId}/request/`;\n\nexport default StrictDict({\n getApiUrl,\n baseAppUrl,\n courseUnenroll,\n creditPurchaseUrl,\n creditRequestUrl,\n entitlementEnrollment,\n event,\n getInitApiUrl,\n programsUrl,\n updateEmailSettings,\n});\n"]}
1
+ {"version":3,"file":"urls.js","sourceRoot":"","sources":["../../../../src/data/services/lms/urls.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEzF,MAAM,UAAU,GAAG,GAAG,EAAE,CAAC,aAAa,EAAE,CAAC,UAAU,CAAC;AAEpD,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,CAAC,GAAG,aAAa,EAAE,CAAC,UAAU,MAAM,CAAC,CAAC;AAErE,MAAM,aAAa,GAAG,GAAG,EAAE,CAAC,CAAC,GAAG,SAAS,EAAE,oBAAoB,CAAC,CAAC;AAEjE,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,GAAG,UAAU,EAAE,QAAQ,CAAC;AAC5C,MAAM,cAAc,GAAG,GAAG,EAAE,CAAC,GAAG,UAAU,EAAE,oBAAoB,CAAC;AACjE,MAAM,mBAAmB,GAAG,GAAG,EAAE,CAAC,GAAG,SAAS,EAAE,wBAAwB,CAAC;AACzE,MAAM,qBAAqB,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,SAAS,EAAE,iCAAiC,IAAI,cAAc,CAAC;AAE1G,8CAA8C;AAC9C,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,GAAG,EAAE,CAAC,CAAC;AAE3I,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,GAAG,CAAC,CAAC;AAEhE,kBAAkB;AAClB,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAC;AAE5D;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,eAAe,EAAE,EAAE;;IAAC,OAAA,CAC7C,MAAA,MAAA,kBAAkB,CAAC,WAAW,CAAC,0CAAE,GAAG,mCAAI,UAAU,CAAC,eAAe,CAAC,CACpE,CAAA;CAAA,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,QAAQ,EAAE,EAAE;IAC5C,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,MAAM,CAAC,mBAAmB,EAAE,CAAC;QAC/B,OAAO,GAAG,MAAM,CAAC,mBAAmB,IAAI,QAAQ,GAAG,CAAC;IACtD,CAAC;IACD,OAAO,MAAM,CAAC,kBAAkB;QAC9B,CAAC,CAAC,GAAG,MAAM,CAAC,kBAAkB,oBAAoB,QAAQ,GAAG;QAC7D,CAAC,CAAC,IAAI,CAAC;AACX,CAAC,CAAC;AACF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,UAAU,EAAE,EAAE,CAAC,GAAG,SAAS,EAAE,wBAAwB,UAAU,WAAW,CAAC;AAE5G,eAAe,UAAU,CAAC;IACxB,SAAS;IACT,UAAU;IACV,cAAc;IACd,UAAU;IACV,iBAAiB;IACjB,gBAAgB;IAChB,qBAAqB;IACrB,KAAK;IACL,aAAa;IACb,WAAW;IACX,mBAAmB;CACpB,CAAC,CAAC","sourcesContent":["import { appId, coursesRole } from '../../../constants';\nimport { StrictDict } from '../../../utils';\n\nimport { getAppConfig, getSiteConfig, resolveRouteByRole } from '@openedx/frontend-base';\n\nconst getBaseUrl = () => getSiteConfig().lmsBaseUrl;\n\nexport const getApiUrl = () => (`${getSiteConfig().lmsBaseUrl}/api`);\n\nconst getInitApiUrl = () => (`${getApiUrl()}/learner_home/init`);\n\nconst event = () => `${getBaseUrl()}/event`;\nconst courseUnenroll = () => `${getBaseUrl()}/change_enrollment`;\nconst updateEmailSettings = () => `${getApiUrl()}/change_email_settings`;\nconst entitlementEnrollment = (uuid) => `${getApiUrl()}/entitlements/v1/entitlements/${uuid}/enrollments`;\n\n// if url is null or absolute, return it as is\nexport const updateUrl = (base, url) => ((url == null || url.startsWith('http://') || url.startsWith('https://')) ? url : `${base}${url}`);\n\nexport const baseAppUrl = (url) => updateUrl(getBaseUrl(), url);\n\n// static view url\nconst programsUrl = () => baseAppUrl('/dashboard/programs');\n\n/**\n * Returns the URL for browsing courses: the courses route if an app in the running site\n * provides one, so navigation stays within the SPA, or otherwise the course search URL the\n * LMS reports (`courseSearchUrl`), made absolute.\n */\nexport const coursesUrl = (courseSearchUrl) => (\n resolveRouteByRole(coursesRole)?.url ?? baseAppUrl(courseSearchUrl)\n);\n\n/**\n * Returns the credit purchase URL for a course, or `null` when the site\n * configures neither CREDIT_PURCHASE_URL nor ECOMMERCE_BASE_URL.\n */\nexport const creditPurchaseUrl = (courseId) => {\n const config = getAppConfig(appId);\n if (config.CREDIT_PURCHASE_URL) {\n return `${config.CREDIT_PURCHASE_URL}/${courseId}/`;\n }\n return config.ECOMMERCE_BASE_URL\n ? `${config.ECOMMERCE_BASE_URL}/credit/checkout/${courseId}/`\n : null;\n};\nexport const creditRequestUrl = (providerId) => `${getApiUrl()}/credit/v1/providers/${providerId}/request/`;\n\nexport default StrictDict({\n getApiUrl,\n baseAppUrl,\n courseUnenroll,\n coursesUrl,\n creditPurchaseUrl,\n creditRequestUrl,\n entitlementEnrollment,\n event,\n getInitApiUrl,\n programsUrl,\n updateEmailSettings,\n});\n"]}
@@ -84,3 +84,5 @@ export class MockUseState {
84
84
  get values(): any;
85
85
  }
86
86
  export function mockLocation(href: any): void;
87
+ export function provideRoute(role: any, path: any): void;
88
+ export function LocationDisplay(): import("react/jsx-runtime").JSX.Element;
package/dist/testUtils.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import react from 'react';
3
+ import { useLocation } from 'react-router-dom';
4
+ import { mergeSiteConfig } from '@openedx/frontend-base';
3
5
  import { StrictDict } from './utils';
4
6
  /**
5
7
  * Mocked formatMessage provided by react-intl
@@ -180,4 +182,24 @@ export const mockLocation = (href) => {
180
182
  global.window = Object.create(window);
181
183
  global.window.location = { href };
182
184
  };
185
+ /**
186
+ * Adds an app to the site config whose route at `path` provides `role`, so that
187
+ * `resolveRouteByRole(role)` finds it. Restore the previous site config afterwards with
188
+ * `setSiteConfig`.
189
+ */
190
+ export const provideRoute = (role, path) => {
191
+ mergeSiteConfig({
192
+ apps: [{
193
+ appId: 'org.openedx.frontend.app.test',
194
+ routes: [{ path, handle: { roles: [role] } }],
195
+ }],
196
+ });
197
+ };
198
+ /**
199
+ * Shows the current pathname, so a test can click a link and assert the client navigated.
200
+ */
201
+ export const LocationDisplay = () => {
202
+ const { pathname } = useLocation();
203
+ return _jsx("div", { "data-testid": "location", children: pathname });
204
+ };
183
205
  //# sourceMappingURL=testUtils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"testUtils.js","sourceRoot":"","sources":["../src/testUtils.js"],"names":[],"mappings":"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","sourcesContent":["import react from 'react';\n\nimport { StrictDict } from '@src/utils';\n\n/**\n * Mocked formatMessage provided by react-intl\n */\nexport const formatMessage = (msg, values) => {\n let message = msg.defaultMessage;\n if (values === undefined) {\n return message;\n }\n // check if value is not a primitive type.\n if (Object.values(values).filter(value => Object(value) === value).length) {\n return <format-message-function {...{ message: msg, values }} />;\n }\n Object.keys(values).forEach((key) => {\n message = message.replaceAll(`{${key}}`, values[key]);\n });\n return message;\n};\n\n/**\n * Mock a single component, or a nested component so that its children render nicely\n * in snapshots.\n * @param {string} name - parent component name\n * @param {obj} contents - object of child components with intended component\n * render name.\n * @return {func} - mock component with nested children.\n *\n * usage:\n * mockNestedComponent('Card', { Body: 'Card.Body', Form: { Control: { Feedback: 'Form.Control.Feedback' }}... });\n * mockNestedComponent('IconButton', 'IconButton');\n */\nexport const mockNestedComponent = (name, contents) => {\n if (typeof contents === 'function') {\n return contents();\n }\n if (typeof contents !== 'object') {\n return contents;\n }\n const fn = () => name;\n Object.defineProperty(fn, 'name', { value: name });\n Object.keys(contents).forEach((nestedName) => {\n const value = contents[nestedName];\n fn[nestedName] = typeof value !== 'object'\n ? value\n : mockNestedComponent(`${name}.${nestedName}`, value);\n });\n return fn;\n};\n\n/**\n * Mock a module of components. nested components will be rendered nicely in snapshots.\n * @param {obj} mapping - component module mock config.\n * @return {obj} - module of flat and nested components that will render nicely in snapshots.\n * usage:\n * mockNestedComponents({\n * Card: { Body: 'Card.Body' },\n * IconButton: 'IconButton',\n * })\n */\nexport const mockNestedComponents = (mapping) => Object.entries(mapping).reduce(\n (obj, [name, value]) => ({\n ...obj,\n [name]: mockNestedComponent(name, value),\n }),\n {},\n);\n\n/**\n * Mock utility for working with useState in a hooks module.\n * Expects/requires an object containing the state object in order to ensure\n * the mock behavior works appropriately.\n *\n * Expected format:\n * hooks = { state: { <key>: (val) => React.createRef(val), ... } }\n *\n * Returns a utility for mocking useState and providing access to specific state values\n * and setState methods, as well as allowing per-test configuration of useState value returns.\n *\n * Example usage:\n * // hooks.js\n * import * as module from './hooks';\n * const state = {\n * isOpen: (val) => React.useState(val),\n * hasDoors: (val) => React.useState(val),\n * selected: (val) => React.useState(val),\n * };\n * ...\n * export const exampleHook = () => {\n * const [isOpen, setIsOpen] = module.state.isOpen(false);\n * if (!isOpen) { return null; }\n * return { isOpen, setIsOpen };\n * }\n * ...\n *\n * // hooks.test.js\n * import * as hooks from './hooks';\n * const state = new MockUseState(hooks)\n * ...\n * describe('state hooks', () => {\n * state.testGetter(state.keys.isOpen);\n * state.testGetter(state.keys.hasDoors);\n * state.testGetter(state.keys.selected);\n * });\n * describe('exampleHook', () => {\n * beforeEach(() => { state.mock(); });\n * it('returns null if isOpen is default value', () => {\n * expect(hooks.exampleHook()).toEqual(null);\n * });\n * it('returns isOpen and setIsOpen if isOpen is not null', () => {\n * state.mockVal(state.keys.isOpen, true);\n * expect(hooks.exampleHook()).toEqual({\n * isOpen: true,\n * setIsOpen: state.setState[state.keys.isOpen],\n * });\n * });\n * afterEach(() => { state.restore(); });\n * });\n *\n * @param {obj} hooks - hooks module containing a 'state' object\n */\nexport class MockUseState {\n constructor(hooks) {\n this.hooks = hooks;\n this.oldState = null;\n this.setState = {};\n this.stateVals = {};\n\n this.mock = this.mock.bind(this);\n this.restore = this.restore.bind(this);\n this.mockVal = this.mockVal.bind(this);\n this.testGetter = this.testGetter.bind(this);\n }\n\n /**\n * @return {object} - StrictDict of state object keys\n */\n get keys() {\n return StrictDict(Object.keys(this.hooks.state).reduce(\n (obj, key) => ({ ...obj, [key]: key }),\n {},\n ));\n }\n\n /**\n * Replace the hook module's state object with a mocked version, initialized to default values.\n */\n mock() {\n this.oldState = this.hooks.state;\n Object.keys(this.keys).forEach(key => {\n this.hooks.state[key] = jest.fn(val => {\n this.stateVals[key] = val;\n return [val, this.setState[key]];\n });\n });\n this.setState = Object.keys(this.keys).reduce(\n (obj, key) => ({\n ...obj,\n [key]: jest.fn(val => {\n this.hooks.state[key] = val;\n }),\n }),\n {},\n );\n }\n\n expectInitializedWith(key, value) {\n expect(this.hooks.state[key]).toHaveBeenCalledWith(value);\n }\n\n expectSetStateCalledWith(key, value) {\n expect(this.setState[key]).toHaveBeenCalledWith(value);\n }\n\n /**\n * Restore the hook module's state object to the actual code.\n */\n restore() {\n this.hooks.state = this.oldState;\n }\n\n /**\n * Mock the state getter associated with a single key to return a specific value one time.\n * @param {string} key - state key (from this.keys)\n * @param {any} val - new value to be returned by the useState call.\n */\n mockVal(key, val) {\n this.hooks.state[key].mockReturnValueOnce([val, this.setState[key]]);\n }\n\n testGetter(key) {\n test(`${key} state getter should return useState passthrough`, () => {\n const testValue = 'some value';\n const useState = (val) => ({ useState: val });\n jest.spyOn(react, 'useState').mockImplementationOnce(useState);\n expect(this.hooks.state[key](testValue)).toEqual(useState(testValue));\n });\n }\n\n get values() {\n return StrictDict({ ...this.hooks.state });\n }\n}\n\nexport const mockLocation = (href) => {\n delete global.window.location;\n global.window = Object.create(window);\n global.window.location = { href };\n};\n"]}
1
+ {"version":3,"file":"testUtils.js","sourceRoot":"","sources":["../src/testUtils.js"],"names":[],"mappings":"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","sourcesContent":["import react from 'react';\nimport { useLocation } from 'react-router-dom';\nimport { mergeSiteConfig } from '@openedx/frontend-base';\n\nimport { StrictDict } from '@src/utils';\n\n/**\n * Mocked formatMessage provided by react-intl\n */\nexport const formatMessage = (msg, values) => {\n let message = msg.defaultMessage;\n if (values === undefined) {\n return message;\n }\n // check if value is not a primitive type.\n if (Object.values(values).filter(value => Object(value) === value).length) {\n return <format-message-function {...{ message: msg, values }} />;\n }\n Object.keys(values).forEach((key) => {\n message = message.replaceAll(`{${key}}`, values[key]);\n });\n return message;\n};\n\n/**\n * Mock a single component, or a nested component so that its children render nicely\n * in snapshots.\n * @param {string} name - parent component name\n * @param {obj} contents - object of child components with intended component\n * render name.\n * @return {func} - mock component with nested children.\n *\n * usage:\n * mockNestedComponent('Card', { Body: 'Card.Body', Form: { Control: { Feedback: 'Form.Control.Feedback' }}... });\n * mockNestedComponent('IconButton', 'IconButton');\n */\nexport const mockNestedComponent = (name, contents) => {\n if (typeof contents === 'function') {\n return contents();\n }\n if (typeof contents !== 'object') {\n return contents;\n }\n const fn = () => name;\n Object.defineProperty(fn, 'name', { value: name });\n Object.keys(contents).forEach((nestedName) => {\n const value = contents[nestedName];\n fn[nestedName] = typeof value !== 'object'\n ? value\n : mockNestedComponent(`${name}.${nestedName}`, value);\n });\n return fn;\n};\n\n/**\n * Mock a module of components. nested components will be rendered nicely in snapshots.\n * @param {obj} mapping - component module mock config.\n * @return {obj} - module of flat and nested components that will render nicely in snapshots.\n * usage:\n * mockNestedComponents({\n * Card: { Body: 'Card.Body' },\n * IconButton: 'IconButton',\n * })\n */\nexport const mockNestedComponents = (mapping) => Object.entries(mapping).reduce(\n (obj, [name, value]) => ({\n ...obj,\n [name]: mockNestedComponent(name, value),\n }),\n {},\n);\n\n/**\n * Mock utility for working with useState in a hooks module.\n * Expects/requires an object containing the state object in order to ensure\n * the mock behavior works appropriately.\n *\n * Expected format:\n * hooks = { state: { <key>: (val) => React.createRef(val), ... } }\n *\n * Returns a utility for mocking useState and providing access to specific state values\n * and setState methods, as well as allowing per-test configuration of useState value returns.\n *\n * Example usage:\n * // hooks.js\n * import * as module from './hooks';\n * const state = {\n * isOpen: (val) => React.useState(val),\n * hasDoors: (val) => React.useState(val),\n * selected: (val) => React.useState(val),\n * };\n * ...\n * export const exampleHook = () => {\n * const [isOpen, setIsOpen] = module.state.isOpen(false);\n * if (!isOpen) { return null; }\n * return { isOpen, setIsOpen };\n * }\n * ...\n *\n * // hooks.test.js\n * import * as hooks from './hooks';\n * const state = new MockUseState(hooks)\n * ...\n * describe('state hooks', () => {\n * state.testGetter(state.keys.isOpen);\n * state.testGetter(state.keys.hasDoors);\n * state.testGetter(state.keys.selected);\n * });\n * describe('exampleHook', () => {\n * beforeEach(() => { state.mock(); });\n * it('returns null if isOpen is default value', () => {\n * expect(hooks.exampleHook()).toEqual(null);\n * });\n * it('returns isOpen and setIsOpen if isOpen is not null', () => {\n * state.mockVal(state.keys.isOpen, true);\n * expect(hooks.exampleHook()).toEqual({\n * isOpen: true,\n * setIsOpen: state.setState[state.keys.isOpen],\n * });\n * });\n * afterEach(() => { state.restore(); });\n * });\n *\n * @param {obj} hooks - hooks module containing a 'state' object\n */\nexport class MockUseState {\n constructor(hooks) {\n this.hooks = hooks;\n this.oldState = null;\n this.setState = {};\n this.stateVals = {};\n\n this.mock = this.mock.bind(this);\n this.restore = this.restore.bind(this);\n this.mockVal = this.mockVal.bind(this);\n this.testGetter = this.testGetter.bind(this);\n }\n\n /**\n * @return {object} - StrictDict of state object keys\n */\n get keys() {\n return StrictDict(Object.keys(this.hooks.state).reduce(\n (obj, key) => ({ ...obj, [key]: key }),\n {},\n ));\n }\n\n /**\n * Replace the hook module's state object with a mocked version, initialized to default values.\n */\n mock() {\n this.oldState = this.hooks.state;\n Object.keys(this.keys).forEach(key => {\n this.hooks.state[key] = jest.fn(val => {\n this.stateVals[key] = val;\n return [val, this.setState[key]];\n });\n });\n this.setState = Object.keys(this.keys).reduce(\n (obj, key) => ({\n ...obj,\n [key]: jest.fn(val => {\n this.hooks.state[key] = val;\n }),\n }),\n {},\n );\n }\n\n expectInitializedWith(key, value) {\n expect(this.hooks.state[key]).toHaveBeenCalledWith(value);\n }\n\n expectSetStateCalledWith(key, value) {\n expect(this.setState[key]).toHaveBeenCalledWith(value);\n }\n\n /**\n * Restore the hook module's state object to the actual code.\n */\n restore() {\n this.hooks.state = this.oldState;\n }\n\n /**\n * Mock the state getter associated with a single key to return a specific value one time.\n * @param {string} key - state key (from this.keys)\n * @param {any} val - new value to be returned by the useState call.\n */\n mockVal(key, val) {\n this.hooks.state[key].mockReturnValueOnce([val, this.setState[key]]);\n }\n\n testGetter(key) {\n test(`${key} state getter should return useState passthrough`, () => {\n const testValue = 'some value';\n const useState = (val) => ({ useState: val });\n jest.spyOn(react, 'useState').mockImplementationOnce(useState);\n expect(this.hooks.state[key](testValue)).toEqual(useState(testValue));\n });\n }\n\n get values() {\n return StrictDict({ ...this.hooks.state });\n }\n}\n\nexport const mockLocation = (href) => {\n delete global.window.location;\n global.window = Object.create(window);\n global.window.location = { href };\n};\n\n/**\n * Adds an app to the site config whose route at `path` provides `role`, so that\n * `resolveRouteByRole(role)` finds it. Restore the previous site config afterwards with\n * `setSiteConfig`.\n */\nexport const provideRoute = (role, path) => {\n mergeSiteConfig({\n apps: [{\n appId: 'org.openedx.frontend.app.test',\n routes: [{ path, handle: { roles: [role] } }],\n }],\n });\n};\n\n/**\n * Shows the current pathname, so a test can click a link and assert the client navigated.\n */\nexport const LocationDisplay = () => {\n const { pathname } = useLocation();\n return <div data-testid=\"location\">{pathname}</div>;\n};\n"]}
@@ -1,4 +1,4 @@
1
- export function findCoursesClicked(href: any, args?: {}): (e: any) => number;
1
+ export function findCoursesClicked(href: any, args?: {}): (() => void) | ((e: any) => number);
2
2
  declare namespace _default {
3
3
  export { findCoursesClicked };
4
4
  }
@@ -1,6 +1,13 @@
1
+ import { isInternalUrl } from '@openedx/frontend-base';
1
2
  import { createLinkTracker, createEventTracker } from '../../data/services/segment/utils';
2
3
  import { categories, eventNames } from '../constants';
3
- export const findCoursesClicked = (href, args = {}) => createLinkTracker(createEventTracker(eventNames.findCoursesClicked, Object.assign({ pageName: 'learner_home', linkType: 'button', linkCategory: categories.searchButton }, args)), href);
4
+ export const findCoursesClicked = (href, args = {}) => {
5
+ const tracker = createEventTracker(eventNames.findCoursesClicked, Object.assign({ pageName: 'learner_home', linkType: 'button', linkCategory: categories.searchButton }, args));
6
+ // A path in this site is followed by react-router in the client, so the event is just sent
7
+ // along the way. Anything else is a full page load, which createLinkTracker holds back until
8
+ // the event is on its way.
9
+ return isInternalUrl(href) ? tracker : createLinkTracker(tracker, href);
10
+ };
4
11
  export default {
5
12
  findCoursesClicked,
6
13
  };
@@ -1 +1 @@
1
- {"version":3,"file":"findCourses.js","sourceRoot":"","sources":["../../../src/tracking/trackers/findCourses.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAC1F,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEtD,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,EAAE,EAAE,EAAE,CAAC,iBAAiB,CACtE,kBAAkB,CAAC,UAAU,CAAC,kBAAkB,kBAC9C,QAAQ,EAAE,cAAc,EACxB,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,UAAU,CAAC,YAAY,IAClC,IAAI,EACP,EACF,IAAI,CACL,CAAC;AAEF,eAAe;IACb,kBAAkB;CACnB,CAAC","sourcesContent":["import { createLinkTracker, createEventTracker } from '../../data/services/segment/utils';\nimport { categories, eventNames } from '../constants';\n\nexport const findCoursesClicked = (href, args = {}) => createLinkTracker(\n createEventTracker(eventNames.findCoursesClicked, {\n pageName: 'learner_home',\n linkType: 'button',\n linkCategory: categories.searchButton,\n ...args,\n }),\n href,\n);\n\nexport default {\n findCoursesClicked,\n};\n"]}
1
+ {"version":3,"file":"findCourses.js","sourceRoot":"","sources":["../../../src/tracking/trackers/findCourses.js"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEvD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAC1F,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEtD,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,EAAE,EAAE,EAAE;IACpD,MAAM,OAAO,GAAG,kBAAkB,CAAC,UAAU,CAAC,kBAAkB,kBAC9D,QAAQ,EAAE,cAAc,EACxB,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,UAAU,CAAC,YAAY,IAClC,IAAI,EACP,CAAC;IACH,2FAA2F;IAC3F,8FAA8F;IAC9F,2BAA2B;IAC3B,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AAC1E,CAAC,CAAC;AAEF,eAAe;IACb,kBAAkB;CACnB,CAAC","sourcesContent":["import { isInternalUrl } from '@openedx/frontend-base';\n\nimport { createLinkTracker, createEventTracker } from '../../data/services/segment/utils';\nimport { categories, eventNames } from '../constants';\n\nexport const findCoursesClicked = (href, args = {}) => {\n const tracker = createEventTracker(eventNames.findCoursesClicked, {\n pageName: 'learner_home',\n linkType: 'button',\n linkCategory: categories.searchButton,\n ...args,\n });\n // A path in this site is followed by react-router in the client, so the event is just sent\n // along the way. Anything else is a full page load, which createLinkTracker holds back until\n // the event is on its way.\n return isInternalUrl(href) ? tracker : createLinkTracker(tracker, href);\n};\n\nexport default {\n findCoursesClicked,\n};\n"]}
@@ -11,7 +11,7 @@ function DiscoverLink() {
11
11
  export default function DiscoverLinkMenuItem({ variant = 'hyperlink' }) {
12
12
  const { platformSettings } = useContext(GlobalDataContext);
13
13
  const { courseSearchUrl } = platformSettings;
14
- const url = urls.baseAppUrl(courseSearchUrl);
14
+ const url = urls.coursesUrl(courseSearchUrl);
15
15
  return (_jsx(LinkMenuItem, { label: _jsx(DiscoverLink, {}), url: url, variant: variant }));
16
16
  }
17
17
  //# sourceMappingURL=DiscoverLinkMenuItem.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DiscoverLinkMenuItem.js","sourceRoot":"","sources":["../../../src/widgets/LearnerDashboardHeader/DiscoverLinkMenuItem.jsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAE/D,OAAO,iBAAiB,MAAM,uCAAuC,CAAC;AACtE,OAAO,IAAI,MAAM,8BAA8B,CAAC;AAChD,OAAO,QAAQ,MAAM,YAAY,CAAC;AAElC,SAAS,YAAY;IACnB,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC;IAEpC,OAAO,CACL,cAAK,SAAS,EAAC,QAAQ,YACrB,yBAAO,aAAa,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAQ,GAC9C,CACP,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAAC,EAAE,OAAO,GAAG,WAAW,EAAE;IACpE,MAAM,EAAE,gBAAgB,EAAE,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAC3D,MAAM,EAAE,eAAe,EAAE,GAAG,gBAAgB,CAAC;IAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;IAE7C,OAAO,CACL,KAAC,YAAY,IACX,KAAK,EAAE,KAAC,YAAY,KAAG,EACvB,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,OAAO,GAChB,CACH,CAAC;AACJ,CAAC","sourcesContent":["import { useContext } from 'react';\nimport { LinkMenuItem, useIntl } from '@openedx/frontend-base';\n\nimport GlobalDataContext from '../../data/contexts/GlobalDataContext';\nimport urls from '../../data/services/lms/urls';\nimport messages from './messages';\n\nfunction DiscoverLink() {\n const { formatMessage } = useIntl();\n\n return (\n <div className=\"d-flex\">\n <span>{formatMessage(messages.discoverNew)}</span>\n </div>\n );\n}\n\nexport default function DiscoverLinkMenuItem({ variant = 'hyperlink' }) {\n const { platformSettings } = useContext(GlobalDataContext);\n const { courseSearchUrl } = platformSettings;\n const url = urls.baseAppUrl(courseSearchUrl);\n\n return (\n <LinkMenuItem\n label={<DiscoverLink />}\n url={url}\n variant={variant}\n />\n );\n}\n"]}
1
+ {"version":3,"file":"DiscoverLinkMenuItem.js","sourceRoot":"","sources":["../../../src/widgets/LearnerDashboardHeader/DiscoverLinkMenuItem.jsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAE/D,OAAO,iBAAiB,MAAM,uCAAuC,CAAC;AACtE,OAAO,IAAI,MAAM,8BAA8B,CAAC;AAChD,OAAO,QAAQ,MAAM,YAAY,CAAC;AAElC,SAAS,YAAY;IACnB,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC;IAEpC,OAAO,CACL,cAAK,SAAS,EAAC,QAAQ,YACrB,yBAAO,aAAa,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAQ,GAC9C,CACP,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAAC,EAAE,OAAO,GAAG,WAAW,EAAE;IACpE,MAAM,EAAE,gBAAgB,EAAE,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAC3D,MAAM,EAAE,eAAe,EAAE,GAAG,gBAAgB,CAAC;IAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;IAE7C,OAAO,CACL,KAAC,YAAY,IACX,KAAK,EAAE,KAAC,YAAY,KAAG,EACvB,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,OAAO,GAChB,CACH,CAAC;AACJ,CAAC","sourcesContent":["import { useContext } from 'react';\nimport { LinkMenuItem, useIntl } from '@openedx/frontend-base';\n\nimport GlobalDataContext from '../../data/contexts/GlobalDataContext';\nimport urls from '../../data/services/lms/urls';\nimport messages from './messages';\n\nfunction DiscoverLink() {\n const { formatMessage } = useIntl();\n\n return (\n <div className=\"d-flex\">\n <span>{formatMessage(messages.discoverNew)}</span>\n </div>\n );\n}\n\nexport default function DiscoverLinkMenuItem({ variant = 'hyperlink' }) {\n const { platformSettings } = useContext(GlobalDataContext);\n const { courseSearchUrl } = platformSettings;\n const url = urls.coursesUrl(courseSearchUrl);\n\n return (\n <LinkMenuItem\n label={<DiscoverLink />}\n url={url}\n variant={variant}\n />\n );\n}\n"]}
@@ -1,10 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useIntl } from '@openedx/frontend-base';
2
+ import { getLinkProps, useIntl } from '@openedx/frontend-base';
3
3
  import { Card, Hyperlink, Icon } from '@openedx/paragon';
4
4
  import { ArrowForward } from '@openedx/paragon/icons';
5
5
  import { useInitializeLearnerHome } from '../../data/hooks';
6
6
  import moreCoursesSVG from '../../assets/more-courses-sidewidget.svg';
7
- import { baseAppUrl } from '../../data/services/lms/urls';
7
+ import { coursesUrl } from '../../data/services/lms/urls';
8
8
  import { findCoursesWidgetClicked } from './track';
9
9
  import messages from './messages';
10
10
  import './index.scss';
@@ -14,8 +14,8 @@ export const LookingForChallengeWidget = () => {
14
14
  const { formatMessage } = useIntl();
15
15
  const { data: learnerData } = useInitializeLearnerHome();
16
16
  const courseSearchUrl = ((_a = learnerData === null || learnerData === void 0 ? void 0 : learnerData.platformSettings) === null || _a === void 0 ? void 0 : _a.courseSearchUrl) || '';
17
- const hyperlinkDestination = baseAppUrl(courseSearchUrl) || '';
18
- return (_jsxs(Card, { orientation: "horizontal", id: "looking-for-challenge-widget", children: [_jsx(Card.ImageCap, { src: moreCoursesSVG, srcAlt: "course side widget" }), _jsxs(Card.Body, { className: "m-auto pr-2", children: [_jsx("h4", { children: formatMessage(messages.lookingForChallengePrompt) }), _jsx("h5", { children: _jsx(Hyperlink, { variant: "brand", destination: hyperlinkDestination, onClick: findCoursesWidgetClicked(hyperlinkDestination), className: "d-flex align-items-center", children: formatMessage(messages.findCoursesButton, { arrow: arrowIcon }) }) })] })] }));
17
+ const url = coursesUrl(courseSearchUrl);
18
+ return (_jsxs(Card, { orientation: "horizontal", id: "looking-for-challenge-widget", children: [_jsx(Card.ImageCap, { src: moreCoursesSVG, srcAlt: "course side widget" }), _jsxs(Card.Body, { className: "m-auto pr-2", children: [_jsx("h4", { children: formatMessage(messages.lookingForChallengePrompt) }), _jsx("h5", { children: _jsx(Hyperlink, Object.assign({ variant: "brand" }, getLinkProps(url), { onClick: findCoursesWidgetClicked(url), className: "d-flex align-items-center", children: formatMessage(messages.findCoursesButton, { arrow: arrowIcon }) })) })] })] }));
19
19
  };
20
20
  LookingForChallengeWidget.propTypes = {};
21
21
  export default LookingForChallengeWidget;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/widgets/LookingForChallengeWidget/index.jsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,cAAc,MAAM,0CAA0C,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAE1D,OAAO,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,QAAQ,MAAM,YAAY,CAAC;AAClC,OAAO,cAAc,CAAC;AAEtB,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAC,IAAI,IAAC,SAAS,EAAC,MAAM,EAAC,GAAG,EAAE,YAAY,GAAI,CAAC,CAAC;AAExE,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,EAAE;;IAC5C,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC;IACpC,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,wBAAwB,EAAE,CAAC;IACzD,MAAM,eAAe,GAAG,CAAA,MAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,gBAAgB,0CAAE,eAAe,KAAI,EAAE,CAAC;IAC7E,MAAM,oBAAoB,GAAG,UAAU,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;IAE/D,OAAO,CACL,MAAC,IAAI,IAAC,WAAW,EAAC,YAAY,EAAC,EAAE,EAAC,8BAA8B,aAC9D,KAAC,IAAI,CAAC,QAAQ,IACZ,GAAG,EAAE,cAAc,EACnB,MAAM,EAAC,oBAAoB,GAC3B,EACF,MAAC,IAAI,CAAC,IAAI,IAAC,SAAS,EAAC,aAAa,aAChC,uBACG,aAAa,CAAC,QAAQ,CAAC,yBAAyB,CAAC,GAC/C,EACL,uBACE,KAAC,SAAS,IACR,OAAO,EAAC,OAAO,EACf,WAAW,EAAE,oBAAoB,EACjC,OAAO,EAAE,wBAAwB,CAAC,oBAAoB,CAAC,EACvD,SAAS,EAAC,2BAA2B,YAEpC,aAAa,CAAC,QAAQ,CAAC,iBAAiB,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,GACtD,GACT,IACK,IACP,CACR,CAAC;AACJ,CAAC,CAAC;AAEF,yBAAyB,CAAC,SAAS,GAAG,EAAE,CAAC;AAEzC,eAAe,yBAAyB,CAAC","sourcesContent":["import { useIntl } from '@openedx/frontend-base';\nimport { Card, Hyperlink, Icon } from '@openedx/paragon';\nimport { ArrowForward } from '@openedx/paragon/icons';\n\nimport { useInitializeLearnerHome } from '@src/data/hooks';\nimport moreCoursesSVG from '../../assets/more-courses-sidewidget.svg';\nimport { baseAppUrl } from '../../data/services/lms/urls';\n\nimport { findCoursesWidgetClicked } from './track';\nimport messages from './messages';\nimport './index.scss';\n\nexport const arrowIcon = (<Icon className=\"mx-1\" src={ArrowForward} />);\n\nexport const LookingForChallengeWidget = () => {\n const { formatMessage } = useIntl();\n const { data: learnerData } = useInitializeLearnerHome();\n const courseSearchUrl = learnerData?.platformSettings?.courseSearchUrl || '';\n const hyperlinkDestination = baseAppUrl(courseSearchUrl) || '';\n\n return (\n <Card orientation=\"horizontal\" id=\"looking-for-challenge-widget\">\n <Card.ImageCap\n src={moreCoursesSVG}\n srcAlt=\"course side widget\"\n />\n <Card.Body className=\"m-auto pr-2\">\n <h4>\n {formatMessage(messages.lookingForChallengePrompt)}\n </h4>\n <h5>\n <Hyperlink\n variant=\"brand\"\n destination={hyperlinkDestination}\n onClick={findCoursesWidgetClicked(hyperlinkDestination)}\n className=\"d-flex align-items-center\"\n >\n {formatMessage(messages.findCoursesButton, { arrow: arrowIcon })}\n </Hyperlink>\n </h5>\n </Card.Body>\n </Card>\n );\n};\n\nLookingForChallengeWidget.propTypes = {};\n\nexport default LookingForChallengeWidget;\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/widgets/LookingForChallengeWidget/index.jsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,cAAc,MAAM,0CAA0C,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAE1D,OAAO,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,QAAQ,MAAM,YAAY,CAAC;AAClC,OAAO,cAAc,CAAC;AAEtB,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAC,IAAI,IAAC,SAAS,EAAC,MAAM,EAAC,GAAG,EAAE,YAAY,GAAI,CAAC,CAAC;AAExE,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,EAAE;;IAC5C,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC;IACpC,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,wBAAwB,EAAE,CAAC;IACzD,MAAM,eAAe,GAAG,CAAA,MAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,gBAAgB,0CAAE,eAAe,KAAI,EAAE,CAAC;IAC7E,MAAM,GAAG,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC;IAExC,OAAO,CACL,MAAC,IAAI,IAAC,WAAW,EAAC,YAAY,EAAC,EAAE,EAAC,8BAA8B,aAC9D,KAAC,IAAI,CAAC,QAAQ,IACZ,GAAG,EAAE,cAAc,EACnB,MAAM,EAAC,oBAAoB,GAC3B,EACF,MAAC,IAAI,CAAC,IAAI,IAAC,SAAS,EAAC,aAAa,aAChC,uBACG,aAAa,CAAC,QAAQ,CAAC,yBAAyB,CAAC,GAC/C,EACL,uBACE,KAAC,SAAS,kBACR,OAAO,EAAC,OAAO,IACX,YAAY,CAAC,GAAG,CAAC,IACrB,OAAO,EAAE,wBAAwB,CAAC,GAAG,CAAC,EACtC,SAAS,EAAC,2BAA2B,YAEpC,aAAa,CAAC,QAAQ,CAAC,iBAAiB,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,IACtD,GACT,IACK,IACP,CACR,CAAC;AACJ,CAAC,CAAC;AAEF,yBAAyB,CAAC,SAAS,GAAG,EAAE,CAAC;AAEzC,eAAe,yBAAyB,CAAC","sourcesContent":["import { getLinkProps, useIntl } from '@openedx/frontend-base';\nimport { Card, Hyperlink, Icon } from '@openedx/paragon';\nimport { ArrowForward } from '@openedx/paragon/icons';\n\nimport { useInitializeLearnerHome } from '@src/data/hooks';\nimport moreCoursesSVG from '../../assets/more-courses-sidewidget.svg';\nimport { coursesUrl } from '../../data/services/lms/urls';\n\nimport { findCoursesWidgetClicked } from './track';\nimport messages from './messages';\nimport './index.scss';\n\nexport const arrowIcon = (<Icon className=\"mx-1\" src={ArrowForward} />);\n\nexport const LookingForChallengeWidget = () => {\n const { formatMessage } = useIntl();\n const { data: learnerData } = useInitializeLearnerHome();\n const courseSearchUrl = learnerData?.platformSettings?.courseSearchUrl || '';\n const url = coursesUrl(courseSearchUrl);\n\n return (\n <Card orientation=\"horizontal\" id=\"looking-for-challenge-widget\">\n <Card.ImageCap\n src={moreCoursesSVG}\n srcAlt=\"course side widget\"\n />\n <Card.Body className=\"m-auto pr-2\">\n <h4>\n {formatMessage(messages.lookingForChallengePrompt)}\n </h4>\n <h5>\n <Hyperlink\n variant=\"brand\"\n {...getLinkProps(url)}\n onClick={findCoursesWidgetClicked(url)}\n className=\"d-flex align-items-center\"\n >\n {formatMessage(messages.findCoursesButton, { arrow: arrowIcon })}\n </Hyperlink>\n </h5>\n </Card.Body>\n </Card>\n );\n};\n\nLookingForChallengeWidget.propTypes = {};\n\nexport default LookingForChallengeWidget;\n"]}
@@ -1,5 +1,5 @@
1
1
  export const linkNames: any;
2
- export function findCoursesWidgetClicked(href: any): (e: any) => number;
2
+ export function findCoursesWidgetClicked(href: any): (() => void) | ((e: any) => number);
3
3
  declare namespace _default {
4
4
  export { linkNames };
5
5
  export { findCoursesWidgetClicked };
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@openedx/frontend-app-learner-dashboard",
3
- "version": "2.0.0-alpha.2",
3
+ "version": "2.0.0-alpha.4",
4
+ "packageManager": "npm@11.12.1",
4
5
  "description": "",
5
6
  "repository": {
6
7
  "type": "git",
@@ -70,6 +71,8 @@
70
71
  },
71
72
  "devDependencies": {
72
73
  "@edx/browserslist-config": "^1.5.0",
74
+ "@openedx/frontend-base": "^2.0.0-alpha.15 || 0.0.0-dev",
75
+ "@openedx/paragon": "^23 || 0.0.0-dev",
73
76
  "@testing-library/jest-dom": "^6.6.3",
74
77
  "@testing-library/react": "^16.3.0",
75
78
  "@testing-library/user-event": "^14.6.1",
@@ -82,11 +85,12 @@
82
85
  "nodemon": "^3.1.14",
83
86
  "react-dev-utils": "^12.0.0",
84
87
  "tsc-alias": "^1.8.16",
85
- "turbo": "^2.8.20"
88
+ "turbo": "^2.8.20",
89
+ "typescript": "^5.9.3"
86
90
  },
87
91
  "peerDependencies": {
88
- "@openedx/frontend-base": "^2.0.0-alpha.4 || 0.0.0-dev",
89
- "@openedx/paragon": "^23",
92
+ "@openedx/frontend-base": "^2.0.0-alpha.15 || 0.0.0-dev",
93
+ "@openedx/paragon": "^23 || 0.0.0-dev",
90
94
  "@tanstack/react-query": "^5",
91
95
  "@types/react": "^18",
92
96
  "@types/react-dom": "^18",