Skip to content

Tree-Grid update cascade selection when filtering is applied #10434

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Nov 15, 2021
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing';
import { SortingDirection } from '../../data-operations/sorting-expression.interface';
import { IgxTreeGridComponent } from './tree-grid.component';
import { IgxGridCell, IgxTreeGridModule } from './public_api';
import { IgxTreeGridCellComponent } from './tree-cell.component';
import {
IgxTreeGridSimpleComponent,
IgxTreeGridCellSelectionComponent,
Expand All @@ -11,7 +10,8 @@ import {
IgxTreeGridRowEditingTransactionComponent,
IgxTreeGridCustomRowSelectorsComponent,
IgxTreeGridCascadingSelectionComponent,
IgxTreeGridCascadingSelectionTransactionComponent
IgxTreeGridCascadingSelectionTransactionComponent,
IgxTreeGridPrimaryForeignKeyCascadeSelectionComponent
} from '../../test-utils/tree-grid-components.spec';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import {
Expand Down Expand Up @@ -53,7 +53,8 @@ describe('IgxTreeGrid - Selection #tGrid', () => {
IgxTreeGridRowEditingTransactionComponent,
IgxTreeGridCustomRowSelectorsComponent,
IgxTreeGridCascadingSelectionComponent,
IgxTreeGridCascadingSelectionTransactionComponent
IgxTreeGridCascadingSelectionTransactionComponent,
IgxTreeGridPrimaryForeignKeyCascadeSelectionComponent
],
imports: [IgxTreeGridModule, NoopAnimationsModule, IgxGridSelectionModule, IgxActionStripModule]
})
Expand Down Expand Up @@ -1040,7 +1041,7 @@ describe('IgxTreeGrid - Selection #tGrid', () => {
}));
});

describe('Cascading Row Selection', () => {
describe('Cascading Row Selection - Child collection data', () => {
beforeEach(fakeAsync(() => {
fix = TestBed.createComponent(IgxTreeGridCascadingSelectionComponent);
fix.detectChanges();
Expand Down Expand Up @@ -1755,6 +1756,185 @@ describe('IgxTreeGrid - Selection #tGrid', () => {
});
});

describe('Cascading Row Selection - Primary/Foreign key data', () => {
beforeEach(fakeAsync(() => {
fix = TestBed.createComponent(IgxTreeGridPrimaryForeignKeyCascadeSelectionComponent);
fix.detectChanges();
treeGrid = fix.componentInstance.treeGrid;
actionStrip = fix.componentInstance.actionStrip;
}));

it(`Filter out all children for a certain parent, except for one. Select it.
Parent should also become selected. Clear filters. Parent should become in
indeterminate state as there are non-selected children.`, async () => {
treeGrid.filter('ID', 475, IgxNumberFilteringOperand.instance().condition('equals'));
await wait(100);
fix.detectChanges();

treeGrid.selectRows([475], true);
await wait(100);
fix.detectChanges();

expect(getVisibleSelectedRows(fix).length).toBe(2);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 0, true, true);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 1, true, true);

treeGrid.clearFilter();
await wait(100);
fix.detectChanges();

expect(getVisibleSelectedRows(fix).length).toBe(1);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 0, false, null);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 1, true, true);
});

it(`If there is only one selected leaf row for a particular parent and we filter it out parent's checkbox state -> non-selected.
All non-direct parents’ checkbox states should be set correctly as well`, async () => {
treeGrid.selectRows([711], true);
fix.detectChanges();

expect(getVisibleSelectedRows(fix).length).toBe(1);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 0, false, null);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 3, false, null);

treeGrid.filter('ID', 711, IgxNumberFilteringOperand.instance().condition('doesNotEqual'));
fix.detectChanges();

await wait(100);
fix.detectChanges();

expect(getVisibleSelectedRows(fix).length).toBe(0);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 0, false, false);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 3, false, false);
});

it(`If there is only one non-selected row for a particular parent and we filter it out parent's checkbox state -> selected.
All non-direct parents’ checkbox states should be set correctly as well`, async () => {
treeGrid.selectRows([711, 998], true);
fix.detectChanges();

expect(getVisibleSelectedRows(fix).length).toBe(2);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 0, false, null);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 3, false, null);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 4, true, true);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 5, true, true);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 6, false, false);

treeGrid.filter('ID', 299, IgxNumberFilteringOperand.instance().condition('doesNotEqual'));
fix.detectChanges();

await wait(200);
fix.detectChanges();

expect(getVisibleSelectedRows(fix).length).toBe(3);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 0, false, null);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 3, true, true);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 4, true, true);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 5, true, true);
});

it('After adding a new child row to a selected parent its checkbox state SHOULD be indeterminate.', async () => {
treeGrid.selectRows([847], true);
fix.detectChanges();
expect(getVisibleSelectedRows(fix).length).toBe(2);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 8, true, true);
TreeGridFunctions.verifyHeaderCheckboxSelection(fix, null);

const row = treeGrid.gridAPI.get_row_by_index(8);
actionStrip.show(row);
fix.detectChanges();

// add new child through the UI
const editActions = fix.debugElement.queryAll(By.css(`igx-grid-action-button`));
const addChildBtn = editActions[2].componentInstance;
addChildBtn.actionClick.emit();
fix.detectChanges();
endTransition();

const addRow = treeGrid.gridAPI.get_row_by_index(9);
expect(addRow.addRowUI).toBeTrue();

treeGrid.gridAPI.crudService.endEdit(true);
await wait(100);
fix.detectChanges();
const addedRow = treeGrid.gridAPI.get_row_by_index(10);
expect(addedRow.rowData.Name).toBe(undefined);

TreeGridFunctions.verifyDataRowsSelection(fix, [9], true);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 8, false, null);
TreeGridFunctions.verifyHeaderCheckboxSelection(fix, null);
});

it('If parent and its children are selected and we delete a child, parent SHOULD be still selected.', async () => {
treeGrid.selectRows([147], true);
fix.detectChanges();
expect(getVisibleSelectedRows(fix).length).toBe(7);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 0, true, true);
TreeGridFunctions.verifyHeaderCheckboxSelection(fix, null);

expect(treeGrid.dataRowList.length).toBe(10);

const childRow = treeGrid.gridAPI.get_row_by_index(5);
actionStrip.show(childRow);
fix.detectChanges();

// delete the child through the UI
const editActions = fix.debugElement.queryAll(By.css(`igx-grid-action-button`));
const deleteBtn = editActions[2].componentInstance;
deleteBtn.actionClick.emit();
fix.detectChanges();

await wait(100);
fix.detectChanges();

expect(treeGrid.dataRowList.length).toBe(9);
expect(getVisibleSelectedRows(fix).length).toBe(6);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 3, true, true);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 0, true, true);
TreeGridFunctions.verifyHeaderCheckboxSelection(fix, null);
});

it('If we delete the only selected child of a parent row, the parent checkbox state SHOULD be deselected', async () => {
treeGrid.selectRows([711], true);
fix.detectChanges();
expect(getVisibleSelectedRows(fix).length).toBe(1);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 3, false, null);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 0, false, null);
TreeGridFunctions.verifyHeaderCheckboxSelection(fix, null);

expect(treeGrid.dataRowList.length).toBe(10);

// delete the child through the API
const childRow = treeGrid.gridAPI.get_row_by_index(4);
childRow.delete();
fix.detectChanges();

await wait(100);
fix.detectChanges();

expect(treeGrid.dataRowList.length).toBe(9);
expect(getVisibleSelectedRows(fix).length).toBe(0);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 3, false, false);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 0, false, false);
TreeGridFunctions.verifyHeaderCheckboxSelection(fix, false);
});

it(`Set nested child row, that has its own children, as initially selected and verify
that both direct and indirect parent's checkboxes are set in the correct state.`, fakeAsync(() => {
treeGrid.selectedRows = [317];
fix.detectChanges();
tick(100);
fix.detectChanges();

expect(getVisibleSelectedRows(fix).length).toBe(4);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 0, false, null);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 3, true, true);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 4, true, true);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 5, true, true);
TreeGridFunctions.verifyRowByIndexSelectionAndCheckboxState(fix, 6, true, true);
}));
});

describe('Cascading Row Selection with Transaction', () => {
beforeEach(fakeAsync(() => {
fix = TestBed.createComponent(IgxTreeGridCascadingSelectionTransactionComponent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ export class IgxTreeGridComponent extends IgxGridBaseDirective implements GridTy
// if a row has been added and before commiting the transaction deleted
const leafRowsDirectParents = new Set<any>();
this.records.forEach(record => {
if (record && !record.children && record.parent) {
if (record && (!record.children || record.children.length === 0) && record.parent) {
leafRowsDirectParents.add(record.parent);
}
});
Expand All @@ -494,7 +494,7 @@ export class IgxTreeGridComponent extends IgxGridBaseDirective implements GridTy
if (this.rowSelection === GridSelectionMode.multipleCascade) {
const leafRowsDirectParents = new Set<any>();
this.records.forEach(record => {
if (record && !record.children && record.parent) {
if (record && (!record.children || record.children.length === 0) && record.parent) {
leafRowsDirectParents.add(record.parent);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,79 @@ export class SampleTestData {
}
]);

public static employeeSmallPrimaryForeignKeyTreeData = () => ([
{
ID: 147,
ParentID: -1,
Name: 'John Winchester',
HireDate: new Date(2008, 3, 20),
Age: 55,
},
{
ID: 475,
ParentID: 147,
Name: 'Michael Langdon',
HireDate: new Date(2011, 6, 3),
Age: 30,
},
{
ID: 957,
ParentID: 147,
Name: 'Thomas Hardy',
HireDate: new Date(2009, 6, 19),
Age: 29,
},
{
ID: 317,
ParentID: 147,
Name: 'Monica Reyes',
HireDate: new Date(2014, 8, 18),
Age: 31,
},
{
ID: 711,
ParentID: 317,
Name: 'Roland Mendel',
HireDate: new Date(2015, 9, 17),
Age: 35
},
{
ID: 998,
ParentID: 317,
Name: 'Sven Ottlieb',
HireDate: new Date(2009, 10, 11),
Age: 44
},
{
ID: 299,
ParentID: 317,
Name: 'Peter Lewis',
HireDate: new Date(2018, 3, 18),
Age: 25
},
{
ID: 19,
ParentID: -1,
Name: 'Yang Wang',
HireDate: new Date(2010, 1, 1),
Age: 61,
},
{
ID: 847,
ParentID: -1,
Name: 'Ana Sanders',
HireDate: new Date(2014, 1, 22),
Age: 42,
},
{
ID: 663,
ParentID: 847,
Name: 'Elizabeth Richards',
HireDate: new Date(2017, 11, 9),
Age: 25
}
]);

/* Search tree data: Every employee node has ID, Name, HireDate, Age, JobTitle and Employees */
public static employeeSearchTreeData = () => ([
{
Expand Down Expand Up @@ -1399,7 +1472,6 @@ export class SampleTestData {
Age: 25,
OnPTO: false
},

{
ID: 141,
ParentID: 663,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -973,3 +973,23 @@ export class IgxTreeGridGroupByAreaTestComponent {
@ViewChild(IgxTreeGridGroupByAreaComponent, { static: true }) public groupByArea: IgxTreeGridGroupByAreaComponent;
}

@Component({
template: `
<igx-tree-grid #treeGrid [data]="data" primaryKey="ID" foreignKey="ParentID" [allowFiltering]="true"
[rowSelection]="'multipleCascade'" [rowEditable]="true" width="900px" height="600px">
<igx-column [field]="'ID'" dataType="number"></igx-column>
<igx-column [field]="'Name'" dataType="string"></igx-column>
<igx-column [field]="'HireDate'" dataType="date"></igx-column>
<igx-column [field]="'Age'" dataType="number"></igx-column>
<igx-action-strip #actionStrip>
<igx-grid-editing-actions [addRow]="true" [addChild]='true'></igx-grid-editing-actions>
</igx-action-strip>
</igx-tree-grid>
`
})
export class IgxTreeGridPrimaryForeignKeyCascadeSelectionComponent {
@ViewChild(IgxTreeGridComponent, { static: true }) public treeGrid: IgxTreeGridComponent;
@ViewChild('actionStrip', { read: IgxActionStripComponent, static: true })
public actionStrip: IgxActionStripComponent;
public data = SampleTestData.employeeSmallPrimaryForeignKeyTreeData();
}