Results 1 to 8 of 8

Thread: Datefield / Datepickerfield double bug with minDate / maxDate - Modern 6.6.0

    You found a bug! We've classified it as EXTJS-27515 . We encourage you to continue the discussion and to find an acceptable workaround while we work on a permanent fix.
  1. #1
    Sencha Premium User
    Join Date
    Jun 2018
    Posts
    21

    Default Datefield / Datepickerfield double bug with minDate / maxDate - Modern 6.6.0

    Ext version tested:
    6.6.0 Modern Toolkit

    Browsers tested:
    Chrome
    Edge

    Fiddle:



    Description:

    I wanted to make a date range field using two datefields / datepickerfields for the starting date and ending date, like this Classic Kitchen Sink example. However, I am using the Modern toolkit, not Classic, so I was not able to use VTypes. Instead, I used change listeners to set either the min or max date for the opposite field. If a start date is selected, that selected date should be set as the minDate for the end date field, and if an end date is selected, that should be set as the maxDate for the start date field.

    There are two issues, likely bugs with Ext.field.Date, that come up here.

    The first is that setting either a minDate or maxDate in a different month than that of a datefield's current value messes up the month navigation. You must navigate in the opposite direction of the minDate or maxDate month in order to then navigate towards it.

    The second is that when a minDate or maxDate is set in a month other than that of a datefield's current value, invalid days are disabled (as they should be), but they remain disabled even when the minDate / maxDate that made them invalid is set to a different day that would make them valid.


    Steps to reproduce the problem:

    Issue #1: Navigation Issue:

    Navigation issue with minDate:

    1. Open up the date picker for the starting date, move back one month, and pick any day.
    2. Open up the date picker for the ending date, and try to move back one month. You will not be able to, despite there being valid days to select in that month.
    3. To successfully move back one month, you must move forward two months, and then move back three. Yes, "what the heck" indeed.

    The navigation issue with maxDate is pretty much the same, just in reverse. However, I wasn't able to reproduce this one as reliably.

    -Expected Behavior-
    I can move back a month to pick valid dates.

    -Actual Behavior-
    I can't.


    Issue #2: Dates Remaining Disabled After minDate is Changed:

    1. Do steps 1-3 of Issue #1 above. You should be in the same month as the minDate that was set, and you should see that the dates before whatever you selected are disabled.
    2. Select one of the remaining valid dates.
    3. Open up the date picker for the starting date, and choose an earlier date in the same month.
    4. Open up the date picker for the ending date. Despite the minDate being pushed back, the disabled dates from the first minDate remain disabled, and thus, there are dates that should be valid choices that are disabled.

    5. Interestingly, if you then move forward three months and back three months (yep), you will see that the dates that were previously disabled despite being valid are no longer disabled. The date picker seems to update properly when you move through multiple months.

    -Expected Behavior-
    The previously disabled dates that are now valid due to a change in minDate will no longer be disabled.

    -Actual Behavior-
    These valid dates remain disabled, unless multiple months are moved through in the date picker.

  2. #2
    Sencha Staff
    Join Date
    Sep 2017
    Posts
    126

    Default

    Thanks for the report! I have opened a bug in our bug tracker.

  3. #3

    Default

    I have the same problem.

  4. #4
    Sencha Premium User
    Join Date
    Jan 2018
    Posts
    12

    Default

    Facing the same issue! The datepickerfield is buggy since at least August 2017 ( see https://www.sencha.com/forum/showthr...ate-selectable ) - 1.5 years and still no response/fix.


  5. #5
    Sencha Premium User
    Join Date
    Jan 2018
    Posts
    12

    Default

    Any news on this? We need a fix please.

  6. #6
    Sencha Premium Member kimosabi's Avatar
    Join Date
    Jan 2008
    Location
    Perth, Western Australia
    Posts
    54

    Default

    I've had the same issues.

    The issue is in:

    Ext.panel.Date line refreshPanes - line 1597

    getPanes actually returns the number of panes, but this is wrong, returns 1 instead of the initial 3 (previous month, current month and next month).

    I think whoever wrote this, originally intended on getPanes to return an array of date views. But instead it returns some internal count of the panes that isn't correct anyway.

    So as a result the for loop to refresh all of the panes doesn't work, which is where the disabledDates magic kicks in.

    I've got this override in place to get it work on my projects. There are other bugs, like navigating immediately back in the first month not working when navigating to the second, and I've still got another override for DST support.

    I'll update this post as I make them. It's painstaking work stepping through the debugger.

    [Edit] I've included all of my overrides to the Date Panel and Date View classes, which also fix up DST issues, minDate, maxDate and disabledDates

    Code:
    Ext.define(null,{
        override: 'Ext.panel.Date',
        privates:{
            refreshPanes: function() {
                if (this.isConfiguring) {
                    return;
                }
              
                var i, panes = this.getInnerItems();
                //
                for (i = 0; i < panes.length; ++i) {
                    panes[i].refresh();
                }
            },
            applyDisabledDates: function(dates) {
                if (!dates) {
                    return dates;
                }
         
                // eslint-disable-next-line vars-on-top
                var cfg = {
                        dates: {}
                    },
                    re = [],
                    item, i, len;
         
                if (dates instanceof RegExp) {
                    cfg.re = dates;
                }
                else {
                    if (!Ext.isArray(dates)) {
                        dates = [dates];
                    }
         
                    for (i = 0, len = dates.length; i < len; i++) {
                        item = dates[i];
         
                        if (item instanceof Date) {
                            // disable clearing of time.
                            //item = Ext.Date.clearTime(item);
                            cfg.dates[item.getTime()] = true;
                        }
                        else if (item instanceof RegExp) {
                            re.push(item.source);
                        }
                        else {
                            re.push(Ext.String.escapeRegex(item));
                        }
                    }
         
                    if (re.length) {
                        cfg.re = new RegExp('(?:' + re.join('|') + ')');
                    }
                }
         
                return cfg;
            },
            isDateDisabled: function(date) {
               
                var me = this,
                    ms = date.getTime(),
                    minDate = me.getMinDate(),
                    maxDate = me.getMaxDate(),
                    disabled, disabledDays, disabledDates, formatted, re;
        
                disabledDates = me.getDisabledDates();
                
                if (minDate) minDate.setHours(12);
                if (maxDate) maxDate.setHours(12);
    
                disabled = (minDate && ms < minDate.getTime()) || (maxDate && ms > maxDate.getTime());
        
                if (!disabled) {
                    disabledDays = me.getDisabledDays();
        
                    if (disabledDays) {
                        disabled = disabledDays[date.getDay()];
                    }
                }
        
                if (!disabled) {
                    
                    if (disabledDates) {
                        
                        disabled = disabledDates.dates[ms];
                        re = disabledDates.re;
        
                        if (!disabled && re) {
                            formatted = Ext.Date.format(date, me.getFormat());
                            disabled = re.test(formatted);
                        }
                    }
                }
                
                return !!disabled;
            }
        }
    });
    Ext.define(null, {
        override: 'Ext.panel.DateView',
        refreshCell: function(params) {
            var me = this,
                cell = params.cell,
                date = params.date,
                dayOfWeek = date.getDay(),
                ms = date.getTime(),
                specialDates = params.specialDates,
                specialDays = params.specialDays,
                cls = [me.cellCls],
                formatted = Ext.Date.format(date, params.format),
                empty = params.outside && params.hideOutside,
                html, special, disabled;
    
            if (!empty) {
                if (params.outsidePrevious) {
                    cls.push(me.outsideCls, me.prevMonthCls);
                }
                else if (params.outsideNext) {
                    cls.push(me.outsideCls, me.nextMonthCls);
                }
                else {
                    cls.push(me.currentMonthCls);
    
                    // Today should not be marked in previous or next month
                    if (Ext.Date.isEqual(date, params.today)) {
                        cls.push(me.todayCls);
                    }
                }
    
                if (params.weekendDays[dayOfWeek]) {
                    cls.push(me.weekendDayCls);
                }
    
                if (!special && specialDays) {
                    special = specialDays[dayOfWeek];
                }
    
                if (specialDates) {
                    special = specialDates.dates[ms] ||
                              (specialDates.re && specialDates.re.test(formatted));
                }
    
                if (special) {
                    cls.push(me.specialDateCls);
                }
            }
            else {
                cls.push(me.emptyCls);
            }
           
            disabled = me.getParent().isDateDisabled(date);
           
            if (!empty && disabled) {
                cls.push(me.disabledDayCls);
            }
    
            cell.tabIndex = -1;
    
            if (empty) {
                html = ' ';
            }
            else {
                html = Ext.Date.format(date, params.dateCellFormat);
            }
    
            cell.firstChild.innerHTML = html;
    
            if (me.transformCellCls) {
                me.transformCellCls(date, cls);
            }
    
            cell.className = cls.join(' ');
    
            // We need this in event handlers
            cell.date = date;
            cell.disabled = disabled;
        },
        refresh: function() {
            var me = this,
                ExtDate = Ext.Date,
                cells = me.bodyCells,
                monthStart, startOffset, startDate, startDay, date,
                cellMap, cell, params, i, len, outPrev, outNext,
                currentMonth, month;
    
            // Calling getters might cause recursive refresh() calls, we don't want that
            if (me.refreshing) {
                return;
            }
    
            me.refreshing = true;
    
            monthStart = me.getMonth();
            monthStart.setHours(12);
            startDay = me.getStartDay();
            startOffset = startDay - monthStart.getDay();
    
            if (startOffset > 0) {
                startOffset -= 7;
            }
    
            startDate = ExtDate.add(monthStart, ExtDate.DAY, startOffset);
    
            cellMap = me.cellMap = {};
    
            currentMonth = monthStart.getMonth();
    
            params = {
                today: Ext.Date.clearTime(new Date()),
                weekendDays: me.getWeekendDays(),
                specialDates: me.getSpecialDates(),
                specialDays: me.getSpecialDays(),
                format: me.getFormat(),
                dateCellFormat: me.getDateCellFormat(),
                hideOutside: me.getHideOutside()
            };
    
            for (i = 0, len = cells.length; i < len; i++) {
                cell = cells[i];
    
                date = ExtDate.add(startDate, ExtDate.DAY, i);
    
                month = date.getMonth();
                outPrev = month < currentMonth;
                outNext = month > currentMonth;
    
                cellMap[ExtDate.clearTime(date, true).getTime()] = cell;
    
                params.cell = cell;
                params.date = date;
    
                params.outside = outPrev || outNext;
                params.outsidePrevious = outPrev;
                params.outsideNext = outNext;
    
                me.refreshCell(params);
            }
    
            me.captionElement.setHtml(Ext.Date.format(monthStart, me.getCaptionFormat()));
    
            me.refreshing = false;
        }
    });

  7. #7
    Sencha Premium Member kimosabi's Avatar
    Join Date
    Jan 2008
    Location
    Perth, Western Australia
    Posts
    54

    Default

    I managed to solve the previous month issue when skipping forward a month and you can't get back.

    Here is my full override for Date Panel and Date View.

    Code:
    Ext.define(null,{
        override: 'Ext.panel.Date',
        privates:{
            refreshPanes: function() {
                if (this.isConfiguring) {
                    return;
                }
              
                var i, panes = this.getInnerItems();
                //
                for (i = 0; i < panes.length; ++i) {
                    panes[i].refresh();
                }
            },
            applyDisabledDates: function(dates) {
                if (!dates) {
                    return dates;
                }
         
                // eslint-disable-next-line vars-on-top
                var cfg = {
                        dates: {}
                    },
                    re = [],
                    item, i, len;
         
                if (dates instanceof RegExp) {
                    cfg.re = dates;
                }
                else {
                    if (!Ext.isArray(dates)) {
                        dates = [dates];
                    }
         
                    for (i = 0, len = dates.length; i < len; i++) {
                        item = dates[i];
         
                        if (item instanceof Date) {
                            // disable clearing of time.
                            //item = Ext.Date.clearTime(item);
                            cfg.dates[item.getTime()] = true;
                        }
                        else if (item instanceof RegExp) {
                            re.push(item.source);
                        }
                        else {
                            re.push(Ext.String.escapeRegex(item));
                        }
                    }
         
                    if (re.length) {
                        cfg.re = new RegExp('(?:' + re.join('|') + ')');
                    }
                }
         
                return cfg;
            },
            isDateDisabled: function(date) {
               
                var me = this,
                    ms = date.getTime(),
                    minDate = me.getMinDate(),
                    maxDate = me.getMaxDate(),
                    disabled, disabledDays, disabledDates, formatted, re;
        
                disabledDates = me.getDisabledDates();
                
                if (minDate) minDate.setHours(12);
                if (maxDate) maxDate.setHours(12);
    
                disabled = (minDate && ms < minDate.getTime()) || (maxDate && ms > maxDate.getTime());
        
                if (!disabled) {
                    disabledDays = me.getDisabledDays();
        
                    if (disabledDays) {
                        disabled = disabledDays[date.getDay()];
                    }
                }
        
                if (!disabled) {
                    
                    if (disabledDates) {
                        
                        disabled = disabledDates.dates[ms];
                        re = disabledDates.re;
        
                        if (!disabled && re) {
                            formatted = Ext.Date.format(date, me.getFormat());
                            disabled = re.test(formatted);
                        }
                    }
                }
                
                return !!disabled;
            },
           getCenterIndex: function() {
                var count = this.getInnerItems().length,
                    index = count;
     
                return !index ? index : index % 2 ? Math.floor(index / 2) + 1 : Math.floor(index / 2);
            }
        }
    });
    Ext.define(null, {
        override: 'Ext.panel.DateView',
        refreshCell: function(params) {
            var me = this,
                cell = params.cell,
                date = params.date,
                dayOfWeek = date.getDay(),
                ms = date.getTime(),
                specialDates = params.specialDates,
                specialDays = params.specialDays,
                cls = [me.cellCls],
                formatted = Ext.Date.format(date, params.format),
                empty = params.outside && params.hideOutside,
                html, special, disabled;
    
            if (!empty) {
                if (params.outsidePrevious) {
                    cls.push(me.outsideCls, me.prevMonthCls);
                }
                else if (params.outsideNext) {
                    cls.push(me.outsideCls, me.nextMonthCls);
                }
                else {
                    cls.push(me.currentMonthCls);
    
                    // Today should not be marked in previous or next month
                    if (Ext.Date.isEqual(date, params.today)) {
                        cls.push(me.todayCls);
                    }
                }
    
                if (params.weekendDays[dayOfWeek]) {
                    cls.push(me.weekendDayCls);
                }
    
                if (!special && specialDays) {
                    special = specialDays[dayOfWeek];
                }
    
                if (specialDates) {
                    special = specialDates.dates[ms] ||
                              (specialDates.re && specialDates.re.test(formatted));
                }
    
                if (special) {
                    cls.push(me.specialDateCls);
                }
            }
            else {
                cls.push(me.emptyCls);
            }
           
            disabled = me.getParent().isDateDisabled(date);
           
            if (!empty && disabled) {
                cls.push(me.disabledDayCls);
            }
    
            cell.tabIndex = -1;
    
            if (empty) {
                html = ' ';
            }
            else {
                html = Ext.Date.format(date, params.dateCellFormat);
            }
    
            cell.firstChild.innerHTML = html;
    
            if (me.transformCellCls) {
                me.transformCellCls(date, cls);
            }
    
            cell.className = cls.join(' ');
    
            // We need this in event handlers
            cell.date = date;
            cell.disabled = disabled;
        },
        refresh: function() {
            var me = this,
                ExtDate = Ext.Date,
                cells = me.bodyCells,
                monthStart, startOffset, startDate, startDay, date,
                cellMap, cell, params, i, len, outPrev, outNext,
                currentMonth, month;
    
            // Calling getters might cause recursive refresh() calls, we don't want that
            if (me.refreshing) {
                return;
            }
    
            me.refreshing = true;
    
            monthStart = me.getMonth();
            monthStart.setHours(12);
            startDay = me.getStartDay();
            startOffset = startDay - monthStart.getDay();
    
            if (startOffset > 0) {
                startOffset -= 7;
            }
    
            startDate = ExtDate.add(monthStart, ExtDate.DAY, startOffset);
    
            cellMap = me.cellMap = {};
    
            currentMonth = monthStart.getMonth();
    
            params = {
                today: Ext.Date.clearTime(new Date()),
                weekendDays: me.getWeekendDays(),
                specialDates: me.getSpecialDates(),
                specialDays: me.getSpecialDays(),
                format: me.getFormat(),
                dateCellFormat: me.getDateCellFormat(),
                hideOutside: me.getHideOutside()
            };
    
            for (i = 0, len = cells.length; i < len; i++) {
                cell = cells[i];
    
                date = ExtDate.add(startDate, ExtDate.DAY, i);
    
                month = date.getMonth();
                outPrev = month < currentMonth;
                outNext = month > currentMonth;
    
                cellMap[ExtDate.clearTime(date, true).getTime()] = cell;
    
                params.cell = cell;
                params.date = date;
    
                params.outside = outPrev || outNext;
                params.outsidePrevious = outPrev;
                params.outsideNext = outNext;
    
                me.refreshCell(params);
            }
    
            me.captionElement.setHtml(Ext.Date.format(monthStart, me.getCaptionFormat()));
    
            me.refreshing = false;
        }
    });

  8. #8
    Sencha Premium Member
    Join Date
    Jul 2014
    Posts
    9

    Default

    Thank you kimosabi !

Similar Threads

  1. Replies: 1
    Last Post: 19 Mar 2017, 9:16 PM
  2. Replies: 0
    Last Post: 21 Nov 2016, 10:39 PM
  3. [Solved]Problem with mindate validation on form datefield
    By Neil Thole in forum Ext 2.x: Help & Discussion
    Replies: 14
    Last Post: 11 May 2012, 12:03 AM
  4. set minDate to value from another dateField?
    By NightAvatar in forum Ext 2.x: Help & Discussion
    Replies: 6
    Last Post: 31 May 2010, 8:00 PM

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •