Files
frontend-app-learning/src/courseware/sequence/Sequence.jsx
Adam Butterworth 24ca1aa730 Improve sequence padding and containers on mobile (#27)
TNL-7072.
- Refactors some of the css container/content class naming
- Moved UnitNavigation out of the Sequence and into its own component.
- Fixes an issue with course tabs where multi-word titles would wrap text.
2020-03-11 11:43:17 -04:00

194 lines
5.1 KiB
JavaScript

/* eslint-disable no-use-before-define */
import React, {
useEffect, useContext, Suspense, useState,
} from 'react';
import PropTypes from 'prop-types';
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import Unit from './Unit';
import SequenceNavigation from './SequenceNavigation';
import PageLoading from '../../PageLoading';
import messages from './messages';
import UserMessagesContext from '../../user-messages/UserMessagesContext';
import UnitNavigation from './UnitNavigation';
const ContentLock = React.lazy(() => import('./content-lock'));
function Sequence({
activeUnitId,
bannerText,
courseUsageKey,
displayName,
intl,
isFirstUnit,
isGated,
isLastUnit,
onNavigateUnit,
onNext,
onPrevious,
prerequisite,
showCompletion,
unitIds,
}) {
const handleNext = () => {
const nextIndex = unitIds.indexOf(activeUnitId) + 1;
if (nextIndex < unitIds.length) {
const newUnitId = unitIds[nextIndex];
handleNavigate(newUnitId);
} else {
onNext();
}
};
const handlePrevious = () => {
const previousIndex = unitIds.indexOf(activeUnitId) - 1;
if (previousIndex >= 0) {
const newUnitId = unitIds[previousIndex];
handleNavigate(newUnitId);
} else {
onPrevious();
}
};
const handleNavigate = (unitId) => {
onNavigateUnit(unitId);
};
const logEvent = (eventName, widgetPlacement, targetUnitId) => {
// Note: tabs are tracked with a 1-indexed position
// as opposed to a 0-index used throughout this MFE
const currentIndex = unitIds.indexOf(activeUnitId);
const payload = {
current_tab: currentIndex + 1,
id: activeUnitId,
tab_count: unitIds.length,
widget_placement: widgetPlacement,
};
if (targetUnitId) {
const targetIndex = unitIds.indexOf(targetUnitId);
payload.target_tab = targetIndex + 1;
}
sendTrackEvent(eventName, payload);
};
const { add, remove } = useContext(UserMessagesContext);
useEffect(() => {
let id = null;
if (bannerText) {
id = add({
code: null,
dismissible: false,
text: bannerText,
type: 'info',
topic: 'sequence',
});
}
return () => {
if (id) {
remove(id);
}
};
}, [bannerText]);
const [unitHasLoaded, setUnitHasLoaded] = useState(false);
const handleUnitLoaded = () => {
setUnitHasLoaded(true);
};
useEffect(() => {
setUnitHasLoaded(false);
}, [activeUnitId]);
return (
<div className="sequence">
<SequenceNavigation
activeUnitId={activeUnitId}
className="mb-4"
isFirstUnit={isFirstUnit}
isLastUnit={isLastUnit}
isLocked={isGated}
onNext={() => {
logEvent('edx.ui.lms.sequence.next_selected', 'top');
handleNext();
}}
onNavigate={(unitId) => {
logEvent('edx.ui.lms.sequence.tab_selected', 'top', unitId);
handleNavigate(unitId);
}}
onPrevious={() => {
logEvent('edx.ui.lms.sequence.previous_selected', 'top');
handlePrevious();
}}
showCompletion={showCompletion}
unitIds={unitIds}
/>
<div className="unit-container flex-grow-1">
{isGated && (
<Suspense
fallback={(
<PageLoading
srMessage={intl.formatMessage(messages['learn.loading.content.lock'])}
/>
)}
>
<ContentLock
courseUsageKey={courseUsageKey}
sectionName={displayName}
prereqSectionName={prerequisite.name}
prereqId={prerequisite.id}
/>
</Suspense>
)}
{!isGated && (
<Unit
key={activeUnitId}
id={activeUnitId}
onLoaded={handleUnitLoaded}
/>
)}
{unitHasLoaded && (
<UnitNavigation
isFirstUnit={isFirstUnit}
onClickPrevious={() => {
logEvent('edx.ui.lms.sequence.previous_selected', 'bottom');
handlePrevious();
}}
onClickNext={() => {
logEvent('edx.ui.lms.sequence.next_selected', 'bottom');
handleNext();
}}
isLastUnit={isLastUnit}
/>
)}
</div>
</div>
);
}
Sequence.propTypes = {
activeUnitId: PropTypes.string.isRequired,
bannerText: PropTypes.string,
courseUsageKey: PropTypes.string.isRequired,
displayName: PropTypes.string.isRequired,
intl: intlShape.isRequired,
isFirstUnit: PropTypes.bool.isRequired,
isGated: PropTypes.bool.isRequired,
isLastUnit: PropTypes.bool.isRequired,
onNavigateUnit: PropTypes.func,
onNext: PropTypes.func.isRequired,
onPrevious: PropTypes.func.isRequired,
showCompletion: PropTypes.bool.isRequired,
prerequisite: PropTypes.shape({
name: PropTypes.string,
id: PropTypes.string,
}).isRequired,
unitIds: PropTypes.arrayOf(PropTypes.string).isRequired,
};
Sequence.defaultProps = {
onNavigateUnit: null,
bannerText: undefined,
};
export default injectIntl(Sequence);