Skip to content

Commit adbb125

Browse files
authored
Merge branch '21.1.x' into iminchev/fix-16493
2 parents ea7c32a + f6cb6a9 commit adbb125

13 files changed

Lines changed: 281 additions & 141 deletions

File tree

projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-search.component.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -482,17 +482,26 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy {
482482
const matchedData = cloneHierarchicalArray(this.esf.listData, 'children');
483483
this.displayedListData = this.hierarchicalSelectMatches(matchedData, searchVal);
484484
this.cdr.detectChanges();
485+
/**
486+
* There are two calls of `matchesNumericValue` in this method: one when we generate the displayedListData in hierarchicalSelectMatches method
487+
* and another one when going through the tree nodes. We can avoid the second call by storing the items in a set.
488+
* However, if the datasource is small there is no significant difference in performance but we would be adding extra memory overhead.
489+
* We should test this when https://github.com/IgniteUI/igniteui-angular/issues/17144 issue is fixed with 100k or 1m records
490+
*/
485491
this.tree.nodes.forEach(n => {
486492
n.selected = true;
487-
if ((n.data as FilterListItem).label.toString().toLowerCase().indexOf(searchVal) > -1) {
493+
const item = n.data as FilterListItem;
494+
if (item.label.toString().toLowerCase().indexOf(searchVal) > -1 ||
495+
this.matchesNumericValue(item, searchVal)) {
488496
this.expandAllParentNodes(n);
489497
}
490498
});
491499
} else {
492500
this.displayedListData = this.esf.listData.filter((it, i) => (i === 0 && it.isSpecial) ||
493501
(it.label !== null && it.label !== undefined) &&
494502
!it.isBlanks &&
495-
it.label.toString().toLowerCase().indexOf(searchVal) > -1);
503+
(it.label.toString().toLowerCase().indexOf(searchVal) > -1 ||
504+
this.matchesNumericValue(it, searchVal)));
496505

497506
this.esf.listData.forEach(i => i.isSelected = false);
498507
this.displayedListData.forEach(i => i.isSelected = true);
@@ -724,7 +733,8 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy {
724733
node.expanded = false;
725734
}
726735

727-
if (element.label.toString().toLowerCase().indexOf(searchVal) > -1) {
736+
if (element.label.toString().toLowerCase().indexOf(searchVal) > -1 ||
737+
this.matchesNumericValue(element, searchVal)) {
728738
element.isSelected = true;
729739
this.hierarchicalSelectAllChildren(element);
730740
this._hierarchicalSelectedItems.push(element);
@@ -802,6 +812,23 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy {
802812
}
803813
}
804814

815+
private matchesNumericValue(item: FilterListItem, searchVal: string): boolean {
816+
const columnDataType = this.esf.column?.dataType;
817+
if (typeof item.value !== 'number' ||
818+
(columnDataType !== GridColumnDataType.Number &&
819+
columnDataType !== GridColumnDataType.Currency &&
820+
columnDataType !== GridColumnDataType.Percent)) {
821+
return false;
822+
}
823+
824+
let numericValue = item.value;
825+
if (columnDataType === GridColumnDataType.Percent) {
826+
numericValue = parseFloat((item.value * 100).toPrecision(15));
827+
}
828+
829+
return numericValue.toString().toLowerCase().indexOf(searchVal) > -1;
830+
}
831+
805832
private onArrowUpKeyDown() {
806833
if (this.focusedItem && this.focusedItem.index === 0 && this.virtDir.state.startIndex === 0) {
807834
// on ArrowUp the focus stays on the same element if it is the first focused

projects/igniteui-angular/grids/grid/src/grid-filtering-ui.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4113,6 +4113,25 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => {
41134113
expect(listItems.length).toBe(8, 'incorrect rendered list items count');
41144114
});
41154115

4116+
it('Should match numeric column values when searching without locale-specific formatting characters.', async () => {
4117+
GridFunctions.clickExcelFilterIconFromCodeAsync(fix, grid, 'Downloads');
4118+
fix.detectChanges();
4119+
await wait(100);
4120+
const searchComponent = GridFunctions.getExcelStyleSearchComponent(fix);
4121+
const inputNativeElement = GridFunctions.getExcelStyleSearchComponentInput(fix, searchComponent);
4122+
4123+
// Type 1000 (without thousands separator) in search box.
4124+
// The value 1000 is displayed as "1,000" in the ESF list due to locale formatting.
4125+
// Searching "1000" should still match the "1,000" entry.
4126+
UIInteractions.clickAndSendInputElementValue(inputNativeElement, '1000', fix);
4127+
fix.detectChanges();
4128+
await wait(100);
4129+
4130+
const listItems = GridFunctions.getExcelStyleSearchComponentListItems(fix, searchComponent);
4131+
// Expect 3 items: Select All + Add to current filter + the matched "1,000" item
4132+
expect(listItems.length).toBe(3, 'searching plain number should match locale-formatted label');
4133+
});
4134+
41164135
it('Should enable/disable the apply button correctly.', async () => {
41174136
GridFunctions.clickExcelFilterIconFromCodeAsync(fix, grid, 'ProductName');
41184137
fix.detectChanges();

projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.component.html

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,16 @@
151151
[igxForSizePropName]="'height'"
152152
#verticalRowDimScrollContainer
153153
>
154-
<igx-pivot-row-dimension-content
155-
class="igx-grid-thead" [grid]="this"
156-
[dimension]="rowData.dimensions[dimIndex]"
157-
[rootDimension]="dim"
158-
[style.height.px]="renderedRowHeight * (rowData.rowSpan || 1)"
159-
[rowIndex]="rowIndex" [rowData]="rowData"
160-
[width]="rowDimensionWidthToPixels(dim)">
161-
</igx-pivot-row-dimension-content>
154+
<div [attr.data-index]="rowIndex">
155+
<igx-pivot-row-dimension-content
156+
class="igx-grid-thead" [grid]="this"
157+
[dimension]="rowData.dimensions[dimIndex]"
158+
[rootDimension]="dim"
159+
[style.height.px]="renderedRowHeight * (rowData.rowSpan || 1)"
160+
[rowIndex]="rowIndex" [rowData]="rowData"
161+
[width]="rowDimensionWidthToPixels(dim)">
162+
</igx-pivot-row-dimension-content>
163+
</div>
162164
</ng-template>
163165
<ng-container *ngTemplateOutlet="emptyRowDimensionFill; context: { $implicit: this, widthPx: rowDimensionWidthToPixels(dim) }"></ng-container>
164166
</div>
@@ -177,7 +179,9 @@
177179
[igxForItemSize]="renderedRowHeight"
178180
[igxForSizePropName]="'height'"
179181
>
180-
<igx-pivot-row-dimension-mrl-row [rowIndex]="rowIndex" [rowGroup]="rowGroup" [groupedData]="groupedData" [style.height.px]="renderedRowHeight * rowGroup.length"></igx-pivot-row-dimension-mrl-row>
182+
<div [attr.data-index]="rowIndex">
183+
<igx-pivot-row-dimension-mrl-row [rowIndex]="rowIndex" [rowGroup]="rowGroup" [groupedData]="groupedData" [style.height.px]="renderedRowHeight * rowGroup.length"></igx-pivot-row-dimension-mrl-row>
184+
</div>
181185
</ng-template>
182186
}
183187
<ng-container *ngTemplateOutlet="emptyRowDimensionFill; context: { $implicit: this, widthPx: pivotRowWidths }"></ng-container>

projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.component.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2475,6 +2475,8 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni
24752475
if (this.hasHorizontalLayout) {
24762476
// Trigger pipes to recalc heights for the horizontal layout mrl rows.
24772477
this.regroupTrigger++;
2478+
} else {
2479+
this.pipeTrigger++;
24782480
}
24792481
}
24802482

skills/igniteui-angular-components/references/form-controls.md

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,20 +124,66 @@ Implements `ControlValueAccessor` and `Validator`. Works with both reactive and
124124
> **Docs:** [Date Range Picker](https://www.infragistics.com/products/ignite-ui-angular/angular/components/date-range-picker)
125125
126126
```typescript
127-
import { IgxDateRangePickerComponent } from 'igniteui-angular/date-picker';
127+
import { IgxDateRangePickerComponent, IgxDateRangeStartComponent, IgxDateRangeEndComponent } from 'igniteui-angular/date-picker';
128128
import { IgxDateTimeEditorDirective } from 'igniteui-angular/directives';
129129
import { IGX_INPUT_GROUP_DIRECTIVES } from 'igniteui-angular/input-group';
130+
import { IgxIconComponent } from 'igniteui-angular/icon';
131+
import { IgxPickerToggleComponent, IgxPickerClearComponent } from 'igniteui-angular/core';
132+
```
130133

131-
// import { IgxDateTimeEditorDirective, IGX_INPUT_GROUP_DIRECTIVES } from '@infragistics/igniteui-angular'; for licensed package
134+
```html
135+
<igx-date-range-picker [(ngModel)]="dateRange">
136+
<igx-date-range-start>
137+
<input igxInput igxDateTimeEditor type="text" />
138+
</igx-date-range-start>
139+
<igx-date-range-end>
140+
<input igxInput igxDateTimeEditor type="text" />
141+
</igx-date-range-end>
142+
</igx-date-range-picker>
132143
```
133144

145+
146+
`IgxDateRangePickerComponent` is imported from `igniteui-angular/date-picker`.
147+
148+
In the two-input configuration:
149+
150+
- place the `input` directly inside `igx-date-range-start` and `igx-date-range-end`
151+
- use `igx-picker-toggle igxPrefix` for the calendar action
152+
- use `igx-picker-clear igxSuffix` for the clear action
153+
154+
A plain `igx-prefix` or `igx-suffix` with an `igx-icon` is decorative only and does not trigger picker actions.
155+
Do not wrap the inputs in an additional `igx-input-group`.
156+
157+
**Avoid these patterns in two-input mode:**
158+
159+
- `<igx-prefix><igx-icon>calendar_today</igx-icon></igx-prefix>`
160+
161+
- placing the toggle on only one input unless explicitly requested
162+
163+
Common two-input configuration with calendar toggles:
164+
134165
```html
135166
<igx-date-range-picker [(ngModel)]="dateRange">
136167
<igx-date-range-start>
168+
<igx-picker-toggle igxPrefix>
169+
<igx-icon>calendar_today</igx-icon>
170+
</igx-picker-toggle>
171+
<label igxLabel>Start Date</label>
137172
<input igxInput igxDateTimeEditor type="text" />
173+
<igx-picker-clear igxSuffix>
174+
<igx-icon>clear</igx-icon>
175+
</igx-picker-clear>
138176
</igx-date-range-start>
177+
139178
<igx-date-range-end>
179+
<igx-picker-toggle igxPrefix>
180+
<igx-icon>calendar_today</igx-icon>
181+
</igx-picker-toggle>
182+
<label igxLabel>End Date</label>
140183
<input igxInput igxDateTimeEditor type="text" />
184+
<igx-picker-clear igxSuffix>
185+
<igx-icon>clear</igx-icon>
186+
</igx-picker-clear>
141187
</igx-date-range-end>
142188
</igx-date-range-picker>
143189
```
@@ -303,6 +349,8 @@ export class MyFormComponent {
303349
- **Always check `app.config.ts` first** — add `provideAnimations()` before using Combo, Select, Date Picker, or any overlay component
304350
- **Import from specific entry points** — avoid the root `igniteui-angular` barrel
305351
- Date/Time pickers implement both `ControlValueAccessor` and `Validator` — they integrate with reactive forms natively
352+
- For `igx-date-range-picker` with separate start and end inputs, use this structure for both inputs: `igx-picker-toggle igxPrefix`, then `input igxInput igxDateTimeEditor`, then optional `igx-picker-clear igxSuffix`.
353+
- Do not use a plain `igx-prefix` / `igx-suffix` icon for calendar or clear actions.
306354

307355
## See Also
308356

skills/igniteui-angular-components/references/setup.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,7 @@ import { IgxComboComponent } from 'igniteui-angular';
8989
| Input Group | `igniteui-angular/input-group` |
9090
| Combo / Simple Combo | `igniteui-angular/combo` |
9191
| Select | `igniteui-angular/select` |
92-
| Date Picker | `igniteui-angular/date-picker` |
93-
| Date Range Picker | `igniteui-angular/date-range-picker` |
92+
| Date Picker / Date Range Picker | `igniteui-angular/date-picker` |
9493
| Time Picker | `igniteui-angular/time-picker` |
9594
| Calendar | `igniteui-angular/calendar` |
9695
| Checkbox | `igniteui-angular/checkbox` |

src/app/grid-cellMerging/grid-cellMerging.component.html

Lines changed: 78 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -43,79 +43,84 @@ <h4 class="sample-title">Grid with cell merge</h4>
4343
}
4444
</igx-input-group>
4545
</div>
46-
<igx-grid [data]="data" width="800px" height="550px" [moving]="true" #grid1
47-
[cellMergeMode]="'always'" [rowSelection]="'single'">
48-
<igx-column field="OrderID" header="Order ID">
49-
<ng-template igxCell let-cell="cell" let-val>
50-
Value: {{val}},Index: {{cell.row.index}}
51-
</ng-template>
52-
</igx-column>
53-
<igx-column field="ShipCountry" [groupable]="true" [hasSummary]="true" header="Ship Country" [merge]="true" width="200px" sortable="true">
54-
</igx-column>
55-
<igx-column field="OrderDate" header="Order Date" width="200px" [merge]="true" [groupable]="true" [dataType]="'date'" sortable="true">
56-
</igx-column>
57-
<igx-column field="PostalCode" header="Postal Code" width="200px" >
58-
</igx-column>
59-
<igx-column field="Discontinued" header="Discontinued" width="200px" sortable="true">
60-
</igx-column>
61-
<igx-column field="ShipName" header="Ship Name" width="250px">
62-
</igx-column>
63-
<igx-column field="ShipCity" header="Ship City" width="250px">
64-
</igx-column>
65-
<igx-column field="ShipperName" header="Shipper Name" width="250px">
66-
</igx-column>
67-
<igx-column field="Salesperson" header="Salesperson" width="250px">
68-
</igx-column>
69-
<igx-column field="UnitPrice" header="Unit Price" width="150px" dataType="number">
70-
</igx-column>
71-
<igx-column field="Quantity" header="Quantity" width="150px" dataType="number">
72-
</igx-column>
73-
74-
<igx-action-strip>
75-
<igx-grid-pinning-actions></igx-grid-pinning-actions>
76-
</igx-action-strip>
77-
<igx-grid-toolbar>
78-
<igx-grid-toolbar-actions>
79-
<igx-grid-toolbar-hiding></igx-grid-toolbar-hiding>
80-
<igx-grid-toolbar-pinning></igx-grid-toolbar-pinning>
81-
</igx-grid-toolbar-actions>
82-
</igx-grid-toolbar>
83-
</igx-grid>
46+
<div class="grids-grid">
47+
<igx-grid [data]="data" height="550px" [moving]="true" #grid1
48+
[cellMergeMode]="'always'" [rowSelection]="'single'" [allowFiltering]="true" [filterMode]="'excelStyleFilter'">
49+
<igx-column field="OrderID" header="Order ID">
50+
<ng-template igxCell let-cell="cell" let-val>
51+
Value: {{val}},Index: {{cell.row.index}}
52+
</ng-template>
53+
</igx-column>
54+
<igx-column field="ShipCountry" [groupable]="true" [hasSummary]="true" header="Ship Country" [merge]="true" width="200px" sortable="true">
55+
</igx-column>
56+
<igx-column field="OrderDate" header="Order Date" width="200px" [merge]="true" [groupable]="true" [dataType]="'date'" sortable="true">
57+
</igx-column>
58+
<igx-column field="PostalCode" header="Postal Code" width="200px" >
59+
</igx-column>
60+
<igx-column field="Discontinued" header="Discontinued" width="200px" sortable="true">
61+
</igx-column>
62+
<igx-column field="ShipName" header="Ship Name" width="250px">
63+
</igx-column>
64+
<igx-column field="ShipCity" header="Ship City" width="250px">
65+
</igx-column>
66+
<igx-column field="ShipperName" header="Shipper Name" width="250px">
67+
</igx-column>
68+
<igx-column field="Salesperson" header="Salesperson" width="250px">
69+
</igx-column>
70+
<igx-column field="UnitPrice" header="Unit Price" width="150px" dataType="currency">
71+
</igx-column>
72+
<igx-column field="ExtendedPrice" header="Extended Price" width="150px" dataType="currency">
73+
</igx-column>
74+
<igx-column field="Quantity" header="Quantity" width="150px" dataType="number">
75+
</igx-column>
8476

85-
<h4 class="sample-title">Hierarchical grid with cell merge</h4>
77+
<igx-action-strip>
78+
<igx-grid-pinning-actions></igx-grid-pinning-actions>
79+
</igx-action-strip>
80+
<igx-grid-toolbar>
81+
<igx-grid-toolbar-actions>
82+
<igx-grid-toolbar-hiding></igx-grid-toolbar-hiding>
83+
<igx-grid-toolbar-pinning></igx-grid-toolbar-pinning>
84+
</igx-grid-toolbar-actions>
85+
</igx-grid-toolbar>
86+
</igx-grid>
8687

87-
<igx-hierarchical-grid #hierarchicalGrid width="800px" [rowSelection]="'single'" [height]="'550px'" [data]="hierarchicalData" [autoGenerate]="false"
88-
[allowAdvancedFiltering]="true" [cellMergeMode]="'always'">
89-
<igx-grid-toolbar></igx-grid-toolbar>
90-
<igx-column field="EmployeeID" width="200px" [hidden]="true">
91-
</igx-column>
92-
<igx-column field="FirstName" width="200px">
93-
</igx-column>
94-
<igx-column field="LastName" width="200px">
95-
</igx-column>
96-
<igx-column field="Title" width="200px" [merge]="true" [hasSummary]="true">
97-
</igx-column>
98-
<igx-column [groupable]="true" [hasSummary]="true" field="City" width="200px" [merge]="true" editable="true" sortable="true">
99-
</igx-column>
100-
<igx-row-island [key]="'Orders'" [cellMergeMode]="'always'">
101-
<igx-column field="CustomerID" width="200px">
102-
</igx-column>
103-
<igx-column field="ShipName" width="200px">
104-
</igx-column>
105-
<igx-column field="ShipCountry" width="200px" [merge]="true" sortable="true">
106-
</igx-column>
107-
<igx-column field="ShipCity" width="200px" [merge]="true" sortable="true">
108-
</igx-column>
109-
</igx-row-island>
110-
</igx-hierarchical-grid>
88+
<igx-hierarchical-grid #hierarchicalGrid [rowSelection]="'single'" [height]="'550px'" [data]="hierarchicalData" [autoGenerate]="false"
89+
[allowAdvancedFiltering]="true" [cellMergeMode]="'always'">
90+
<igx-grid-toolbar [title]="'Hierarchical grid with cell merging'"></igx-grid-toolbar>
91+
<igx-column field="EmployeeID" width="200px" [hidden]="true">
92+
</igx-column>
93+
<igx-column field="FirstName" width="200px">
94+
</igx-column>
95+
<igx-column field="LastName" width="200px">
96+
</igx-column>
97+
<igx-column field="Title" width="200px" [merge]="true" [hasSummary]="true">
98+
</igx-column>
99+
<igx-column [groupable]="true" [hasSummary]="true" field="City" width="200px" [merge]="true" editable="true" sortable="true">
100+
</igx-column>
101+
<igx-row-island [key]="'Orders'" [cellMergeMode]="'always'">
102+
<igx-column field="CustomerID" width="200px">
103+
</igx-column>
104+
<igx-column field="ShipName" width="200px">
105+
</igx-column>
106+
<igx-column field="ShipCountry" width="200px" [merge]="true" sortable="true">
107+
</igx-column>
108+
<igx-column field="ShipCity" width="200px" [merge]="true" sortable="true">
109+
</igx-column>
110+
</igx-row-island>
111+
</igx-hierarchical-grid>
111112

112-
<h4 class="sample-title">Tree grid with cell merge</h4>
113-
<button (click)="toggleStrategy()">Toggle strategy</button>
114-
<igx-tree-grid [autoGenerate]="false" [rowSelection]="'single'" [data]="treeData" [cellMergeMode]="'always'" [mergeStrategy]="treeGridMergeStrategy"
115-
childDataKey="ChildCompanies" primaryKey="ID" [expansionDepth]="1" width="800px" [height]="'550px'">
116-
<igx-column field="ContactName" width="200px" [hasSummary]="true">
117-
</igx-column>
118-
<igx-column field="ContactTitle" width="200px"></igx-column>
119-
<igx-column field="Country" width="200px" [merge]="true" sortable="true"></igx-column>
120-
<igx-column field="City" width="200px" [merge]="true" sortable="true"></igx-column>
121-
</igx-tree-grid>
113+
<igx-tree-grid [autoGenerate]="false" [rowSelection]="'single'" [data]="treeData" [cellMergeMode]="'always'" [mergeStrategy]="treeGridMergeStrategy"
114+
childDataKey="ChildCompanies" primaryKey="ID" [expansionDepth]="1" [height]="'550px'">
115+
<igx-grid-toolbar [title]="'Tree grid with cell merging'">
116+
<igx-grid-toolbar-actions>
117+
<button igxButton (click)="toggleStrategy()">Toggle strategy</button>
118+
</igx-grid-toolbar-actions>
119+
</igx-grid-toolbar>
120+
<igx-column field="ContactName" width="200px" [hasSummary]="true">
121+
</igx-column>
122+
<igx-column field="ContactTitle" width="200px"></igx-column>
123+
<igx-column field="Country" width="200px" [merge]="true" sortable="true"></igx-column>
124+
<igx-column field="City" width="200px" [merge]="true" sortable="true"></igx-column>
125+
</igx-tree-grid>
126+
</div>

src/app/grid-cellMerging/grid-cellMerging.component.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,9 @@
2020
.searchInput{
2121
width: 800px;
2222
}
23+
24+
.grids-grid {
25+
display: grid;
26+
grid-template-columns: repeat(auto-fill, minmax(800px, 1fr));
27+
gap: 1rem;
28+
}

0 commit comments

Comments
 (0)