Skip to content

Commit

Permalink
fix: ignore dates set in studio edit modal when non-manual date confi…
Browse files Browse the repository at this point in the history
…g is set
  • Loading branch information
jansenk committed Sep 9, 2024
1 parent 8dbf14c commit 6260140
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 14 deletions.
40 changes: 40 additions & 0 deletions openassessment/xblock/static/js/spec/studio/oa_edit_fields.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,46 @@ describe("OpenAssessment.DatetimeControl", function() {
datetimeControl.clearValidationErrors();
expect(datetimeControl.validationErrors()).toEqual([]);
});

describe("stash and pop", function() {
const dates = ["2024-09-09", "2030-03-02", "2031-03-05"]
const times = ["05:28", "01:01", "02:13"]

beforeEach(function() {
datetimeControl.datetime(dates[0], times[0]);
expect(datetimeControl.describe()).toEqual(`${dates[0]}T${times[0]}`);
})

it('update values correctly', function() {
datetimeControl.stash(dates[1], times[1]);
expect(datetimeControl.describe()).toEqual(`${dates[1]}T${times[1]}`);

datetimeControl.pop()
expect(datetimeControl.describe()).toEqual(`${dates[0]}T${times[0]}`);
});

it('stashing multiple times does nothing until popping', function() {
datetimeControl.stash(dates[1], times[1]);
expect(datetimeControl.describe()).toEqual(`${dates[1]}T${times[1]}`);
datetimeControl.stash(dates[2], times[2]);
expect(datetimeControl.describe()).toEqual(`${dates[1]}T${times[1]}`);

datetimeControl.pop()
expect(datetimeControl.describe()).toEqual(`${dates[0]}T${times[0]}`);
});

it('popping multiple times does nothing', function() {
datetimeControl.stash(dates[1], times[1]);
expect(datetimeControl.describe()).toEqual(`${dates[1]}T${times[1]}`);

datetimeControl.pop()
expect(datetimeControl.describe()).toEqual(`${dates[0]}T${times[0]}`);

datetimeControl.pop()
expect(datetimeControl.describe()).toEqual(`${dates[0]}T${times[0]}`);
});
})

});

describe("OpenAssessment.ToggleControl", function() {
Expand Down
60 changes: 60 additions & 0 deletions openassessment/xblock/static/js/spec/studio/oa_edit_schedule.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,66 @@ describe('OpenAssessment.EditScheduleView', function() {
expect(courseEndEl.prop('disabled')).toBe(true);
});

it('stashes and restores manual dates on date config change', function() {
const dateConfigRadios = {
manual: $('input#manual_date_config_type', this.element),
subsection: $('input#subsection_date_config_type', this.element),
course_end: $('input#course_end_date_config_type', this.element),
}
function expectSelectedRadio(expectedValue) {
// Helper to cleanly assert the currently selected radio button
expect($('input[name="date_config_type"][type="radio"]:checked').val()).toEqual(expectedValue);
}
function getCurrentValues() {
// Helper to get the current state of the controls as an object
return {
submission: {
start: view.submissionStart(),
due: view.submissionDue(),
},
self: {
start: assessmentViews.oa_self_assessment_editor.startDatetimeControl.datetime(),
due: assessmentViews.oa_self_assessment_editor.dueDatetimeControl.datetime(),
},
peer: {
start: assessmentViews.oa_peer_assessment_editor.startDatetimeControl.datetime(),
due: assessmentViews.oa_peer_assessment_editor.dueDatetimeControl.datetime(),
}
}
}
// If we select a non-manual setting, the current manual values should be stashed, and the
// values of the controls should be set to a default, to avoid any backend validation issues.
const startingValues = getCurrentValues();
expectSelectedRadio('manual')
dateConfigRadios.subsection.trigger('click');
expectSelectedRadio('subsection');

const expectedPostStashValues = {
submission: {
start: "2001-01-01",
due: "2099-12-31",
},
self: {
start: "2001-01-01",
due: "2099-12-31",
},
peer: {
start: "2001-01-01",
due: "2099-12-31",
},
}

expect(getCurrentValues()).toEqual(expectedPostStashValues);
// clicking course end now should have no effect
dateConfigRadios.course_end.trigger('click');
expectSelectedRadio('course_end');
expect(getCurrentValues()).toEqual(expectedPostStashValues);
// clicking manual will pop the stashed values back into the controls
dateConfigRadios.manual.trigger('click');
expectSelectedRadio('manual');
expect(getCurrentValues()).toEqual(startingValues);
});

it('validates submission start datetime fields', function() {
testValidateDate(
view.startDatetimeControl,
Expand Down
26 changes: 26 additions & 0 deletions openassessment/xblock/static/js/src/studio/oa_edit_fields.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ export class DatetimeControl {
this.element = element;
this.datePicker = datePicker;
this.timePicker = timePicker;
this.stashedDate = null;
this.stashedTime = null;
}

/**
Expand Down Expand Up @@ -234,6 +236,30 @@ export class DatetimeControl {
return `${datePickerSel.val()}T${timePickerSel.val()}`;
}

hasStash() {
return this.stashedDate !== null && this.stashedTime !== null;
}

stash(dateString) {
if (this.hasStash()) { return; }
const datePickerSel = $(this.datePicker, this.element);
const timePickerSel = $(this.timePicker, this.element);
this.stashedDate = datePickerSel.val();
this.stashedTime = timePickerSel.val();
datePickerSel.val(dateString);
timePickerSel.val('00:00');
}

pop() {
if (!this.hasStash()) { return; }
const datePickerSel = $(this.datePicker, this.element);
const timePickerSel = $(this.timePicker, this.element);
datePickerSel.val(this.stashedDate);
timePickerSel.val(this.stashedTime);
this.stashedDate = null;
this.stashedTime = null;
}

/**
Mark validation errors.
Expand Down
105 changes: 91 additions & 14 deletions openassessment/xblock/static/js/src/studio/oa_edit_schedule.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { DatetimeControl, SelectControl } from './oa_edit_fields';
import Notifier from './oa_edit_notifier';
/**
Editing interface for OpenAssessment schedule settings.
Expand Down Expand Up @@ -30,23 +29,11 @@ export class EditScheduleView {
'#openassessment_submission_due_time',
).install();

const configSettingsList = $('.schedule_setting_list ', this.element);

this.selectedDateConfigType = $('input[name="date_config_type"][type="radio"]:checked', this.element).val();

new SelectControl(
$('input[name="date_config_type"][type="radio"]', this.element),
(value) => {
configSettingsList.each((_, el) => {
if (el.id !== `${value}_schedule_settings_list`) {
$(el).addClass('is--hidden');
} else {
$(el).removeClass('is--hidden');
}
});
this.selectedDateConfigType = value;
},
new Notifier([]),
this.handleDateTimeConfigChanged,
).install();
}

Expand Down Expand Up @@ -85,12 +72,96 @@ export class EditScheduleView {
}

/**
* Handles the date config selection being changed.
* "Stashes" the current start and due dates and displays the correct date settings panel.
*
* @param {string} value The currently selected date config type, one of [ manual, subsection, course_end ]
*/
handleDateTimeConfigChanged(value) {
if (value === 'manual') {
this.popManualDates();
} else {
this.stashManualDates();
}
this.showSelectedDateConfigSettings(value);
this.selectedDateConfigType = value;
}

/**
* Displays the schedule_setting_list entry for the associated date config type and hides the others
*
* @param {string} value The currently selected date config type, one of [ manual, subsection, course_end ]
*/
showSelectedDateConfigSettings(value) {
$('.schedule_setting_list ', this.element).each((_, el) => {
if (el.id !== `${value}_schedule_settings_list`) {
$(el).addClass('is--hidden');
} else {
$(el).removeClass('is--hidden');
}
});
}

/**
* "Stashes" the current values of all manual start and due date settings and replaces the value of the disabled
* input elements with default date values.
*
* Calling this function once will do the stash, and calling it repeatedly after that will have no effect until
* `popManualDates` is called
*
* This allows us to essentially ignore all date validation for non-manual date config on the backend
*/
stashManualDates() {
const defaultStartDate = '2001-01-01';
const defaultDueDate = '2099-12-31';

this.startDatetimeControl.stash(defaultStartDate);
this.dueDatetimeControl.stash(defaultDueDate);

const peerStep = this.assessmentViews.oa_peer_assessment_editor;
peerStep.startDatetimeControl.stash(defaultStartDate);
peerStep.dueDatetimeControl.stash(defaultDueDate);

const selfStep = this.assessmentViews.oa_self_assessment_editor;
selfStep.startDatetimeControl.stash(defaultStartDate);
selfStep.dueDatetimeControl.stash(defaultDueDate);
}

/**
* "Pops" the stashed values of all manual start and due date settings back into the actual input controls.
*
* Calling this function once will perform the pop action.
* Calling it repeatedly after that will have no effect until`stashManualDates` is called again,
* and if `stashManualDates` has never been called, this function will have no effect.
*/

popManualDates() {
this.startDatetimeControl.pop();
this.dueDatetimeControl.pop();

const peerStep = this.assessmentViews.oa_peer_assessment_editor;
peerStep.startDatetimeControl.pop();
peerStep.dueDatetimeControl.pop();

const selfStep = this.assessmentViews.oa_self_assessment_editor;
selfStep.startDatetimeControl.pop();
selfStep.dueDatetimeControl.pop();
}

/**
* Returns the current date config type
*/
dateConfigType() {
return this.selectedDateConfigType;
}

/**
* Returns true if the current date config type is manual
*/
isManualDateConfig() {
return this.dateConfigType() === 'manual';
}

/**
Mark validation errors.
Expand All @@ -99,6 +170,12 @@ export class EditScheduleView {
* */
validate() {
// If we are using a non-manual date config type, don't validate
// the hidden fields
if (!this.isManualDateConfig()) {
return true;
}

// Validate the start and due datetime controls
let isValid = true;

Expand Down

0 comments on commit 6260140

Please sign in to comment.