Skip to content
Merged
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
1 change: 1 addition & 0 deletions src/course-outline/CourseOutline.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ const CourseOutline = ({ courseId }) => {
onConfigureSubmit={handleConfigureItemSubmit}
currentItemData={currentItemData}
enableProctoredExams={enableProctoredExams}
isSelfPaced={statusBarData.isSelfPaced}
/>
<DeleteModal
category={deleteCategory}
Expand Down
11 changes: 11 additions & 0 deletions src/course-outline/CourseOutline.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,7 @@ describe('<CourseOutline />', () => {
publish: 'republish',
metadata: {
visible_to_staff_only: isVisibleToStaffOnly,
discussion_enabled: false,
group_access: newGroupAccess,
},
})
Expand All @@ -1427,6 +1428,7 @@ describe('<CourseOutline />', () => {

// after configuraiton response
unit.visibilityState = 'staff_only';
unit.discussion_enabled = false;
unit.userPartitionInfo = {
selectablePartitions: [
{
Expand Down Expand Up @@ -1469,6 +1471,11 @@ describe('<CourseOutline />', () => {
)).toBeInTheDocument();
let visibilityCheckbox = await within(configureModal).findByTestId('unit-visibility-checkbox');
await act(async () => fireEvent.click(visibilityCheckbox));
let discussionCheckbox = await within(configureModal).findByLabelText(
configureModalMessages.discussionEnabledCheckbox.defaultMessage,
);
expect(discussionCheckbox).toBeChecked();
await act(async () => fireEvent.click(discussionCheckbox));

let groupeType = await within(configureModal).findByTestId('group-type-select');
fireEvent.change(groupeType, { target: { value: '0' } });
Expand All @@ -1485,6 +1492,10 @@ describe('<CourseOutline />', () => {
configureModal = await findByTestId('configure-modal');
visibilityCheckbox = await within(configureModal).findByTestId('unit-visibility-checkbox');
expect(visibilityCheckbox).toBeChecked();
discussionCheckbox = await within(configureModal).findByLabelText(
configureModalMessages.discussionEnabledCheckbox.defaultMessage,
);
expect(discussionCheckbox).not.toBeChecked();

groupeType = await within(configureModal).findByTestId('group-type-select');
expect(groupeType).toHaveValue('0');
Expand Down
3 changes: 2 additions & 1 deletion src/course-outline/data/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,14 +311,15 @@ export async function configureCourseSubsection(
* @param {object} groupAccess
* @returns {Promise<Object>}
*/
export async function configureCourseUnit(unitId, isVisibleToStaffOnly, groupAccess) {
export async function configureCourseUnit(unitId, isVisibleToStaffOnly, groupAccess, discussionEnabled) {
const { data } = await getAuthenticatedHttpClient()
.post(getCourseItemApiUrl(unitId), {
publish: 'republish',
metadata: {
// The backend expects metadata.visible_to_staff_only to either true or null
visible_to_staff_only: isVisibleToStaffOnly ? true : null,
group_access: groupAccess,
discussion_enabled: discussionEnabled,
},
});

Expand Down
4 changes: 2 additions & 2 deletions src/course-outline/data/thunk.js
Original file line number Diff line number Diff line change
Expand Up @@ -334,11 +334,11 @@ export function configureCourseSubsectionQuery(
};
}

export function configureCourseUnitQuery(itemId, sectionId, isVisibleToStaffOnly, groupAccess) {
export function configureCourseUnitQuery(itemId, sectionId, isVisibleToStaffOnly, groupAccess, discussionEnabled) {
return async (dispatch) => {
dispatch(configureCourseItemQuery(
sectionId,
async () => configureCourseUnit(itemId, isVisibleToStaffOnly, groupAccess),
async () => configureCourseUnit(itemId, isVisibleToStaffOnly, groupAccess, discussionEnabled),
));
};
}
Expand Down
1 change: 1 addition & 0 deletions src/course-outline/highlights-modal/HighlightsModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const HighlightsModal = ({
onClose={onClose}
hasCloseButton
isFullscreenOnMobile
isOverflowVisible={false}
>
<ModalDialog.Header className="highlights-modal__header">
<ModalDialog.Title>
Expand Down
1 change: 1 addition & 0 deletions src/course-outline/publish-modal/PublishModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const PublishModal = ({
onClose={onClose}
hasCloseButton
isFullscreenOnMobile
isOverflowVisible={false}
>
<ModalDialog.Header className="publish-modal__header">
<ModalDialog.Title>
Expand Down
86 changes: 47 additions & 39 deletions src/generic/configure-modal/BasicTab.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const BasicTab = ({
setFieldValue,
courseGraders,
isSubsection,
isSelfPaced,
}) => {
const intl = useIntl();

Expand All @@ -27,26 +28,30 @@ const BasicTab = ({

return (
<>
<h5 className="mt-4 text-gray-700"><FormattedMessage {...messages.releaseDateAndTime} /></h5>
<hr />
<div data-testid="release-date-stack">
<Stack className="mt-3" direction="horizontal" gap={5}>
<DatepickerControl
type={DATEPICKER_TYPES.date}
value={releaseDate}
label={intl.formatMessage(messages.releaseDate)}
controlName="state-date"
onChange={(val) => setFieldValue('releaseDate', val)}
/>
<DatepickerControl
type={DATEPICKER_TYPES.time}
value={releaseDate}
label={intl.formatMessage(messages.releaseTimeUTC)}
controlName="start-time"
onChange={(val) => setFieldValue('releaseDate', val)}
/>
</Stack>
</div>
{!isSelfPaced && (
<>
<h5 className="mt-4 text-gray-700"><FormattedMessage {...messages.releaseDateAndTime} /></h5>
<hr />
<div data-testid="release-date-stack">
<Stack className="mt-3" direction="horizontal" gap={5}>
<DatepickerControl
type={DATEPICKER_TYPES.date}
value={releaseDate}
label={intl.formatMessage(messages.releaseDate)}
controlName="state-date"
onChange={(val) => setFieldValue('releaseDate', val)}
/>
<DatepickerControl
type={DATEPICKER_TYPES.time}
value={releaseDate}
label={intl.formatMessage(messages.releaseTimeUTC)}
controlName="start-time"
onChange={(val) => setFieldValue('releaseDate', val)}
/>
</Stack>
</div>
</>
)}
{
isSubsection && (
<div>
Expand All @@ -66,25 +71,27 @@ const BasicTab = ({
{createOptions()}
</Form.Control>
</Form.Group>
<div data-testid="due-date-stack">
<Stack className="mt-3" direction="horizontal" gap={5}>
<DatepickerControl
type={DATEPICKER_TYPES.date}
value={dueDate}
label={intl.formatMessage(messages.dueDate)}
controlName="state-date"
onChange={(val) => setFieldValue('dueDate', val)}
data-testid="due-date-picker"
/>
<DatepickerControl
type={DATEPICKER_TYPES.time}
value={dueDate}
label={intl.formatMessage(messages.dueTimeUTC)}
controlName="start-time"
onChange={(val) => setFieldValue('dueDate', val)}
/>
</Stack>
</div>
{!isSelfPaced && (
<div data-testid="due-date-stack">
<Stack className="mt-3" direction="horizontal" gap={5}>
<DatepickerControl
type={DATEPICKER_TYPES.date}
value={dueDate}
label={intl.formatMessage(messages.dueDate)}
controlName="state-date"
onChange={(val) => setFieldValue('dueDate', val)}
data-testid="due-date-picker"
/>
<DatepickerControl
type={DATEPICKER_TYPES.time}
value={dueDate}
label={intl.formatMessage(messages.dueTimeUTC)}
controlName="start-time"
onChange={(val) => setFieldValue('dueDate', val)}
/>
</Stack>
</div>
)}
</div>
)
}
Expand All @@ -101,6 +108,7 @@ BasicTab.propTypes = {
}).isRequired,
courseGraders: PropTypes.arrayOf(PropTypes.string).isRequired,
setFieldValue: PropTypes.func.isRequired,
isSelfPaced: PropTypes.bool.isRequired,
};

export default injectIntl(BasicTab);
11 changes: 10 additions & 1 deletion src/generic/configure-modal/ConfigureModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const ConfigureModal = ({
currentItemData,
enableProctoredExams,
isXBlockComponent,
isSelfPaced,
}) => {
const intl = useIntl();
const {
Expand Down Expand Up @@ -58,6 +59,7 @@ const ConfigureModal = ({
supportsOnboarding,
showReviewRules,
onlineProctoringRules,
discussionEnabled,
} = currentItemData;

const getSelectedGroups = () => {
Expand Down Expand Up @@ -98,6 +100,7 @@ const ConfigureModal = ({
// by default it is -1 i.e. accessible to all learners & staff
selectedPartitionIndex: userPartitionInfo?.selectedPartitionIndex,
selectedGroups: getSelectedGroups(),
discussionEnabled,
};

const validationSchema = Yup.object().shape({
Expand Down Expand Up @@ -127,6 +130,7 @@ const ConfigureModal = ({
).nullable(true),
selectedPartitionIndex: Yup.number().integer(),
selectedGroups: Yup.array().of(Yup.string()),
discussionEnabled: Yup.boolean(),
});

const isSubsection = category === COURSE_BLOCK_NAMES.sequential.id;
Expand Down Expand Up @@ -168,7 +172,7 @@ const ConfigureModal = ({
const partitionId = userPartitionInfo.selectablePartitions[data.selectedPartitionIndex].id;
groupAccess[partitionId] = data.selectedGroups.map(g => parseInt(g, 10));
}
onConfigureSubmit(data.isVisibleToStaffOnly, groupAccess);
onConfigureSubmit(data.isVisibleToStaffOnly, groupAccess, data.discussionEnabled);
break;
default:
break;
Expand All @@ -186,6 +190,7 @@ const ConfigureModal = ({
setFieldValue={setFieldValue}
isSubsection={isSubsection}
courseGraders={courseGraders === 'undefined' ? [] : courseGraders}
isSelfPaced={isSelfPaced}
/>
</Tab>
<Tab eventKey="visibility" title={intl.formatMessage(messages.visibilityTabTitle)}>
Expand All @@ -208,6 +213,7 @@ const ConfigureModal = ({
setFieldValue={setFieldValue}
isSubsection={isSubsection}
courseGraders={courseGraders === 'undefined' ? [] : courseGraders}
isSelfPaced={isSelfPaced}
/>
</Tab>
<Tab eventKey="visibility" title={intl.formatMessage(messages.visibilityTabTitle)}>
Expand Down Expand Up @@ -259,6 +265,7 @@ const ConfigureModal = ({
onClose={onClose}
hasCloseButton
isFullscreenOnMobile
isOverflowVisible={false}
>
<div data-testid="configure-modal">
<ModalDialog.Header className="configure-modal__header">
Expand Down Expand Up @@ -358,8 +365,10 @@ ConfigureModal.propTypes = {
supportsOnboarding: PropTypes.bool,
showReviewRules: PropTypes.bool,
onlineProctoringRules: PropTypes.string,
discussionEnabled: PropTypes.bool.isRequired,
}).isRequired,
isXBlockComponent: PropTypes.bool,
isSelfPaced: PropTypes.bool.isRequired,
};

export default ConfigureModal;
14 changes: 13 additions & 1 deletion src/generic/configure-modal/ConfigureModal.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const renderComponent = () => render(
onClose={onCloseMock}
onConfigureSubmit={onConfigureSubmitMock}
currentItemData={currentSectionMock}
isSelfPaced={false}
/>
</IntlProvider>,
</AppProvider>,
Expand Down Expand Up @@ -85,14 +86,16 @@ describe('<ConfigureModal /> for Section', () => {
});
});

const renderSubsectionComponent = () => render(
const renderSubsectionComponent = (props) => render(
<AppProvider store={store}>
<IntlProvider locale="en">
<ConfigureModal
isOpen
onClose={onCloseMock}
onConfigureSubmit={onConfigureSubmitMock}
currentItemData={currentSubsectionMock}
isSelfPaced={false}
{...props}
/>
</IntlProvider>,
</AppProvider>,
Expand Down Expand Up @@ -129,6 +132,14 @@ describe('<ConfigureModal /> for Subsection', () => {
expect(getByRole('button', { name: messages.saveButton.defaultMessage })).toBeInTheDocument();
});

it('hides release and due dates for self paced courses', () => {
const { queryByText } = renderSubsectionComponent({ isSelfPaced: true });
expect(queryByText(messages.releaseDate.defaultMessage)).not.toBeInTheDocument();
expect(queryByText(messages.releaseTimeUTC.defaultMessage)).not.toBeInTheDocument();
expect(queryByText(messages.dueDate.defaultMessage)).not.toBeInTheDocument();
expect(queryByText(messages.dueTimeUTC.defaultMessage)).not.toBeInTheDocument();
});

it('switches to the subsection Visibility tab and renders correctly', () => {
const { getByRole, getByText } = renderSubsectionComponent();

Expand Down Expand Up @@ -198,6 +209,7 @@ describe('<ConfigureModal /> for Unit', () => {
expect(getByText(`${currentUnitMock.displayName} settings`)).toBeInTheDocument();
expect(getByText(messages.unitVisibility.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.hideFromLearners.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.discussionEnabledCheckbox.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.restrictAccessTo.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.unitSelectGroupType.defaultMessage)).toBeInTheDocument();

Expand Down
Loading
Loading