diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/aiColumn/columnChooser.functional.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/aiColumn/columnChooser.functional.ts new file mode 100644 index 000000000000..d71747585bcd --- /dev/null +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/aiColumn/columnChooser.functional.ts @@ -0,0 +1,445 @@ +import DataGrid from 'devextreme-testcafe-models/dataGrid'; +import url from '../../../../helpers/getPageUrl'; +import { createWidget } from '../../../../helpers/createWidget'; + +fixture.disablePageReloads`Ai Column - Column Chooser.Functional` + .page(url(__dirname, '../../../container.html')); + +const DATA_GRID_SELECTOR = '#container'; + +const checkCells = async (t, cells: Selector, expectedCells: string[]) => { + const count = await cells.count; + + await t.expect(cells.count).eql(expectedCells.length); + + for (let i = 0; i < count; i += 1) { + await t.expect(cells.nth(i).innerText).eql(expectedCells[i]); + } +}; + +const checkVisibleHeaders = async (t, headers: Selector, expectedCells: string[]) => { + await checkCells(t, headers, expectedCells); +}; + +const checkChooserColumns = async (t, chooserColumns: Selector, expectedCells: string[]) => { + await checkCells(t, chooserColumns, expectedCells); +}; + +test('The AI column can be hidden when columnChooser.mode is "dragAndDrop"', async (t) => { + // arrange + const dataGrid = new DataGrid(DATA_GRID_SELECTOR); + + await t.expect(dataGrid.isReady()).ok(); + + const headerCells = dataGrid.getHeaders().getHeaderRow(0).getHeaderCells(); + + // assert + await t.expect(dataGrid.apiColumnOption('myAiColumn', 'visible')).ok(); + await checkVisibleHeaders(t, headerCells, ['AI Column', 'ID', 'Name', 'Value']); + + // act + await dataGrid.apiShowColumnChooser(); + + // assert + await t.expect(dataGrid.getColumnChooser().isOpened).ok(); + await checkChooserColumns(t, dataGrid.getColumnChooser().getColumns(), []); + + // act + await t.dragToElement( + dataGrid.getHeaders().getHeaderRow(0).getHeaderCell(0).element, + dataGrid.getColumnChooser().content, + ); + + // assert + await t.expect(dataGrid.apiColumnOption('myAiColumn', 'visible')).notOk(); + await checkVisibleHeaders(t, headerCells, ['ID', 'Name', 'Value']); + await checkChooserColumns(t, dataGrid.getColumnChooser().getColumns(), ['AI Column']); +}).before(async () => createWidget('dxDataGrid', { + dataSource: [ + { id: 1, name: 'Name 1', value: 10 }, + { id: 2, name: 'Name 2', value: 20 }, + { id: 3, name: 'Name 3', value: 30 }, + ], + width: 600, + columnWidth: 200, + columnChooser: { + enabled: true, + mode: 'dragAndDrop', + }, + columns: [ + { + type: 'ai', + caption: 'AI Column', + name: 'myAiColumn', + }, + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + ], +})); + +test('The AI column cannot be hidden when columnChooser.mode is "dragAndDrop" and allowHiding is false', async (t) => { + // arrange + const dataGrid = new DataGrid(DATA_GRID_SELECTOR); + + await t.expect(dataGrid.isReady()).ok(); + + const headerCells = dataGrid.getHeaders().getHeaderRow(0).getHeaderCells(); + + // assert + await t.expect(dataGrid.apiColumnOption('myAiColumn', 'visible')).ok(); + await checkVisibleHeaders(t, headerCells, ['AI Column', 'ID', 'Name', 'Value']); + + // act + await dataGrid.apiShowColumnChooser(); + + // assert + await t.expect(dataGrid.getColumnChooser().isOpened).ok(); + await checkChooserColumns(t, dataGrid.getColumnChooser().getColumns(), []); + + // act + await t.dragToElement( + dataGrid.getHeaders().getHeaderRow(0).getHeaderCell(0).element, + dataGrid.getColumnChooser().content, + ); + + // assert + await t.expect(dataGrid.apiColumnOption('myAiColumn', 'visible')).ok(); + await checkVisibleHeaders(t, headerCells, ['AI Column', 'ID', 'Name', 'Value']); + await checkChooserColumns(t, dataGrid.getColumnChooser().getColumns(), []); +}).before(async () => createWidget('dxDataGrid', { + dataSource: [ + { id: 1, name: 'Name 1', value: 10 }, + { id: 2, name: 'Name 2', value: 20 }, + { id: 3, name: 'Name 3', value: 30 }, + ], + width: 600, + columnWidth: 200, + columnChooser: { + enabled: true, + mode: 'dragAndDrop', + }, + columns: [ + { + type: 'ai', + caption: 'AI Column', + name: 'myAiColumn', + allowHiding: false, + }, + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + ], +})); + +test('The AI column can be hidden when columnChooser.mode is "select"', async (t) => { + // arrange + const dataGrid = new DataGrid(DATA_GRID_SELECTOR); + + await t.expect(dataGrid.isReady()).ok(); + + const headerCells = dataGrid.getHeaders().getHeaderRow(0).getHeaderCells(); + + // assert + await t.expect(dataGrid.apiColumnOption('myAiColumn', 'visible')).ok(); + await checkVisibleHeaders(t, headerCells, ['AI Column', 'ID', 'Name', 'Value']); + + // act + await dataGrid.apiShowColumnChooser(); + + const columnChooser = dataGrid.getColumnChooser(); + + // assert + await t.expect(columnChooser.isOpened).ok(); + + // act + await t.click(columnChooser.getCheckbox(0)); + + // assert + await t + .expect(columnChooser.isCheckboxChecked(0)).notOk() + .expect(dataGrid.apiColumnOption('myAiColumn', 'visible')).notOk(); + + await checkVisibleHeaders(t, headerCells, ['ID', 'Name', 'Value']); +}).before(async () => createWidget('dxDataGrid', { + dataSource: [ + { id: 1, name: 'Name 1', value: 10 }, + { id: 2, name: 'Name 2', value: 20 }, + { id: 3, name: 'Name 3', value: 30 }, + ], + width: 600, + columnWidth: 200, + columnChooser: { + enabled: true, + mode: 'select', + }, + columns: [ + { + type: 'ai', + caption: 'AI Column', + name: 'myAiColumn', + }, + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + ], +})); + +test('The AI column cannot be hidden when columnChooser.mode is "select" and allowHiding is false', async (t) => { +// arrange + const dataGrid = new DataGrid(DATA_GRID_SELECTOR); + + await t.expect(dataGrid.isReady()).ok(); + + const headerCells = dataGrid.getHeaders().getHeaderRow(0).getHeaderCells(); + + // assert + await t.expect(dataGrid.apiColumnOption('myAiColumn', 'visible')).ok(); + await checkVisibleHeaders(t, headerCells, ['AI Column', 'ID', 'Name', 'Value']); + + // act + await dataGrid.apiShowColumnChooser(); + + const columnChooser = dataGrid.getColumnChooser(); + + // assert + await t + .expect(columnChooser.isOpened) + .ok() + .expect(columnChooser.isCheckboxDisabled(0)) + .ok() + .expect(columnChooser.isCheckboxChecked(0)) + .ok() + .expect(dataGrid.apiColumnOption('myAiColumn', 'visible')) + .ok(); +}).before(async () => createWidget('dxDataGrid', { + dataSource: [ + { id: 1, name: 'Name 1', value: 10 }, + { id: 2, name: 'Name 2', value: 20 }, + { id: 3, name: 'Name 3', value: 30 }, + ], + width: 600, + columnWidth: 200, + columnChooser: { + enabled: true, + mode: 'select', + }, + columns: [ + { + type: 'ai', + caption: 'AI Column', + name: 'myAiColumn', + allowHiding: false, + }, + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + ], +})); + +test('The AI column can be shown when columnChooser.mode is "dragAndDrop"', async (t) => { + // arrange + const dataGrid = new DataGrid(DATA_GRID_SELECTOR); + + await t.expect(dataGrid.isReady()).ok(); + + const headerCells = dataGrid.getHeaders().getHeaderRow(0).getHeaderCells(); + + // assert + await t.expect(dataGrid.apiColumnOption('myAiColumn', 'visible')).notOk(); + await checkVisibleHeaders(t, headerCells, ['ID', 'Name', 'Value']); + + // act + await dataGrid.apiShowColumnChooser(); + + // assert + await t.expect(dataGrid.getColumnChooser().isOpened).ok(); + await checkChooserColumns(t, dataGrid.getColumnChooser().getColumns(), ['AI Column']); + + // act + await t.dragToElement( + dataGrid.getColumnChooser().getColumn(0), + dataGrid.getHeaders().getHeaderRow(0).getHeaderCell(0).element, + ); + + // assert + await t.expect(dataGrid.apiColumnOption('myAiColumn', 'visible')).ok(); + await checkVisibleHeaders(t, headerCells, ['AI Column', 'ID', 'Name', 'Value']); + await checkChooserColumns(t, dataGrid.getColumnChooser().getColumns(), []); +}).before(async () => createWidget('dxDataGrid', { + dataSource: [ + { id: 1, name: 'Name 1', value: 10 }, + { id: 2, name: 'Name 2', value: 20 }, + { id: 3, name: 'Name 3', value: 30 }, + ], + width: 600, + columnWidth: 200, + columnChooser: { + enabled: true, + mode: 'dragAndDrop', + }, + columns: [ + { + type: 'ai', + caption: 'AI Column', + name: 'myAiColumn', + visible: false, + }, + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + ], +})); + +test('The AI column cannot be shown when columnChooser.mode is "dragAndDrop" and allowHiding is false', async (t) => { + // arrange + const dataGrid = new DataGrid(DATA_GRID_SELECTOR); + + await t.expect(dataGrid.isReady()).ok(); + + const headerCells = dataGrid.getHeaders().getHeaderRow(0).getHeaderCells(); + + // assert + await t.expect(dataGrid.apiColumnOption('myAiColumn', 'visible')).notOk(); + await checkVisibleHeaders(t, headerCells, ['ID', 'Name', 'Value']); + + // act + await dataGrid.apiShowColumnChooser(); + + // assert + await t + .expect(dataGrid.getColumnChooser().isOpened).ok() + .expect(dataGrid.getColumnChooser().isColumnDisabled(0)).ok(); + await checkChooserColumns(t, dataGrid.getColumnChooser().getColumns(), ['AI Column']); +}).before(async () => createWidget('dxDataGrid', { + dataSource: [ + { id: 1, name: 'Name 1', value: 10 }, + { id: 2, name: 'Name 2', value: 20 }, + { id: 3, name: 'Name 3', value: 30 }, + ], + width: 600, + columnWidth: 200, + columnChooser: { + enabled: true, + mode: 'dragAndDrop', + }, + columns: [ + { + type: 'ai', + caption: 'AI Column', + name: 'myAiColumn', + allowHiding: false, + visible: false, + }, + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + ], +})); + +test('The AI column can be shown when columnChooser.mode is "select"', async (t) => { + // arrange + const dataGrid = new DataGrid(DATA_GRID_SELECTOR); + + await t.expect(dataGrid.isReady()).ok(); + + const headerCells = dataGrid.getHeaders().getHeaderRow(0).getHeaderCells(); + + // assert + await t.expect(dataGrid.apiColumnOption('myAiColumn', 'visible')).notOk(); + await checkVisibleHeaders(t, headerCells, ['ID', 'Name', 'Value']); + + // act + await dataGrid.apiShowColumnChooser(); + + const columnChooser = dataGrid.getColumnChooser(); + + // assert + await t.expect(columnChooser.isOpened).ok(); + + // act + await t.click(columnChooser.getCheckbox(0)); + + // assert + await t + .expect(columnChooser.isCheckboxChecked(0)).ok() + .expect(dataGrid.apiColumnOption('myAiColumn', 'visible')).ok(); + + await checkVisibleHeaders(t, headerCells, ['AI Column', 'ID', 'Name', 'Value']); +}).before(async () => createWidget('dxDataGrid', { + dataSource: [ + { id: 1, name: 'Name 1', value: 10 }, + { id: 2, name: 'Name 2', value: 20 }, + { id: 3, name: 'Name 3', value: 30 }, + ], + width: 600, + columnWidth: 200, + columnChooser: { + enabled: true, + mode: 'select', + }, + columns: [ + { + type: 'ai', + caption: 'AI Column', + name: 'myAiColumn', + visible: false, + }, + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + ], +})); + +test('The AI column cannot be shown when columnChooser.mode is "select" and allowHiding is false', async (t) => { +// arrange + const dataGrid = new DataGrid(DATA_GRID_SELECTOR); + + await t.expect(dataGrid.isReady()).ok(); + + const headerCells = dataGrid.getHeaders().getHeaderRow(0).getHeaderCells(); + + // assert + await t.expect(dataGrid.apiColumnOption('myAiColumn', 'visible')).notOk(); + await checkVisibleHeaders(t, headerCells, ['ID', 'Name', 'Value']); + + // act + await dataGrid.apiShowColumnChooser(); + + const columnChooser = dataGrid.getColumnChooser(); + + // assert + await t + .expect(columnChooser.isOpened) + .ok() + .expect(columnChooser.isCheckboxDisabled(0)) + .ok() + .expect(columnChooser.isCheckboxChecked(0)) + .notOk() + .expect(dataGrid.apiColumnOption('myAiColumn', 'visible')) + .notOk(); +}).before(async () => createWidget('dxDataGrid', { + dataSource: [ + { id: 1, name: 'Name 1', value: 10 }, + { id: 2, name: 'Name 2', value: 20 }, + { id: 3, name: 'Name 3', value: 30 }, + ], + width: 600, + columnWidth: 200, + columnChooser: { + enabled: true, + mode: 'select', + }, + columns: [ + { + type: 'ai', + caption: 'AI Column', + name: 'myAiColumn', + allowHiding: false, + visible: false, + }, + { dataField: 'id', caption: 'ID' }, + { dataField: 'name', caption: 'Name' }, + { dataField: 'value', caption: 'Value' }, + ], +})); diff --git a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts index 08f7b2c98a8a..5082fcab4641 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts @@ -25,6 +25,7 @@ import type { DataController } from '@ts/grids/grid_core/data_controller/m_data_ import type { FocusController } from '@ts/grids/grid_core/focus/m_focus'; import type { StateStoringController } from '@ts/grids/grid_core/state_storing/m_state_storing_core'; +import { AI_COLUMN_NAME } from '../ai_column/const'; import modules from '../m_modules'; import type { Module } from '../m_types'; import gridCoreUtils from '../m_utils'; @@ -141,6 +142,14 @@ export class ColumnsController extends modules.Controller { public _isWarnedAboutUnsupportedProperties?: boolean; + private getCommonColumnSettings(column): Partial { + if (!column?.type) { + return this.option('commonColumnSettings'); + } + + return column.type === AI_COLUMN_NAME ? { allowHiding: true } : {}; + } + public init(isApplyingUserState?): void { this._dataController = this.getController('data'); this._focusController = this.getController('focus'); @@ -391,7 +400,7 @@ export class ColumnsController extends modules.Controller { } public getCommonSettings(column?) { - const commonColumnSettings = (!column || !column.type) && this.option('commonColumnSettings') || {}; + const commonColumnSettings = this.getCommonColumnSettings(column); const groupingOptions: any = this.option('grouping') ?? {}; const groupPanelOptions: any = this.option('groupPanel') ?? {}; diff --git a/packages/testcafe-models/dataGrid/columnChooser.ts b/packages/testcafe-models/dataGrid/columnChooser.ts index 8362cdee31ae..e5b4a4371e3a 100644 --- a/packages/testcafe-models/dataGrid/columnChooser.ts +++ b/packages/testcafe-models/dataGrid/columnChooser.ts @@ -7,7 +7,7 @@ const CLASS = { overlayWrapper: 'dx-overlay-wrapper', columnChooser: 'dx-datagrid-column-chooser', checkboxChecked: 'dx-checkbox-checked', - checkboxDisabled: 'dx-state-disabled', + stateDisabled: 'dx-state-disabled', checkbox: 'dx-checkbox', treeViewItem: 'dx-treeview-item', treeView: 'dx-treeview', @@ -54,7 +54,11 @@ export default class ColumnChooser extends FocusableElement { } isCheckboxDisabled(nth = 0): Promise { - return this.getCheckbox(nth).hasClass(CLASS.checkboxDisabled); + return this.getCheckbox(nth).hasClass(CLASS.stateDisabled); + } + + isColumnDisabled(index = 0): Promise { + return this.getColumn(index).hasClass(CLASS.stateDisabled); } getColumnsCount(): Promise { @@ -65,6 +69,10 @@ export default class ColumnChooser extends FocusableElement { return this.content.find(`.${CLASS.treeViewItem}`).nth(index); } + getColumns(): Selector { + return this.content.find(`.${CLASS.treeViewItem}`); + } + getTitle(): Selector { return this.content.find(`.${CLASS.itemContent}.${CLASS.itemContentToolbar}`).nth(0); } diff --git a/packages/testcafe-models/dataGrid/headers/row.ts b/packages/testcafe-models/dataGrid/headers/row.ts index 260181710fe5..dfff828f2101 100644 --- a/packages/testcafe-models/dataGrid/headers/row.ts +++ b/packages/testcafe-models/dataGrid/headers/row.ts @@ -14,6 +14,10 @@ export default class HeaderRow extends FocusableElement { return new HeaderCell(this.element, index, this.widgetName); } + getHeaderCells(): Selector { + return this.element.child('td'); + } + getCommandCell(index: number): CommandCell { return new CommandCell(this.element, index, this.widgetName); }