Description
Description
It looks like I made a lot of terrible mistakes while making the Date and Time pickers.
- too hard to customize the format
- formatting doesn't work in Safari or iOS with dates before 1583 due to a bug in Intl.DateTimeFormat or Safari itself.
- restricting dates is too hard
- limited customizations and re-styling is hard
- no text field implementation, so it is annoying on desktop to select a date
- clicking on a readonly textfield in iOS actually focuses the text field and keeps focus there
- selecting a specific year within the DatePicker using the "Year Picker" side of the DatePicker has almost 0 user discoverability
- Time pickers don't even work most of the time
- almost 0 keyboard accessibility
- I didn't know much about javascript dates to begin with
- probably lots of other bugs
Related issues: #634, #370 , #193, #287, #435
Things to consider for the future
Add isDateDisabled
Since it is too hard to disable specific dates, it would be a lot easier to add an additional isDateDisabled
prop that provides the current date and must return true or false if the date is disabled.
For example:
const bookedDates = [ pretend a list of dates that are booked ];
const isDateDisabled = date => bookedDates.indexOf(date) !== -1 || date.getDate() === 6;
// This would make sure that any dates in the list of dates or the 6th day of each month would be disabled
This would and should still work with the minDate
and maxDate
props.
Only accept date objects, or date strings that are formatted in ISO-8601 format (iffy?)
The main reason for this change is to make sure that whatever date is initially applied IS at the specific instance wanted. It still causes problems since it is a loose ISO-8601 compliance.
new Date('2018-01-01'); // This could be Dec 31, 2017 for some time zones
new Date('2018-01-01T00:00:00'); // this would be Dec 31, 2017 in most Safari browsers and timezones with a negative offset. (-01:00, -02:00, ...) since it is actually considered UTC instead of local time
The second problem can be fixed by updating the string to include the current local timezone offset to the string, but it's a bit extra work.
Expose more of the components used to generate the date picker
This will solve some of the customization problems.
Make a Calendar Component
This one would be really nice since having calendars outside of DatePickers would be nice. One of the big features would be to be able to render any child into a date "block" so that additional information could be supplied to each date in the calendar. This would probably be something like:
// render a list of booked items in the date "block"
const getDateChildren = date => {
if (bookings.indexOf(date) !== -1) {
return <List>{getBookingItems(date)}</List>;
}
return null;
};
Handle Daylight Savings Time Changes
Some bugs that can happen is if the user sets a date that is within DST when the user is currently not in DST or vice-versa that will cause the hour to be off by 1, which can cause a different day to be displayed. I will probably have to do some updates to check if there is DST change between two dates.
// TODO: Finish adding considerations and reminders
Intl.DateTimeFormat bug
const formatter1 = new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'numeric',
day: 'numeric',
});
const formatter2 = new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
});
// January 1, 1583 08:00 AM MST
const date1583 = new Date('1583-01-01T08:00:00-07:00');
// January 1, 1582 08:00 AM MST
const date1582 = new Date('1582-01-01T08:00:00-07:00');
const expected1 = '1/1/1583';
const expected2 = '1/1/1583, 8:00 AM';
const expected3 = '1/1/1582';
const expected4 = '1/1/1582, 8:00 AM';
expect(formatter1.format(date1583)).toBe(expected1);
expect(formatter2.format(date1583)).toBe(expected2);
expect(formatter1.format(date1583)).toBe(expected3);
expect(formatter2.format(date1583)).toBe(expected4);
// This will pass for most browsers and node versions. However in Safari or mobile safari, you actually get
expect(formatter1.format(date1583)).toBe(expected1);
expect(formatter2.format(date1583)).toBe(expected2);
expect(formatter1.format(date1582)).toBe('12/22/1581');
expect(formatter2.format(date1582)).toBe('12/22/1581, 8:00 AM');
// Any date before 1583 fails in Safari/Mobile Safari **regardless** of the config provided.
new Intl.DateTimeFormat('en-US', { month: 'long' }).format(date1582); // 'December'
date1582.toLocaleString(); // '12/22/1581, 8:00:04 AM'