diff --git a/src/course-outline/CourseOutline.jsx b/src/course-outline/CourseOutline.jsx
index 2d17118c4..b0c9844dd 100644
--- a/src/course-outline/CourseOutline.jsx
+++ b/src/course-outline/CourseOutline.jsx
@@ -211,6 +211,7 @@ const CourseOutline = ({ courseId }) => {
background: 'white',
padding: '1.75rem',
marginBottom: '1.5rem',
+ borderRadius: '0.35rem',
boxShadow: '0 0 .125rem rgba(0, 0, 0, .15), 0 0 .25rem rgba(0, 0, 0, .15)',
}}
>
@@ -244,6 +245,7 @@ const CourseOutline = ({ courseId }) => {
background: '#f8f7f6',
padding: '1rem 1.5rem',
marginBottom: '1.5rem',
+ borderRadius: '0.35rem',
boxShadow: '0 0 .125rem rgba(0, 0, 0, .15), 0 0 .25rem rgba(0, 0, 0, .15)',
}}
>
diff --git a/src/course-outline/card-header/messages.js b/src/course-outline/card-header/messages.js
index 0197722dd..751ba4815 100644
--- a/src/course-outline/card-header/messages.js
+++ b/src/course-outline/card-header/messages.js
@@ -21,6 +21,10 @@ const messages = defineMessages({
id: 'course-authoring.course-outline.card.status-badge.draft',
defaultMessage: 'Draft',
},
+ statusBadgeUnpublishedChanges: {
+ id: 'course-authoring.course-outline.card.status-badge.draft-unpublished-changes',
+ defaultMessage: 'Draft (Unpublished changes)',
+ },
altButtonEdit: {
id: 'course-authoring.course-outline.card.button.edit.alt',
defaultMessage: 'Edit',
diff --git a/src/course-outline/constants.js b/src/course-outline/constants.js
index bf3ec53ef..e14752726 100644
--- a/src/course-outline/constants.js
+++ b/src/course-outline/constants.js
@@ -1,12 +1,11 @@
export const ITEM_BADGE_STATUS = /** @type {const} */ ({
live: 'live',
publishedNotLive: 'published_not_live',
+ unpublishedChanges: 'unpublished_changes',
staffOnly: 'staff_only',
draft: 'draft',
});
-export const STAFF_ONLY = 'staff_only';
-
export const HIGHLIGHTS_FIELD_MAX_LENGTH = 250;
export const CHECKLIST_FILTERS = /** @type {const} */ ({
diff --git a/src/course-outline/section-card/SectionCard.jsx b/src/course-outline/section-card/SectionCard.jsx
index 2498bb527..61f54f9b1 100644
--- a/src/course-outline/section-card/SectionCard.jsx
+++ b/src/course-outline/section-card/SectionCard.jsx
@@ -51,10 +51,7 @@ const SectionCard = ({
displayName,
hasChanges,
published,
- releasedToStudents,
- visibleToStaffOnly = false,
visibilityState,
- staffOnlyMessage,
highlights,
actions,
isHeaderVisible = true,
@@ -63,10 +60,8 @@ const SectionCard = ({
const sectionStatus = getItemStatus({
published,
- releasedToStudents,
- visibleToStaffOnly,
visibilityState,
- staffOnlyMessage,
+ hasChanges,
});
const handleExpandContent = () => {
@@ -110,7 +105,7 @@ const SectionCard = ({
>
@@ -190,10 +185,7 @@ SectionCard.propTypes = {
displayName: PropTypes.string.isRequired,
published: PropTypes.bool.isRequired,
hasChanges: PropTypes.bool.isRequired,
- releasedToStudents: PropTypes.bool.isRequired,
- visibleToStaffOnly: PropTypes.bool,
visibilityState: PropTypes.string.isRequired,
- staffOnlyMessage: PropTypes.bool.isRequired,
highlights: PropTypes.arrayOf(PropTypes.string).isRequired,
shouldScroll: PropTypes.bool,
explanatoryMessage: PropTypes.string,
diff --git a/src/course-outline/section-card/SectionCard.test.jsx b/src/course-outline/section-card/SectionCard.test.jsx
index 41dfba995..05789f5f6 100644
--- a/src/course-outline/section-card/SectionCard.test.jsx
+++ b/src/course-outline/section-card/SectionCard.test.jsx
@@ -10,7 +10,6 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import initializeStore from '../../store';
import SectionCard from './SectionCard';
-import cardHeaderMessages from '../card-header/messages';
// eslint-disable-next-line no-unused-vars
let axiosMock;
@@ -20,10 +19,7 @@ const section = {
id: '123',
displayName: 'Section Name',
published: true,
- releasedToStudents: true,
- visibleToStaffOnly: false,
- visibilityState: 'visible',
- staffOnlyMessage: false,
+ visibilityState: 'live',
hasChanges: false,
highlights: ['highlight 1', 'highlight 2'],
actions: {
@@ -111,53 +107,6 @@ describe('', () => {
expect(onEditSectionSubmit).toHaveBeenCalled();
});
- it('renders live status', async () => {
- const { findByText } = renderComponent();
- expect(await findByText(cardHeaderMessages.statusBadgeLive.defaultMessage)).toBeInTheDocument();
- });
-
- it('renders published but live status', async () => {
- const { findByText } = renderComponent({
- section: {
- ...section,
- published: true,
- releasedToStudents: false,
- visibleToStaffOnly: false,
- visibilityState: 'visible',
- staffOnlyMessage: false,
- },
- });
- expect(await findByText(cardHeaderMessages.statusBadgePublishedNotLive.defaultMessage)).toBeInTheDocument();
- });
-
- it('renders staff status', async () => {
- const { findByText } = renderComponent({
- section: {
- ...section,
- published: false,
- releasedToStudents: false,
- visibleToStaffOnly: true,
- visibilityState: 'staff_only',
- staffOnlyMessage: true,
- },
- });
- expect(await findByText(cardHeaderMessages.statusBadgeStaffOnly.defaultMessage)).toBeInTheDocument();
- });
-
- it('renders draft status', async () => {
- const { findByText } = renderComponent({
- section: {
- ...section,
- published: false,
- releasedToStudents: false,
- visibleToStaffOnly: false,
- visibilityState: 'staff_only',
- staffOnlyMessage: false,
- },
- });
- expect(await findByText(cardHeaderMessages.statusBadgeDraft.defaultMessage)).toBeInTheDocument();
- });
-
it('hides header based on isHeaderVisible flag', async () => {
const { queryByTestId } = renderComponent({
section: {
diff --git a/src/course-outline/subsection-card/SubsectionCard.jsx b/src/course-outline/subsection-card/SubsectionCard.jsx
index 86dbab1aa..f6b0b943e 100644
--- a/src/course-outline/subsection-card/SubsectionCard.jsx
+++ b/src/course-outline/subsection-card/SubsectionCard.jsx
@@ -35,10 +35,7 @@ const SubsectionCard = ({
displayName,
hasChanges,
published,
- releasedToStudents,
- visibleToStaffOnly = false,
visibilityState,
- staffOnlyMessage,
actions,
isHeaderVisible = true,
} = subsection;
@@ -46,10 +43,8 @@ const SubsectionCard = ({
const [isExpanded, setIsExpanded] = useState(!isHeaderVisible);
const subsectionStatus = getItemStatus({
published,
- releasedToStudents,
- visibleToStaffOnly,
visibilityState,
- staffOnlyMessage,
+ hasChanges,
});
const handleExpandContent = () => {
@@ -154,10 +149,7 @@ SubsectionCard.propTypes = {
displayName: PropTypes.string.isRequired,
published: PropTypes.bool.isRequired,
hasChanges: PropTypes.bool.isRequired,
- releasedToStudents: PropTypes.bool.isRequired,
- visibleToStaffOnly: PropTypes.bool,
visibilityState: PropTypes.string.isRequired,
- staffOnlyMessage: PropTypes.bool.isRequired,
shouldScroll: PropTypes.bool,
}).isRequired,
subsection: PropTypes.shape({
@@ -165,10 +157,7 @@ SubsectionCard.propTypes = {
displayName: PropTypes.string.isRequired,
published: PropTypes.bool.isRequired,
hasChanges: PropTypes.bool.isRequired,
- releasedToStudents: PropTypes.bool.isRequired,
- visibleToStaffOnly: PropTypes.bool,
visibilityState: PropTypes.string.isRequired,
- staffOnlyMessage: PropTypes.bool.isRequired,
shouldScroll: PropTypes.bool,
actions: PropTypes.shape({
deletable: PropTypes.bool.isRequired,
diff --git a/src/course-outline/subsection-card/SubsectionCard.test.jsx b/src/course-outline/subsection-card/SubsectionCard.test.jsx
index 82d464e96..800cfafb8 100644
--- a/src/course-outline/subsection-card/SubsectionCard.test.jsx
+++ b/src/course-outline/subsection-card/SubsectionCard.test.jsx
@@ -10,6 +10,7 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import initializeStore from '../../store';
import SubsectionCard from './SubsectionCard';
+import cardHeaderMessages from '../card-header/messages';
// eslint-disable-next-line no-unused-vars
let axiosMock;
@@ -19,10 +20,7 @@ const section = {
id: '123',
displayName: 'Section Name',
published: true,
- releasedToStudents: true,
- visibleToStaffOnly: false,
- visibilityState: 'visible',
- staffOnlyMessage: false,
+ visibilityState: 'live',
hasChanges: false,
highlights: ['highlight 1', 'highlight 2'],
};
@@ -31,10 +29,7 @@ const subsection = {
id: '123',
displayName: 'Subsection Name',
published: true,
- releasedToStudents: true,
- visibleToStaffOnly: false,
- visibilityState: 'visible',
- staffOnlyMessage: false,
+ visibilityState: 'live',
hasChanges: false,
actions: {
draggable: true,
@@ -161,4 +156,42 @@ describe('', () => {
expect(within(element).queryByTestId('subsection-card-header__menu-delete-button')).not.toBeInTheDocument();
expect(queryByTestId('new-unit-button')).not.toBeInTheDocument();
});
+
+ it('renders live status', async () => {
+ const { findByText } = renderComponent();
+ expect(await findByText(cardHeaderMessages.statusBadgeLive.defaultMessage)).toBeInTheDocument();
+ });
+
+ it('renders published but live status', async () => {
+ const { findByText } = renderComponent({
+ subsection: {
+ ...subsection,
+ published: true,
+ visibilityState: 'ready',
+ },
+ });
+ expect(await findByText(cardHeaderMessages.statusBadgePublishedNotLive.defaultMessage)).toBeInTheDocument();
+ });
+
+ it('renders staff status', async () => {
+ const { findByText } = renderComponent({
+ subsection: {
+ ...subsection,
+ published: false,
+ visibilityState: 'staff_only',
+ },
+ });
+ expect(await findByText(cardHeaderMessages.statusBadgeStaffOnly.defaultMessage)).toBeInTheDocument();
+ });
+
+ it('renders draft status', async () => {
+ const { findByText } = renderComponent({
+ subsection: {
+ ...subsection,
+ published: false,
+ visibilityState: 'needs_attention',
+ },
+ });
+ expect(await findByText(cardHeaderMessages.statusBadgeDraft.defaultMessage)).toBeInTheDocument();
+ });
});
diff --git a/src/course-outline/unit-card/UnitCard.jsx b/src/course-outline/unit-card/UnitCard.jsx
index 9a2c8e927..0abb1943d 100644
--- a/src/course-outline/unit-card/UnitCard.jsx
+++ b/src/course-outline/unit-card/UnitCard.jsx
@@ -31,20 +31,15 @@ const UnitCard = ({
displayName,
hasChanges,
published,
- releasedToStudents,
- visibleToStaffOnly = false,
visibilityState,
- staffOnlyMessage,
actions,
isHeaderVisible = true,
} = unit;
const unitStatus = getItemStatus({
published,
- releasedToStudents,
- visibleToStaffOnly,
visibilityState,
- staffOnlyMessage,
+ hasChanges,
});
const handleClickMenuButton = () => {
@@ -124,10 +119,7 @@ UnitCard.propTypes = {
displayName: PropTypes.string.isRequired,
published: PropTypes.bool.isRequired,
hasChanges: PropTypes.bool.isRequired,
- releasedToStudents: PropTypes.bool.isRequired,
- visibleToStaffOnly: PropTypes.bool,
visibilityState: PropTypes.string.isRequired,
- staffOnlyMessage: PropTypes.bool.isRequired,
shouldScroll: PropTypes.bool,
actions: PropTypes.shape({
deletable: PropTypes.bool.isRequired,
@@ -142,10 +134,7 @@ UnitCard.propTypes = {
displayName: PropTypes.string.isRequired,
published: PropTypes.bool.isRequired,
hasChanges: PropTypes.bool.isRequired,
- releasedToStudents: PropTypes.bool.isRequired,
- visibleToStaffOnly: PropTypes.bool,
visibilityState: PropTypes.string.isRequired,
- staffOnlyMessage: PropTypes.bool.isRequired,
shouldScroll: PropTypes.bool,
}).isRequired,
section: PropTypes.shape({
@@ -153,10 +142,7 @@ UnitCard.propTypes = {
displayName: PropTypes.string.isRequired,
published: PropTypes.bool.isRequired,
hasChanges: PropTypes.bool.isRequired,
- releasedToStudents: PropTypes.bool.isRequired,
- visibleToStaffOnly: PropTypes.bool,
visibilityState: PropTypes.string.isRequired,
- staffOnlyMessage: PropTypes.bool.isRequired,
shouldScroll: PropTypes.bool,
}).isRequired,
onOpenPublishModal: PropTypes.func.isRequired,
diff --git a/src/course-outline/unit-card/UnitCard.test.jsx b/src/course-outline/unit-card/UnitCard.test.jsx
index a18799009..8fc2dd321 100644
--- a/src/course-outline/unit-card/UnitCard.test.jsx
+++ b/src/course-outline/unit-card/UnitCard.test.jsx
@@ -19,10 +19,7 @@ const section = {
id: '1',
displayName: 'Section Name',
published: true,
- releasedToStudents: true,
- visibleToStaffOnly: false,
- visibilityState: 'visible',
- staffOnlyMessage: false,
+ visibilityState: 'live',
hasChanges: false,
highlights: ['highlight 1', 'highlight 2'],
};
@@ -31,10 +28,7 @@ const subsection = {
id: '12',
displayName: 'Subsection Name',
published: true,
- releasedToStudents: true,
- visibleToStaffOnly: false,
- visibilityState: 'visible',
- staffOnlyMessage: false,
+ visibilityState: 'live',
hasChanges: false,
};
@@ -42,10 +36,7 @@ const unit = {
id: '123',
displayName: 'unit Name',
published: true,
- releasedToStudents: true,
- visibleToStaffOnly: false,
- visibilityState: 'visible',
- staffOnlyMessage: false,
+ visibilityState: 'live',
hasChanges: false,
actions: {
draggable: true,
diff --git a/src/course-outline/utils.jsx b/src/course-outline/utils.jsx
index 481dd1f72..2dccd59b3 100644
--- a/src/course-outline/utils.jsx
+++ b/src/course-outline/utils.jsx
@@ -4,35 +4,31 @@ import {
EditOutline as EditOutlineIcon,
} from '@edx/paragon/icons';
-import { ITEM_BADGE_STATUS, STAFF_ONLY, VIDEO_SHARING_OPTIONS } from './constants';
+import { ITEM_BADGE_STATUS, VIDEO_SHARING_OPTIONS } from './constants';
+import { VisibilityTypes } from '../data/constants';
/**
* Get section status depended on section info
* @param {bool} published - value from section info
- * @param {bool} releasedToStudents - value from section info
- * @param {bool} visibleToStaffOnly - value from section info
* @param {string} visibilityState - value from section info
- * @param {bool} staffOnlyMessage - value from section info
* @returns {ITEM_BADGE_STATUS[keyof ITEM_BADGE_STATUS]}
*/
const getItemStatus = ({
published,
- releasedToStudents,
- visibleToStaffOnly,
visibilityState,
- staffOnlyMessage,
+ hasChanges,
}) => {
switch (true) {
- case published && releasedToStudents:
- return ITEM_BADGE_STATUS.live;
- case published && !releasedToStudents:
- return ITEM_BADGE_STATUS.publishedNotLive;
- case visibleToStaffOnly && staffOnlyMessage && visibilityState === STAFF_ONLY:
+ case visibilityState === VisibilityTypes.STAFF_ONLY:
return ITEM_BADGE_STATUS.staffOnly;
- case !published:
- return ITEM_BADGE_STATUS.draft;
+ case visibilityState === VisibilityTypes.LIVE:
+ return ITEM_BADGE_STATUS.live;
+ case published && !hasChanges:
+ return ITEM_BADGE_STATUS.publishedNotLive;
+ case published && hasChanges:
+ return ITEM_BADGE_STATUS.unpublishedChanges;
default:
- return '';
+ return ITEM_BADGE_STATUS.draft;
}
};
@@ -61,6 +57,11 @@ const getItemStatusBadgeContent = (status, messages, intl) => {
badgeTitle: intl.formatMessage(messages.statusBadgeStaffOnly),
badgeIcon: LockIcon,
};
+ case ITEM_BADGE_STATUS.unpublishedChanges:
+ return {
+ badgeTitle: intl.formatMessage(messages.statusBadgeUnpublishedChanges),
+ badgeIcon: EditOutlineIcon,
+ };
case ITEM_BADGE_STATUS.draft:
return {
badgeTitle: intl.formatMessage(messages.statusBadgeDraft),
diff --git a/src/data/constants.js b/src/data/constants.js
index bd01f09dd..c025510b4 100644
--- a/src/data/constants.js
+++ b/src/data/constants.js
@@ -5,7 +5,7 @@
* @readonly
* @enum {string}
*/
-export const RequestStatus = {
+export const RequestStatus = /** @type {const} */ ({
IN_PROGRESS: 'in-progress',
SUCCESSFUL: 'successful',
FAILED: 'failed',
@@ -14,36 +14,37 @@ export const RequestStatus = {
CLEAR: 'clear',
PARTIAL: 'partial',
NOT_FOUND: 'not-found',
-};
+});
/**
* Team sizes enum
* @enum
* @type {{MIN: number, MAX: number, DEFAULT: number}}
*/
-export const TeamSizes = {
+export const TeamSizes = /** @type {const} */ ({
DEFAULT: 5,
MIN: 1,
MAX: 500,
-};
+});
/**
* Group types enum
* @enum
* @type {{PRIVATE_MANAGED: string, PUBLIC_MANAGED: string, OPEN: string}}
*/
-export const GroupTypes = {
+export const GroupTypes = /** @type {const} */ ({
OPEN: 'open',
PUBLIC_MANAGED: 'public_managed',
PRIVATE_MANAGED: 'private_managed',
-};
+});
-export const DivisionSchemes = {
+export const DivisionSchemes = /** @type {const} */ ({
NONE: 'none',
COHORT: 'cohort',
-};
+});
-export const VisibilityTypes = {
+export const VisibilityTypes = /** @type {const} */ ({
+ LIVE: 'live',
STAFF_ONLY: 'staff_only',
HIDE_AFTER_DUE: 'hide_after_due',
-};
+});