diff --git a/projects/ui/src/lib/components/po-table/po-table-base.component.spec.ts b/projects/ui/src/lib/components/po-table/po-table-base.component.spec.ts index f16f7d472d..a10c1e600f 100644 --- a/projects/ui/src/lib/components/po-table/po-table-base.component.spec.ts +++ b/projects/ui/src/lib/components/po-table/po-table-base.component.spec.ts @@ -759,6 +759,46 @@ describe('PoTableBaseComponent:', () => { expect(component['emitSelectEvents']).toHaveBeenCalledWith(row); }); + it('selectRow: should update child items $selected property if row has detail', () => { + const childItems = [ + { id: 2, $selected: false }, + { id: 3, $selected: false } + ]; + const row = { id: 1, $selected: false, detail: childItems }; + + component.selectRow(row); + + expect(row.$selected).toBeTruthy(); + row.detail.forEach(childItem => { + expect(childItem.$selected).toBeTruthy(); + }); + }); + + it('selectRow: should call setSelectedList after selecting a row', () => { + const row = { id: 1, $selected: false }; + + spyOn(component, 'setSelectedList'); + + component.selectRow(row); + + expect(component.setSelectedList).toHaveBeenCalled(); + }); + + it('selectRow: should update child items $selected property if row has detail and $selected is false', () => { + const childItems = [ + { id: 2, $selected: true }, + { id: 3, $selected: true } + ]; + const row = { id: 1, $selected: true, detail: childItems }; + + component.selectRow(row); + + expect(row.$selected).toBeFalsy(); + row.detail.forEach(childItem => { + expect(childItem.$selected).toBeFalsy(); + }); + }); + it('emitSelectEvents: should emit `selected` if `row.$selected` is `true`', () => { const row = { id: 1, $selected: true }; spyOn(component.selected, 'emit'); @@ -787,6 +827,80 @@ describe('PoTableBaseComponent:', () => { expect(component['emitSelectAllEvents']).toHaveBeenCalled(); }); + describe('selectAllRows:', () => { + beforeEach(() => { + component.hideSelectAll = false; + component.selectAll = false; + component.items = [ + { id: 1, $selected: false, detail: [{ id: 4, $selected: false }] }, + { id: 2, $selected: false, detail: [{ id: 5, $selected: false }] } + ]; + }); + + it('should call emitSelectEvents and updateParentRowSelection', () => { + const row = { id: 1, $selected: false, detail: [{ id: 4 }] }; + const parentRow = { id: 2, detail: [row] }; + + spyOn(component, 'emitSelectEvents'); + spyOn(component, 'updateParentRowSelection'); + + component.selectDetailRow({ item: row, parentRow }); + + expect(component['emitSelectEvents']).toHaveBeenCalledWith(row); + expect(component['updateParentRowSelection']).toHaveBeenCalledWith(parentRow); + }); + + it('should select all rows when selectAll is false', () => { + component.selectAllRows(); + + expect(component.selectAll).toBeTrue(); + component.items.forEach(item => { + expect(item.$selected).toBeTrue(); + item.detail.forEach(childItem => { + expect(childItem.$selected).toBeTrue(); + }); + }); + }); + + it('should unselect all rows when selectAll is true', () => { + component.selectAll = true; + component.items.forEach(item => { + item.$selected = true; + item.detail.forEach(childItem => (childItem.$selected = true)); + }); + + component.selectAllRows(); + + expect(component.selectAll).toBeFalse(); + component.items.forEach(item => { + expect(item.$selected).toBeFalse(); + item.detail.forEach(childItem => { + expect(childItem.$selected).toBeFalse(); + }); + }); + }); + + it('should not select all rows if hideSelectAll is true', () => { + component.hideSelectAll = true; + + component.selectAllRows(); + + component.items.forEach(item => { + expect(item.$selected).toBeFalse(); + }); + }); + + it('should emit selectAll events and update selected list', () => { + spyOn(component as any, 'emitSelectAllEvents'); + spyOn(component, 'setSelectedList'); + + component.selectAllRows(); + + expect((component as any).emitSelectAllEvents).toHaveBeenCalledWith(true, jasmine.any(Array)); + expect(component.setSelectedList).toHaveBeenCalled(); + }); + }); + it('emitSelectAllEvents: should emit `allSelected` if `selectAll` is `true`', () => { const rows = [{ id: 1, $selected: true }]; const selectAll = true; @@ -809,14 +923,34 @@ describe('PoTableBaseComponent:', () => { expect(component.allUnselected.emit).toHaveBeenCalledWith(rows); }); - it('selectDetailRow: should call `emitSelectEvents`', () => { - const row = { id: 1, $selected: false }; + it('updateParentRowSelection: should set parentRow.$selected to true if all child items are selected', () => { + const parentRow: any = { + detail: [{ $selected: true }, { $selected: true }, { $selected: true }] + }; - spyOn(component, 'emitSelectEvents'); + component.updateParentRowSelection(parentRow); - component.selectDetailRow(row); + expect(parentRow.$selected).toBe(true); + }); - expect(component['emitSelectEvents']).toHaveBeenCalledWith(row); + it('updateParentRowSelection: should set parentRow.$selected to false if all child items are not selected', () => { + const parentRow: any = { + detail: [{ $selected: false }, { $selected: false }, { $selected: false }] + }; + + component.updateParentRowSelection(parentRow); + + expect(parentRow.$selected).toBe(false); + }); + + it('updateParentRowSelection: should set parentRow.$selected to null if some child items are selected and some are not', () => { + const parentRow: any = { + detail: [{ $selected: true }, { $selected: false }, { $selected: true }] + }; + + component.updateParentRowSelection(parentRow); + + expect(parentRow.$selected).toBeNull(); }); describe('getColumnColor:', () => { diff --git a/projects/ui/src/lib/components/po-table/po-table-base.component.ts b/projects/ui/src/lib/components/po-table/po-table-base.component.ts index 0671e2daa5..10aafafb7d 100644 --- a/projects/ui/src/lib/components/po-table/po-table-base.component.ts +++ b/projects/ui/src/lib/components/po-table/po-table-base.component.ts @@ -1024,6 +1024,11 @@ export abstract class PoTableBaseComponent implements OnChanges, OnDestroy { this.items.forEach(item => { item.$selected = this.selectAll; + + if (item[this.nameColumnDetail]) { + const childItems = item[this.nameColumnDetail]; + childItems.forEach(childItem => (childItem.$selected = this.selectAll)); + } }); this.emitSelectAllEvents(this.selectAll, [...this.items]); @@ -1035,8 +1040,13 @@ export abstract class PoTableBaseComponent implements OnChanges, OnDestroy { row.$selected = !row.$selected; this.emitSelectEvents(row); - this.configAfterSelectRow(this.items, row); + + if (row[this.nameColumnDetail] && (row.$selected === true || row.$selected === false)) { + const childItems = row[this.nameColumnDetail]; + childItems.forEach(item => (item.$selected = row.$selected)); + } + this.setSelectedList(); } @@ -1044,8 +1054,29 @@ export abstract class PoTableBaseComponent implements OnChanges, OnDestroy { return this.selectable && this.selectableEntireLine; } - selectDetailRow(row: any) { - this.emitSelectEvents(row); + selectDetailRow(event: any) { + const { item, parentRow } = event; + this.emitSelectEvents(item); + this.updateParentRowSelection(parentRow); + } + + updateParentRowSelection(parentRow: any) { + const old = parentRow.$selected || null; + const childItems = parentRow[this.nameColumnDetail]; + + if (childItems.every(item => item.$selected)) { + parentRow.$selected = true; + } else if (childItems.every(item => !item.$selected)) { + parentRow.$selected = false; + } else { + parentRow.$selected = null; + } + if (old != parentRow.$selected && !(old == null && parentRow.$selected === false)) { + this.emitSelectEvents(parentRow); + } + + this.configAfterSelectRow(this.items, parentRow); + this.setSelectedList(); } setSelectedList() { diff --git a/projects/ui/src/lib/components/po-table/po-table-detail/po-table-detail.component.html b/projects/ui/src/lib/components/po-table/po-table-detail/po-table-detail.component.html index 367a10d86c..15eda74d60 100644 --- a/projects/ui/src/lib/components/po-table/po-table-detail/po-table-detail.component.html +++ b/projects/ui/src/lib/components/po-table/po-table-detail/po-table-detail.component.html @@ -22,12 +22,7 @@ - + diff --git a/projects/ui/src/lib/components/po-table/po-table-detail/po-table-detail.component.spec.ts b/projects/ui/src/lib/components/po-table/po-table-detail/po-table-detail.component.spec.ts index f5752def56..488258532c 100644 --- a/projects/ui/src/lib/components/po-table/po-table-detail/po-table-detail.component.spec.ts +++ b/projects/ui/src/lib/components/po-table/po-table-detail/po-table-detail.component.spec.ts @@ -178,7 +178,7 @@ describe('PoTableDetailComponent', () => { component.onSelectRow(row); expect(row.$selected).toBeTruthy(); - expect(component.selectRow.emit).toHaveBeenCalledWith(row); + expect(component.selectRow.emit).toHaveBeenCalledWith({ item: row, parentRow: undefined }); }); describe('returnPoTableDetailObject: ', () => { diff --git a/projects/ui/src/lib/components/po-table/po-table-detail/po-table-detail.component.ts b/projects/ui/src/lib/components/po-table/po-table-detail/po-table-detail.component.ts index c4f350959a..53e17dd8fb 100644 --- a/projects/ui/src/lib/components/po-table/po-table-detail/po-table-detail.component.ts +++ b/projects/ui/src/lib/components/po-table/po-table-detail/po-table-detail.component.ts @@ -23,6 +23,12 @@ export class PoTableDetailComponent { */ @Input('p-items') items: Array; + /** + * Linha do registro pai correspondente ao item de detalhe selecionado. Utilizado para gerenciar o estado de seleção do elemento pai, + * permitindo que o mesmo seja atualizado para refletir a seleção de todos os filhos ou estado indeterminado. + */ + @Input('p-parent-row') parentRow: PoTableDetail; + /** * Define se a tabela possui a opção de `selectable` habilitada. */ @@ -92,7 +98,7 @@ export class PoTableDetailComponent { onSelectRow(item) { item.$selected = !item.$selected; - this.selectRow.emit(item); + this.selectRow.emit({ item: item, parentRow: this.parentRow }); } private returnPoTableDetailObject(value: any) { diff --git a/projects/ui/src/lib/components/po-table/po-table.component.html b/projects/ui/src/lib/components/po-table/po-table.component.html index b6d1dbbff4..c7fe1d2689 100644 --- a/projects/ui/src/lib/components/po-table/po-table.component.html +++ b/projects/ui/src/lib/components/po-table/po-table.component.html @@ -434,6 +434,7 @@ [p-selectable]="selectable && !detailHideSelect" [p-detail]="columnMasterDetail.detail" [p-items]="row[nameColumnDetail]" + [p-parent-row]="row" (p-select-row)="selectDetailRow($event)" > @@ -790,6 +791,7 @@ [p-selectable]="selectable && !detailHideSelect" [p-detail]="columnMasterDetail.detail" [p-items]="row[nameColumnDetail]" + [p-parent-row]="row" (p-select-row)="selectDetailRow($event)" > @@ -820,7 +822,7 @@