Skip to content

Commit

Permalink
fix(kit): DateTime fails to process value without any separators (p…
Browse files Browse the repository at this point in the history
…aste from clipboard) (#1779)
  • Loading branch information
nsbarsukov authored Oct 17, 2024
1 parent 8f4253f commit 1733422
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 29 deletions.
4 changes: 4 additions & 0 deletions projects/demo-integrations/src/support/commands/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/// <reference types="cypress" />

import {paste} from './paste';
import {smartTick} from './smart-tick';

declare global {
Expand All @@ -9,6 +10,8 @@ declare global {
durationMs: number,
options?: Parameters<typeof smartTick>[2],
): Chainable<Subject>;

paste(value: string): Chainable<Subject>;
}
}
}
Expand All @@ -18,3 +21,4 @@ Cypress.Commands.add(
{prevSubject: ['optional', 'element', 'window', 'document']},
smartTick,
);
Cypress.Commands.add('paste', {prevSubject: 'element'}, paste);
45 changes: 45 additions & 0 deletions projects/demo-integrations/src/support/commands/paste.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Cypress does not have built-in support for pasting from clipboard.
* This utility is VERY approximate alternative for it.
*
* @see https://github.com/cypress-io/cypress/issues/28861
*/
export function paste<T extends Cypress.PrevSubjectMap['element']>(
$subject: T,
data: string,
): ReturnType<Cypress.CommandFn<'paste'>> {
const inputType = 'insertFromPaste';
const element = Cypress.dom.unwrap($subject)[0] as
| HTMLInputElement
| HTMLTextAreaElement;
const {value, selectionStart, selectionEnd} = element;

Cypress.log({
displayName: 'paste',
message: data,
consoleProps() {
return {
value,
selectionStart,
selectionEnd,
};
},
});

return cy
.wrap($subject, {log: false})
.trigger('beforeinput', {
inputType,
data,
log: false,
})
.invoke(
'val',
value.slice(0, selectionStart ?? 0) + data + value.slice(selectionEnd ?? 0),
)
.trigger('input', {
inputType,
data,
log: false,
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -353,4 +353,38 @@ describe('DateTime | Basic', () => {
);
});
});

describe('Paste', () => {
it('value without segment separators', () => {
cy.get('@input')
.paste('02112018, 16:20')
.should('have.value', '02.11.2018, 16:20')
.should('have.prop', 'selectionStart', '02.11.2018, 16:20'.length)
.should('have.prop', 'selectionEnd', '02.11.2018, 16:20'.length);
});

it('value without separator between date and time', () => {
cy.get('@input')
.paste('02.11.201816:20')
.should('have.value', '02.11.2018, 16:20')
.should('have.prop', 'selectionStart', '02.11.2018, 16:20'.length)
.should('have.prop', 'selectionEnd', '02.11.2018, 16:20'.length);
});

it('value with incomplete separator between date and time', () => {
cy.get('@input')
.paste('02.11.2018,16:20')
.should('have.value', '02.11.2018, 16:20')
.should('have.prop', 'selectionStart', '02.11.2018, 16:20'.length)
.should('have.prop', 'selectionEnd', '02.11.2018, 16:20'.length);
});

it('value without any separators', () => {
cy.get('@input')
.paste('021120181620')
.should('have.value', '02.11.2018, 16:20')
.should('have.prop', 'selectionStart', '02.11.2018, 16:20'.length)
.should('have.prop', 'selectionEnd', '02.11.2018, 16:20'.length);
});
});
});
12 changes: 6 additions & 6 deletions projects/kit/src/lib/masks/date-time/date-time-mask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ export function maskitoDateTimeOptionsGenerator({
timeSegmentMinValues,
timeSegmentMaxValues,
parseValue: (x) => {
const [dateString, timeString] = parseDateTimeString(x, {
const [dateString, timeString] = parseDateTimeString(
x,
dateModeTemplate,
dateTimeSeparator,
});
);

return {timeString, restValue: dateString + dateTimeSeparator};
},
Expand All @@ -109,10 +109,10 @@ export function maskitoDateTimeOptionsGenerator({
dateModeTemplate,
dateSegmentSeparator: dateSeparator,
splitFn: (value) => {
const [dateString, timeString] = parseDateTimeString(value, {
const [dateString, timeString] = parseDateTimeString(
value,
dateModeTemplate,
dateTimeSeparator,
});
);

return {dateStrings: [dateString], restPart: timeString};
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,7 @@ export function createMinMaxDateTimePostprocessor({
dateTimeSeparator: string;
}): MaskitoPostprocessor {
return ({value, selection}) => {
const [dateString, timeString] = parseDateTimeString(value, {
dateModeTemplate,
dateTimeSeparator,
});
const [dateString, timeString] = parseDateTimeString(value, dateModeTemplate);
const parsedDate = parseDateString(dateString, dateModeTemplate);
const parsedTime = parseTimeString(timeString, timeMode);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ export function createValidDateTimePreprocessor({
let to = rawTo + data.length;
const newPossibleValue = value.slice(0, from) + newCharacters + value.slice(to);

const [dateString, timeString] = parseDateTimeString(newPossibleValue, {
const [dateString, timeString] = parseDateTimeString(
newPossibleValue,
dateModeTemplate,
dateTimeSeparator,
});
);
let validatedValue = '';
const hasDateTimeSeparator = newPossibleValue.includes(dateTimeSeparator);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
const NON_DIGIT_PLACEHOLDER_RE = /[^dmy]/g;
const LEADING_NON_DIGIT_RE = /^\D*/;

export function parseDateTimeString(
dateTime: string,
{
dateModeTemplate,
dateTimeSeparator,
}: {
dateModeTemplate: string;
dateTimeSeparator: string;
},
dateModeTemplate: string,
): [date: string, time: string] {
const hasSeparator = dateTime.includes(dateTimeSeparator);
const dateDigitsCount = dateModeTemplate.replaceAll(
NON_DIGIT_PLACEHOLDER_RE,
'',
).length;
const [date = ''] =
new RegExp(`(\\d[^\\d]*){0,${dateDigitsCount - 1}}\\d?`).exec(dateTime) || [];
const [dateTimeSeparator = ''] =
LEADING_NON_DIGIT_RE.exec(dateTime.slice(date.length)) || [];

return [
dateTime.slice(0, dateModeTemplate.length),
dateTime.slice(
hasSeparator
? dateModeTemplate.length + dateTimeSeparator.length
: dateModeTemplate.length,
),
];
return [date, dateTime.slice(date.length + dateTimeSeparator.length)];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {parseDateTimeString} from '../parse-date-time-string';

describe('parseDateTimeString', () => {
describe('dd.mm.yyyy', () => {
const parse = (value: string): [string, string] =>
parseDateTimeString(value, 'dd.mm.yyyy');

(
[
{input: '', output: ['', '']},
{input: '02', output: ['02', '']},
{input: '02.', output: ['02.', '']},
{input: '0211', output: ['0211', '']},
{input: '0211.', output: ['0211.', '']},
{input: '02.112018', output: ['02.112018', '']},
{input: '02.112018,', output: ['02.112018', '']},
{input: '02.112018, ', output: ['02.112018', '']},
{input: '02.11.2018, ', output: ['02.11.2018', '']},
{input: '021120181620', output: ['02112018', '1620']},
{input: '02112018,1620', output: ['02112018', '1620']},
{input: '02112018, 1620', output: ['02112018', '1620']},
{input: '02112018, 16:20', output: ['02112018', '16:20']},
{input: '02112018,16:20', output: ['02112018', '16:20']},
{input: '02.11.2018,1620', output: ['02.11.2018', '1620']},
{input: '02.11.2018, 1620', output: ['02.11.2018', '1620']},
{input: '02.11.2018, 16:20', output: ['02.11.2018', '16:20']},
{input: '02.11.2018,16:20', output: ['02.11.2018', '16:20']},
] as const
).forEach(({input, output}) => {
it(`${input} -> ${JSON.stringify(output)}`, () => {
expect(parse(input)).toEqual(output);
});
});
});

describe('dd. mm. yyyy (date segment separator consists of space and dot)', () => {
const parse = (value: string): [string, string] =>
parseDateTimeString(value, 'dd. mm. yyyy');

(
[
{input: '', output: ['', '']},
{input: '02', output: ['02', '']},
{input: '02.', output: ['02.', '']},
{input: '02. ', output: ['02. ', '']},
{input: '0211', output: ['0211', '']},
{input: '0211. ', output: ['0211. ', '']},
{input: '02. 112018', output: ['02. 112018', '']},
{input: '02. 112018,', output: ['02. 112018', '']},
{input: '02. 112018, ', output: ['02. 112018', '']},
{input: '02. 11. 2018, ', output: ['02. 11. 2018', '']},
{input: '02. 11. 2018,1620', output: ['02. 11. 2018', '1620']},
{input: '02. 11. 2018, 1620', output: ['02. 11. 2018', '1620']},
{input: '02. 11. 2018, 16:20', output: ['02. 11. 2018', '16:20']},
{input: '02. 11. 2018,16:20', output: ['02. 11. 2018', '16:20']},
] as const
).forEach(({input, output}) => {
it(`${input} -> ${JSON.stringify(output)}`, () => {
expect(parse(input)).toEqual(output);
});
});
});
});

0 comments on commit 1733422

Please sign in to comment.