Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ All notable changes for each version of this project will be documented in this
- `HammerGesturesManager` and related types (`HammerInput`, `HammerStatic`, `HammerManager`, `HammerOptions`) are no longer exported from `igniteui-angular/core`.
- The `ng add` schematic no longer prompts for or installs `hammerjs`.
- If your application imported `hammerjs` solely for Ignite UI components, you can safely remove it from your `package.json` dependencies, `angular.json` scripts/polyfills, and any `import 'hammerjs'` statements.
- `IgxHierarchicalGrid`
- **Deprecation** - `schema` input property has been deprecated and will be removed in a future version.

## 22.0.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
import { IgxHierarchicalGridExportComponent, IgxHierarchicalGridTestBaseComponent, IgxHierGridExternalAdvancedFilteringComponent } from '../../../test-utils/hierarchical-grid-components.spec';
import { SampleTestData } from '../../../test-utils/sample-test-data.spec';
import { By } from '@angular/platform-browser';
import { IgxHGridRemoteOnDemandComponent, IgxHierarchicalGridMissingChildDataComponent } from '../../hierarchical-grid/src/hierarchical-grid.spec';
import { IgxHGridRemoteOnDemandComponent, IgxHierarchicalGridMissingChildDataComponent, IgxHierarchicalGridToggleRIComponent } from '../../hierarchical-grid/src/hierarchical-grid.spec';
import { QueryBuilderFunctions } from '../../../query-builder/src/query-builder/query-builder-functions.spec';
import { IFilteringEventArgs, IgxGridNavigationService, IgxGridToolbarAdvancedFilteringComponent } from 'igniteui-angular/grids/core';
import { FilteringExpressionsTree, FilteringLogic, FormattedValuesFilteringStrategy, IGridResourceStrings, IgxNumberFilteringOperand, IgxStringFilteringOperand } from 'igniteui-angular/core';
Expand Down Expand Up @@ -1746,76 +1746,69 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => {
expect(hgrid.filteredData.length).toBe(5);
}));

it('Should have proper fields in UI when schema is defined with load on demand.', fakeAsync(() => {
const fixture = TestBed.createComponent(IgxHGridRemoteOnDemandComponent);
const hierarchicalGrid = fixture.componentInstance.instance;
it('Should update root grid schema when row island is expanded.', fakeAsync(() => {
const fixture = TestBed.createComponent(IgxHierarchicalGridToggleRIComponent);
const hierarchicalGrid = fixture.componentInstance.hgrid;
hierarchicalGrid.allowAdvancedFiltering = true;
hierarchicalGrid.schema = [
{
name: 'rootLevel',
fields: [
{ field: 'ID', dataType: 'string' },
{ field: 'ChildLevels', dataType: 'number' },
{ field: 'ProductName', dataType: 'string' },
{ field: 'Col1', dataType: 'number' },
{ field: 'Col2', dataType: 'number' },
{ field: 'Col3', dataType: 'number' }
],
childEntities: [
{
name: 'childData',
fields: [
{ field: 'ID', dataType: 'string' },
{ field: 'ProductName', dataType: 'string' }
],
childEntities: [
{
name: 'childData2',
fields: [
{ field: 'ID', dataType: 'string' },
{ field: 'ProductName', dataType: 'string' }
]
}
]
}
]
}
]
fixture.detectChanges();

hierarchicalGrid.openAdvancedFilteringDialog();
fixture.detectChanges();
// Open advanced filtering dialog and create an 'In' condition.
const createInConditionAndGetReturnFields = () => {
// Open Advanced Filtering dialog.
hierarchicalGrid.openAdvancedFilteringDialog();
fixture.detectChanges();

// Click the initial 'Add Condition' button.
QueryBuilderFunctions.clickQueryBuilderInitialAddConditionBtn(fixture, 0);
tick(100);
fixture.detectChanges();
// Populate edit inputs.
QueryBuilderFunctions.selectColumnInEditModeExpression(fixture, 0); // Select 'ID' column.
QueryBuilderFunctions.selectOperatorInEditModeExpression(fixture, 10); // Select 'In' operator.
tick(100);
fixture.detectChanges();
// Click the initial 'Add Condition' button.
QueryBuilderFunctions.clickQueryBuilderInitialAddConditionBtn(fixture, 0);
tick(100);
fixture.detectChanges();
// Populate edit inputs.
QueryBuilderFunctions.selectColumnInEditModeExpression(fixture, 0); // Select 'ID' column.
QueryBuilderFunctions.selectOperatorInEditModeExpression(fixture, 10); // Select 'In' operator.
tick(100);
fixture.detectChanges();

const entityInputGroup = QueryBuilderFunctions.getQueryBuilderEntitySelect(fixture, 1).querySelector('input');
expect(entityInputGroup.value).toBe('childData');
// Verify entities
QueryBuilderFunctions.clickQueryBuilderEntitySelect(fixture, 1);
fixture.detectChanges();
const queryBuilderElement: HTMLElement = fixture.debugElement.queryAll(By.css(`.${QueryBuilderSelectors.QUERY_BUILDER_TREE}`))[1].nativeElement;
let dropdownValues: string[] = QueryBuilderFunctions.getQueryBuilderSelectDropdownItems(queryBuilderElement).map((x: any) => x.innerText);
let expectedValues = ['childData'];
expect(dropdownValues).toEqual(expectedValues);

const fieldInputGroup = QueryBuilderFunctions.getQueryBuilderFieldsCombo(fixture, 1).querySelector('input');
expect(fieldInputGroup.value).toBe('ID');
// Verify return fields
QueryBuilderFunctions.clickQueryBuilderFieldsCombo(fixture, 1);
fixture.detectChanges();
const innerQueryReturnFields = QueryBuilderFunctions.getQueryBuilderSelectDropdown(queryBuilderElement, 1);

return innerQueryReturnFields;
};

let innerQueryReturnFields = createInConditionAndGetReturnFields();
expect(innerQueryReturnFields).toBeNull();

// Verify entities
QueryBuilderFunctions.clickQueryBuilderEntitySelect(fixture, 1);
// Close Advanced Filtering dialog.
hierarchicalGrid.closeAdvancedFilteringDialog(false);
tick(200);
fixture.detectChanges();
const queryBuilderElement: HTMLElement = fixture.debugElement.queryAll(By.css(`.${QueryBuilderSelectors.QUERY_BUILDER_TREE}`))[1].nativeElement;
let dropdownValues: string[] = QueryBuilderFunctions.getQueryBuilderSelectDropdownItems(queryBuilderElement).map((x: any) => x.innerText);
let expectedValues = ['childData'];
expect(dropdownValues).toEqual(expectedValues);

// Verify return fileds
QueryBuilderFunctions.clickQueryBuilderFieldsCombo(fixture, 1);
// Expand row island
const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as any;
UIInteractions.simulateClickAndSelectEvent(row.expander);
fixture.detectChanges();
dropdownValues = QueryBuilderFunctions.getQueryBuilderSelectDropdownItems(queryBuilderElement, 1).map((x: any) => x.innerText);
expectedValues = ['ID', 'ProductName'];
expect(dropdownValues).toEqual(expectedValues);

// Open advanced filtering dialog and verify that schema is updated.
innerQueryReturnFields = createInConditionAndGetReturnFields();
expect(innerQueryReturnFields).not.toBeNull();
const items = Array.from(innerQueryReturnFields.querySelectorAll('.igx-drop-down__item'));
expect(items.length).toBe(7);
expect((items[0] as HTMLElement).innerText).toBe('ID');
expect((items[1] as HTMLElement).innerText).toBe('ChildLevels');
expect((items[2] as HTMLElement).innerText).toBe('ProductName');
expect((items[3] as HTMLElement).innerText).toBe('Col1');
expect((items[4] as HTMLElement).innerText).toBe('Col2');
expect((items[5] as HTMLElement).innerText).toBe('Col3');
expect((items[6] as HTMLElement).innerText).toBe('childData2');
}));

it('Should correctly change resource strings for hierarchical Advanced Filtering dialog.', fakeAsync(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -549,11 +549,10 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti
* const schema = this.grid.schema;
* this.grid.schema = [{ name: 'Products', fields: [...], childEntities: [...] }];
* ```
* @deprecated in version 20.2.0.
*/
Comment on lines 549 to 553
@Input()
public set schema(entities: EntityType[]) {
this._hGridSchema = entities;
}
public set schema(entities: EntityType[]) {}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit iffy on the setter being noop - while it's doubtful that its seen major usage, ideally we'd have it fully functional until its removed.
Otherwise, at least make sure whatever scenario this prop was meant to support, that even with the inactive setter doesn't change with the new functionality handling schema internally.


Comment on lines 554 to 556
/* blazorSuppress */
public get schema() {
Expand Down Expand Up @@ -660,6 +659,10 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti
this.batchEditing = val;
});
}
this.columnsAutogenerated.subscribe((e) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With pipe(takeUntilDestroyed()) like other subs?

this.updateRootGridSchema(e);
});

super.ngOnInit();
}

Expand Down Expand Up @@ -1255,7 +1258,7 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti
}

private generateSchema() {
const filterableFields = this.columns.filter((column) => !column.columnGroup && column.filterable);
const filterableFields: FieldType[] = this.columns.filter((column) => !column.columnGroup && column.filterable);
let entities: EntityType[];

if(filterableFields.length !== 0) {
Expand All @@ -1265,6 +1268,7 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti
fields: filterableFields.map(f => ({
field: f.field,
dataType: f.dataType,
label: f.label,
header: f.header,
editorOptions: f.editorOptions,
filters: f.filters,
Expand All @@ -1276,44 +1280,62 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti
];

entities[0].childEntities = this.childLayoutList.reduce((acc, rowIsland) => {
const childFirstRowData = this.data?.length > 0 && this.data[0][rowIsland.key]?.length > 0 ?
this.data[0][rowIsland.key][0] : null;
return acc.concat(this.generateChildEntity(rowIsland, childFirstRowData));
return acc.concat(this.generateChildEntity(rowIsland));
}
, []);
}

return entities;
}

private generateChildEntity(rowIsland: IgxRowIslandComponent, firstRowData: any[]): EntityType {
private updateRootGridSchema(event: any) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sure the even has a type, other than any

const schema = (this.rootGrid as IgxHierarchicalGridComponent).schema;
if (!schema || schema.length === 0) {
return;
}

let path = [];
let parentIsland = this.parentIsland;
while (parentIsland) {
path.push(parentIsland.key);
parentIsland = parentIsland.parentIsland;
}

path.reverse();
if (path.length > 0) {
let childEntity = schema[0];
for (let i = 0; i < path.length; i++) {
childEntity = childEntity.childEntities.find(e => e.name === path[i]);
}

Comment on lines +1306 to +1310
if (childEntity) {
childEntity.fields = event.columns.filter((column) => !column.columnGroup && column.filterable)
.map(f => ({
field: f.field,
dataType: f.dataType,
label: f.label,
header: f.header,
editorOptions: f.editorOptions,
filters: f.filters,
pipeArgs: f.pipeArgs,
defaultTimeFormat: f.defaultTimeFormat,
defaultDateTimeFormat: f.defaultDateTimeFormat
})) as FieldType[];
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the optimization for a single path update sounds nice, grid setups rarely changes w/ high frequency and the structures being processed are rarely, but sure - I can recall a few massive column lists, but even those are likely in the 100s, which is nothing much for list.

Regardless, two questions about this:

  • This logic is more/less a repeat of the one in generateSchema()? If so, would be nice if those are either combined, reused or otherwise refactored into a single thing that does the islands/columns -> entity/fields mapping
  • And isn't the column directly compatible with FieldType? Does the QB modify something in the schema fields? Otherwise, I wonder why not just take the filtered columns directly?

}
}

private generateChildEntity(rowIsland: IgxRowIslandComponent): EntityType {
const entityName = rowIsland.key;
let fields = [];
let childEntities;
if (!rowIsland.autoGenerate) {
if (rowIsland.childColumns?.length > 0) {
fields = flatten(rowIsland.childColumns.toArray()).filter(col => col.field)
.map(f => ({ field: f.field, dataType: f.dataType })) as FieldType[];
} else if (firstRowData) {
const rowIslandFields = Object.keys(firstRowData).map(key => {
if (firstRowData[key] instanceof Array) {
return null;
}

return {
field: key,
dataType: this.resolveDataTypes(firstRowData[key])
}
});
fields = rowIslandFields.filter(f => f !== null) as FieldType[];
}

const rowIslandChildEntities = rowIsland.childLayoutList.reduce((acc, childRowIsland) => {
if (!firstRowData) {
return null;
}
const childFirstRowData = firstRowData.length > 0 && firstRowData[childRowIsland.key]?.length > 0 ?
firstRowData[childRowIsland.key][0] : null;
return acc.concat(this.generateChildEntity(childRowIsland, childFirstRowData));
return acc.concat(this.generateChildEntity(childRowIsland));
}, []);

if (rowIslandChildEntities?.length > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1380,7 +1380,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy {
* @hidden @internal
*/
public getFormat(field: string) {
return this.fields?.find(el => el.field === field).pipeArgs.format;
return this.fields?.find(el => el.field === field)?.pipeArgs.format;
}
Comment on lines 1382 to 1384

/**
Expand Down
Loading