Skip to content

Commit 1a887c5

Browse files
authored
DataGrid - AI Column: Support column name (#31120)
Co-authored-by: Alyar <>
1 parent 1128d44 commit 1a887c5

File tree

5 files changed

+197
-3
lines changed

5 files changed

+197
-3
lines changed
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import {
2+
afterEach, beforeEach, describe, expect, it, jest,
3+
} from '@jest/globals';
4+
import type { dxElementWrapper } from '@js/core/renderer';
5+
import $ from '@js/core/renderer';
6+
import type { Properties as DataGridProperties } from '@js/ui/data_grid';
7+
import DataGrid from '@js/ui/data_grid';
8+
import errors from '@js/ui/widget/ui.errors';
9+
10+
const SELECTORS = {
11+
gridContainer: '#gridContainer',
12+
};
13+
14+
const GRID_CONTAINER_ID = 'gridContainer';
15+
16+
const createDataGrid = async (
17+
options: DataGridProperties = {},
18+
): Promise<{ $container: dxElementWrapper; instance: DataGrid }> => new Promise((resolve) => {
19+
const $container = $('<div>')
20+
.attr('id', GRID_CONTAINER_ID)
21+
.appendTo(document.body);
22+
23+
const instance = new DataGrid($container.get(0) as HTMLDivElement, options);
24+
25+
const contentReadyHandler = (): void => {
26+
resolve({ $container, instance });
27+
instance.off('contentReady', contentReadyHandler);
28+
};
29+
30+
instance.on('contentReady', contentReadyHandler);
31+
});
32+
33+
describe('GridCore AI Column', () => {
34+
beforeEach(() => {
35+
jest.spyOn(errors, 'log').mockImplementation(jest.fn());
36+
});
37+
afterEach(() => {
38+
const $container = $(SELECTORS.gridContainer);
39+
const dataGrid = ($container as any).dxDataGrid('instance') as DataGrid;
40+
41+
dataGrid.dispose();
42+
$container.remove();
43+
jest.clearAllMocks();
44+
});
45+
46+
describe('when the name is not set', () => {
47+
it('should throw E1066', async () => {
48+
await createDataGrid({
49+
dataSource: [
50+
{ id: 1, name: 'Name 1', value: 10 },
51+
],
52+
columns: [
53+
{ dataField: 'id', caption: 'ID' },
54+
{ dataField: 'name', caption: 'Name' },
55+
{ dataField: 'value', caption: 'Value' },
56+
{
57+
type: 'ai',
58+
caption: 'AI Column',
59+
},
60+
],
61+
});
62+
63+
expect(errors.log).toHaveBeenCalledWith('E1066');
64+
});
65+
});
66+
67+
describe('when the name specified is not unique', () => {
68+
it('should throw E1059', async () => {
69+
await createDataGrid({
70+
dataSource: [
71+
{ id: 1, name: 'Name 1', value: 10 },
72+
],
73+
columns: [
74+
{ dataField: 'id', caption: 'ID' },
75+
{ dataField: 'name', caption: 'Name' },
76+
{
77+
dataField: 'value',
78+
caption: 'Value',
79+
name: 'myColumn',
80+
},
81+
{
82+
type: 'ai',
83+
caption: 'AI Column',
84+
name: 'myColumn',
85+
},
86+
],
87+
});
88+
89+
expect(errors.log).toHaveBeenCalledWith('E1059', '"myColumn"');
90+
});
91+
});
92+
93+
describe('columnOption', () => {
94+
it('should return a column by name', async () => {
95+
const { instance } = await createDataGrid({
96+
dataSource: [
97+
{ id: 1, name: 'Name 1', value: 10 },
98+
],
99+
columns: [
100+
{ dataField: 'id', caption: 'ID' },
101+
{ dataField: 'name', caption: 'Name' },
102+
{ dataField: 'value', caption: 'Value' },
103+
{
104+
type: 'ai',
105+
caption: 'AI Column',
106+
name: 'myColumn',
107+
},
108+
],
109+
});
110+
111+
const aiColumn = instance.columnOption('myColumn');
112+
113+
expect(aiColumn.type).toBe('ai');
114+
expect(aiColumn.caption).toBe('AI Column');
115+
expect(aiColumn.index).toBe(3);
116+
});
117+
118+
describe('when the name is reset', () => {
119+
it('should throw E1066', async () => {
120+
const { instance } = await createDataGrid({
121+
dataSource: [
122+
{ id: 1, name: 'Name 1', value: 10 },
123+
],
124+
columns: [
125+
{ dataField: 'id', caption: 'ID' },
126+
{ dataField: 'name', caption: 'Name' },
127+
{ dataField: 'value', caption: 'Value' },
128+
{
129+
type: 'ai',
130+
caption: 'AI Column',
131+
name: 'myColumn',
132+
},
133+
],
134+
});
135+
136+
instance.columnOption('myColumn', 'name', '');
137+
138+
expect(errors.log).toHaveBeenCalledWith('E1066');
139+
});
140+
});
141+
142+
describe('when the name specified is not unique', () => {
143+
it('should throw E1059', async () => {
144+
const { instance } = await createDataGrid({
145+
dataSource: [
146+
{ id: 1, name: 'Name 1', value: 10 },
147+
],
148+
columns: [
149+
{ dataField: 'id', caption: 'ID' },
150+
{ dataField: 'name', caption: 'Name' },
151+
{
152+
dataField: 'value',
153+
caption: 'Value',
154+
name: 'myColumn1',
155+
},
156+
{
157+
type: 'ai',
158+
caption: 'AI Column',
159+
name: 'myColumn2',
160+
},
161+
],
162+
});
163+
164+
instance.columnOption('myColumn2', 'name', 'myColumn1');
165+
166+
expect(errors.log).toHaveBeenCalledWith('E1059', '"myColumn1"');
167+
});
168+
});
169+
});
170+
});

packages/devextreme/js/__internal/grids/grid_core/columns_controller/const.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,5 @@ export const UNSUPPORTED_PROPERTIES_FOR_CHILD_COLUMNS = [
3434
'type',
3535
'buttons',
3636
];
37+
38+
export const COMMAND_COLUMNS_WITH_REQUIRED_NAMES = ['ai'];

packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ import {
6565
getSerializationFormat,
6666
getValueDataType,
6767
isColumnFixed,
68+
isColumnNameRequired,
6869
isFirstOrLastColumn,
6970
isSortOrderValid,
7071
mergeColumns,
@@ -1553,7 +1554,9 @@ export class ColumnsController extends modules.Controller {
15531554
}
15541555

15551556
public setName(column) {
1556-
column.name = column.name || column.dataField || column.type;
1557+
if (!isColumnNameRequired(column)) {
1558+
column.name = column.name || column.dataField || column.type;
1559+
}
15571560
}
15581561

15591562
public setUserState(state) {
@@ -1594,18 +1597,23 @@ export class ColumnsController extends modules.Controller {
15941597

15951598
public _checkColumns() {
15961599
const usedNames = {};
1597-
let hasEditableColumnWithoutName = false;
15981600
const duplicatedNames: any = [];
1601+
let hasEditableColumnWithoutName = false;
1602+
let hasColumnsWithoutRequiredNames = false;
1603+
15991604
this._columns.forEach((column) => {
16001605
const { name } = column;
16011606
const isBand = column.columns?.length;
16021607
const isEditable = column.allowEditing && (column.dataField || column.setCellValue) && !isBand;
1608+
16031609
if (name) {
16041610
if (usedNames[name]) {
16051611
duplicatedNames.push(`"${name}"`);
16061612
}
16071613

16081614
usedNames[name] = true;
1615+
} else if (isColumnNameRequired(column)) {
1616+
hasColumnsWithoutRequiredNames = true;
16091617
} else if (isEditable) {
16101618
hasEditableColumnWithoutName = true;
16111619
}
@@ -1615,6 +1623,10 @@ export class ColumnsController extends modules.Controller {
16151623
errors.log('E1059', duplicatedNames.join(', '));
16161624
}
16171625

1626+
if (hasColumnsWithoutRequiredNames) {
1627+
errors.log('E1066');
1628+
}
1629+
16181630
if (hasEditableColumnWithoutName) {
16191631
errors.log('E1060');
16201632
}

packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller_utils.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { getColumnFixedPosition } from '../sticky_columns/utils';
2121
import {
2222
COLUMN_CHOOSER_LOCATION,
2323
COLUMN_INDEX_OPTIONS,
24+
COMMAND_COLUMNS_WITH_REQUIRED_NAMES,
2425
DEFAULT_COLUMN_OPTIONS,
2526
GROUP_COMMAND_COLUMN_NAME,
2627
GROUP_LOCATION,
@@ -29,7 +30,7 @@ import {
2930
USER_STATE_FIELD_NAMES,
3031
USER_STATE_FIELD_NAMES_15_1,
3132
} from './const';
32-
import type { ColumnsController } from './m_columns_controller';
33+
import type { Column, ColumnsController } from './m_columns_controller';
3334

3435
const warnFixedInChildColumnsOnce = (controller: ColumnsController, childColumns: any[]): void => {
3536
if (controller?._isWarnedAboutUnsupportedProperties) return;
@@ -1026,3 +1027,7 @@ export const isFirstOrLastColumn = function (
10261027

10271028
return onlyWithinBandColumn || isFirstOrLastColumnCore(that, targetColumn, rowIndex, onlyWithinBandColumn, isLast, fixedPosition);
10281029
};
1030+
1031+
export const isColumnNameRequired = function ({ type = '' }: Column): boolean {
1032+
return COMMAND_COLUMNS_WITH_REQUIRED_NAMES.includes(type);
1033+
};

packages/devextreme/js/ui/widget/ui.errors.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,11 @@ export default errorUtils(errors.ERROR_MESSAGES, {
266266
*/
267267
E1065: 'The browser does not support Web Speech API (SpeechRecognition)',
268268

269+
/**
270+
* @name ErrorsUIWidgets.E1066
271+
*/
272+
E1066: 'All AI columns must have names.',
273+
269274
/**
270275
* @name ErrorsUIWidgets.W1001
271276
*/

0 commit comments

Comments
 (0)