Skip to content

Commit 914323e

Browse files
Merge remote-tracking branch 'my/26_1_WIP_check_ng22' into 26_1_WIP_check_ng22
2 parents f0c66a7 + c348f55 commit 914323e

30 files changed

Lines changed: 207 additions & 124 deletions

packages/devextreme/js/__internal/grids/grid_core/ai_assistant/commands/__tests__/filtering.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,38 @@ describe('filterValueCommand', () => {
436436
expect(result.status).toBe('failure');
437437
});
438438

439+
it('returns failure when a field has no corresponding column', async () => {
440+
const instance = await createGrid();
441+
const spy = jest.spyOn(instance, 'option');
442+
const callbacks = createCallbacks();
443+
444+
const result = await filterValueCommand.execute(instance, callbacks)({
445+
expression: singleBasic('nonexistent', '=', 'Alpha'),
446+
});
447+
448+
expect(spy).not.toHaveBeenCalled();
449+
expect(result.status).toBe('failure');
450+
});
451+
452+
it('succeeds when a field maps to a hidden but existing column', async () => {
453+
const instance = await createGrid({
454+
columns: [
455+
{ dataField: 'id', dataType: 'number' },
456+
{ dataField: 'name', dataType: 'string' },
457+
{ dataField: 'age', dataType: 'number', visible: false },
458+
],
459+
});
460+
const spy = jest.spyOn(instance, 'option');
461+
const callbacks = createCallbacks();
462+
463+
const result = await filterValueCommand.execute(instance, callbacks)({
464+
expression: singleBasic('age', '>', 10),
465+
});
466+
467+
expect(spy).toHaveBeenCalledWith('filterValue', ['age', '>', 10]);
468+
expect(result.status).toBe('success');
469+
});
470+
439471
it('tolerates unreachable extra nodes', async () => {
440472
const instance = await createGrid();
441473
const spy = jest.spyOn(instance, 'option');

packages/devextreme/js/__internal/grids/grid_core/ai_assistant/commands/filtering.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,15 @@ function convertFilterExprToArray(
9494
const { expr } = node;
9595
switch (expr.type) {
9696
case 'basic': {
97-
const dataType = component.columnOption(expr.field, 'dataType');
98-
const resolved = resolveFilterValue(dataType, expr.value);
97+
const column = component.columnOption(expr.field);
98+
99+
if (!isDefined(column)) {
100+
// Filter sync requires an existing column for every field in a basic expression
101+
throw new Error(`Unknown column: ${expr.field}`);
102+
}
103+
104+
const resolved = resolveFilterValue(column.dataType, expr.value);
105+
99106
return [expr.field, expr.operator, resolved];
100107
}
101108
case 'combined':
@@ -131,7 +138,7 @@ const getFilterSuccessMessage = async (
131138

132139
export const filterValueCommand = defineGridCommand({
133140
name: 'filterValue',
134-
description: 'Apply a filter expression to the grid. Replaces any existing filter; pass null for expression to clear. The expression is a flat node list: {"rootId":id,"nodes":[...]}. Each node is {"id":<unique string like "n1">,"expr":<expression>}, where "expr" is one of: basic {"type":"basic","field":dataField,"operator":op,"value":val}, combined {"type":"combined","combiner":"and"|"or","leftId":nodeId,"rightId":nodeId}, negated {"type":"negated","expressionId":nodeId}. "rootId" MUST be the "id" of the outermost node (the top of the expression tree) and must match one of the node ids exactly — never invent a value like "root". Every "leftId"/"rightId"/"expressionId" must also match a node "id". Ids must be unique and must not form cycles. The "field" is the column dataField (not the caption). Supported operators: "=", "<>", "<", "<=", ">", ">=", "contains", "notcontains", "startswith", "endswith". DATE VALUES: When a value is a date or datetime, always use "YYYY-MM-DDTHH:mm:ss" format without timezone suffix, e.g. "2024-05-10T00:00:00" for midnight or "2024-05-10T14:30:00" for a specific time. Always include the "T" and time part. Do NOT use date-only format like "2024-05-10" without time. Do NOT append "Z" or any timezone offset unless the user explicitly requests it. Do NOT use natural language for dates. To express "not and" / "not or", add a negated node whose expressionId points at a combined node. Example for name = "Alpha" AND age > 10 (rootId is "n3", the combined node): {"rootId":"n3","nodes":[{"id":"n1","expr":{"type":"basic","field":"name","operator":"=","value":"Alpha"}},{"id":"n2","expr":{"type":"basic","field":"age","operator":">","value":10}},{"id":"n3","expr":{"type":"combined","combiner":"and","leftId":"n1","rightId":"n2"}}]}.',
141+
description: 'Apply a filter expression to the grid. Replaces any existing filter; pass null for expression to clear. The expression is a flat node list: {"rootId":id,"nodes":[...]}. Each node is {"id":<unique string like "n1">,"expr":<expression>}, where "expr" is one of: basic {"type":"basic","field":dataField,"operator":op,"value":val}, combined {"type":"combined","combiner":"and"|"or","leftId":nodeId,"rightId":nodeId}, negated {"type":"negated","expressionId":nodeId}. "rootId" MUST be the "id" of the outermost node (the top of the expression tree) and must match one of the node ids exactly — never invent a value like "root". Every "leftId"/"rightId"/"expressionId" must also match a node "id". Ids must be unique and must not form cycles. The "field" of every basic expression MUST be the dataField of a column that exists in the grid (not the caption); the column may be hidden, but it must exist. Never filter on a field that has no corresponding column. Supported operators: "=", "<>", "<", "<=", ">", ">=", "contains", "notcontains", "startswith", "endswith". DATE VALUES: When a value is a date or datetime, always use "YYYY-MM-DDTHH:mm:ss" format without timezone suffix, e.g. "2024-05-10T00:00:00" for midnight or "2024-05-10T14:30:00" for a specific time. Always include the "T" and time part. Do NOT use date-only format like "2024-05-10" without time. Do NOT append "Z" or any timezone offset unless the user explicitly requests it. Do NOT use natural language for dates. To express "not and" / "not or", add a negated node whose expressionId points at a combined node. Example for name = "Alpha" AND age > 10 (rootId is "n3", the combined node): {"rootId":"n3","nodes":[{"id":"n1","expr":{"type":"basic","field":"name","operator":"=","value":"Alpha"}},{"id":"n2","expr":{"type":"basic","field":"age","operator":">","value":10}},{"id":"n3","expr":{"type":"combined","combiner":"and","leftId":"n1","rightId":"n2"}}]}.',
135142
schema: filterValueCommandSchema,
136143
execute: (component, { success, failure }) => async (args): Promise<CommandResult> => {
137144
let defaultMessage = args.expression === null

packages/devextreme/js/__internal/scheduler/appointment_popup/form.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import type { Properties as SwitchProperties } from '@js/ui/switch';
2424
import type { Properties as TextAreaProperties } from '@js/ui/text_area';
2525
import { current, isFluent } from '@js/ui/themes';
2626
import { dateSerialization } from '@ts/core/utils/m_date_serialization';
27-
import DropDownEditor from '@ts/ui/drop_down_editor/m_drop_down_editor';
27+
import DropDownEditor from '@ts/ui/drop_down_editor/drop_down_editor';
2828
import type Popup from '@ts/ui/popup/m_popup';
2929

3030
import timeZoneUtils from '../m_utils_time_zone';

packages/devextreme/js/__internal/ui/color_box/color_box.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { DeferredObj } from '@js/core/utils/deferred';
55
import type { Properties } from '@js/ui/color_box';
66
import type { OptionChanged } from '@ts/core/widget/types';
77
import Color from '@ts/m_color';
8-
import DropDownEditor from '@ts/ui/drop_down_editor/m_drop_down_editor';
8+
import DropDownEditor from '@ts/ui/drop_down_editor/drop_down_editor';
99
import type { ValueChangedEvent } from '@ts/ui/editor/editor';
1010

1111
import type { PopupProperties } from '../popup/m_popup';

packages/devextreme/js/__internal/ui/date_box/date_box.base.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import type {
2121
} from '@js/ui/date_box';
2222
import type { ToolbarItem } from '@js/ui/popup';
2323
import type { OptionChanged } from '@ts/core/widget/types';
24-
import DropDownEditor from '@ts/ui/drop_down_editor/m_drop_down_editor';
24+
import DropDownEditor from '@ts/ui/drop_down_editor/drop_down_editor';
2525
import type { ValueChangedEvent } from '@ts/ui/editor/editor';
2626

2727
import type { PopupProperties } from '../popup/m_popup';

packages/devextreme/js/__internal/ui/date_box/m_date_box.strategy.list.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type { DxEvent } from '@js/events';
1111
import type { Format } from '@js/localization';
1212
import type { ItemClickEvent } from '@js/ui/list';
1313
import { getGlobalFormatByDataType } from '@ts/core/m_global_format_config';
14-
import { getSizeValue } from '@ts/ui/drop_down_editor/m_utils';
14+
import { getSizeValue } from '@ts/ui/drop_down_editor/utils';
1515
import List from '@ts/ui/list/list.edit.search';
1616

1717
import type { PopupProperties } from '../popup/m_popup';

packages/devextreme/js/__internal/ui/date_range_box/m_date_range_box.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { each } from '@js/core/utils/iterator';
1717
import type { Properties } from '@js/ui/date_range_box';
1818
import Editor from '@js/ui/editor/editor';
1919
import { current, isFluent, isMaterial } from '@js/ui/themes';
20-
import DropDownButton from '@ts/ui/drop_down_editor/m_drop_down_button';
20+
import DropDownButton from '@ts/ui/drop_down_editor/drop_down_button';
2121
import ClearButton from '@ts/ui/text_box/text_editor.clear';
2222
import TextEditorButtonCollection from '@ts/ui/text_box/texteditor_button_collection/index';
2323

packages/devextreme/js/__internal/ui/drop_down_editor/m_drop_down_button.ts renamed to packages/devextreme/js/__internal/ui/drop_down_editor/drop_down_button.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import Button from '@js/ui/button';
99

1010
import type TextEditorBase from '../text_box/text_editor.base';
1111
import TextEditorButton from '../text_box/texteditor_button_collection/button';
12-
import type DropDownEditor from './m_drop_down_editor';
13-
import type { DropDownEditorProperties } from './m_drop_down_editor';
12+
import type DropDownEditor from './drop_down_editor';
13+
import type { DropDownEditorProperties } from './drop_down_editor';
1414

1515
const DROP_DOWN_EDITOR_BUTTON_CLASS = 'dx-dropdowneditor-button';
1616
const DROP_DOWN_EDITOR_BUTTON_VISIBLE = 'dx-dropdowneditor-button-visible';
@@ -125,7 +125,6 @@ export default class DropDownButton extends TextEditorButton {
125125
}
126126

127127
// TODO: get rid of it
128-
// eslint-disable-next-line class-methods-use-this
129128
_legacyRender(
130129
$editor?: dxElementWrapper,
131130
$element?: dxElementWrapper,

packages/devextreme/js/__internal/ui/drop_down_editor/m_drop_down_editor.ts renamed to packages/devextreme/js/__internal/ui/drop_down_editor/drop_down_editor.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ import TextBox from '@ts/ui/text_box/text_box';
4141

4242
import type Popover from '../popover/m_popover';
4343
import type { TextEditorButtonInfo } from '../text_box/texteditor_button_collection/index';
44-
import DropDownButton from './m_drop_down_button';
45-
import { getElementWidth } from './m_utils';
44+
import DropDownButton from './drop_down_button';
45+
import { getElementWidth } from './utils';
4646

4747
export const DROP_DOWN_EDITOR_CLASS = 'dx-dropdowneditor';
4848
const DROP_DOWN_EDITOR_INPUT_WRAPPER = 'dx-dropdowneditor-input-wrapper';
@@ -261,12 +261,10 @@ class DropDownEditor<
261261
};
262262
}
263263

264-
// eslint-disable-next-line class-methods-use-this
265264
_useTemplates(): boolean {
266265
return true;
267266
}
268267

269-
// eslint-disable-next-line class-methods-use-this
270268
_getDefaultPopupPosition(isRtlEnabled?: boolean): PositionConfig {
271269
const position = getDefaultAlignment(isRtlEnabled);
272270

@@ -377,17 +375,14 @@ class DropDownEditor<
377375
.eq(0);
378376
}
379377

380-
// eslint-disable-next-line class-methods-use-this
381378
_getAriaHasPopup(): string {
382379
return 'true';
383380
}
384381

385-
// eslint-disable-next-line class-methods-use-this
386382
_getAriaAutocomplete(): string {
387383
return 'none';
388384
}
389385

390-
// eslint-disable-next-line class-methods-use-this
391386
_getAriaRole(): string {
392387
return 'combobox';
393388
}
@@ -818,7 +813,6 @@ class DropDownEditor<
818813
this.setAria('label', OVERLAY_CONTENT_LABEL, $overlayContent);
819814
}
820815

821-
// eslint-disable-next-line class-methods-use-this
822816
_renderPopupContent(): void {}
823817

824818
_renderPopup(): void {
@@ -882,7 +876,6 @@ class DropDownEditor<
882876
this.setAria('id', this._popupContentId, $popupContent);
883877
}
884878

885-
// eslint-disable-next-line class-methods-use-this
886879
_contentReadyHandler(): void {}
887880

888881
_popupConfig(): PopupProperties {
@@ -925,7 +918,6 @@ class DropDownEditor<
925918
return config;
926919
}
927920

928-
// eslint-disable-next-line class-methods-use-this
929921
_popupInitializedHandler(): void {}
930922

931923
_getPopupInitializedHandler(): (e: PopupInitializedEvent) => void {
@@ -983,7 +975,6 @@ class DropDownEditor<
983975
}
984976
}
985977

986-
// eslint-disable-next-line class-methods-use-this
987978
_popupShowingHandler(): void {}
988979

989980
_popupHidingHandler(): void {
@@ -1171,7 +1162,6 @@ class DropDownEditor<
11711162
return super._getSubmitElement();
11721163
}
11731164

1174-
// eslint-disable-next-line class-methods-use-this
11751165
_shouldLogFieldTemplateDeprecationWarning(): boolean {
11761166
return false;
11771167
}

packages/devextreme/js/__internal/ui/drop_down_editor/m_drop_down_list.ts renamed to packages/devextreme/js/__internal/ui/drop_down_editor/drop_down_list.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import errors from '@js/ui/widget/ui.errors';
3131
import type { OptionChanged } from '@ts/core/widget/types';
3232
import { getDataSourceOptions } from '@ts/data/data_converter/grouped';
3333
import type DataController from '@ts/ui/collection/m_data_controller';
34-
import DropDownEditor from '@ts/ui/drop_down_editor/m_drop_down_editor';
34+
import DropDownEditor from '@ts/ui/drop_down_editor/drop_down_editor';
3535
import type { ListBaseProperties } from '@ts/ui/list/list.base';
3636
import List from '@ts/ui/list/list.edit.search';
3737

@@ -252,7 +252,6 @@ class DropDownList<
252252
}
253253
}
254254

255-
// eslint-disable-next-line class-methods-use-this
256255
_fitIntoRange(value: number, start: number, end: number): number {
257256
if (value > end) {
258257
return start;
@@ -312,7 +311,6 @@ class DropDownList<
312311
}
313312
}
314313

315-
// eslint-disable-next-line class-methods-use-this
316314
_popupWrapperClass(): string {
317315
return DROPDOWNLIST_POPUP_WRAPPER_CLASS;
318316
}
@@ -576,7 +574,6 @@ class DropDownList<
576574
this.setAria('owns', this._popup && this._popupContentId);
577575
}
578576

579-
// eslint-disable-next-line class-methods-use-this
580577
_getAriaHasPopup(): string {
581578
return 'listbox';
582579
}
@@ -593,7 +590,6 @@ class DropDownList<
593590
return Boolean(dataSource) !== this._needPassDataSourceToList();
594591
}
595592

596-
// eslint-disable-next-line class-methods-use-this
597593
_isDesktopDevice(): boolean {
598594
return devices.real().deviceType === 'desktop';
599595
}
@@ -646,7 +642,6 @@ class DropDownList<
646642
return options;
647643
}
648644

649-
// eslint-disable-next-line class-methods-use-this
650645
_canListHaveFocus(): boolean {
651646
return false;
652647
}
@@ -655,7 +650,6 @@ class DropDownList<
655650
return this._needPassDataSourceToList() ? this._dataSource : null;
656651
}
657652

658-
// eslint-disable-next-line class-methods-use-this
659653
_dataSourceOptions(): Partial<DataSourceOptions<Item>> {
660654
return {
661655
paginate: false,
@@ -675,7 +669,6 @@ class DropDownList<
675669
return dataSource;
676670
}
677671

678-
// eslint-disable-next-line class-methods-use-this
679672
_dataSourceFromUrlLoadMode(): string {
680673
return 'raw';
681674
}
@@ -712,7 +705,7 @@ class DropDownList<
712705
this._itemClickAction(e);
713706
}
714707

715-
// eslint-disable-next-line @typescript-eslint/no-unused-vars, class-methods-use-this
708+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
716709
_listItemClickHandler(e?: ItemClickEvent<Item>): void { }
717710

718711
_setListDataSource(): void {

0 commit comments

Comments
 (0)