Skip to content

Commit 57e3210

Browse files
authored
Merge branch '18.2.x' into didimmova/fix-15515-18.2.x
2 parents a44972b + 7f1bedb commit 57e3210

File tree

10 files changed

+130
-14
lines changed

10 files changed

+130
-14
lines changed

projects/igniteui-angular/src/lib/directives/text-highlight/text-highlight.directive.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,17 @@ describe('IgxHighlight', () => {
299299
expect(() => component.highlight.activateIfNecessary()).not.toThrowError();
300300
});
301301

302+
it('Should not throw when attempting to activate a non-existing group.', () => {
303+
const fix = TestBed.createComponent(HighlightLoremIpsumComponent);
304+
fix.detectChanges();
305+
306+
const component: HighlightLoremIpsumComponent = fix.debugElement.componentInstance;
307+
component.highlightText('a');
308+
component.groupName = 'test1';
309+
fix.detectChanges();
310+
311+
expect(() => component.highlight.activateIfNecessary()).not.toThrowError();
312+
});
302313
});
303314

304315
@Component({

projects/igniteui-angular/src/lib/directives/text-highlight/text-highlight.directive.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ export class IgxTextHighlightDirective implements AfterViewInit, AfterViewChecke
318318
public activateIfNecessary(): void {
319319
const group = this.service.highlightGroupsMap.get(this.groupName);
320320

321-
if (group.index >= 0 && group.column === this.column && group.row === this.row && compareMaps(this.metadata, group.metadata)) {
321+
if (group && group.index >= 0 && group.column === this.column && group.row === this.row && compareMaps(this.metadata, group.metadata)) {
322322
this.activate(group.index);
323323
}
324324
}

projects/igniteui-angular/src/lib/directives/tooltip/tooltip-target.directive.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ export class IgxTooltipTargetDirective extends IgxToggleActionDirective implemen
314314
* @hidden
315315
*/
316316
public ngOnDestroy() {
317+
this.hideTooltip();
317318
this.destroy$.next();
318319
this.destroy$.complete();
319320
}

projects/igniteui-angular/src/lib/directives/tooltip/tooltip.directive.spec.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,19 @@ describe('IgxTooltip', () => {
271271
verifyTooltipVisibility(tooltipNativeElement, tooltipTarget, false);
272272
}));
273273

274+
it('IgxTooltip is hidden when its target is destroyed', fakeAsync(() => {
275+
hoverElement(button);
276+
flush();
277+
278+
verifyTooltipVisibility(tooltipNativeElement, tooltipTarget, true);
279+
280+
fix.componentInstance.showButton = false;
281+
fix.detectChanges();
282+
flush();
283+
284+
verifyTooltipVisibility(tooltipNativeElement, tooltipTarget, false);
285+
}));
286+
274287
describe('Tooltip events', () => {
275288
// configureTestSuite();
276289
it('should emit the proper events when hovering/unhovering target', fakeAsync(() => {
@@ -596,7 +609,7 @@ const touchElement = (element) => element.nativeElement.dispatchEvent(new TouchE
596609
const verifyTooltipVisibility = (tooltipNativeElement, tooltipTarget, shouldBeVisible: boolean) => {
597610
expect(tooltipNativeElement.classList.contains(TOOLTIP_CLASS)).toBe(shouldBeVisible);
598611
expect(tooltipNativeElement.classList.contains(HIDDEN_TOOLTIP_CLASS)).not.toBe(shouldBeVisible);
599-
expect(tooltipTarget.tooltipHidden).not.toBe(shouldBeVisible);
612+
expect(tooltipTarget?.tooltipHidden).not.toBe(shouldBeVisible);
600613
};
601614

602615
const verifyTooltipPosition = (tooltipNativeElement, actualTarget, shouldBeAligned: boolean) => {

projects/igniteui-angular/src/lib/services/csv/char-separated-value-data.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@ export class CharSeparatedValueData {
5252
this._escapeCharacters.push(this._delimiter);
5353

5454
const headers = columns && columns.length ?
55-
columns.map(c => c.header ?? c.field) :
55+
/* When column groups are present, always use the field as it indicates the group the column belongs to.
56+
* Otherwise, in PivotGrid scenarios we can end up with many duplicated column names without a hint what they represent.
57+
*/
58+
columns.map(c => c.columnGroupParent ? c.field : c.header ?? c.field) :
5659
keys;
5760

5861
this._headerRecord = this.processHeaderRecord(headers, this._data.length);

projects/igniteui-angular/src/lib/services/csv/csv-exporter-grid.spec.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ import { FilteringLogic } from '../../data-operations/filtering-expression.inter
2222
import { configureTestSuite } from '../../test-utils/configure-suite';
2323
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
2424
import { wait } from '../../test-utils/ui-interactions.spec';
25+
import { IgxPivotGridComponent } from '../../grids/pivot-grid/pivot-grid.component';
26+
import { IgxPivotGridTestBaseComponent } from '../../test-utils/pivot-grid-samples.spec';
27+
import { IgxPivotNumericAggregate } from '../../grids/pivot-grid/pivot-grid-aggregate';
2528

2629
describe('CSV Grid Exporter', () => {
2730
configureTestSuite();
@@ -513,6 +516,48 @@ describe('CSV Grid Exporter', () => {
513516
});
514517
});
515518

519+
describe('Pivot Grid CSV export', () => {
520+
let fix;
521+
let pivotGrid: IgxPivotGridComponent;
522+
beforeEach(() => {
523+
fix = TestBed.createComponent(IgxPivotGridTestBaseComponent);
524+
fix.detectChanges();
525+
pivotGrid = fix.componentInstance.pivotGrid;
526+
pivotGrid.pivotConfiguration = {
527+
columns: [
528+
{
529+
enabled: true,
530+
memberName: 'Country'
531+
}
532+
],
533+
rows: [
534+
{
535+
enabled: true,
536+
memberName: 'ProductCategory'
537+
}
538+
],
539+
values: [
540+
{
541+
enabled: true,
542+
member: 'UnitsSold',
543+
aggregate: {
544+
aggregator: IgxPivotNumericAggregate.sum,
545+
key: 'SUM',
546+
label: 'Sum',
547+
},
548+
}
549+
]
550+
};
551+
fix.detectChanges();
552+
});
553+
554+
it('should export pivot grid successfully.', async () => {
555+
await wait();
556+
const wrapper = await getExportedData(pivotGrid, options);
557+
wrapper.verifyData(wrapper.pivotGridData);
558+
});
559+
});
560+
516561
const getExportedData = (grid, csvOptions: IgxCsvExporterOptions) => {
517562
const result = new Promise<CSVWrapper>((resolve) => {
518563
exporter.exportEnded.pipe(first()).subscribe((value) => {

projects/igniteui-angular/src/lib/services/csv/csv-exporter.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { EventEmitter, Injectable } from '@angular/core';
2-
import { DEFAULT_OWNER, IExportRecord, IgxBaseExporter } from '../exporter-common/base-export-service';
2+
import { DEFAULT_OWNER, ExportHeaderType, IColumnInfo, IExportRecord, IgxBaseExporter } from '../exporter-common/base-export-service';
33
import { ExportUtilities } from '../exporter-common/export-utilities';
44
import { CharSeparatedValueData } from './char-separated-value-data';
55
import { CsvFileTypes, IgxCsvExporterOptions } from './csv-exporter-options';
@@ -50,10 +50,29 @@ export class IgxCsvExporterService extends IgxBaseExporter {
5050
private _stringData: string;
5151

5252
protected exportDataImplementation(data: IExportRecord[], options: IgxCsvExporterOptions, done: () => void) {
53-
data = data.map((item) => item.data);
53+
const dimensionKeys = data[0]?.dimensionKeys;
54+
data = dimensionKeys?.length ?
55+
data.map((item) => item.rawData):
56+
data.map((item) => item.data);
5457
const columnList = this._ownersMap.get(DEFAULT_OWNER);
58+
const columns = columnList?.columns.filter(c => c.headerType === ExportHeaderType.ColumnHeader);
59+
if (dimensionKeys) {
60+
const dimensionCols = dimensionKeys.map((key) => {
61+
const columnInfo: IColumnInfo = {
62+
header: key,
63+
field: key,
64+
dataType: 'string',
65+
skip: false,
66+
headerType: ExportHeaderType.ColumnHeader,
67+
columnSpan: 1,
68+
startIndex: 0
69+
};
70+
return columnInfo;
71+
});
72+
columns.unshift(...dimensionCols);
73+
}
5574

56-
const csvData = new CharSeparatedValueData(data, options.valueDelimiter, columnList?.columns);
75+
const csvData = new CharSeparatedValueData(data, options.valueDelimiter, columns);
5776
csvData.prepareDataAsync((r) => {
5877
this._stringData = r;
5978
this.saveFile(options);

projects/igniteui-angular/src/lib/services/csv/csv-verification-wrapper.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,4 +288,12 @@ export class CSVWrapper {
288288
`B's Beverages${this._delimiter}Victoria Ashworth${this._delimiter}Fauntleroy Circus${this._delimiter}0${this._delimiter}` +
289289
`2500${this._delimiter}5000${this._eor}`;
290290
}
291+
292+
public get pivotGridData() {
293+
return `ProductCategory${this._delimiter}Bulgaria${this._delimiter}USA${this._delimiter}Uruguay${this._eor}` +
294+
`Accessories${this._delimiter}${this._delimiter}293${this._delimiter}${this._eor}` +
295+
`Bikes${this._delimiter}${this._delimiter}${this._delimiter}68${this._eor}` +
296+
`Clothing${this._delimiter}774${this._delimiter}296${this._delimiter}456${this._eor}` +
297+
`Components${this._delimiter}${this._delimiter}240${this._delimiter}${this._eor}`;
298+
}
291299
}

projects/igniteui-angular/src/lib/services/exporter-common/base-export-service.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ export interface IExportRecord {
4444
summaryKey?: string;
4545
hierarchicalOwner?: string;
4646
references?: IColumnInfo[];
47+
/* Adding `rawData` and `dimesnionKeys` properties to support properly exporting pivot grid data to CSV. */
48+
rawData?: any;
49+
dimensionKeys?: string[];
4750
}
4851

4952
export interface IColumnList {
@@ -448,13 +451,18 @@ export abstract class IgxBaseExporter {
448451
if (!isSpecialData) {
449452
const owner = record.owner === undefined ? DEFAULT_OWNER : record.owner;
450453
const ownerCols = this._ownersMap.get(owner).columns;
454+
const hasRowHeaders = ownerCols.some(c => c.headerType === ExportHeaderType.RowHeader);
451455

452456
if (record.type !== ExportRecordType.HeaderRecord) {
453457
const columns = ownerCols
454458
.filter(c => c.headerType === ExportHeaderType.ColumnHeader && !c.skip)
455459
.sort((a, b) => a.startIndex - b.startIndex)
456460
.sort((a, b) => a.pinnedIndex - b.pinnedIndex);
457461

462+
if (hasRowHeaders) {
463+
record.rawData = record.data;
464+
}
465+
458466
record.data = columns.reduce((a, e) => {
459467
if (!e.skip) {
460468
let rawValue = resolveNestedPath(record.data, e.field);
@@ -592,6 +600,10 @@ export abstract class IgxBaseExporter {
592600

593601
this.flatRecords.push(pivotGridRecord);
594602
}
603+
604+
if (this.flatRecords.length) {
605+
this.flatRecords[0].dimensionKeys = Object.values(this.pivotGridRowDimensionsMap);
606+
}
595607
}
596608

597609
private prepareHierarchicalGridData(grid: GridType, hasFiltering: boolean, hasSorting: boolean) {
@@ -1342,8 +1354,8 @@ export abstract class IgxBaseExporter {
13421354

13431355
for (const k of Object.keys(groupedRecords)) {
13441356
groupedRecords[k] = groupedRecords[k].filter(row => mapKeys.every(mk => Object.keys(row).includes(mk))
1345-
&& mapValues.every(mv => Object.values(row).includes(mv)));
1346-
1357+
&& mapValues.every(mv => Object.values(row).includes(mv)));
1358+
13471359
if (groupedRecords[k].length === 0) {
13481360
delete groupedRecords[k];
13491361
}

projects/igniteui-angular/src/lib/test-utils/tooltip-components.spec.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@ import { IgxToggleActionDirective, IgxToggleDirective } from '../directives/togg
66
@Component({
77
template: `
88
<div class="dummyDiv">dummy div for touch tests</div>
9-
<button [igxTooltipTarget]="tooltipRef" [tooltip]="'Infragistics Inc. HQ'"
10-
(tooltipShow)="showing($event)" (tooltipHide)="hiding($event)"
11-
style="margin: 200px">
12-
Hover me
13-
</button>
9+
10+
@if (showButton) {
11+
<button [igxTooltipTarget]="tooltipRef" [tooltip]="'Infragistics Inc. HQ'"
12+
(tooltipShow)="showing($event)" (tooltipHide)="hiding($event)"
13+
style="margin: 200px">
14+
Hover me
15+
</button>
16+
}
1417
<div igxTooltip #tooltipRef="tooltip">
1518
Hello, I am a tooltip!
1619
</div>
@@ -20,9 +23,10 @@ import { IgxToggleActionDirective, IgxToggleDirective } from '../directives/togg
2023
})
2124
export class IgxTooltipSingleTargetComponent {
2225
@ViewChild(IgxTooltipDirective, { static: true }) public tooltip: IgxTooltipDirective;
23-
@ViewChild(IgxTooltipTargetDirective, { static: true }) public tooltipTarget: IgxTooltipTargetDirective;
26+
@ViewChild(IgxTooltipTargetDirective, { static: false }) public tooltipTarget: IgxTooltipTargetDirective;
2427
public cancelShowing = false;
2528
public cancelHiding = false;
29+
public showButton = true;
2630

2731
public showing(args: ITooltipShowEventArgs) {
2832
if (this.cancelShowing) {

0 commit comments

Comments
 (0)