Browse Source

fix: fix datePicker disabledDate bug #1592 (#1593)

Co-authored-by: shijia.me <[email protected]>
走鹃 2 years ago
parent
commit
2b83aa870b

+ 19 - 10
cypress/integration/datePicker.spec.js

@@ -122,7 +122,7 @@ describe('DatePicker', () => {
     });
 
     it('insetInput + date', () => {
-        cy.visit('http://localhost:6006/iframe.html?id=datepicker--inset-input-e-2-e&args=&viewMode=story');   
+        cy.visit('http://localhost:6006/iframe.html?id=datepicker--inset-input-e-2-e&args=&viewMode=story');
         cy.get('[data-cy=date] .semi-input').click();
         cy.get('.semi-popover .semi-input-wrapper-focus').type("2021-12-15");
         cy.get('.semi-datepicker-day-selected').contains("15");
@@ -135,7 +135,7 @@ describe('DatePicker', () => {
     });
 
     it('insetInput + month', () => {
-        cy.visit('http://localhost:6006/iframe.html?id=datepicker--inset-input-e-2-e&args=&viewMode=story');   
+        cy.visit('http://localhost:6006/iframe.html?id=datepicker--inset-input-e-2-e&args=&viewMode=story');
         cy.get('[data-cy=month] .semi-input').click();
         cy.get('.semi-popover .semi-input-wrapper-focus');
         cy.get('.semi-popover .semi-input').should("have.value", "2021-12");
@@ -157,7 +157,7 @@ describe('DatePicker', () => {
     });
 
     it('insetInput + dateTime', () => {
-        cy.visit('http://localhost:6006/iframe.html?id=datepicker--inset-input-e-2-e&args=&viewMode=story');   
+        cy.visit('http://localhost:6006/iframe.html?id=datepicker--inset-input-e-2-e&args=&viewMode=story');
         cy.get('[data-cy=dateTime] .semi-input').click();
         cy.get('.semi-datepicker-day').contains("5").click();
         cy.get('.semi-popover .semi-input').eq(0).should("have.value", "2021-12-05");
@@ -169,7 +169,7 @@ describe('DatePicker', () => {
     });
 
     it('insetInput + dateRange', () => {
-        cy.visit('http://localhost:6006/iframe.html?id=datepicker--inset-input-e-2-e&args=&viewMode=story');   
+        cy.visit('http://localhost:6006/iframe.html?id=datepicker--inset-input-e-2-e&args=&viewMode=story');
         // 点击开始日期 trigger
         cy.get('[data-cy=dateRange] .semi-input').eq(0).click();
         // 查看聚焦
@@ -194,7 +194,7 @@ describe('DatePicker', () => {
     });
 
     it('insetInput + dateTimeRange', () => {
-        cy.visit('http://localhost:6006/iframe.html?id=datepicker--inset-input-e-2-e&args=&viewMode=story');   
+        cy.visit('http://localhost:6006/iframe.html?id=datepicker--inset-input-e-2-e&args=&viewMode=story');
         // 滚动到视窗
         cy.get('[data-cy=dateTimeRange]').scrollIntoView();
         // 点击开始日期 trigger
@@ -224,7 +224,7 @@ describe('DatePicker', () => {
     });
 
     it('insetInput + dateTimeRange + check click range start ', () => {
-        cy.visit('http://localhost:6006/iframe.html?id=datepicker--inset-input-e-2-e&args=&viewMode=story');   
+        cy.visit('http://localhost:6006/iframe.html?id=datepicker--inset-input-e-2-e&args=&viewMode=story');
         cy.get('[data-cy=dateTimeRange]').scrollIntoView();
         cy.get('[data-cy=dateTimeRange] .semi-input').eq(0).click();
         cy.get('.semi-popover .semi-datepicker-month-grid-right').contains('10').click();
@@ -237,7 +237,7 @@ describe('DatePicker', () => {
     });
 
     it('insetInput + dateTimeRange + check click range end', () => {
-        cy.visit('http://localhost:6006/iframe.html?id=datepicker--inset-input-e-2-e&args=&viewMode=story');   
+        cy.visit('http://localhost:6006/iframe.html?id=datepicker--inset-input-e-2-e&args=&viewMode=story');
         cy.get('[data-cy=dateTimeRange]').scrollIntoView();
         cy.get('[data-cy=dateTimeRange] .semi-input').eq(1).click();
         cy.get('.semi-popover .semi-datepicker-month-grid-right').contains('10').click();
@@ -250,7 +250,7 @@ describe('DatePicker', () => {
     });
 
     it('insetInput + custom format', () => {
-        cy.visit('http://localhost:6006/iframe.html?id=datepicker--inset-input-e-2-e&args=&viewMode=story');   
+        cy.visit('http://localhost:6006/iframe.html?id=datepicker--inset-input-e-2-e&args=&viewMode=story');
         cy.get('[data-cy=customFormat]').scrollIntoView();
         cy.get('[data-cy=customFormat] .semi-input').eq(0).click();
         cy.get('.semi-popover .semi-datepicker-month-grid-left').contains('5').click();
@@ -265,7 +265,7 @@ describe('DatePicker', () => {
     it('insetInput + trigger input disabled', () => {
         cy.visit('http://localhost:6006/iframe.html?id=datepicker--inset-input-e-2-e&args=&viewMode=story');
         const testCases = ['date', 'dateTime', 'dateRange', 'dateTimeRange', 'month'];
-        
+
         for (let item of testCases) {
             const wrapper = `[data-cy=${item}]`;
             cy.get(wrapper).scrollIntoView();
@@ -607,7 +607,7 @@ describe('DatePicker', () => {
         cy.get('.semi-scrolllist-body .semi-scrolllist-item:nth-child(1) .semi-scrolllist-item-sel').should('contain.text', '2021年');
         cy.get('.semi-scrolllist-body .semi-scrolllist-item:nth-child(2) .semi-scrolllist-item-sel').should('contain.text', '11月');
         cy.get('.semi-input').eq(0).should('have.value', '2021-11');
-        
+
         cy.get('.semi-scrolllist-body .semi-scrolllist-item:nth-child(2) li').contains('12').click({ force: true });
         cy.get('.semi-scrolllist-body .semi-scrolllist-item:nth-child(1) li').contains('2022').click({ force: true });
         cy.wait(100);
@@ -766,4 +766,13 @@ describe('DatePicker', () => {
         cy.get('.semi-popover .semi-datepicker-month-grid-right .semi-datepicker-day-disabled').contains('27');
         cy.get('.semi-popover .semi-datepicker-month-grid-right .semi-datepicker-day-disabled').contains('26').should('not.exist');
     });
+
+    it('test disabled date', () => {
+        cy.visit('http://localhost:6006/iframe.html?id=datepicker--fix-disabled-date&viewMode=story');
+        cy.get('.semi-input').eq(0).click();
+        cy.get('.semi-popover .semi-datepicker-month-grid-left .semi-datepicker-day').contains('3').click();
+        cy.get('.semi-popover .semi-datepicker-month-grid-left .semi-datepicker-day').contains('10').click();
+        cy.get('.semi-input').eq(0).should('have.value', '2023-05-03');
+        cy.get('.semi-input').eq(1).should('have.value', '2023-05-10');
+    });
 });

+ 9 - 46
packages/semi-foundation/datePicker/foundation.ts

@@ -178,7 +178,8 @@ export interface DatePickerFoundationState {
     /** value of trigger input */
     inputValue: string;
     value: Date[];
-    cachedSelectedValue: Date[];
+    // Save last selected date, maybe include null
+    cachedSelectedValue: (Date | null)[];
     prevTimeZone: string | number;
     rangeInputFocus: RangeType;
     autofocus: boolean;
@@ -357,41 +358,6 @@ export default class DatePickerFoundation extends BaseFoundation<DatePickerAdapt
         }
     }
 
-    /**
-     * @deprecated
-     * do these side effects when type is dateRange or dateTimeRange
-     *   1. trigger input blur, if input value is invalid, set input value and state value to previous status
-     *   2. set cachedSelectedValue using given dates(in needConfirm mode)
-     *      - directly closePanel without click confirm will set cachedSelectedValue to state value
-     *      - select one date(which means that the selection value is incomplete) and click confirm also set cachedSelectedValue to state value
-     */
-    // rangeTypeSideEffectsWhenClosePanel(inputValue: string, willUpdateDates: Date[]) {
-    //     if (this._isRangeType()) {
-    //         this._adapter.setRangeInputFocus(false);
-    //         /**
-    //          * inputValue is string when it is not disabled or can't parsed
-    //          * when inputValue is null, picker value will back to last selected value
-    //          */
-    //         this.handleInputBlur(inputValue);
-    //         this.resetCachedSelectedValue(willUpdateDates);
-    //     }
-    // }
-
-    /**
-     * @deprecated
-     * clear input value when selected date is not confirmed
-     */
-    // needConfirmSideEffectsWhenClosePanel(willUpdateDates: Date[] | null | undefined) {
-    //     if (this._adapter.needConfirm() && !this._isRangeType()) {
-    //         /**
-    //          * if `null` input element will show `cachedSelectedValue` formatted value(format in DateInput render)
-    //          * if `` input element will show `` directly
-    //          */
-    //         this._adapter.updateInputValue(null);
-    //         this.resetCachedSelectedValue(willUpdateDates);
-    //     }
-    // }
-
     /**
      * clear inset input value when close panel
      */
@@ -1218,32 +1184,29 @@ export default class DatePickerFoundation extends BaseFoundation<DatePickerAdapt
 
     /**
      * Get the date changed through the date panel or enter
-     * @param {Date[]} dates
-     * @returns {Date[]}
      */
     _getChangedDates(dates: Date[]) {
         const type = this._adapter.getProp('type');
-        const stateValue: Date[] = this._adapter.getState('value');
-
+        const { cachedSelectedValue: lastDate } = this._adapter.getStates();
         const changedDates = [];
 
         switch (type) {
             case 'dateRange':
             case 'dateTimeRange':
-                const [stateStart, stateEnd] = stateValue;
+                const [lastStart, lastEnd] = lastDate;
                 const [start, end] = dates;
-                if (!isDateEqual(start, stateStart)) {
+                if (!isDateEqual(start, lastStart)) {
                     changedDates.push(start);
                 }
-                if (!isDateEqual(end, stateEnd)) {
+                if (!isDateEqual(end, lastEnd)) {
                     changedDates.push(end);
                 }
                 break;
             default:
-                const stateValueSet = new Set<number>();
-                stateValue.forEach(value => stateValueSet.add(isDate(value) && value.valueOf()));
+                const lastValueSet = new Set<number>();
+                lastDate.forEach(value => lastValueSet.add(isDate(value) && value.valueOf()));
                 for (const date of dates) {
-                    if (!stateValueSet.has(isDate(date) && date.valueOf())) {
+                    if (!lastValueSet.has(isDate(date) && date.valueOf())) {
                         changedDates.push(date);
                     }
                 }

+ 36 - 0
packages/semi-ui/datePicker/_story/v2/FixDisabledDate.tsx

@@ -0,0 +1,36 @@
+import React from 'react';
+import { DatePicker } from '@douyinfe/semi-ui';
+import * as dateFns from 'date-fns';
+
+App.storyName = 'fix disabled date';
+/**
+ * test with cypress, please do not modify
+ * @returns 
+ */
+export default function App() {
+    const today = new Date('2023-05-06');
+    const disabledDate = (date, options) => {
+        const { rangeInputFocus } = options;
+        const baseDate = dateFns.set(today, { hours: 0, minutes: 0, seconds: 0, milliseconds: 0 });
+        if (rangeInputFocus === 'rangeStart') {
+            const disabledStart = dateFns.subDays(baseDate, 2);
+            const disabledEnd = dateFns.addDays(baseDate, 2);
+            return disabledStart <= date && date <= disabledEnd;
+        } else if (rangeInputFocus === 'rangeEnd') {
+            const disabledStart = dateFns.subDays(baseDate, 3);
+            const disabledEnd = dateFns.addDays(baseDate, 3);
+            const result = disabledStart <= date && date <= disabledEnd;
+            console.log('date', date, options, result);
+            return result;
+        } else {
+            return false;
+        }
+    };
+
+    return (
+        <div>
+            <h4>{`开始日期禁用今天前2日和后2日,结束日期禁用今天前3天和后3天`}</h4>
+            <DatePicker motion={false} type='dateRange' disabledDate={disabledDate} defaultPickerValue={today} />
+        </div>
+    );
+}

+ 1 - 0
packages/semi-ui/datePicker/_story/v2/index.js

@@ -23,3 +23,4 @@ export { default as FeatRefClass } from './FeatRefClass';
 export { default as FixNeedConfirmInTabs } from './FixNeedConfirmInTabs';
 export { default as DynamicDisabledDate } from './dynamicDisabledDate';
 export { default as FeatEtcGMT } from './FeatEtcGMT';
+export { default as FixDisabledDate } from './FixDisabledDate';

+ 2 - 3
packages/semi-ui/datePicker/datePicker.tsx

@@ -217,7 +217,7 @@ export default class DatePicker extends BaseComponent<DatePickerProps, DatePicke
             isRange: false,
             inputValue: null, // Staging input values
             value: [], // The currently selected date, each date is a Date object
-            cachedSelectedValue: null, // Save last selected date, maybe include null
+            cachedSelectedValue: [], // Save last selected date, maybe include null
             prevTimeZone: null,
             rangeInputFocus: undefined, // Optional'rangeStart ',' rangeEnd ', false
             autofocus: props.autoFocus || (this.isRangeType(props.type, props.triggerRender) && (props.open || props.defaultOpen)),
@@ -225,7 +225,6 @@ export default class DatePicker extends BaseComponent<DatePickerProps, DatePicke
             triggerDisabled: undefined,
         };
 
-        this.adapter.setCache('cachedSelectedValue', null);
         this.triggerElRef = React.createRef();
         this.panelRef = React.createRef();
         this.monthGrid = React.createRef();
@@ -580,7 +579,7 @@ export default class DatePicker extends BaseComponent<DatePickerProps, DatePicke
             defaultPickerValue
         };
 
-        return insetInput ? <DateInput {...props} insetInput={insetInput}/> : null;
+        return insetInput ? <DateInput {...props} insetInput={insetInput} /> : null;
     }
 
     handleOpenPanel = () => this.foundation.openPanel();