diff --git a/src/components/microlearning/BetaBadge.jsx b/src/components/microlearning/BetaBadge.jsx
new file mode 100644
index 0000000000..a963be99cc
--- /dev/null
+++ b/src/components/microlearning/BetaBadge.jsx
@@ -0,0 +1,34 @@
+import {
+ Badge, OverlayTrigger, Tooltip,
+} from '@openedx/paragon';
+import './styles/VideoDetailPage.scss';
+import { FormattedMessage } from '@edx/frontend-platform/i18n';
+
+const BetaBadge = () => (
+
+ {msg},
+ }}
+ />
+
+ )}
+ >
+
+
+
+
+);
+
+export default BetaBadge;
diff --git a/src/components/microlearning/VideoBanner.jsx b/src/components/microlearning/VideoBanner.jsx
new file mode 100644
index 0000000000..22f48f1412
--- /dev/null
+++ b/src/components/microlearning/VideoBanner.jsx
@@ -0,0 +1,73 @@
+import {
+ Card, Button,
+} from '@openedx/paragon';
+import './styles/VideoDetailPage.scss';
+import { Link } from 'react-router-dom';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
+import { AppContext } from '@edx/frontend-platform/react';
+import { useContext } from 'react';
+import { FormattedMessage } from '@edx/frontend-platform/i18n';
+import BetaBadge from './BetaBadge';
+import { useEnterpriseCustomer } from '../app/data';
+
+const VideoBanner = () => {
+ const { data: enterpriseCustomer } = useEnterpriseCustomer();
+ const { authenticatedUser: { userId } } = useContext(AppContext);
+ const sendPushEvent = () => {
+ sendEnterpriseTrackEvent(
+ enterpriseCustomer.uuid,
+ 'edx.ui.enterprise.learner_portal.video_banner.explore_videos_clicked',
+ {
+ userId,
+ },
+ );
+ };
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default VideoBanner;
diff --git a/src/components/microlearning/VideoDetailPage.jsx b/src/components/microlearning/VideoDetailPage.jsx
index 31d1a08954..f0884c4838 100644
--- a/src/components/microlearning/VideoDetailPage.jsx
+++ b/src/components/microlearning/VideoDetailPage.jsx
@@ -52,7 +52,7 @@ const VideoDetailPage = () => {
};
const enableVideos = (
features.FEATURE_ENABLE_VIDEO_CATALOG
- && hasActivatedAndCurrentSubscription(subscriptionLicense)
+ && hasActivatedAndCurrentSubscription(subscriptionLicense, enterpriseCustomer.enableBrowseAndRequest)
);
useEffect(() => {
diff --git a/src/components/microlearning/styles/VideoDetailPage.scss b/src/components/microlearning/styles/VideoDetailPage.scss
index 08c18d917f..dc47148a14 100644
--- a/src/components/microlearning/styles/VideoDetailPage.scss
+++ b/src/components/microlearning/styles/VideoDetailPage.scss
@@ -82,12 +82,14 @@
.vjs-transcribe-cueline {
color: #00688d;
}
+
.logo-cutom-style {
max-width: 59px;
max-height: 30px;
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.15), 0px 0px 4px 0px rgba(0, 0, 0, 0.15);
}
}
+
.preview-enroll-expand-body {
max-height: 120px;
-webkit-transition: max-height .5s ease;
@@ -107,3 +109,12 @@
padding-left: 1.5rem;
}
}
+.video-banner-class {
+ width: 906px;
+ height: 100px;
+}
+.video-beta-badge-tooltip{
+ .tooltip-inner {
+ max-width: 310px;
+ }
+}
diff --git a/src/components/microlearning/tests/VideoBanner.test.jsx b/src/components/microlearning/tests/VideoBanner.test.jsx
new file mode 100644
index 0000000000..cd57310c92
--- /dev/null
+++ b/src/components/microlearning/tests/VideoBanner.test.jsx
@@ -0,0 +1,73 @@
+import { screen, waitFor } from '@testing-library/react';
+import { AppContext } from '@edx/frontend-platform/react';
+import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+import userEvent from '@testing-library/user-event';
+import { useEnterpriseCustomer } from '../../app/data';
+import VideoBanner from '../VideoBanner';
+import { renderWithRouter } from '../../../utils/tests';
+import '@testing-library/jest-dom/extend-expect';
+
+jest.mock('../../app/data', () => ({
+ useEnterpriseCustomer: jest.fn(),
+}));
+jest.mock('@edx/frontend-enterprise-utils', () => ({
+ sendEnterpriseTrackEvent: jest.fn(),
+}));
+
+describe('VideoBanner', () => {
+ const mockEnterpriseCustomer = {
+ uuid: 'mock-uuid',
+ };
+
+ const mockAuthenticatedUser = {
+ userId: 'test-user-id',
+ };
+ const VideoBannerWrapper = () => (
+
+
+
+
+
+ );
+
+ beforeEach(() => {
+ useEnterpriseCustomer.mockReturnValue({ data: mockEnterpriseCustomer });
+ });
+
+ it('renders the video banner with correct title and description', () => {
+ renderWithRouter();
+
+ expect(screen.getByText('New!')).toBeInTheDocument();
+ expect(screen.getByText('Videos Now Available with Your Subscription')).toBeInTheDocument();
+ expect(screen.getByText('Transform your potential into success.')).toBeInTheDocument();
+ });
+
+ it('renders the explore videos button', () => {
+ renderWithRouter();
+
+ expect(screen.getByText('Explore Videos')).toBeInTheDocument();
+ });
+
+ it('calls sendEnterpriseTrackEvent when explore videos button is clicked', () => {
+ renderWithRouter();
+
+ const exploreVideosButton = screen.getByText('Explore Videos');
+ exploreVideosButton.click();
+
+ expect(sendEnterpriseTrackEvent).toHaveBeenCalledWith(
+ mockEnterpriseCustomer.uuid,
+ 'edx.ui.enterprise.learner_portal.video_banner.explore_videos_clicked',
+ {
+ userId: mockAuthenticatedUser.userId,
+ },
+ );
+ });
+ it('hover on Beta badge', async () => {
+ renderWithRouter();
+ userEvent.hover(screen.getByText('Beta'));
+ await waitFor(() => {
+ expect(screen.getByText('Beta version of the Videos.')).toBeVisible();
+ });
+ });
+});
diff --git a/src/components/microlearning/tests/VideoDetailPage.test.jsx b/src/components/microlearning/tests/VideoDetailPage.test.jsx
index e9ff8f74dc..dec6fb469f 100644
--- a/src/components/microlearning/tests/VideoDetailPage.test.jsx
+++ b/src/components/microlearning/tests/VideoDetailPage.test.jsx
@@ -1,4 +1,3 @@
-import React from 'react';
import axios from 'axios';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { AppContext } from '@edx/frontend-platform/react';
diff --git a/src/components/search/Search.jsx b/src/components/search/Search.jsx
index 03616f9502..1c392493dd 100644
--- a/src/components/search/Search.jsx
+++ b/src/components/search/Search.jsx
@@ -33,6 +33,7 @@ import { useAlgoliaSearch } from '../../utils/hooks';
import ContentTypeSearchResultsContainer from './ContentTypeSearchResultsContainer';
import SearchVideo from './SearchVideo';
import { hasActivatedAndCurrentSubscription } from './utils';
+import VideoBanner from '../microlearning/VideoBanner';
export const sendPushEvent = (isPreQueryEnabled, courseKeyMetadata) => {
if (isPreQueryEnabled) {
@@ -97,7 +98,7 @@ const Search = () => {
const enableVideos = (
canOnlyViewHighlightSets === false
&& features.FEATURE_ENABLE_VIDEO_CATALOG
- && hasActivatedAndCurrentSubscription(subscriptionLicense)
+ && hasActivatedAndCurrentSubscription(subscriptionLicense, enterpriseCustomer.enableBrowseAndRequest)
);
const PAGE_TITLE = intl.formatMessage({
@@ -179,12 +180,13 @@ const Search = () => {
{/* No content type refinement */}
{(contentType === undefined || contentType.length === 0) && (
+ {enableVideos && }
{!hasRefinements && }
{canOnlyViewHighlightSets === false && enterpriseCustomer.enableAcademies && }
{features.ENABLE_PATHWAYS && (canOnlyViewHighlightSets === false) && }
{features.ENABLE_PROGRAMS && (canOnlyViewHighlightSets === false) && }
- {canOnlyViewHighlightSets === false && }
- {enableVideos && }
+ {canOnlyViewHighlightSets === false && }
+ {enableVideos && }
)}
{/* render a single contentType if the refinement exist and is either a course, program or learnerpathway */}
diff --git a/src/components/search/SearchPage.jsx b/src/components/search/SearchPage.jsx
index 1e7bb40e43..13c384bf7c 100644
--- a/src/components/search/SearchPage.jsx
+++ b/src/components/search/SearchPage.jsx
@@ -2,19 +2,20 @@ import React from 'react';
import { SearchData } from '@edx/frontend-enterprise-catalog-search';
import { useIntl } from '@edx/frontend-platform/i18n';
-import { useSubscriptions } from '../app/data';
+import { useEnterpriseCustomer, useSubscriptions } from '../app/data';
import Search from './Search';
import { SEARCH_TRACKING_NAME } from './constants';
import { getSearchFacetFilters, hasActivatedAndCurrentSubscription } from './utils';
import { features } from '../../config';
const SearchPage = () => {
+ const { data: enterpriseCustomer } = useEnterpriseCustomer();
const intl = useIntl();
const { data: { subscriptionLicense } } = useSubscriptions();
const enableVideos = (
features.FEATURE_ENABLE_VIDEO_CATALOG
- && hasActivatedAndCurrentSubscription(subscriptionLicense)
+ && hasActivatedAndCurrentSubscription(subscriptionLicense, enterpriseCustomer.enableBrowseAndRequest)
);
return (
diff --git a/src/components/search/SearchResults.jsx b/src/components/search/SearchResults.jsx
index 9bbcf80768..1a6722313c 100644
--- a/src/components/search/SearchResults.jsx
+++ b/src/components/search/SearchResults.jsx
@@ -20,6 +20,7 @@ import {
CARDGRID_COLUMN_SIZES,
} from './constants';
import { getContentTypeFromTitle, getNoOfResultsFromTitle, getSkeletonCardFromTitle } from '../utils/search';
+import BetaBadge from '../microlearning/BetaBadge';
const SearchResults = ({
className,
@@ -32,6 +33,7 @@ const SearchResults = ({
contentType,
translatedTitle,
isPathwaySearchResults,
+ showBetaBadge,
}) => {
const { refinements, dispatch } = useContext(SearchContext);
const nbHits = useNbHitsFromSearchResults(searchResults);
@@ -96,11 +98,14 @@ const SearchResults = ({
defaultMessage: 'result',
description: 'Label for the search result count when we have only one result.',
});
+ // I have added a condition to check if showBetaBadge is true then show the BetaBadge component
+ // Theses changes are temporary and will be removed once the BetaBadge component is removed from
+ // the SearchResults component
return (
- <>
- {translatedTitle || title} ({nbHits} {resultsLabel})
+
+ {translatedTitle || title} ({nbHits} {resultsLabel}) {showBetaBadge && }
{query && <>{' '}for "{query}">}
- >
+
);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -196,6 +201,7 @@ SearchResults.propTypes = {
title: PropTypes.string.isRequired,
translatedTitle: PropTypes.string,
isPathwaySearchResults: PropTypes.bool,
+ showBetaBadge: PropTypes.bool,
};
SearchResults.defaultProps = {
@@ -206,6 +212,7 @@ SearchResults.defaultProps = {
contentType: undefined,
translatedTitle: undefined,
isPathwaySearchResults: false,
+ showBetaBadge: false,
};
export default connectStateResults(SearchResults);
diff --git a/src/components/search/SearchVideo.jsx b/src/components/search/SearchVideo.jsx
index 1996278a49..32f51878fd 100644
--- a/src/components/search/SearchVideo.jsx
+++ b/src/components/search/SearchVideo.jsx
@@ -29,6 +29,7 @@ const SearchVideo = ({ filter }) => {
description: 'Translated title for the enterprise search page videos section.',
})
}
+ showBetaBadge
/>
);
diff --git a/src/components/search/tests/Search.test.jsx b/src/components/search/tests/Search.test.jsx
new file mode 100644
index 0000000000..b5d0686807
--- /dev/null
+++ b/src/components/search/tests/Search.test.jsx
@@ -0,0 +1,92 @@
+import React from 'react';
+import { screen } from '@testing-library/react';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+import { SearchContext } from '@edx/frontend-enterprise-catalog-search';
+import { AppContext } from '@edx/frontend-platform/react';
+import { QueryClientProvider } from '@tanstack/react-query';
+import '../../skills-quiz/__mocks__/react-instantsearch-dom';
+import { queryClient, renderWithRouter } from '../../../utils/tests';
+import '@testing-library/jest-dom';
+import Search from '../Search';
+import { useDefaultSearchFilters, useEnterpriseFeatures, useEnterpriseCustomer } from '../../app/data';
+import { useAlgoliaSearch } from '../../../utils/hooks';
+import { enterpriseCustomerFactory } from '../../app/data/services/data/__factories__';
+import { features } from '../../../config';
+
+jest.mock('../../app/data', () => ({
+ ...jest.requireActual('../../app/data'),
+ useEnterpriseCustomer: jest.fn(),
+ useSubscriptions: jest.fn(() => ({
+ data: {
+ subscriptionLicense: {
+ status: 'activated',
+ subscriptionPlan: { isCurrent: true },
+ },
+ },
+ })),
+ useRedeemablePolicies: jest.fn(() => ({ data: { redeemablePolicies: [] } })),
+ useCouponCodes: jest.fn(() => ({ data: { couponCodeAssignments: [] } })),
+ useEnterpriseOffers: jest.fn(() => ({ data: { currentEnterpriseOffers: [] } })),
+ useBrowseAndRequestConfiguration: jest.fn(() => ({ data: {} })),
+ useContentHighlightsConfiguration: jest.fn(() => ({ data: {} })),
+ useCanOnlyViewHighlights: jest.fn(() => ({ data: false })),
+ useIsAssignmentsOnlyLearner: jest.fn().mockReturnValue(false),
+ useEnterpriseFeatures: jest.fn(),
+ useDefaultSearchFilters: jest.fn(),
+}));
+jest.mock('../../../utils/hooks', () => ({
+ ...jest.requireActual('../../../utils/hooks'),
+ useAlgoliaSearch: jest.fn(),
+}));
+jest.mock('../../../utils/optimizely', () => ({
+ ...jest.requireActual('../../../utils/optimizely'),
+ pushEvent: jest.fn(),
+}));
+const searchContext4 = {
+ refinements: { content_type: undefined },
+ dispatch: () => null,
+};
+const initialAppState = {
+ authenticatedUser: { userId: 'test-user-id' },
+};
+
+jest.mock('@edx/frontend-enterprise-catalog-search', () => ({
+ ...jest.requireActual('@edx/frontend-enterprise-catalog-search'),
+ SearchHeader: jest.fn(() => ),
+}));
+
+const SearchWrapper = ({
+ appState = initialAppState,
+ searchContext = searchContext4,
+ children,
+}) => (
+
+
+
+
+ {children}
+
+
+
+
+);
+const mockEnterpriseCustomer = enterpriseCustomerFactory();
+const mockFilter = `enterprise_customer_uuids: ${mockEnterpriseCustomer.uuid}`;
+describe('', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ useEnterpriseCustomer.mockReturnValue({ data: mockEnterpriseCustomer });
+ useEnterpriseFeatures.mockReturnValue({ data: { featurePrequerySearchSuggestions: false } });
+ useDefaultSearchFilters.mockReturnValue(mockFilter);
+ useAlgoliaSearch.mockReturnValue([{ search: jest.fn(), appId: 'test-app-id' }, { indexName: 'mock-index-name' }]);
+ });
+ test('renders the video beta banner component', () => {
+ features.FEATURE_ENABLE_VIDEO_CATALOG = true;
+ renderWithRouter(
+
+
+ ,
+ );
+ expect(screen.getByText('Videos Now Available with Your Subscription')).toBeInTheDocument();
+ });
+});
diff --git a/src/components/search/tests/SearchPage.test.jsx b/src/components/search/tests/SearchPage.test.jsx
index 8a75476abf..90247e8a2a 100644
--- a/src/components/search/tests/SearchPage.test.jsx
+++ b/src/components/search/tests/SearchPage.test.jsx
@@ -3,14 +3,16 @@ import { screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { AppContext } from '@edx/frontend-platform/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
-import { useSubscriptions } from '../../app/data';
+import { useEnterpriseCustomer, useSubscriptions } from '../../app/data';
import { LICENSE_STATUS } from '../../enterprise-user-subsidy/data/constants';
import SearchPage from '../SearchPage';
import { features } from '../../../config';
import { renderWithRouter } from '../../../utils/tests';
+import { enterpriseCustomerFactory } from '../../app/data/services/data/__factories__';
jest.mock('../../app/data', () => ({
useSubscriptions: jest.fn(),
+ useEnterpriseCustomer: jest.fn(),
}));
jest.mock('../utils', () => ({
@@ -33,11 +35,12 @@ const renderSearchPage = () => renderWithRouter(
,
);
-
+const mockEnterpriseCustomer = enterpriseCustomerFactory();
describe('SearchPage', () => {
beforeEach(() => {
jest.clearAllMocks();
features.FEATURE_ENABLE_VIDEO_CATALOG = true;
+ useEnterpriseCustomer.mockReturnValue({ data: mockEnterpriseCustomer });
});
it('renders SearchPage component', () => {
diff --git a/src/components/search/tests/utils.test.js b/src/components/search/tests/utils.test.js
index 1a6247f0cf..6c9a3b4ab7 100644
--- a/src/components/search/tests/utils.test.js
+++ b/src/components/search/tests/utils.test.js
@@ -24,8 +24,9 @@ describe('hasActivatedAndCurrentSubscription', () => {
isCurrent: true,
},
};
+ const enableBrowseAndRequest = false;
- const result = hasActivatedAndCurrentSubscription(subscriptionLicense);
+ const result = hasActivatedAndCurrentSubscription(subscriptionLicense, enableBrowseAndRequest);
expect(result).toBe(true);
});
@@ -36,13 +37,20 @@ describe('hasActivatedAndCurrentSubscription', () => {
isCurrent: false,
},
};
+ const enableBrowseAndRequest = false;
- const result = hasActivatedAndCurrentSubscription(subscriptionLicense);
+ const result = hasActivatedAndCurrentSubscription(subscriptionLicense, enableBrowseAndRequest);
expect(result).toBe(false);
});
it('should return false when subscriptionLicense is undefined', () => {
- const result = hasActivatedAndCurrentSubscription(undefined);
+ const enableBrowseAndRequest = false;
+ const result = hasActivatedAndCurrentSubscription(undefined, enableBrowseAndRequest);
expect(result).toBe(false);
});
+ it('should return true when enableBrowseAndRequest is true', () => {
+ const enableBrowseAndRequest = true;
+ const result = hasActivatedAndCurrentSubscription(undefined, enableBrowseAndRequest);
+ expect(result).toBe(true);
+ });
});
diff --git a/src/components/search/utils.js b/src/components/search/utils.js
index 6fa5bd2120..d41fde3ab2 100644
--- a/src/components/search/utils.js
+++ b/src/components/search/utils.js
@@ -60,7 +60,10 @@ export function getSearchFacetFilters(intl) {
return searchFilters;
}
-export const hasActivatedAndCurrentSubscription = (subscriptionLicense) => (
- subscriptionLicense?.status === LICENSE_STATUS.ACTIVATED
- && subscriptionLicense?.subscriptionPlan?.isCurrent
+export const hasActivatedAndCurrentSubscription = (
+ subscriptionLicense,
+ enableBrowseAndRequest,
+) => (
+ (subscriptionLicense?.status === LICENSE_STATUS.ACTIVATED
+ && subscriptionLicense?.subscriptionPlan?.isCurrent) || enableBrowseAndRequest
);