Skip to content

Commit

Permalink
Merge branch '25_1' of https://github.com/DevExpress/DevExtreme into …
Browse files Browse the repository at this point in the history
…fix-bug-T1269950(1)
  • Loading branch information
dxvladislavvolkov committed Jan 29, 2025
2 parents 5509476 + 27ddab2 commit 4ce3c63
Show file tree
Hide file tree
Showing 15 changed files with 383 additions and 202 deletions.
18 changes: 16 additions & 2 deletions .github/workflows/pr-filter-stubs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -432,14 +432,28 @@ jobs:
steps:
- run: exit 1
testcafe_tests36:
name: 'form - material (1/2)'
name: 'form - material (1/4)'
needs: [ changesSegregation ]
if: false
runs-on: ubuntu-latest
steps:
- run: exit 1
testcafe_tests37:
name: 'form - material (2/2)'
name: 'form - material (2/4)'
needs: [ changesSegregation ]
if: false
runs-on: ubuntu-latest
steps:
- run: exit 1
testcafe_tests58:
name: 'form - material (3/4)'
needs: [ changesSegregation ]
if: false
runs-on: ubuntu-latest
steps:
- run: exit 1
testcafe_tests59:
name: 'form - material (4/4)'
needs: [ changesSegregation ]
if: false
runs-on: ubuntu-latest
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
The DataGrid includes an integrated toolbar that displays predefined and custom controls. To add or remove toolbar items, declare the [toolbar](/Documentation/ApiReference/UI_Components/dxDataGrid/Configuration/toolbar/).[items[]](/Documentation/ApiReference/UI_Components/dxDataGrid/Configuration/toolbar/items/) array.

[note] If **toolbar.items[]** is specified, DataGrid does not display controls missing from the array. Ensure this array includes controls for all enabled features.
<!--split-->

This demo illustrates how to add the following items to the toolbar:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
The DataGrid includes an integrated toolbar that displays predefined and custom controls. To add or remove toolbar items, declare the [toolbar](/Documentation/ApiReference/UI_Components/dxDataGrid/Configuration/toolbar/).[items[]](/Documentation/ApiReference/UI_Components/dxDataGrid/Configuration/toolbar/items/) array.

[note] If **toolbar.items[]** is specified, DataGrid does not display controls missing from the array. Ensure this array includes controls for all enabled features.
<!--split-->

This demo illustrates how to add the following items to the toolbar:
Expand Down
2 changes: 2 additions & 0 deletions apps/demos/Demos/DataGrid/ToolbarCustomization/description.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
The DataGrid includes an integrated toolbar that displays predefined and custom controls. To add or remove toolbar items, declare the [toolbar](/Documentation/ApiReference/UI_Components/dxDataGrid/Configuration/toolbar/).[items[]](/Documentation/ApiReference/UI_Components/dxDataGrid/Configuration/toolbar/items/) array.

[note] If **toolbar.items[]** is specified, DataGrid does not display controls missing from the array. Ensure this array includes controls for all enabled features.
<!--split-->

This demo illustrates how to add the following items to the toolbar:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
The DataGrid includes an integrated toolbar that displays predefined and custom controls. To add or remove toolbar items, declare the [toolbar](/Documentation/ApiReference/UI_Components/dxDataGrid/Configuration/toolbar/).[items[]](/Documentation/ApiReference/UI_Components/dxDataGrid/Configuration/toolbar/items/) array.

[note] If **toolbar.items[]** is specified, DataGrid does not display controls missing from the array. Ensure this array includes controls for all enabled features.
<!--split-->

This demo illustrates how to add the following items to the toolbar:

* **Predefined Controls**
Expand Down
41 changes: 41 additions & 0 deletions e2e/testcafe-devextreme/tests/dataGrid/searchPanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,44 @@ safeSizeTest('searchPanel has correct view inside masterDetail', async (t) => {
},
});
}).after(async () => { await changeTheme(Themes.genericLight); });

// T1272535
safeSizeTest('Base sensitivity search should accept rows with accent letters in lookup columns', async (t) => {
const dataGrid = new DataGrid('#container');

await t
.click(dataGrid.getSearchBox().input)
.pressKey('a');

await t.expect(dataGrid.dataRows.count).eql(2);
await t.expect(dataGrid.dataRows.withText('another').exists).ok();
await t.expect(dataGrid.dataRows.withText('ánother').exists).ok();
}, [800, 800]).before(async () => createWidget('dxDataGrid', {
dataSource: {
store: [
{ id: 1, text: 'tešt', lookup: 1 },
{ id: 2, text: 'test', lookup: 2 },
{ id: 3, text: 'chest', lookup: 3 },
],
langParams: {
locale: 'en-US',
collatorOptions: {
sensitivity: 'base',
},
},
},
keyExpr: 'id',
searchPanel: { visible: true },
columns: ['id', 'text', {
dataField: 'lookup',
lookup: {
dataSource: [
{ id: 1, text: 'another' },
{ id: 2, text: 'ánother' },
{ id: 3, text: 'other' },
],
valueExpr: 'id',
displayExpr: 'text',
},
}],
}));
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable max-classes-per-file */
/* eslint-disable @typescript-eslint/method-signature-style */
import messageLocalization from '@js/common/core/localization/message';
import type { LangParams } from '@js/common/data';
import dataQuery from '@js/common/data/query';
import domAdapter from '@js/core/dom_adapter';
import $ from '@js/core/renderer';
Expand Down Expand Up @@ -56,8 +57,11 @@ const dataController = (
}

protected _calculateAdditionalFilter(): Filter {
const dataSource = this._dataController?.getDataSource?.();
const langParams = dataSource?.loadOptions?.()?.langParams;

const filter = super._calculateAdditionalFilter();
const searchFilter = this.calculateSearchFilter(this.option('searchPanel.text'));
const searchFilter = this.calculateSearchFilter(this.option('searchPanel.text'), langParams);

return gridCoreUtils.combineFilters([filter, searchFilter]);
}
Expand All @@ -66,8 +70,7 @@ const dataController = (
this.option('searchPanel.text', text);
}

private calculateSearchFilter(text: string | undefined): Filter {
let i;
private calculateSearchFilter(text: string | undefined, langParams?: LangParams): Filter {
let column;
const columns = this._columnsController.getColumns();
const searchVisibleColumnsOnly = this.option('searchPanel.searchVisibleColumnsOnly');
Expand All @@ -87,17 +90,33 @@ const dataController = (
}
}

for (i = 0; i < columns.length; i++) {
for (let i = 0; i < columns.length; i++) {
column = columns[i];

if (searchVisibleColumnsOnly && !column.visible) continue;

if (allowSearch(column) && column.calculateFilterExpression) {
lookup = column.lookup;
const filterValue = parseValue(column, text);
if (lookup && lookup.items) {

if (lookup?.items) {
// @ts-expect-error
dataQuery(lookup.items).filter(column.createFilterExpression.call({ dataField: lookup.displayExpr, dataType: lookup.dataType, calculateFilterExpression: column.calculateFilterExpression }, filterValue, null, 'search')).enumerate().done(onQueryDone);
dataQuery(lookup.items, { langParams })
// @ts-expect-error
.filter(
column.createFilterExpression.call(
{
dataField: lookup.displayExpr,
dataType: lookup.dataType,
calculateFilterExpression: column.calculateFilterExpression,
},
filterValue,
null,
'search',
),
)
.enumerate()
.done(onQueryDone);
} else if (filterValue !== undefined) {
filters.push(column.createFilterExpression(filterValue, null, 'search'));
}
Expand Down
178 changes: 178 additions & 0 deletions packages/devextreme/js/__internal/ui/collection/m_search_box_mixin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import messageLocalization from '@js/common/core/localization/message';
import $ from '@js/core/renderer';
import { Deferred } from '@js/core/utils/deferred';
import { extend } from '@js/core/utils/extend';
import { stubComponent } from '@js/core/utils/stubs';
import errors from '@js/ui/widget/ui.errors';

let EditorClass = stubComponent('TextBox');

export default {
_getDefaultOptions() {
return extend(this.callBase(), {
searchMode: '',
searchExpr: null,
searchValue: '',
searchEnabled: false,
searchEditorOptions: {},
});
},

_initMarkup(): void {
this._renderSearch();
this.callBase();
},

_renderSearch(): void {
const $element = this.$element();
const searchEnabled = this.option('searchEnabled');
const searchBoxClassName = this._addWidgetPrefix('search');
const rootElementClassName = this._addWidgetPrefix('with-search');

if (!searchEnabled) {
$element.removeClass(rootElementClassName);
this._removeSearchBox();
return;
}

const editorOptions = this._getSearchEditorOptions();

if (this._searchEditor) {
this._searchEditor.option(editorOptions);
} else {
$element.addClass(rootElementClassName);
this._$searchEditorElement = $('<div>').addClass(searchBoxClassName).prependTo($element);
this._searchEditor = this._createComponent(this._$searchEditorElement, EditorClass, editorOptions);
}
},

_removeSearchBox(): void {
this._$searchEditorElement && this._$searchEditorElement.remove();
delete this._$searchEditorElement;
delete this._searchEditor;
},

_getSearchEditorOptions() {
const that = this;
const userEditorOptions = that.option('searchEditorOptions');
const searchText = messageLocalization.format('Search');

return extend({
mode: 'search',
placeholder: searchText,
tabIndex: that.option('tabIndex'),
value: that.option('searchValue'),
valueChangeEvent: 'input',
inputAttr: {
'aria-label': searchText,
},
onValueChanged(e) {
const searchTimeout = that.option('searchTimeout');
that._valueChangeDeferred = Deferred();
clearTimeout(that._valueChangeTimeout);

that._valueChangeDeferred.done(function () {
this.option('searchValue', e.value);
}.bind(that));

if (e.event && e.event.type === 'input' && searchTimeout) {
that._valueChangeTimeout = setTimeout(() => {
that._valueChangeDeferred.resolve();
}, searchTimeout);
} else {
that._valueChangeDeferred.resolve();
}
},
}, userEditorOptions);
},

_getAriaTarget() {
if (this.option('searchEnabled')) {
return this._itemContainer(true);
}
return this.callBase();
},

_focusTarget() {
if (this.option('searchEnabled')) {
return this._itemContainer(true);
}

return this.callBase();
},

_updateFocusState(e, isFocused): void {
if (this.option('searchEnabled')) {
this._toggleFocusClass(isFocused, this.$element());
}
this.callBase(e, isFocused);
},

getOperationBySearchMode(searchMode) {
return searchMode === 'equals' ? '=' : searchMode;
},

_optionChanged(args) {
switch (args.name) {
case 'searchEnabled':
case 'searchEditorOptions':
this._invalidate();
break;
case 'searchExpr':
case 'searchMode':
case 'searchValue':
if (!this._dataSource) {
errors.log('W1009');
return;
}
if (args.name === 'searchMode') {
this._dataSource.searchOperation(this.getOperationBySearchMode(args.value));
} else {
this._dataSource[args.name](args.value);
}
this._dataSource.load();
break;
case 'searchTimeout':
break;
default:
this.callBase(args);
}
},

focus() {
if (!this.option('focusedElement') && this.option('searchEnabled')) {
this._searchEditor && this._searchEditor.focus();
return;
}

this.callBase();
},

_cleanAria(): void {
const $element = this.$element();

this.setAria({
role: null,
activedescendant: null,
}, $element);

$element.attr('tabIndex', null);
},

_clean(): void {
this.callBase();
this._cleanAria();
},

_refresh(): void {
if (this._valueChangeDeferred) {
this._valueChangeDeferred.resolve();
}

this.callBase();
},

setEditorClass(value): void {
EditorClass = value;
},
};
17 changes: 17 additions & 0 deletions packages/devextreme/js/__internal/ui/list/m_list.edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,23 @@ const ListEdit = ListBase.inherit({
this._editProvider.afterItemsRendered();
},

_renderItem(index, itemData, $container, $itemToReplace) {
const { showSelectionControls, selectionMode } = this.option();
const $itemFrame = this.callBase(index, itemData, $container, $itemToReplace);

if (showSelectionControls && selectionMode !== 'none') {
this._updateItemAriaLabel($itemFrame, itemData);
}

return $itemFrame;
},

_updateItemAriaLabel($itemFrame, itemData) {
const label = this._displayGetter?.(itemData) ?? itemData?.text ?? itemData;

this.setAria('label', label, $itemFrame);
},

_selectedItemClass() {
return LIST_ITEM_SELECTED_CLASS;
},
Expand Down
Loading

0 comments on commit 4ce3c63

Please sign in to comment.