Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
d29b8a3
Core(utils): type m_iterator and m_extend
EugeniyKiyashko Oct 6, 2026
156de9b
Core(utils): type m_error and m_date_serialization
EugeniyKiyashko Oct 6, 2026
abc632c
Core(utils): type m_selection_filter and m_shadow_dom
EugeniyKiyashko Oct 6, 2026
c11e5f9
Core(utils): add Jest tests for the six typed m_ files
EugeniyKiyashko Oct 6, 2026
7008217
Core(utils): cover the mutants of m_error and m_selection_filter that…
EugeniyKiyashko Oct 6, 2026
80e1e13
Core(utils): type each, map and reverseEach
EugeniyKiyashko Oct 6, 2026
ef49c47
Core(utils): type extend
EugeniyKiyashko Oct 6, 2026
f6dbc54
Core(utils): type the results of the date serialization functions
EugeniyKiyashko Oct 6, 2026
9446877
Core(utils): type errors.Error and errors.log
EugeniyKiyashko Oct 6, 2026
11037a8
Core(utils): fix the caller of the typed extend in the filter builder
EugeniyKiyashko Oct 6, 2026
533e637
Core(utils): type the callers of the typed hubs instead of ignoring them
EugeniyKiyashko Oct 6, 2026
2e5f4bb
Core(utils): fix the lint errors that the typed hubs cause in other f…
EugeniyKiyashko Oct 6, 2026
f3a3aee
Core(utils): type the context menu and the scroll action calls instea…
EugeniyKiyashko Oct 6, 2026
c505f0a
Core(utils): keep extra of the normalized load result untyped
EugeniyKiyashko Oct 6, 2026
7b56e6c
Core(utils): type the merged animation config of the sortable
EugeniyKiyashko Oct 6, 2026
c63dff3
Core(utils): keep the properties of a skippable extend source optional
EugeniyKiyashko Oct 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/devextreme/js/__internal/common/core/animation/fx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,8 +337,8 @@ const FrameAnimationStrategy = {

each(transformString.match(/\w+\d*\w*\([^)]*\)\s*/g), (i, part) => {
const translateData = parseTranslate(part);
const scaleData = part.match(/scale\((.+?)\)/);
const rotateData = part.match(/(rotate.)\((.+)deg\)/);
const scaleData = /scale\((.+?)\)/.exec(part);
const rotateData = /(rotate.)\((.+)deg\)/.exec(part);

if (translateData) {
result.translate = translateData;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,24 @@ const defaultPositionResult = {
},
};

interface AxisPositionResult {
location: number;
flip: boolean;
fit: boolean;
oversize: number;
collisionSide?: string;
}

interface PositionResult {
h: AxisPositionResult;
v: AxisPositionResult;
precise?: boolean;
}

const calculatePosition = function (what, options) {
const $what = $(what);
const currentOffset = $what.offset();
const result = extend(true, {}, defaultPositionResult, {
const result: PositionResult = extend(true, {}, defaultPositionResult, {
// @ts-expect-error
h: { location: currentOffset.left },
// @ts-expect-error
Expand Down
2 changes: 1 addition & 1 deletion packages/devextreme/js/__internal/core/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export type ActionArguments<
validatingTargetName?: string;
};

interface ActionEvent {
export interface ActionEvent {
action: unknown;
args: unknown[];
context: unknown;
Expand Down
2 changes: 1 addition & 1 deletion packages/devextreme/js/__internal/core/devices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ class Devices {
return undefined;
}
/// #ENDDEBUG
return extend({}, this._realDevice) as ResolvedDevice;
return extend({}, this._realDevice);
}

orientation(): Orientation | undefined {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export class DomComponentWrapper extends InfernoComponent<DomComponentWrapperPro
const {
valueChange,
} = normalizedProps;
const properties = extend({
const properties: Record<string, any> = extend({
rtlEnabled: this.getConfig()?.rtlEnabled,
isRenovated: true,
}, normalizedProps);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
import {
afterEach, describe, expect, it,
} from '@jest/globals';
import config from '@js/core/config';
import { dateSerialization } from '@ts/core/utils/m_date_serialization';

const {
createLocalDateFromUTCTimestamp,
dateParser,
deserializeDate,
serializeDate,
getDateSerializationFormat,
} = dateSerialization;

const time = (value: unknown): number => (value as Date).getTime();

describe('Date serialization utils', () => {
afterEach(() => {
config({ forceIsoDateParsing: true });
});

describe('createLocalDateFromUTCTimestamp', () => {
it('should create the local midnight of the UTC date', () => {
const result = createLocalDateFromUTCTimestamp(Date.UTC(2020, 4, 17, 23, 59, 59));

expect(time(result)).toBe(time(new Date(2020, 4, 17)));
});
});

describe('deserializeDate', () => {
it('should create a date from a number', () => {
const result = deserializeDate(1234567890000);

expect(result).toBeInstanceOf(Date);
expect(time(result)).toBe(1234567890000);
});

it('should parse an ISO string without a time zone as a local date', () => {
expect(time(deserializeDate('2020-05-17'))).toBe(time(new Date(2020, 4, 17)));
expect(time(deserializeDate('2020-05-17T10:20:30'))).toBe(time(new Date(2020, 4, 17, 10, 20, 30)));
expect(time(deserializeDate('2020-05-17T10:20'))).toBe(time(new Date(2020, 4, 17, 10, 20)));
expect(time(deserializeDate('2020-05-17T10'))).toBe(time(new Date(2020, 4, 17, 10)));
});

it('should parse an ISO string with the Z designator as UTC', () => {
expect(time(deserializeDate('2020-05-17T10:20:30Z'))).toBe(Date.UTC(2020, 4, 17, 10, 20, 30));
});

it('should apply the time zone offset of an ISO string', () => {
expect(time(deserializeDate('2020-05-17T10:20:30+03:00'))).toBe(Date.UTC(2020, 4, 17, 7, 20, 30));
expect(time(deserializeDate('2020-05-17T10:20:30-02:30'))).toBe(Date.UTC(2020, 4, 17, 12, 50, 30));
expect(time(deserializeDate('2020-05-17T10:20:30+0300'))).toBe(Date.UTC(2020, 4, 17, 7, 20, 30));
expect(time(deserializeDate('2020-05-17T10:20:30+03'))).toBe(Date.UTC(2020, 4, 17, 7, 20, 30));
});

it('should parse the milliseconds of an ISO string', () => {
expect(time(deserializeDate('2020-05-17T10:20:30.5'))).toBe(time(new Date(2020, 4, 17, 10, 20, 30, 500)));
expect(time(deserializeDate('2020-05-17T10:20:30.12'))).toBe(time(new Date(2020, 4, 17, 10, 20, 30, 120)));
expect(time(deserializeDate('2020-05-17T10:20:30.123456'))).toBe(time(new Date(2020, 4, 17, 10, 20, 30, 123)));
});

it('should parse an ISO string without separators', () => {
expect(time(deserializeDate('20200517T102030'))).toBe(time(new Date(2020, 4, 17, 10, 20, 30)));
expect(time(deserializeDate('20200517'))).toBe(time(new Date(2020, 4, 17)));
});

it('should parse a time string as a time of the zero date', () => {
expect(time(deserializeDate('10:20:30'))).toBe(time(new Date(0, 0, 0, 10, 20, 30)));
expect(time(deserializeDate('10:20'))).toBe(time(new Date(0, 0, 0, 10, 20, 0)));
});

it('should keep the year of an ISO string below 100', () => {
const result = deserializeDate('0050-01-02T03:04:05') as Date;

expect(result.getFullYear()).toBe(50);
expect(result.getMonth()).toBe(0);
expect(result.getDate()).toBe(2);
expect((deserializeDate('0050-01-02T03:04:05Z') as Date).getUTCFullYear()).toBe(50);
});

it('should parse a partial ISO date as a local date', () => {
expect(time(deserializeDate('2020-05'))).toBe(time(new Date(2020, 4, 1)));
expect(time(deserializeDate('2020'))).toBe(time(new Date(2020, 0, 1)));
});

it('should parse the default serialization formats', () => {
expect(time(deserializeDate('2020/05/17'))).toBe(time(new Date(2020, 4, 17)));
expect(time(deserializeDate('2020/05/17 10:20:30'))).toBe(time(new Date(2020, 4, 17, 10, 20, 30)));
});

it('should build a date of the default serialization format that the native parser rejects', () => {
expect(time(deserializeDate('2020/13/45'))).toBe(time(new Date(2021, 1, 14)));
});

it('should return a string that is not a date as is', () => {
expect(deserializeDate('not a date')).toBe('not a date');
expect(deserializeDate('')).toBe('');
});

it('should return the values that are not strings and numbers as is', () => {
const date = new Date(5);
const object = {};

expect(deserializeDate(date)).toBe(date);
expect(deserializeDate(object)).toBe(object);
expect(deserializeDate(null)).toBeNull();
expect(deserializeDate(undefined)).toBeUndefined();
expect(deserializeDate(true)).toBe(true);
});

it('should parse an ISO date with the native parser when the ISO parsing is not forced', () => {
config({ forceIsoDateParsing: false });

expect(time(deserializeDate('2020-05-17'))).toBe(Date.UTC(2020, 4, 17));
expect(time(deserializeDate('2020-05-17T10:20:30Z'))).toBe(Date.UTC(2020, 4, 17, 10, 20, 30));
expect(time(deserializeDate('2020/05/17'))).toBe(time(new Date(2020, 4, 17)));
});
});

describe('dateParser', () => {
it('should parse ISO strings by default', () => {
expect(time(dateParser('2020-05-17T10:20:30'))).toBe(time(new Date(2020, 4, 17, 10, 20, 30)));
expect(time(dateParser('2020-05-17T10:20:30Z'))).toBe(Date.UTC(2020, 4, 17, 10, 20, 30));
});

it('should skip the ISO parsing on demand', () => {
expect(time(dateParser('2020-05-17', true))).toBe(Date.UTC(2020, 4, 17));
});

it('should return a value that is not a date as is', () => {
expect(dateParser('abc')).toBe('abc');
expect(dateParser(null)).toBeNull();
});
});

describe('serializeDate', () => {
const date = new Date(2020, 4, 17, 10, 20, 30);

it('should return the value as is without a serialization format', () => {
const object = {};

expect(serializeDate(date)).toBe(date);
expect(serializeDate(date, '')).toBe(date);
expect(serializeDate(date, null)).toBe(date);
expect(serializeDate('text')).toBe('text');
expect(serializeDate(object, undefined)).toBe(object);
expect(serializeDate(5)).toBe(5);
});

it('should return null for a value that is not a date', () => {
expect(serializeDate('2020-05-17', 'yyyy-MM-dd')).toBeNull();
expect(serializeDate(5, 'number')).toBeNull();
expect(serializeDate(null, 'yyyy')).toBeNull();
expect(serializeDate(undefined, 'yyyy')).toBeNull();
});

it('should serialize a date to the number of milliseconds', () => {
expect(serializeDate(new Date(5), 'number')).toBe(5);
});

it('should format a date with the serialization format', () => {
expect(serializeDate(date, 'yyyy-MM-dd')).toBe('2020-05-17');
expect(serializeDate(date, 'yyyy/MM/dd HH:mm:ss')).toBe('2020/05/17 10:20:30');
expect(serializeDate(date, 'HH:mm')).toBe('10:20');
expect(serializeDate(date, 'yyyyMMdd')).toBe('20200517');
});
});

describe('getDateSerializationFormat', () => {
it('should return the number format for a number', () => {
expect(getDateSerializationFormat(0)).toBe('number');
expect(getDateSerializationFormat(1234567890000)).toBe('number');
});

it('should return the format of an ISO string', () => {
expect(getDateSerializationFormat('2020-05-17')).toBe('yyyy-MM-dd');
expect(getDateSerializationFormat('20200517')).toBe('yyyyMMdd');
expect(getDateSerializationFormat('2020-05-17T10')).toBe('yyyy-MM-ddTHH');
expect(getDateSerializationFormat('2020-05-17T10:20')).toBe('yyyy-MM-ddTHH:mm');
expect(getDateSerializationFormat('2020-05-17T10:20:30')).toBe('yyyy-MM-ddTHH:mm:ss');
expect(getDateSerializationFormat('2020-05-17T10:20:30.123')).toBe('yyyy-MM-ddTHH:mm:ss.SSS');
expect(getDateSerializationFormat('20200517T102030')).toBe('yyyyMMddTHHmmss');
});

it('should return the format of an ISO string with a time zone', () => {
expect(getDateSerializationFormat('2020-05-17T10:20:30Z')).toBe('yyyy-MM-ddTHH:mm:ss\'Z\'');
expect(getDateSerializationFormat('2020-05-17T10:20:30+03:00')).toBe('yyyy-MM-ddTHH:mm:ssxxx');
expect(getDateSerializationFormat('2020-05-17T10:20:30+0300')).toBe('yyyy-MM-ddTHH:mm:ssxx');
expect(getDateSerializationFormat('2020-05-17T10:20:30+03')).toBe('yyyy-MM-ddTHH:mm:ssx');
});

it('should return the format of a time string', () => {
expect(getDateSerializationFormat('10:20')).toBe('HH:mm');
expect(getDateSerializationFormat('10:20:30')).toBe('HH:mm:ss');
});

it('should return the default formats for the other strings', () => {
expect(getDateSerializationFormat('2020/05/17')).toBe('yyyy/MM/dd');
expect(getDateSerializationFormat('text')).toBe('yyyy/MM/dd');
expect(getDateSerializationFormat('')).toBe('yyyy/MM/dd');
expect(getDateSerializationFormat('2020/05/17 10:20:30')).toBe('yyyy/MM/dd HH:mm:ss');
expect(getDateSerializationFormat('May 17, 2020 10:20')).toBe('yyyy/MM/dd HH:mm:ss');
});

it('should not detect an ISO format when the ISO parsing is not forced', () => {
config({ forceIsoDateParsing: false });

expect(getDateSerializationFormat('2020-05-17')).toBe('yyyy/MM/dd');
expect(getDateSerializationFormat('2020-05-17T10:20:30')).toBe('yyyy/MM/dd HH:mm:ss');
});

it('should return null for the other truthy values', () => {
expect(getDateSerializationFormat(new Date())).toBeNull();
expect(getDateSerializationFormat({})).toBeNull();
expect(getDateSerializationFormat(true)).toBeNull();
});

it('should return undefined for the falsy values that are not numbers or strings', () => {
expect(getDateSerializationFormat(null)).toBeUndefined();
expect(getDateSerializationFormat(undefined)).toBeUndefined();
expect(getDateSerializationFormat(false)).toBeUndefined();
});
});
});
Loading
Loading