From 97dc5a166ae561bfb439af4212d1c68feb6f24c3 Mon Sep 17 00:00:00 2001 From: Nikita Barsukov Date: Tue, 6 Feb 2024 11:44:03 +0300 Subject: [PATCH 1/3] refactor(kit): remove `DateRange`-specific logic from common date-utils --- .../lib/constants/possible-dates-separator.ts | 9 ------ .../kit/src/lib/masks/date-range/constants.ts | 15 +++++++++ .../lib/masks/date-range/date-range-mask.ts | 2 ++ .../preudo-range-separator-preprocessor.ts | 28 +++++++++++++++++ .../lib/masks/date/tests/date-mask.spec.ts | 31 +++++++++++++++++++ .../lib/processors/valid-date-preprocessor.ts | 5 --- 6 files changed, 76 insertions(+), 14 deletions(-) create mode 100644 projects/kit/src/lib/masks/date-range/constants.ts create mode 100644 projects/kit/src/lib/masks/date-range/processors/preudo-range-separator-preprocessor.ts create mode 100644 projects/kit/src/lib/masks/date/tests/date-mask.spec.ts diff --git a/projects/kit/src/lib/constants/possible-dates-separator.ts b/projects/kit/src/lib/constants/possible-dates-separator.ts index 1c7ac7ec4..16d31b913 100644 --- a/projects/kit/src/lib/constants/possible-dates-separator.ts +++ b/projects/kit/src/lib/constants/possible-dates-separator.ts @@ -1,10 +1 @@ -import {CHAR_EM_DASH, CHAR_EN_DASH, CHAR_HYPHEN, CHAR_MINUS} from './unicode-characters'; - -export const POSSIBLE_DATE_RANGE_SEPARATOR = [ - CHAR_HYPHEN, - CHAR_EN_DASH, - CHAR_EM_DASH, - CHAR_MINUS, -]; - export const POSSIBLE_DATE_TIME_SEPARATOR = [',', ' ']; diff --git a/projects/kit/src/lib/masks/date-range/constants.ts b/projects/kit/src/lib/masks/date-range/constants.ts new file mode 100644 index 000000000..f5663a793 --- /dev/null +++ b/projects/kit/src/lib/masks/date-range/constants.ts @@ -0,0 +1,15 @@ +import { + CHAR_EM_DASH, + CHAR_EN_DASH, + CHAR_HYPHEN, + CHAR_JP_HYPHEN, + CHAR_MINUS, +} from '../../constants'; + +export const POSSIBLE_DATE_RANGE_SEPARATOR = [ + CHAR_HYPHEN, + CHAR_EN_DASH, + CHAR_EM_DASH, + CHAR_MINUS, + CHAR_JP_HYPHEN, +]; 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 531104715..8180259cb 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 @@ -9,6 +9,7 @@ import { } from '../../processors'; import {MaskitoDateMode, MaskitoDateSegments} from '../../types'; import {createMinMaxRangeLengthPostprocessor} from './processors/min-max-range-length-postprocessor'; +import {createPseudoRangeSeparatorPreprocessor} from './processors/preudo-range-separator-preprocessor'; import {createSwapDatesPostprocessor} from './processors/swap-dates-postprocessor'; export function maskitoDateRangeOptionsGenerator({ @@ -38,6 +39,7 @@ export function maskitoDateRangeOptionsGenerator({ mask: [...dateMask, ...Array.from(rangeSeparator), ...dateMask], overwriteMode: 'replace', preprocessors: [ + createPseudoRangeSeparatorPreprocessor(rangeSeparator), createZeroPlaceholdersPreprocessor(), normalizeDatePreprocessor({ dateModeTemplate, diff --git a/projects/kit/src/lib/masks/date-range/processors/preudo-range-separator-preprocessor.ts b/projects/kit/src/lib/masks/date-range/processors/preudo-range-separator-preprocessor.ts new file mode 100644 index 000000000..8b66e748a --- /dev/null +++ b/projects/kit/src/lib/masks/date-range/processors/preudo-range-separator-preprocessor.ts @@ -0,0 +1,28 @@ +import {MaskitoPreprocessor} from '@maskito/core'; + +import {POSSIBLE_DATE_RANGE_SEPARATOR} from '../constants'; + +/** + * It replaces pseudo range separators with valid one. + * @example User types hyphen / en-dash / em-dash / minus => it is replaced with valid range separator. + */ +export function createPseudoRangeSeparatorPreprocessor( + rangeSeparator: string, +): MaskitoPreprocessor { + const pseudoSeparatorsRegExp = new RegExp( + `[${POSSIBLE_DATE_RANGE_SEPARATOR.join('')}]`, + 'gi', + ); + + return ({elementState, data}) => { + const {value, selection} = elementState; + + return { + elementState: { + selection, + value: value.replace(pseudoSeparatorsRegExp, rangeSeparator), + }, + data: data.replace(pseudoSeparatorsRegExp, rangeSeparator), + }; + }; +} diff --git a/projects/kit/src/lib/masks/date/tests/date-mask.spec.ts b/projects/kit/src/lib/masks/date/tests/date-mask.spec.ts new file mode 100644 index 000000000..21b4bf84b --- /dev/null +++ b/projects/kit/src/lib/masks/date/tests/date-mask.spec.ts @@ -0,0 +1,31 @@ +import {MASKITO_DEFAULT_OPTIONS, MaskitoOptions, maskitoTransform} from '@maskito/core'; +import {maskitoDateOptionsGenerator} from '@maskito/kit'; + +/** + * If any of these tests fail, + * it can mean that browser autofill or composition are not working properly + * for Date mask + */ +describe('Date (maskitoTransform)', () => { + describe('[mode]="yyyy/mm/dd"', () => { + let options: MaskitoOptions = MASKITO_DEFAULT_OPTIONS; + + beforeEach(() => { + options = maskitoDateOptionsGenerator({ + mode: 'yyyy/mm/dd', + separator: '/', + }); + }); + + // TODO: fix this bug later + xit('pads digit > 1 with zero for months (12345 => 1234/05)', () => { + expect(maskitoTransform('12345', options)).toBe('1234/05'); + }); + + // TODO: https://github.com/taiga-family/maskito/pull/907 + xit('accepts full width characters', () => { + expect(maskitoTransform('12345', options)).toBe('1234/05'); + expect(maskitoTransform('12341226', options)).toBe('1234/12/26'); + }); + }); +}); diff --git a/projects/kit/src/lib/processors/valid-date-preprocessor.ts b/projects/kit/src/lib/processors/valid-date-preprocessor.ts index be27430e2..ceb35b315 100644 --- a/projects/kit/src/lib/processors/valid-date-preprocessor.ts +++ b/projects/kit/src/lib/processors/valid-date-preprocessor.ts @@ -1,6 +1,5 @@ import {MaskitoPreprocessor} from '@maskito/core'; -import {POSSIBLE_DATE_RANGE_SEPARATOR} from '../constants'; import {escapeRegExp, parseDateRangeString, validateDateString} from '../utils'; export function createValidDatePreprocessor({ @@ -22,10 +21,6 @@ export function createValidDatePreprocessor({ }; } - if (POSSIBLE_DATE_RANGE_SEPARATOR.includes(data)) { - return {elementState, data: rangeSeparator}; - } - const newCharacters = data.replace( new RegExp( `[^\\d${escapeRegExp(dateSegmentsSeparator)}${rangeSeparator}]`, From 4a5107bb1355f91b6ec961932991d9d5b0097339 Mon Sep 17 00:00:00 2001 From: Nikita Barsukov Date: Tue, 6 Feb 2024 12:00:30 +0300 Subject: [PATCH 2/3] chore: add units tests --- .../lib/masks/date-range/date-range-mask.ts | 2 +- ...=> pseudo-range-separator-preprocessor.ts} | 0 .../tests/pseudo-range-separators.spec.ts | 40 +++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) rename projects/kit/src/lib/masks/date-range/processors/{preudo-range-separator-preprocessor.ts => pseudo-range-separator-preprocessor.ts} (100%) create mode 100644 projects/kit/src/lib/masks/date-range/tests/pseudo-range-separators.spec.ts 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 8180259cb..cb6db3cbd 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 @@ -9,7 +9,7 @@ import { } from '../../processors'; import {MaskitoDateMode, MaskitoDateSegments} from '../../types'; import {createMinMaxRangeLengthPostprocessor} from './processors/min-max-range-length-postprocessor'; -import {createPseudoRangeSeparatorPreprocessor} from './processors/preudo-range-separator-preprocessor'; +import {createPseudoRangeSeparatorPreprocessor} from './processors/pseudo-range-separator-preprocessor'; import {createSwapDatesPostprocessor} from './processors/swap-dates-postprocessor'; export function maskitoDateRangeOptionsGenerator({ diff --git a/projects/kit/src/lib/masks/date-range/processors/preudo-range-separator-preprocessor.ts b/projects/kit/src/lib/masks/date-range/processors/pseudo-range-separator-preprocessor.ts similarity index 100% rename from projects/kit/src/lib/masks/date-range/processors/preudo-range-separator-preprocessor.ts rename to projects/kit/src/lib/masks/date-range/processors/pseudo-range-separator-preprocessor.ts diff --git a/projects/kit/src/lib/masks/date-range/tests/pseudo-range-separators.spec.ts b/projects/kit/src/lib/masks/date-range/tests/pseudo-range-separators.spec.ts new file mode 100644 index 000000000..9ee713cd7 --- /dev/null +++ b/projects/kit/src/lib/masks/date-range/tests/pseudo-range-separators.spec.ts @@ -0,0 +1,40 @@ +import {MASKITO_DEFAULT_OPTIONS, MaskitoOptions, maskitoTransform} from '@maskito/core'; +import {maskitoDateRangeOptionsGenerator} from '@maskito/kit'; + +import {CHAR_EM_DASH, CHAR_EN_DASH, CHAR_HYPHEN, CHAR_MINUS} from '../../../constants'; + +describe('DateRange (maskitoTransform) | Pseudo range separators', () => { + let options: MaskitoOptions = MASKITO_DEFAULT_OPTIONS; + + beforeEach(() => { + options = maskitoDateRangeOptionsGenerator({ + mode: 'dd/mm/yyyy', + dateSeparator: '.', + rangeSeparator: CHAR_EN_DASH, + }); + }); + + it('works with already valid range separator', () => { + expect(maskitoTransform(`01012000${CHAR_EN_DASH}10102000`, options)).toBe( + `01.01.2000${CHAR_EN_DASH}10.10.2000`, + ); + }); + + it('replaces hyphen with valid range separator', () => { + expect(maskitoTransform(`01012000${CHAR_HYPHEN}10102000`, options)).toBe( + `01.01.2000${CHAR_EN_DASH}10.10.2000`, + ); + }); + + it('replaces em-dash with valid range separator', () => { + expect(maskitoTransform(`01012000${CHAR_EM_DASH}10102000`, options)).toBe( + `01.01.2000${CHAR_EN_DASH}10.10.2000`, + ); + }); + + it('replaces minus with valid range separator', () => { + expect(maskitoTransform(`01012000${CHAR_MINUS}10102000`, options)).toBe( + `01.01.2000${CHAR_EN_DASH}10.10.2000`, + ); + }); +}); From 9c652d6c75d31ab9b8700f9e15c153173c3210a5 Mon Sep 17 00:00:00 2001 From: Nikita Barsukov Date: Tue, 6 Feb 2024 13:47:58 +0300 Subject: [PATCH 3/3] chore: improve `createPseudoRangeSeparatorPreprocessor` --- .../kit/date-range/date-range-separator.cy.ts | 2 +- .../lib/masks/date-range/date-range-mask.ts | 2 +- .../pseudo-range-separator-preprocessor.ts | 22 +++++++++++-------- .../tests/pseudo-range-separators.spec.ts | 3 +++ .../lib/processors/valid-date-preprocessor.ts | 8 +++---- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/projects/demo-integrations/src/tests/kit/date-range/date-range-separator.cy.ts b/projects/demo-integrations/src/tests/kit/date-range/date-range-separator.cy.ts index f90e78646..9326141cf 100644 --- a/projects/demo-integrations/src/tests/kit/date-range/date-range-separator.cy.ts +++ b/projects/demo-integrations/src/tests/kit/date-range/date-range-separator.cy.ts @@ -1,6 +1,6 @@ import {DemoPath} from '@demo/constants'; -describe('DateRange | Separator', () => { +describe('DateRange | dateSeparator', () => { describe('/', () => { beforeEach(() => { cy.visit(`/${DemoPath.DateRange}/API?dateSeparator=/`); 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 cb6db3cbd..3b2d3c74a 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 @@ -39,7 +39,7 @@ export function maskitoDateRangeOptionsGenerator({ mask: [...dateMask, ...Array.from(rangeSeparator), ...dateMask], overwriteMode: 'replace', preprocessors: [ - createPseudoRangeSeparatorPreprocessor(rangeSeparator), + createPseudoRangeSeparatorPreprocessor({rangeSeparator, dateSeparator}), createZeroPlaceholdersPreprocessor(), normalizeDatePreprocessor({ dateModeTemplate, diff --git a/projects/kit/src/lib/masks/date-range/processors/pseudo-range-separator-preprocessor.ts b/projects/kit/src/lib/masks/date-range/processors/pseudo-range-separator-preprocessor.ts index 8b66e748a..edfa45110 100644 --- a/projects/kit/src/lib/masks/date-range/processors/pseudo-range-separator-preprocessor.ts +++ b/projects/kit/src/lib/masks/date-range/processors/pseudo-range-separator-preprocessor.ts @@ -6,13 +6,17 @@ import {POSSIBLE_DATE_RANGE_SEPARATOR} from '../constants'; * It replaces pseudo range separators with valid one. * @example User types hyphen / en-dash / em-dash / minus => it is replaced with valid range separator. */ -export function createPseudoRangeSeparatorPreprocessor( - rangeSeparator: string, -): MaskitoPreprocessor { - const pseudoSeparatorsRegExp = new RegExp( - `[${POSSIBLE_DATE_RANGE_SEPARATOR.join('')}]`, - 'gi', - ); +export function createPseudoRangeSeparatorPreprocessor({ + rangeSeparator, + dateSeparator, +}: { + rangeSeparator: string; + dateSeparator: string; +}): MaskitoPreprocessor { + const pseudoRangeSeparators = POSSIBLE_DATE_RANGE_SEPARATOR.filter( + x => !rangeSeparator.includes(x) && x !== dateSeparator, + ).join(''); + const pseudoRangeSeparatorsRE = new RegExp(`[${pseudoRangeSeparators}]`, 'gi'); return ({elementState, data}) => { const {value, selection} = elementState; @@ -20,9 +24,9 @@ export function createPseudoRangeSeparatorPreprocessor( return { elementState: { selection, - value: value.replace(pseudoSeparatorsRegExp, rangeSeparator), + value: value.replace(pseudoRangeSeparatorsRE, rangeSeparator), }, - data: data.replace(pseudoSeparatorsRegExp, rangeSeparator), + data: data.replace(pseudoRangeSeparatorsRE, rangeSeparator), }; }; } diff --git a/projects/kit/src/lib/masks/date-range/tests/pseudo-range-separators.spec.ts b/projects/kit/src/lib/masks/date-range/tests/pseudo-range-separators.spec.ts index 9ee713cd7..4db67cdd9 100644 --- a/projects/kit/src/lib/masks/date-range/tests/pseudo-range-separators.spec.ts +++ b/projects/kit/src/lib/masks/date-range/tests/pseudo-range-separators.spec.ts @@ -18,6 +18,9 @@ describe('DateRange (maskitoTransform) | Pseudo range separators', () => { expect(maskitoTransform(`01012000${CHAR_EN_DASH}10102000`, options)).toBe( `01.01.2000${CHAR_EN_DASH}10.10.2000`, ); + expect(maskitoTransform(`01012000 ${CHAR_EN_DASH} 10102000`, options)).toBe( + `01.01.2000${CHAR_EN_DASH}10.10.2000`, + ); }); it('replaces hyphen with valid range separator', () => { diff --git a/projects/kit/src/lib/processors/valid-date-preprocessor.ts b/projects/kit/src/lib/processors/valid-date-preprocessor.ts index ceb35b315..c9badf631 100644 --- a/projects/kit/src/lib/processors/valid-date-preprocessor.ts +++ b/projects/kit/src/lib/processors/valid-date-preprocessor.ts @@ -50,9 +50,7 @@ export function createValidDatePreprocessor({ const {validatedDateString, updatedSelection} = validateDateString({ dateString, dateModeTemplate, - offset: validatedValue - ? validatedValue.length + rangeSeparator.length - : 0, + offset: validatedValue.length, selection: [from, to], }); @@ -63,8 +61,8 @@ export function createValidDatePreprocessor({ to = updatedSelection[1]; validatedValue += - hasRangeSeparator && validatedValue - ? rangeSeparator + validatedDateString + hasRangeSeparator && !validatedValue + ? validatedDateString + rangeSeparator : validatedDateString; }