Skip to content

Commit

Permalink
Release 23.1.5
Browse files Browse the repository at this point in the history
Cherry-picked changesets:
  3ef5350 Andrey Dolzhikov - DataGrid - Component sends unexpected filtering request after inserting a new row if focusedRowEnabled is true (T1181477) (#25416)
  a0ba61c Mark Allen Ramirez - Accessibility: DataGrid - Fix #25428 (#25471)
  • Loading branch information
alexslavr committed Sep 4, 2023
1 parent 30f866c commit b8cb849
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 10 deletions.
26 changes: 20 additions & 6 deletions js/__internal/grids/grid_core/editing/m_editing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import type { ICellBasedEditingControllerExtender } from './m_editing_cell_based
import type { IFormBasedEditingControllerExtender } from './m_editing_form_based';
import {
createFailureHandler,
generateNewRowTempKey,
getButtonIndex,
getButtonName,
getEditingTexts,
Expand Down Expand Up @@ -780,12 +781,8 @@ class EditingControllerImpl extends modules.ViewController {

_addInsertInfo(change, parentKey?) {
let insertInfo;
let { key } = change;

if (!isDefined(key)) {
key = String(new Guid());
change.key = key;
}
change.key = this.getChangeKeyValue(change);
const { key } = change;

insertInfo = this._getInternalData(key)?.insertInfo;
if (!isDefined(insertInfo)) {
Expand All @@ -803,6 +800,23 @@ class EditingControllerImpl extends modules.ViewController {
return { insertInfo, key };
}

private getChangeKeyValue(change: any): unknown {
if (isDefined(change.key)) {
return change.key;
}

const keyExpr = this._dataController.key();
let keyValue;
if (change.data && keyExpr && !Array.isArray(keyExpr)) {
keyValue = change.data[keyExpr];
}
if (!isDefined(keyValue)) {
keyValue = generateNewRowTempKey();
}

return keyValue;
}

_setInsertAfterOrBeforeKey(change, parentKey) {
const dataController = this._dataController;
const allItems = dataController.items(true);
Expand Down
10 changes: 10 additions & 0 deletions js/__internal/grids/grid_core/editing/m_editing_utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import Guid from '@js/core/guid';
import { isObject } from '@js/core/utils/type';

const NEW_ROW_TEMP_KEY_PREFIX = '_DX_KEY_';
const GUID_LENGTH = 36;

export const createFailureHandler = function (deferred) {
return function (arg) {
const error = arg instanceof Error ? arg : new Error(arg && String(arg) || 'Unknown error');
Expand Down Expand Up @@ -31,6 +35,12 @@ export const getEditingTexts = (options) => {
};
};

export const generateNewRowTempKey = (): string => `${NEW_ROW_TEMP_KEY_PREFIX}${new Guid()}`;

export const isNewRowTempKey = (key: string): boolean => typeof key === 'string'
&& key.startsWith(NEW_ROW_TEMP_KEY_PREFIX)
&& key.length === NEW_ROW_TEMP_KEY_PREFIX.length + GUID_LENGTH;

export const getButtonIndex = (buttons, name) => {
let result = -1;

Expand Down
2 changes: 1 addition & 1 deletion js/__internal/grids/grid_core/filter/m_filter_row.ts
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ const ColumnHeadersViewFilterRowExtender = (function () {
selectable: false,
items: that._getFilterOperationMenuItems(column),
}],
onItemRendered({ itemElement }) {
onItemRendered: ({ itemElement }) => {
this.setAria('label', ARIA_SEARCH_BOX, $(itemElement));
},
onItemClick(properties) {
Expand Down
3 changes: 2 additions & 1 deletion js/__internal/grids/grid_core/focus/m_focus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Deferred, when } from '@js/core/utils/deferred';
import { each } from '@js/core/utils/iterator';
import { isBoolean, isDefined } from '@js/core/utils/type';

import { isNewRowTempKey } from '../editing/m_editing_utils';
import core from '../m_modules';
import gridCoreUtils from '../m_utils';
import { UiGridCoreFocusUtils } from './m_focus_utils';
Expand Down Expand Up @@ -635,7 +636,7 @@ export const focusModule = {
const deferred = new Deferred();
const dataSource = that._dataSource;

if (Array.isArray(key)) {
if (Array.isArray(key) || isNewRowTempKey(key)) {
return deferred.resolve(-1).promise();
}

Expand Down
5 changes: 5 additions & 0 deletions testing/testcafe/model/dataGrid/data/cell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const CLASS = {
overlay: 'dx-overlay',
checkbox: 'dx-checkbox',
linkEdit: 'dx-link-edit',
linkSave: 'dx-link-save',
};

export default class DataCell extends FocusableElement {
Expand Down Expand Up @@ -60,6 +61,10 @@ export default class DataCell extends FocusableElement {
return this.element.find(`.${CLASS.linkEdit}`);
}

getLinkSave(): Selector {
return this.element.find(`.${CLASS.linkSave}`);
}

getIconByTitle(title: string): Selector {
return this.element.find(`a[title=${title}]`);
}
Expand Down
137 changes: 137 additions & 0 deletions testing/testcafe/tests/dataGrid/editing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2372,3 +2372,140 @@ test('Popup EditForm screenshot', async (t) => {
await changeTheme('generic.light');
});
});

test('Component sends unexpected filtering request after inserting a new row if focusedRowEnabled is true and key set in data source (T1181477)', async (t) => {
const dataGrid = new DataGrid('#container');

await t
.click(dataGrid.getHeaderPanel().getAddRowButton())
.click(dataGrid.getDataCell(0, 2).getLinkSave())

.expect(dataGrid.getDataCell(3, 1).element.innerText)
.notContains('Name 3')

.expect(Selector('#otherContainer').innerText)
.eql('');
}).before(async () => {
await createWidget('dxDataGrid', ClientFunction(() => {
const dataSourceCore = [
{ ID: 1, Name: 'Name 1' },
{ ID: 2, Name: 'Name 2' },
{ ID: 3, Name: 'Name 3' },
];

const sampleAPI = {
load() {
const data = dataSourceCore;
return new Promise((resolve) => {
setTimeout(() => {
resolve(data);
}, 100);
});
},
totalCount() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(dataSourceCore.length);
}, 100);
});
},
insert(values) {
return new Promise((resolve) => {
setTimeout(() => {
const newID = dataSourceCore.length + 1;
values.ID = newID;
dataSourceCore.push(values);
resolve(newID);
}, 100);
});
},
};

const store = new (window as any).DevExpress.data.CustomStore({
key: 'ID',
load(o) {
if (o.filter) {
$('#otherContainer').append('Fail');
}

return Promise.all([sampleAPI.load(), sampleAPI.totalCount()]).then((res) => ({
data: res[0],
totalCount: res[1],
}));
},
insert(values) {
return sampleAPI.insert(values);
},
});

return {
dataSource: store,
showBorders: true,
focusedRowEnabled: true,
autoNavigateToFocusedRow: true,
editing: {
allowAdding: true,
},
remoteOperations: true,
};
}));
});

test('Component sends unexpected filtering request after inserting a new row if focusedRowEnabled is true and key set on event (T1181477)', async (t) => {
const dataGrid = new DataGrid('#container');

await t
.click(dataGrid.getHeaderPanel().getAddRowButton())
.click(dataGrid.getDataCell(0, 2).getLinkSave())

.expect(dataGrid.getDataCell(3, 1).element.innerText)
.notContains('Name 3')

.expect(Selector('#otherContainer').innerText)
.eql('');
}).before(async () => {
await createWidget('dxDataGrid', ClientFunction(() => {
const dataSourceCore = [
{ ID: 1, Name: 'Name 1' },
{ ID: 2, Name: 'Name 2' },
{ ID: 3, Name: 'Name 3' },
];

const sampleAPI = new (window as any).DevExpress.data.ArrayStore(dataSourceCore);

const store = new (window as any).DevExpress.data.CustomStore({
key: 'ID',
load(o) {
if (o.filter) {
$('#otherContainer').append('Fail');
}

return Promise.all([sampleAPI.load(), sampleAPI.totalCount()]).then((res) => ({
data: res[0],
totalCount: res[1],
}));
},
insert(values) {
return sampleAPI.insert(values);
},
});

return {
dataSource: store,
showBorders: true,
focusedRowEnabled: true,
autoNavigateToFocusedRow: true,
editing: {
allowAdding: true,
},
onInitNewRow(e) {
e.promise = new Promise((resolve) => {
const newId = dataSourceCore.length + 1;
e.data.ID = newId;
resolve(undefined);
});
},
remoteOperations: true,
};
}));
});
4 changes: 2 additions & 2 deletions testing/tests/DevExpress.ui.widgets.dataGrid/focus.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -3534,7 +3534,7 @@ QUnit.module('Focused row', getModuleConfig(true), () => {

// assert
assert.ok($(this.getRowElement(0)).find('.dx-texteditor-input').is(':focus'), 'input is focused');
assert.equal(this.option('focusedRowKey').length, 36, 'focusedRowKey is tmp "guid" key');
assert.equal(this.option('focusedRowKey').length, 44, 'focusedRowKey is tmp "guid" key');
assert.equal(this.option('focusedRowIndex'), 0, 'focusedRowIndex');
});

Expand Down Expand Up @@ -4112,7 +4112,7 @@ QUnit.module('Focused row', getModuleConfig(true), () => {
// assert
assert.strictEqual(onFocusedRowChangedSpy.callCount, 1, 'onFocusedRowChanged event is called for a new row');
assert.ok($(this.getRowElement(newRowIndex)).find('.dx-texteditor-input').is(':focus'), 'input is focused');
assert.equal(this.option('focusedRowKey').length, 36, 'focusedRowKey is tmp "guid" key');
assert.equal(this.option('focusedRowKey').length, 44, 'focusedRowKey is tmp "guid" key');
// Skip this check
// focusedRowIndex different with shadow-dom and without it
// Uncomment after fix of the T1160487 ticket.
Expand Down

0 comments on commit b8cb849

Please sign in to comment.