Skip to content

Commit

Permalink
feat(tree-view): seleção única e customizada
Browse files Browse the repository at this point in the history
Permite a função de seleção única utilizando po-radio
e a customizar quais itens devem ou não receber seleção.

Fixes DTHFUI-7624
  • Loading branch information
guilnorth committed Sep 22, 2023
1 parent 323dff2 commit a9538f0
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 8 deletions.
1 change: 0 additions & 1 deletion projects/portal/src/assets/json/version.json

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ describe('PoTreeViewBaseComponent:', () => {

expectPropertiesValues(component, 'maxLevel', invalidValues, 4);
});

it('p-single-select: should update property with `true` if valid values', () => {
const validValues = [true, 'true', 1, ''];

expectPropertiesValues(component, 'singleSelect', validValues, true);
});

it('p-single-select: should update property with `false` if invalid values', () => {
const invalidValues = [10, 0.5, 'test', undefined];

expectPropertiesValues(component, 'singleSelect', invalidValues, false);
});
});

describe('Methods: ', () => {
Expand Down Expand Up @@ -297,6 +309,26 @@ describe('PoTreeViewBaseComponent:', () => {
expect(spyGetItemsWithParentSelected).toHaveBeenCalledWith(component.items);
});

it('updateItemsOnSelect: shouldn`t call selectAllItems if is singleSelect', () => {
const selectedItem = {
label: 'Label 01',
value: '01',
selected: true,
subItems: [{ label: 'Label 01.1', value: '01.1' }]
};
const items = [selectedItem];
component.items = items;
component.singleSelect = true;

const spyGetItemsWithParentSelected = spyOn(component, <any>'getItemsWithParentSelected').and.returnValue(items);
const spySelect = spyOn(component, <any>'selectAllItems');

component['updateItemsOnSelect'](selectedItem);

expect(spySelect).not.toHaveBeenCalled();
expect(spyGetItemsWithParentSelected).toHaveBeenCalledWith(component.items);
});

it('updateItemsOnSelect: should call selectAllItems if selectedItem has subItems and call getItemsWithParentSelected', () => {
const selectedItem = {
label: 'Label 01',
Expand All @@ -305,6 +337,8 @@ describe('PoTreeViewBaseComponent:', () => {
subItems: [{ label: 'Label 01.1', value: '01.1' }]
};
const items = [selectedItem];

component.singleSelect = false;
component.items = items;

const spyGetItemsWithParentSelected = spyOn(component, <any>'getItemsWithParentSelected').and.returnValue(items);
Expand Down Expand Up @@ -370,6 +404,62 @@ describe('PoTreeViewBaseComponent:', () => {
expect(items).toEqual(expectedItems);
});

it('selectAllItems: shouldn`t set `selected` on item if isSelectable is false', () => {
const items = [
{
label: 'Nivel 01',
value: 1,
selected: true,
subItems: [
{
label: 'Nivel 02',
value: 2,
selected: false,
isSelectable: false,
subItems: [
{
label: 'Nivel 03',
value: 3,
selected: false,
subItems: [{ label: 'Nivel 04', value: 4, selected: false }]
}
]
}
]
}
];

const expectedItems = [
{
label: 'Nivel 01',
value: 1,
selected: true,
subItems: [
{
label: 'Nivel 02',
value: 2,
isSelectable: false,
selected: false,
subItems: [
{
label: 'Nivel 03',
value: 3,
selected: true,
subItems: [{ label: 'Nivel 04', value: 4, selected: true }]
}
]
}
]
}
];

const isSelected = true;

component['selectAllItems'](items, isSelected);

expect(items).toEqual(expectedItems);
});

it('selectAllItems: should unselect all items if isSelected is false', () => {
const items = [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ export class PoTreeViewBaseComponent {
private _items: Array<PoTreeViewItem> = [];
private _selectable: boolean = false;
private _maxLevel = poTreeViewMaxLevel;
private _singleSelect: boolean = false;

// armazena o value do item selecionado
selectedValue: string | number;

/**
* Lista de itens do tipo `PoTreeViewItem` que será renderizada pelo componente.
Expand Down Expand Up @@ -99,6 +103,23 @@ export class PoTreeViewBaseComponent {
return this._selectable;
}

/**
* @optional
*
* @description
*
* Habilita a seleção para item único atráves de po-radio.
*
* @default false
*/
@Input('p-single-select') set singleSelect(value: boolean) {
this._singleSelect = convertToBoolean(value);
}

get singleSelect() {
return this._singleSelect;
}

/**
* @optional
*
Expand Down Expand Up @@ -127,6 +148,8 @@ export class PoTreeViewBaseComponent {
protected emitSelected(treeViewItem: PoTreeViewItem) {
const event = treeViewItem.selected ? 'selected' : 'unselected';

this.selectedValue = treeViewItem.value;

this.updateItemsOnSelect(treeViewItem);

this[event].emit({ ...treeViewItem });
Expand Down Expand Up @@ -165,7 +188,7 @@ export class PoTreeViewBaseComponent {
this.selectAllItems(item.subItems, isSelected);
}

item.selected = isSelected;
item.selected = item.isSelectable !== false ? isSelected : false;
});
}

Expand Down Expand Up @@ -217,14 +240,18 @@ export class PoTreeViewBaseComponent {

if (Array.isArray(subItems)) {
// caso um item pai iniciar selecionado, deve selecionar os filhos.
if (currentItem.selected) {
if (currentItem.selected && !this.singleSelect) {
this.selectAllItems(subItems, currentItem.selected);
}

this.getItemsByMaxLevel(subItems, ++level, currentItem);
--level;
}

if (item.selected) {
this.selectedValue = currentItem.value;
}

this.addItem(newItems, currentItem, parentItem, true);
});

Expand All @@ -246,7 +273,7 @@ export class PoTreeViewBaseComponent {
}

private updateItemsOnSelect(selectedItem: PoTreeViewItem) {
if (selectedItem.subItems) {
if (selectedItem.subItems && !this.singleSelect) {
this.selectAllItems(selectedItem.subItems, selectedItem.selected);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
</span>
</button>

<ng-container *ngIf="selectable; then checkboxTemplate; else labelTemplate"></ng-container>
<ng-container
*ngIf="selectable && item.isSelectable !== false; then selectionTemplate; else labelTemplate"
></ng-container>
</div>

<ng-template #labelTemplate>
Expand All @@ -16,6 +18,10 @@
</span>
</ng-template>

<ng-template #selectionTemplate>
<ng-container *ngIf="singleSelect; then radioTemplate; else checkboxTemplate"></ng-container>
</ng-template>

<ng-template #checkboxTemplate>
<po-checkbox
class="po-tree-view-item-header-checkbox"
Expand All @@ -26,3 +32,18 @@
>
</po-checkbox>
</ng-template>

<ng-template #radioTemplate>
<po-radio
class="po-tree-view-item-header-checkbox"
[class.po-tree-view-item-header-padding]="!hasSubItems"
#inputRadio
[name]="idRadio"
[(ngModel)]="item.selected"
[p-label]="item.label"
[p-value]="item.value"
[p-checked]="item.value === selectedValue"
(p-change-selected)="selected.emit(item)"
>
</po-radio>
</ng-template>
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, ViewChild } from '@angular/core';
import { uuid } from '../../../utils/util';

import { PoTreeViewItem } from '../po-tree-view-item/po-tree-view-item.interface';

Expand All @@ -14,10 +15,16 @@ export class PoTreeViewItemHeaderComponent {

@Input('p-selectable') selectable: boolean = false;

@Input('p-single-select') singleSelect: boolean;

@Output('p-expanded') expanded = new EventEmitter<MouseEvent>();

@Output('p-selected') selected = new EventEmitter<any>();

@Input('p-selected-value') selectedValue: string | number;

idRadio = `po-radio[${uuid()}]`;

get hasSubItems() {
return !!(this.item.subItems && this.item.subItems.length);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<po-tree-view-item-header
[p-item]="item"
[p-selectable]="selectable"
[p-single-select]="singleSelect"
[p-selected-value]="selectedValue"
(p-expanded)="onClick($event)"
(p-selected)="onSelect(item)"
>
Expand All @@ -12,6 +14,8 @@
*ngFor="let subItem of item.subItems; trackBy: trackByFunction"
[p-item]="subItem"
[p-selectable]="selectable"
[p-single-select]="singleSelect"
[p-selected-value]="selectedValue"
>
</po-tree-view-item>
</ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ export class PoTreeViewItemComponent {

@Input('p-selectable') selectable: boolean;

@Input('p-single-select') singleSelect: boolean;

@Input('p-selected-value') selectedValue: string | number;

get hasSubItems() {
return !!(this.item.subItems && this.item.subItems.length);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ export interface PoTreeViewItem {
*/
selected?: boolean | null;

/**
* Permite ativar/desativar a seleção do item
*/
isSelectable?: boolean | null;

/** Lista de itens do próximo nível, e assim consecutivamente até que se atinja o quarto nível. */
subItems?: Array<PoTreeViewItem>;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
<po-container *ngIf="hasItems" p-no-padding>
<ul class="po-tree-view">
<po-tree-view-item *ngFor="let item of items; trackBy: trackByFunction" [p-item]="item" [p-selectable]="selectable">
<po-tree-view-item
*ngFor="let item of items; trackBy: trackByFunction"
[p-item]="item"
[p-selectable]="selectable"
[p-single-select]="singleSelect"
[p-selected-value]="selectedValue"
>
</po-tree-view-item>
</ul>
</po-container>
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
(p-selected)="changeEvent('p-selected', $event)"
(p-unselected)="changeEvent('p-unselected', $event)"
[p-max-level]="maxLevel"
[p-single-select]="singleSelect"
>
</po-tree-view>

Expand All @@ -22,7 +23,14 @@
<po-divider p-label="Po Tree View Config"></po-divider>

<div class="po-row">
<po-input class="po-md-4" name="level" [(ngModel)]="maxLevel" p-label="Max Level"> </po-input>
<po-input class="po-md-4" name="level" [(ngModel)]="maxLevel" p-label="Nível máximo"> </po-input>
<po-switch
class="po-md-4"
name="singleSelect"
[(ngModel)]="singleSelect"
p-label="Selecionar somente um item na árvore"
>
</po-switch>
</div>

<po-divider p-label="Po Tree View Item"></po-divider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ export class SamplePoTreeViewLabsComponent implements OnInit {
selectable: boolean;
treeViewItem: PoTreeViewItem;
maxLevel: number = 4;
singleSelect: boolean = false;

readonly itemPropertiesOptions: Array<PoCheckboxGroupOption> = [
{ value: 'selected', label: 'Selected' },
{ value: 'expanded', label: 'Expanded' }
{ value: 'expanded', label: 'Expanded' },
{ value: 'disable-selection', label: 'Disable Selection' }
];

ngOnInit() {
Expand All @@ -28,6 +30,7 @@ export class SamplePoTreeViewLabsComponent implements OnInit {
add(treeViewItem: PoTreeViewItem) {
treeViewItem.selected = this.itemProperties.includes('selected');
treeViewItem.expanded = this.itemProperties.includes('expanded');
treeViewItem.isSelectable = !this.itemProperties.includes('disable-selection');

const treeViewItemClone = { ...treeViewItem };

Expand Down

0 comments on commit a9538f0

Please sign in to comment.