Skip to content

Commit

Permalink
Change the fallback date behavior.
Browse files Browse the repository at this point in the history
Currently if a date is entered into a datepicker input that does not
parse with the format of the current locale (for example if a date is
typed without a time), then a fallback is filled in.  That fallback is
the last displayed valid value if there was one.  If there was an intial
value in the input, then that fallback will initially be the input's
initial value.  It is then updated anytime a new valid value is entered.
However, if there was no initial value for the input, the fallback ends
up being set to the last visible date in what is shown when the calender
popup opens.  That is sommewhat unexpected behavior.

So this changes the way that the fallback works.  If there is an initial
value for the input (as will be the case when editing a set for the
whole class), then that is still used for the initial fallback.
However, if there is no initial value (as will be the case when editing
the set for users), then the class value is used.  In any case, the
fallback will be updated to be the last valid value that is entered
after that.

There is a last case in which the input does not have an initial value
and there also is no class value.  In that case the fallback is the
current date and time.  This usually won't be used on the set detail
page, but applies to the import date shift input on the set list.
  • Loading branch information
drgrice1 committed Mar 18, 2024
1 parent 6910f96 commit aefa295
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 29 deletions.
46 changes: 22 additions & 24 deletions htdocs/js/DatePicker/datepicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@
const name = open_rule.name.replace('.open_date', '');

const groupRules = [
open_rule,
document.getElementById(`${name}.due_date_id`),
document.getElementById(`${name}.answer_date_id`)
[open_rule],
[document.getElementById(`${name}.due_date_id`)],
[document.getElementById(`${name}.answer_date_id`)]
];

const reduced_rule = document.getElementById(`${name}.reduced_scoring_date_id`);
if (reduced_rule) groupRules.splice(1, 0, reduced_rule);
if (reduced_rule) groupRules.splice(1, 0, [reduced_rule]);

// Compute the time difference between the current browser timezone and the course timezone.
// flatpickr gives the time in the browser's timezone, and this is used to adjust to the course timezone.
Expand All @@ -41,30 +41,31 @@
new Date().toLocaleString('en-US', { timeZone: open_rule.dataset.timezone ?? 'America/New_York' })
).getTime();

const classValues = groupRules.map(
(rule) =>
parseInt(document.getElementsByName(`${rule.name}.class_value`)[0]?.dataset.classValue || '0') * 1000 -
timezoneAdjustment
);
for (const rule of groupRules) {
const value =
rule[0].value || document.getElementsByName(`${rule[0].name}.class_value`)[0]?.dataset.classValue;
rule.push(value ? parseInt(value) * 1000 - timezoneAdjustment : 0);
}

const update = () => {
for (let i = 1; i < groupRules.length; ++i) {
const prevFieldDate =
groupRules[i - 1]?.parentNode._flatpickr.selectedDates[0]?.getTime() || classValues[i - 1];
groupRules[i - 1][0]?.parentNode._flatpickr.selectedDates[0]?.getTime() || groupRules[i - 1][1];
const thisFieldDate =
groupRules[i]?.parentNode._flatpickr.selectedDates[0]?.getTime() || classValues[i];
groupRules[i][0]?.parentNode._flatpickr.selectedDates[0]?.getTime() || groupRules[i][1];
if (prevFieldDate && thisFieldDate && prevFieldDate > thisFieldDate) {
groupRules[i].parentNode._flatpickr.setDate(prevFieldDate, true);
groupRules[i][0].parentNode._flatpickr.setDate(prevFieldDate, true);
}
}
};

for (const rule of groupRules) {
const orig_value = rule.value;
const orig_value = rule[0].value;
let fallbackDate = rule[1] ? new Date(rule[1]) : new Date();

luxon.Settings.defaultLocale = rule.dataset.locale ?? 'en';
luxon.Settings.defaultLocale = rule[0].dataset.locale ?? 'en';

const fp = flatpickr(rule.parentNode, {
const fp = flatpickr(rule[0].parentNode, {
allowInput: true,
enableTime: true,
minuteIncrement: 1,
Expand All @@ -82,15 +83,15 @@
disableMobile: true,
wrap: true,
plugins: [
new confirmDatePlugin({ confirmText: rule.dataset.doneText ?? 'Done', showAlways: true }),
new confirmDatePlugin({ confirmText: rule[0].dataset.doneText ?? 'Done', showAlways: true }),
new ShortcutButtonsPlugin({
button: [
{
label: rule.dataset.todayText ?? 'Today',
label: rule[0].dataset.todayText ?? 'Today',
attributes: { class: 'btn btn-sm btn-secondary ms-auto me-1 mb-1' }
},
{
label: rule.dataset.nowText ?? 'Now',
label: rule[0].dataset.nowText ?? 'Now',
attributes: { class: 'btn btn-sm btn-secondary me-auto mb-1' }
}
],
Expand Down Expand Up @@ -141,17 +142,14 @@
// Next attempt to parse the datestr with the current format. This should not be adjusted. It is
// for display only.
const date = luxon.DateTime.fromFormat(datestr.replaceAll(/\u202F/g, ' ').trim(), format);
if (date.isValid) return date.toJSDate();
if (date.isValid) fallbackDate = date.toJSDate();

// Finally, fall back to the previous value in the original input if that failed. This is the case
// that the user typed a time that isn't in the valid format. So fallback to the last valid time
// that was displayed. This also should not be adjusted.
return new Date(this.lastFormattedDate.getTime());
return fallbackDate;
},
formatDate(date, format) {
// Save this date for the fallback in parseDate.
this.lastFormattedDate = date;

// In this case the date provided is in the browser's time zone. So it needs to be adjusted to the
// timezone of the course.
if (format === 'U') return (date.getTime() + timezoneAdjustment) / 1000;
Expand All @@ -162,7 +160,7 @@
}
});

rule.nextElementSibling.addEventListener('keydown', (e) => {
rule[0].nextElementSibling.addEventListener('keydown', (e) => {
if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault();
fp.open();
Expand Down
11 changes: 6 additions & 5 deletions htdocs/js/ProblemSetList/problemsetlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@
new Date().toLocaleString('en-US', { timeZone: importDateShift.dataset.timezone ?? 'America/New_York' })
).getTime();

let fallbackDate = importDateShift.value
? new Date(parseInt(importDateShift.value) * 1000 - timezoneAdjustment)
: new Date();

const fp = flatpickr(importDateShift.parentNode, {
allowInput: true,
enableTime: true,
Expand Down Expand Up @@ -248,17 +252,14 @@
// Next attempt to parse the datestr with the current format. This should not be adjusted. It is
// for display only.
const date = luxon.DateTime.fromFormat(datestr.replaceAll(/\u202F/g, ' ').trim(), format);
if (date.isValid) return date.toJSDate();
if (date.isValid) fallbackDate = date.toJSDate();

// Finally, fall back to the previous value in the original input if that failed. This is the case
// that the user typed a time that isn't in the valid format. So fallback to the last valid time
// that was displayed. This also should not be adjusted.
return new Date(this.lastFormattedDate.getTime());
return fallbackDate;
},
formatDate(date, format) {
// Save this date for the fallback in parseDate.
this.lastFormattedDate = date;

// In this case the date provided is in the browser's time zone. So it needs to be adjusted to the
// timezone of the course.
if (format === 'U') return (date.getTime() + timezoneAdjustment) / 1000;
Expand Down

0 comments on commit aefa295

Please sign in to comment.