refactor: course outline badge status logic

This commit is contained in:
Navin Karkera
2024-01-10 16:18:39 +05:30
committed by Kristin Aoki
parent b417cd64a0
commit eb0c61ce6d
11 changed files with 83 additions and 136 deletions

View File

@@ -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)',
}}
>

View File

@@ -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',

View File

@@ -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} */ ({

View File

@@ -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 = ({
>
<BaseTitleWithStatusBadge
title={displayName}
status={sectionStatus}
status=""
namePrefix={namePrefix}
/>
</TitleButton>
@@ -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,

View File

@@ -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('<SectionCard />', () => {
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: {

View File

@@ -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,

View File

@@ -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('<SubsectionCard />', () => {
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();
});
});

View File

@@ -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,

View File

@@ -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,

View File

@@ -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),

View File

@@ -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',
};
});