diff --git a/projects/demo-integrations/cypress/tests/kit/date-range/date-range-basic.cy.ts b/projects/demo-integrations/cypress/tests/kit/date-range/date-range-basic.cy.ts index a25544015..e68b53671 100644 --- a/projects/demo-integrations/cypress/tests/kit/date-range/date-range-basic.cy.ts +++ b/projects/demo-integrations/cypress/tests/kit/date-range/date-range-basic.cy.ts @@ -452,4 +452,31 @@ describe('DateRange | Basic', () => { ); }); }); + + describe('The 2nd date is less than the 1st one', () => { + it('If caret is at the end, swap dates: 31.12.2023 – 01.01.202| => Type 0 => 01.01.2020 – 31.12.2023|', () => { + cy.get('@input') + .type('31.12.2023-01.01.202') + .should('have.value', '31.12.2023 – 01.01.202') + .should('have.prop', 'selectionStart', '31.12.2023 – 01.01.202'.length) + .should('have.prop', 'selectionEnd', '31.12.2023 – 01.01.202'.length) + .type('0') + .should('have.value', '01.01.2020 – 31.12.2023') + .should('have.prop', 'selectionStart', '01.01.2020 – 31.12.2023'.length) + .should('have.prop', 'selectionEnd', '01.01.2020 – 31.12.2023'.length); + }); + + it('If caret is NOT at the end, do NOT swap dates: 11.11.201|1 – 12.12.2012 => Type 5 => 11.11.2015 – |12.12.2012', () => { + cy.get('@input') + .type('11112011-12122012') + .should('have.value', '11.11.2011 – 12.12.2012') + .should('have.prop', 'selectionStart', '11.11.2011 – 12.12.2012'.length) + .should('have.prop', 'selectionEnd', '11.11.2011 – 12.12.2012'.length) + .type('{leftArrow}'.repeat('1 – 12.12.2012'.length)) + .type('5') + .should('have.value', '11.11.2015 – 12.12.2012') + .should('have.prop', 'selectionStart', '11.11.2015 – '.length) + .should('have.prop', 'selectionEnd', '11.11.2015 – '.length); + }); + }); }); diff --git a/projects/demo-integrations/cypress/tests/kit/date-range/date-range-min-max.cy.ts b/projects/demo-integrations/cypress/tests/kit/date-range/date-range-min-max.cy.ts index 5651385f3..23610a338 100644 --- a/projects/demo-integrations/cypress/tests/kit/date-range/date-range-min-max.cy.ts +++ b/projects/demo-integrations/cypress/tests/kit/date-range/date-range-min-max.cy.ts @@ -70,16 +70,16 @@ describe('DateRange | Min & Max dates', () => { .should('have.prop', 'selectionEnd', '13.04.2001 – 08.03.2002'.length); }); - it('15.11.1997 - 15.12.199| => Type 3 => 15.11.1997 - 14.10.1995 (min)', () => { + it('15.11.1997 - 15.12.199| => Type 3 => (min date validation + dates swap) => 14.10.1995 - 15.11.1997', () => { cy.get('@input') .type('15.11.1997-15.12.199') .should('have.value', '15.11.1997 – 15.12.199') .should('have.prop', 'selectionStart', '15.11.1997 – 15.12.199'.length) .should('have.prop', 'selectionEnd', '15.11.1997 – 15.12.199'.length) .type('3') - .should('have.value', '15.11.1997 – 14.10.1995') - .should('have.prop', 'selectionStart', '15.11.1997 – 14.10.1995'.length) - .should('have.prop', 'selectionEnd', '15.11.1997 – 14.10.1995'.length); + .should('have.value', '14.10.1995 – 15.11.1997') + .should('have.prop', 'selectionStart', '14.10.1995 – 15.11.1997'.length) + .should('have.prop', 'selectionEnd', '14.10.1995 – 15.11.1997'.length); }); it('15.10.1995 - 1|7.10.1995 => Type 2 => 15.10.1995 - 14.|10.1995 (min)', () => { diff --git a/projects/kit/src/lib/masks/date-range/date-range-mask.ts b/projects/kit/src/lib/masks/date-range/date-range-mask.ts index 47297d0d2..2d1303bdf 100644 --- a/projects/kit/src/lib/masks/date-range/date-range-mask.ts +++ b/projects/kit/src/lib/masks/date-range/date-range-mask.ts @@ -7,7 +7,8 @@ import { createZeroPlaceholdersPreprocessor, } from '../../processors'; import {MaskitoDateMode, MaskitoDateSegments} from '../../types'; -import {createMinMaxRangeLengthPostprocessor} from './min-max-range-length-postprocessor'; +import {createMinMaxRangeLengthPostprocessor} from './processors/min-max-range-length-postprocessor'; +import {createSwapDatesPostprocessor} from './processors/swap-dates-postprocessor'; const DATE_RANGE_SEPARATOR = `${CHAR_NO_BREAK_SPACE}${CHAR_EN_DASH}${CHAR_NO_BREAK_SPACE}`; @@ -57,6 +58,10 @@ export function maskitoDateRangeOptionsGenerator({ max, datesSeparator: DATE_RANGE_SEPARATOR, }), + createSwapDatesPostprocessor({ + dateModeTemplate, + datesSeparator: DATE_RANGE_SEPARATOR, + }), ), }; } diff --git a/projects/kit/src/lib/masks/date-range/min-max-range-length-postprocessor.ts b/projects/kit/src/lib/masks/date-range/processors/min-max-range-length-postprocessor.ts similarity index 94% rename from projects/kit/src/lib/masks/date-range/min-max-range-length-postprocessor.ts rename to projects/kit/src/lib/masks/date-range/processors/min-max-range-length-postprocessor.ts index d36c02c5c..321f9a9f0 100644 --- a/projects/kit/src/lib/masks/date-range/min-max-range-length-postprocessor.ts +++ b/projects/kit/src/lib/masks/date-range/processors/min-max-range-length-postprocessor.ts @@ -1,7 +1,7 @@ import {MaskitoOptions} from '@maskito/core'; -import {DEFAULT_MAX_DATE} from '../../constants'; -import {MaskitoDateSegments} from '../../types'; +import {DEFAULT_MAX_DATE} from '../../../constants'; +import {MaskitoDateSegments} from '../../../types'; import { appendDate, clamp, @@ -12,7 +12,7 @@ import { parseDateString, segmentsToDate, toDateString, -} from '../../utils'; +} from '../../../utils'; export function createMinMaxRangeLengthPostprocessor({ dateModeTemplate, diff --git a/projects/kit/src/lib/masks/date-range/processors/swap-dates-postprocessor.ts b/projects/kit/src/lib/masks/date-range/processors/swap-dates-postprocessor.ts new file mode 100644 index 000000000..f6e68ad12 --- /dev/null +++ b/projects/kit/src/lib/masks/date-range/processors/swap-dates-postprocessor.ts @@ -0,0 +1,39 @@ +import {MaskitoOptions} from '@maskito/core'; + +import { + isDateStringComplete, + parseDateRangeString, + parseDateString, + segmentsToDate, +} from '../../../utils'; + +export function createSwapDatesPostprocessor({ + dateModeTemplate, + datesSeparator, +}: { + dateModeTemplate: string; + datesSeparator: string; +}): NonNullable { + return ({value, selection}) => { + const dateStrings = parseDateRangeString(value, dateModeTemplate, datesSeparator); + const isDateRangeComplete = + dateStrings.length === 2 && + dateStrings.every(date => isDateStringComplete(date, dateModeTemplate)); + const [from, to] = selection; + const caretAtTheEnd = from >= value.length; + const allValueSelected = from === 0 && to >= value.length; // dropping text inside with a pointer + + if (!(caretAtTheEnd || allValueSelected) || !isDateRangeComplete) { + return {value, selection}; + } + + const [fromDate, toDate] = dateStrings.map(dateString => + segmentsToDate(parseDateString(dateString, dateModeTemplate)), + ); + + return { + selection, + value: fromDate > toDate ? dateStrings.reverse().join(datesSeparator) : value, + }; + }; +}