Skip to content

Commit

Permalink
feat: added Beta banner and Beta badge for videos (#1169)
Browse files Browse the repository at this point in the history
  • Loading branch information
jajjibhai008 committed Sep 9, 2024
1 parent 334d87c commit 913c976
Show file tree
Hide file tree
Showing 14 changed files with 325 additions and 18 deletions.
34 changes: 34 additions & 0 deletions src/components/microlearning/BetaBadge.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {
Badge, OverlayTrigger, Tooltip,
} from '@openedx/paragon';
import './styles/VideoDetailPage.scss';
import { FormattedMessage } from '@edx/frontend-platform/i18n';

const BetaBadge = () => (
<OverlayTrigger
placement="top"
overlay={(
<Tooltip id="video-beta-version-badge" className="video-beta-badge-tooltip">
<FormattedMessage
id="enterprise.microlearningVideo.beta.tooltip"
defaultMessage="<b>Beta version of the Videos.</b> Some features may not be fully functional yet. We appreciate your patience as we fine-tune the experience."
description="Tooltip message for the beta badge on the video page."
values={{
// eslint-disable-next-line react/no-unstable-nested-components
b: (msg) => <strong>{msg}</strong>,
}}
/>
</Tooltip>
)}
>
<Badge variant="info" className="ml-2">
<FormattedMessage
id="enterprise.microlearningVideo.betaBadge.text"
defaultMessage="Beta"
description="Beta badge for the video page."
/>
</Badge>
</OverlayTrigger>
);

export default BetaBadge;
73 changes: 73 additions & 0 deletions src/components/microlearning/VideoBanner.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<div data-testid="video-banner" className="d-flex justify-content-center">
<Card orientation="horizontal" className="video-banner-class bg-light-300">
<Card.Section className="col-9 text-primary-500">
<span className="d-flex justify-content-center align-items-end">
<h3 className="text-brand-500 pr-1 m-0">
<FormattedMessage
id="enterprise.microlearning.video.banner.new"
defaultMessage="New!"
description="New badge for the video banner on the video page."
/>
</h3>
<h3 className="p-0 m-0">
<FormattedMessage
id="enterprise.microlearning.videoBanner.title"
defaultMessage="Videos Now Available with Your Subscription"
description="Title for the video banner on the video page."
/>
</h3>
<BetaBadge />
</span>
<p className="d-flex justify-content-center">
<FormattedMessage
id="enterprise.microlearning.videoBanner.description"
defaultMessage="Transform your potential into success."
description="Description for the video banner on the video page."
/>
</p>
</Card.Section>
<Card.Footer className="col-3 justify-content-end">
<Button
as={Link}
to="#videos-section"
variant="outline-primary"
onClick={sendPushEvent}
>
<FormattedMessage
id="enterprise.microlearning.videoBanner.exploreVideos"
defaultMessage="Explore Videos"
description="Explore Videos button text for the video banner on the video page."
/>
</Button>
</Card.Footer>
</Card>
</div>
);
};

export default VideoBanner;
2 changes: 1 addition & 1 deletion src/components/microlearning/VideoDetailPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const VideoDetailPage = () => {
};
const enableVideos = (
features.FEATURE_ENABLE_VIDEO_CATALOG
&& hasActivatedAndCurrentSubscription(subscriptionLicense)
&& hasActivatedAndCurrentSubscription(subscriptionLicense, enterpriseCustomer.enableBrowseAndRequest)
);

useEffect(() => {
Expand Down
11 changes: 11 additions & 0 deletions src/components/microlearning/styles/VideoDetailPage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -107,3 +109,12 @@
padding-left: 1.5rem;
}
}
.video-banner-class {
width: 906px;
height: 100px;
}
.video-beta-badge-tooltip{
.tooltip-inner {
max-width: 310px;
}
}
73 changes: 73 additions & 0 deletions src/components/microlearning/tests/VideoBanner.test.jsx
Original file line number Diff line number Diff line change
@@ -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 = () => (
<IntlProvider locale="en">
<AppContext.Provider value={{ authenticatedUser: mockAuthenticatedUser }}>
<VideoBanner />
</AppContext.Provider>
</IntlProvider>
);

beforeEach(() => {
useEnterpriseCustomer.mockReturnValue({ data: mockEnterpriseCustomer });
});

it('renders the video banner with correct title and description', () => {
renderWithRouter(<VideoBannerWrapper />);

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(<VideoBannerWrapper />);

expect(screen.getByText('Explore Videos')).toBeInTheDocument();
});

it('calls sendEnterpriseTrackEvent when explore videos button is clicked', () => {
renderWithRouter(<VideoBannerWrapper />);

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(<VideoBannerWrapper />);
userEvent.hover(screen.getByText('Beta'));
await waitFor(() => {
expect(screen.getByText('Beta version of the Videos.')).toBeVisible();
});
});
});
Original file line number Diff line number Diff line change
@@ -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';
Expand Down
8 changes: 5 additions & 3 deletions src/components/search/Search.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -179,12 +180,13 @@ const Search = () => {
{/* No content type refinement */}
{(contentType === undefined || contentType.length === 0) && (
<Stack className="my-5" gap={5}>
{enableVideos && <VideoBanner />}
{!hasRefinements && <ContentHighlights />}
{canOnlyViewHighlightSets === false && enterpriseCustomer.enableAcademies && <SearchAcademy />}
{features.ENABLE_PATHWAYS && (canOnlyViewHighlightSets === false) && <SearchPathway filter={filters} />}
{features.ENABLE_PROGRAMS && (canOnlyViewHighlightSets === false) && <SearchProgram filter={filters} />}
{canOnlyViewHighlightSets === false && <SearchCourse filter={filters} /> }
{enableVideos && <SearchVideo filter={filters} /> }
{canOnlyViewHighlightSets === false && <SearchCourse filter={filters} />}
{enableVideos && <SearchVideo filter={filters} />}
</Stack>
)}
{/* render a single contentType if the refinement exist and is either a course, program or learnerpathway */}
Expand Down
5 changes: 3 additions & 2 deletions src/components/search/SearchPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
13 changes: 10 additions & 3 deletions src/components/search/SearchResults.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -32,6 +33,7 @@ const SearchResults = ({
contentType,
translatedTitle,
isPathwaySearchResults,
showBetaBadge,
}) => {
const { refinements, dispatch } = useContext(SearchContext);
const nbHits = useNbHitsFromSearchResults(searchResults);
Expand Down Expand Up @@ -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})
<div className="d-flex align-items-center" id={showBetaBadge ? 'videos-section' : 'some-other-section'}>
{translatedTitle || title} ({nbHits} {resultsLabel}) {showBetaBadge && <BetaBadge />}
{query && <>{' '}for &quot;{query}&quot;</>}
</>
</div>
);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down Expand Up @@ -196,6 +201,7 @@ SearchResults.propTypes = {
title: PropTypes.string.isRequired,
translatedTitle: PropTypes.string,
isPathwaySearchResults: PropTypes.bool,
showBetaBadge: PropTypes.bool,
};

SearchResults.defaultProps = {
Expand All @@ -206,6 +212,7 @@ SearchResults.defaultProps = {
contentType: undefined,
translatedTitle: undefined,
isPathwaySearchResults: false,
showBetaBadge: false,
};

export default connectStateResults(SearchResults);
1 change: 1 addition & 0 deletions src/components/search/SearchVideo.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const SearchVideo = ({ filter }) => {
description: 'Translated title for the enterprise search page videos section.',
})
}
showBetaBadge
/>
</Index>
);
Expand Down
Loading

0 comments on commit 913c976

Please sign in to comment.