Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { describe, expect, it } from '@jest/globals';
import type { CustomOperation, Field } from '@js/ui/filter_builder';

import { getCurrentValueText } from '../m_utils';

describe('Formatting', () => {
it('empty string', () => {
const field = {};
const value = '';

expect(getCurrentValueText(field, value, null)).toBe('');
});

it('string', () => {
const field = {};
const value = 'Text';

expect(getCurrentValueText(field, value, null)).toBe('Text');
});

it('shortDate', () => {
const field = { format: 'shortDate' };
const value = new Date(2017, 8, 5);

expect(getCurrentValueText(field, value, null)).toBe('9/5/2017');
});

it('invalid date string (T1319193)', () => {
const field = { format: 'shortDate' };
const dateString = 'Weekend';

expect(getCurrentValueText(field, dateString, null)).toBe(dateString);
});

it('boolean', () => {
const field: Field = { dataType: 'boolean' };
let value = true;

expect(getCurrentValueText(field, value, null)).toBe('true');

value = false;
expect(getCurrentValueText(field, value, null)).toBe('false');

field.falseText = 'False Text';
expect(getCurrentValueText(field, value, null)).toBe('False Text');
});

it('field.customizeText', () => {
const field: Field = {
customizeText(conditionInfo) {
return `${conditionInfo.valueText}Test`;
},
};
const value = 'MyValue';

expect(getCurrentValueText(field, value, null)).toBe('MyValueTest');
});

it('customOperation.customizeText', () => {
const field: Field = {
customizeText(conditionInfo) {
return `${conditionInfo.valueText}Test`;
},
};
const value = 'MyValue';
const customOperation: CustomOperation = {
customizeText(conditionInfo) {
return `${conditionInfo.valueText}CustomOperation`;
},
};

expect(getCurrentValueText(field, value, customOperation)).toBe('MyValueTestCustomOperation');
});

it('customOperation.customizeText for array', async () => {
const field: Field = { dataType: 'string' };

const customOperation = { customizeText: (): string => '(Blanks)' };
let text = await getCurrentValueText(field, '', customOperation);

expect(text).toBe('');

text = await getCurrentValueText(field, [null], customOperation);
expect(text).toEqual(['(Blanks)']);

const field2: Field = { dataType: 'number' };

text = await getCurrentValueText(field2, null, customOperation);

expect(text).toBe('');

text = await getCurrentValueText(field, [null], customOperation);
expect(text).toEqual(['(Blanks)']);
});

it('default format for date', () => {
const field: Field = { dataType: 'date' };
const value = new Date(2017, 8, 5, 12, 30, 0);

expect(getCurrentValueText(field, value, null)).toBe('9/5/2017');
});

it('default format for datetime', () => {
const field: Field = { dataType: 'datetime' };
const value = new Date(2017, 8, 5, 12, 30, 0);

expect(getCurrentValueText(field, value, null)).toBe('9/5/2017, 12:30 PM');
});
});
60 changes: 45 additions & 15 deletions packages/devextreme/js/__internal/filter_builder/m_utils.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import type { Format } from '@js/common/core/localization';
import messageLocalization from '@js/common/core/localization/message';
import { DataSource } from '@js/common/data/data_source/data_source';
import { errors as dataErrors } from '@js/common/data/errors';
import $ from '@js/core/renderer';
import { compileGetter } from '@js/core/utils/data';
import type { DeferredObj } from '@js/core/utils/deferred';
import { Deferred, when } from '@js/core/utils/deferred';
import { extend } from '@js/core/utils/extend';
import { captionize } from '@js/core/utils/inflector';
import { isBoolean, isDefined, isFunction } from '@js/core/utils/type';
import {
isBoolean, isDefined, isFunction, isNumeric, isString,
} from '@js/core/utils/type';
import formatHelper from '@js/format_helper';
import type { CustomOperation, DataType, Field } from '@js/ui/filter_builder';
import filterUtils from '@js/ui/shared/filtering';
import errors from '@js/ui/widget/ui.errors';

import { getConfig } from './m_between';
import filterOperationsDictionary from './m_filter_operations_dictionary';

type FieldValue = string | number | boolean | Date | null | undefined;

const DEFAULT_DATA_TYPE = 'string';
const EMPTY_MENU_ICON = 'icon-none';
const AND_GROUP_OPERATION = 'and';
Expand All @@ -27,7 +34,7 @@ const DATATYPE_OPERATIONS = {
boolean: ['=', '<>', 'isblank', 'isnotblank'],
object: ['isblank', 'isnotblank'],
};
const DEFAULT_FORMAT = {
const DEFAULT_FORMAT: Partial<Record<DataType, Format>> = {
date: 'shortDate',
datetime: 'shortDateShortTime',
};
Expand All @@ -54,19 +61,24 @@ const FILTER_BUILDER_ITEM_TEXT_PART_CLASS = `${FILTER_BUILDER_ITEM_TEXT_CLASS}-p
const FILTER_BUILDER_ITEM_TEXT_SEPARATOR_CLASS = `${FILTER_BUILDER_ITEM_TEXT_CLASS}-separator`;
const FILTER_BUILDER_ITEM_TEXT_SEPARATOR_EMPTY_CLASS = `${FILTER_BUILDER_ITEM_TEXT_SEPARATOR_CLASS}-empty`;

function getFormattedValueText(field, value): string {
const fieldFormat = field.format || DEFAULT_FORMAT[field.dataType];
function getDateFormat(dataType: DataType | undefined): Format | undefined {
return dataType ? DEFAULT_FORMAT[dataType] : undefined;
}

function getFormattedValueText(field: Field, value: FieldValue): string {
const fieldFormat = field.format ?? getDateFormat(field.dataType);

if (isBoolean(value)) {
const trueText: string = field.trueText || messageLocalization.format('dxDataGrid-trueText');
const falseText: string = field.falseText || messageLocalization.format('dxDataGrid-falseText');
const trueText = field.trueText ?? messageLocalization.format('dxDataGrid-trueText');
const falseText = field.falseText ?? messageLocalization.format('dxDataGrid-falseText');

return value ? trueText : falseText;
}

if (field.dataType === 'date' || field.dataType === 'datetime') {
// value can be string or number, we need to convert it to Date object
return formatHelper.format(new Date(value), fieldFormat);
if (isString(value) || isNumeric(value)) {
return formatHelper.format(new Date(value), fieldFormat);
}
}

return formatHelper.format(value, fieldFormat);
Expand Down Expand Up @@ -574,11 +586,18 @@ export function getCurrentLookupValueText(field, value, handler) {
}
}

function getPrimitiveValueText(field, value, customOperation, target, options?) {
function getPrimitiveValueText(
field: Field,
value: FieldValue,
customOperation: CustomOperation | null,
target: string,
options?,
): string {
let valueText = getFormattedValueText(field, value);

if (field.customizeText) {
valueText = field.customizeText.call(field, {
// @ts-expect-error
value,
valueText,
target,
Expand All @@ -591,32 +610,43 @@ function getPrimitiveValueText(field, value, customOperation, target, options?)
valueText,
field,
target,
// @ts-expect-error
}, options);
}

return valueText;
}

function getArrayValueText(field, value, customOperation, target) {
function getArrayValueText(
field: Field,
value: FieldValue[],
customOperation: CustomOperation | null,
target: string,
): string[] {
const options = { values: value };
return value.map((v) => getPrimitiveValueText(field, v, customOperation, target, options));
}

function checkDefaultValue(value) {
function checkDefaultValue(value: FieldValue | FieldValue[]): value is '' | null {
return value === '' || value === null;
}

export function getCurrentValueText(field, value, customOperation, target = 'filterBuilder') {
export function getCurrentValueText(
field: Field,
value: FieldValue | FieldValue[],
customOperation: CustomOperation | null,
target = 'filterBuilder',
): string | DeferredObj<string | string[]> {
if (checkDefaultValue(value)) {
return '';
}

if (Array.isArray(value)) {
// @ts-expect-error Deferred has badly typed ctor function
const result = new Deferred();
const result: DeferredObj<string | string[]> = new Deferred();
when.apply(this, getArrayValueText(field, value, customOperation, target)).done((...args) => {
const text = args.some((item) => !checkDefaultValue(item))
? args.map((item) => (!checkDefaultValue(item) ? item : '?'))
const text: string | string[] = (args as string[]).some((item) => !checkDefaultValue(item))
? (args as string[]).map((item) => (!checkDefaultValue(item) ? item : '?'))
: '';
result.resolve(text);
});
Expand Down
3 changes: 1 addition & 2 deletions packages/devextreme/js/format_helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ import './common/core/localization/currency';
export default dependencyInjector({
format: function(value, format) {
const formatIsValid = isString(format) && format !== '' || isPlainObject(format) || isFunction(format);
const valueIsValid = isNumeric(value) || isDate(value);

const valueIsValid = isNumeric(value) || (isDate(value) && !isNaN(value.getTime()));

if(!formatIsValid || !valueIsValid) {
return isDefined(value) ? value.toString() : '';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1506,101 +1506,6 @@ QUnit.module('Custom filter expressions', {
});
});

QUnit.module('Formatting', function() {
QUnit.test('empty string', function(assert) {
const field = {};
const value = '';

assert.equal(utils.getCurrentValueText(field, value), '');
});

QUnit.test('string', function(assert) {
const field = {};
const value = 'Text';
assert.equal(utils.getCurrentValueText(field, value), 'Text');
});

QUnit.test('shortDate', function(assert) {
const field = { format: 'shortDate' };
const value = new Date(2017, 8, 5);
assert.equal(utils.getCurrentValueText(field, value), '9/5/2017');
});

QUnit.test('boolean', function(assert) {
const field = { dataType: 'boolean' };
let value = true;
assert.equal(utils.getCurrentValueText(field, value), 'true');

value = false;
assert.equal(utils.getCurrentValueText(field, value), 'false');

field.falseText = 'False Text';
assert.equal(utils.getCurrentValueText(field, value), 'False Text');
});

QUnit.test('field.customizeText', function(assert) {
const field = {
customizeText: function(conditionInfo) {
return conditionInfo.valueText + 'Test';
}
};
const value = 'MyValue';
assert.equal(utils.getCurrentValueText(field, value), 'MyValueTest');
});

QUnit.test('customOperation.customizeText', function(assert) {
const field = {
customizeText: function(conditionInfo) {
return conditionInfo.valueText + 'Test';
}
};
const value = 'MyValue';
const customOperation = {
customizeText: function(conditionInfo) {
return conditionInfo.valueText + 'CustomOperation';
}
};
assert.equal(utils.getCurrentValueText(field, value, customOperation), 'MyValueTestCustomOperation');
});

QUnit.test('customOperation.customizeText for array', function(assert) {
const field = { dataType: 'string' };

const customOperation = { customizeText: (fieldInfo) => '(Blanks)' };
let text = utils.getCurrentValueText(field, '', customOperation);

assert.equal(text, '');

text = utils.getCurrentValueText(field, [null], customOperation).done(text => {
assert.equal(text, '(Blanks)');
});

const field2 = { dataType: 'number' };

text = utils.getCurrentValueText(field2, null, customOperation);

assert.equal(text, '');

text = utils.getCurrentValueText(field, [null], customOperation).done(text => {
assert.equal(text, '(Blanks)');
});
});

QUnit.test('default format for date', function(assert) {
const field = { dataType: 'date' };
const value = new Date(2017, 8, 5, 12, 30, 0);

assert.equal(utils.getCurrentValueText(field, value), '9/5/2017');
});

QUnit.test('default format for datetime', function(assert) {
const field = { dataType: 'datetime' };
const value = new Date(2017, 8, 5, 12, 30, 0);

assert.equal(utils.getCurrentValueText(field, value), '9/5/2017, 12:30 PM');
});
});

QUnit.module('Lookup Value', function() {
QUnit.test('array of strings & value=empty', function(assert) {
const field = {
Expand Down
Loading
Loading