Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/course-unit/CourseUnit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ const CourseUnit = () => {
isUnitLegacyLibraryType,
isSplitTestType,
isProblemBankType,
isGenericContainerType,
staticFileNotices,
currentlyVisibleToStudents,
unitXBlockActions,
Expand Down Expand Up @@ -375,6 +376,7 @@ const CourseUnit = () => {
isSplitTestType={isSplitTestType}
isUnitVerticalType={isUnitVerticalType}
isProblemBankType={isProblemBankType}
isGenericContainerType={isGenericContainerType}
handleCreateNewCourseXBlock={handleCreateNewCourseXBlock}
addComponentTemplateData={addComponentTemplateData}
/>
Expand Down
34 changes: 30 additions & 4 deletions src/course-unit/add-component/AddComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { messageTypes } from '../constants';
import messages from './messages';
import AddComponentButton from './add-component-btn';
import ComponentModalView from './add-component-modals/ComponentModalView';
import { getCourseSectionVertical, getCourseUnitData } from '../data/selectors';
import { getCourseSectionVertical, getCourseUnitData, getCourseVerticalChildren } from '../data/selectors';

type ComponentTemplateData = {
displayName: string;
Expand All @@ -35,6 +35,9 @@ type ComponentTemplateData = {
category?: string;
displayName: string;
supportLevel?: string | boolean;
disabled?: boolean;
disabledReason?: string;
singleInstance?: boolean;
}>;
supportLegend: {
allowUnsupportedXblocks?: boolean;
Expand All @@ -46,6 +49,7 @@ type ComponentTemplateData = {
export interface AddComponentProps {
isSplitTestType?: boolean;
isUnitVerticalType?: boolean;
isGenericContainerType?: boolean;
parentLocator: string;
handleCreateNewCourseXBlock: (
args: object,
Expand All @@ -64,6 +68,7 @@ const AddComponent = ({
isSplitTestType,
isUnitVerticalType,
isProblemBankType,
isGenericContainerType,
addComponentTemplateData,
handleCreateNewCourseXBlock,
}: AddComponentProps) => {
Expand All @@ -74,6 +79,12 @@ const AddComponent = ({
const [isOpenHtml, openHtml, closeHtml] = useToggle(false);
const [isOpenOpenAssessment, openOpenAssessment, closeOpenAssessment] = useToggle(false);
const { componentTemplates = {} } = useSelector(getCourseSectionVertical);
const courseVerticalChildren = useSelector(getCourseVerticalChildren);
const existingBlockTypes = new Set(
(courseVerticalChildren?.children ?? []).map((child: { blockType?: string; category?: string; }) =>
child.blockType ?? child.category
),
);
const blockId = addComponentTemplateData?.parentLocator || parentLocator;
const [isAddLibraryContentModalOpen, showAddLibraryContentModal, closeAddLibraryContentModal] = useToggle();
const [isVideoSelectorModalOpen, showVideoSelectorModal, closeVideoSelectorModal] = useToggle();
Expand Down Expand Up @@ -215,10 +226,10 @@ const AddComponent = ({
}
};

if (isUnitVerticalType || isSplitTestType || isProblemBankType) {
if (isUnitVerticalType || isSplitTestType || isProblemBankType || isGenericContainerType) {
return (
<div className="py-4">
{Object.keys(componentTemplates).length && isUnitVerticalType ?
{Object.keys(componentTemplates).length && (isUnitVerticalType || isGenericContainerType) ?
(
<>
<h5 className="h3 mb-4 text-center">{intl.formatMessage(messages.title)}</h5>
Expand Down Expand Up @@ -253,25 +264,40 @@ const AddComponent = ({
isOpen: isOpenOpenAssessment,
};
break;
default:
default: {
const firstTemplate = component.templates[0];
const isSingleInstanceSatisfied = !!firstTemplate?.singleInstance
&& existingBlockTypes.has(firstTemplate.category ?? type);
const isDisabled = !!firstTemplate?.disabled || isSingleInstanceSatisfied;
const disabledReason = firstTemplate?.disabledReason
?? (isSingleInstanceSatisfied ? 'Only one instance of this component is allowed.' : undefined);
return (
<li key={type}>
<AddComponentButton
onClick={() => handleCreateNewXBlock(type)}
displayName={displayName}
type={type}
beta={beta}
disabled={isDisabled}
disabledReason={disabledReason}
/>
</li>
);
}
}

const allTemplatesDisabled = component.templates.every(
(t) => !!t.disabled,
);
const firstDisabledReason = component.templates.find((t) => t.disabledReason)?.disabledReason;
return (
<ComponentModalView
key={type}
component={component}
handleCreateNewXBlock={handleCreateNewXBlock}
modalParams={modalParams}
disabled={allTemplatesDisabled}
disabledReason={allTemplatesDisabled ? firstDisabledReason : undefined}
/>
);
})}
Expand Down
31 changes: 27 additions & 4 deletions src/course-unit/add-component/add-component-btn/index.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import PropTypes from 'prop-types';
import { Badge, Button } from '@openedx/paragon';
import {
Badge,
Button,
OverlayTrigger,
Tooltip,
} from '@openedx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';

import messages from '../messages';
Expand All @@ -10,32 +15,50 @@ const AddComponentButton = ({
displayName,
onClick,
beta,
disabled,
disabledReason,
}) => {
const intl = useIntl();

return (
const button = (
<Button
variant="outline-primary"
className="add-component-button flex-column rounded-sm"
onClick={onClick}
onClick={disabled ? undefined : onClick}
disabled={disabled}
>
<AddComponentIcon type={type} />
<span className="sr-only">{intl.formatMessage(messages.buttonText)}</span>
<span className="small mt-2">{displayName}</span>
{beta && <Badge className="pb-1 mt-1" variant="primary">Beta</Badge>}
</Button>
);

if (disabled && disabledReason) {
return (
<OverlayTrigger
placement="top"
overlay={<Tooltip id={`disabled-${type}`}>{disabledReason}</Tooltip>}
>
<span>{button}</span>
</OverlayTrigger>
);
}
return button;
};

AddComponentButton.defaultProps = {
beta: false,
disabled: false,
disabledReason: null,
};

AddComponentButton.propTypes = {
type: PropTypes.string.isRequired,
displayName: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired,
beta: PropTypes.bool,
disabled: PropTypes.bool,
disabledReason: PropTypes.string,
};

export default AddComponentButton;
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ const ComponentModalView = ({
modalParams,
handleCreateNewXBlock,
isRequestedModalView,
disabled,
disabledReason,
}) => {
const intl = useIntl();
const dispatch = useDispatch();
Expand All @@ -37,9 +39,11 @@ const ComponentModalView = ({
const renderAddComponentButton = () => (
<li>
<AddComponentButton
onClick={open}
onClick={disabled ? undefined : open}
type={type}
displayName={displayName}
disabled={disabled}
disabledReason={disabledReason}
/>
</li>
);
Expand Down Expand Up @@ -102,6 +106,8 @@ const ComponentModalView = ({

ComponentModalView.defaultProps = {
isRequestedModalView: false,
disabled: false,
disabledReason: null,
};

ComponentModalView.propTypes = {
Expand All @@ -111,6 +117,9 @@ ComponentModalView.propTypes = {
isOpen: PropTypes.bool,
}).isRequired,
handleCreateNewXBlock: PropTypes.func.isRequired,
isRequestedModalView: PropTypes.bool,
disabled: PropTypes.bool,
disabledReason: PropTypes.string,
component: PropTypes.shape({
displayName: PropTypes.string.isRequired,
category: PropTypes.string,
Expand All @@ -129,7 +138,6 @@ ComponentModalView.propTypes = {
showLegend: PropTypes.bool,
}),
}).isRequired,
isRequestedModalView: PropTypes.bool,
};

export default ComponentModalView;
5 changes: 5 additions & 0 deletions src/course-unit/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ export const useCourseUnit = ({
COURSE_BLOCK_NAMES.legacyLibraryContent.id,
COURSE_BLOCK_NAMES.itembank.id,
].includes(unitCategory);
// True for generic container XBlocks that are not one of the specially-handled types above.
// These containers can also host add-component buttons via the MFE native strip.
const isGenericContainerType = !isUnitVerticalType && !isSplitTestType && !isProblemBankType &&
!isUnitLegacyLibraryType && !!unitCategory;

const headerNavigationsActions = {
handleViewLive: () => {
Expand Down Expand Up @@ -259,6 +263,7 @@ export const useCourseUnit = ({
isUnitLegacyLibraryType,
isSplitTestType,
isProblemBankType,
isGenericContainerType,
sharedClipboardData,
showPasteXBlock,
showPasteUnit,
Expand Down