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
3 changes: 3 additions & 0 deletions projects/ui/src/lib/components/po-icon/po-icon-dictionary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,11 @@ export const AnimaliaIconDictionary: { [key: string]: string } = {
ICON_ALIGN_RIGHT: 'an an-text-align-right',
ICON_ARROW_ARC_LEFT: 'an an-arrow-arc-left',
ICON_ARROW_DOWN: 'an an-caret-down',
ICON_OTHER_ARROW_DOWN: 'an an-arrow-down',
ICON_ARROW_LEFT: 'an an-caret-left',
ICON_ARROW_RIGHT: 'an an-caret-right',
ICON_ARROW_UP: 'an an-caret-up',
ICON_OTHER_ARROW_UP: 'an an-arrow-up',
ICON_CALENDAR: 'an an-calendar-blank',
ICON_CLEAR_CONTENT: 'an an-x-circle',
ICON_CLOCK: 'an an-clock',
Expand All @@ -205,6 +207,7 @@ export const AnimaliaIconDictionary: { [key: string]: string } = {
ICON_EYE: 'an an-eye',
ICON_EYE_OFF: 'an an-eye-closed',
ICON_FILTER: 'an an-funnel',
ICON_FUNNEL: 'an an-funnel-simple',
ICON_HELP: 'an an-question',
ICON_INFO: 'an an-info',
ICON_LAST_PAGE: 'an an-caret-double-right',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@if (class) {
<i [class]="class" aria-hidden="true">
<i #iconElement [class]="class" aria-hidden="true">
<ng-content></ng-content>
</i>
} @else {
Expand Down
12 changes: 11 additions & 1 deletion projects/ui/src/lib/components/po-icon/po-icon.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { ChangeDetectionStrategy, Component, Inject, Input, Optional, TemplateRef } from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
ElementRef,
Inject,
Input,
Optional,
TemplateRef,
ViewChild
} from '@angular/core';
import { ICONS_DICTIONARY, AnimaliaIconDictionary } from './po-icon-dictionary';
/**
* @docsPrivate
Expand All @@ -16,6 +25,7 @@ import { ICONS_DICTIONARY, AnimaliaIconDictionary } from './po-icon-dictionary';
standalone: false
})
export class PoIconComponent {
@ViewChild('iconElement', { static: false }) iconElement: ElementRef;
class: string;
private _icon: string | TemplateRef<void>;
private _iconToken: { [key: string]: string };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,15 @@ export class PoItemListBaseComponent {
*/
@Input('p-icon') icon: string | TemplateRef<void>;

/**
* @optional
*
* @description
*
* Define se deve ser exibido o ícone indicando subnível.
*/
@Input('p-icon-arrow-right') iconArrowRight: string;

// Define a posição do ícone: 'left' (padrão) ou 'right'.
@Input('p-icon-position') iconPosition: 'left' | 'right' = 'left';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
[class.po-item-list__danger]="danger"
class="po-item-list po-item-list__action"
>
@if (icon && iconPosition === 'left') {
@if (icon) {
<po-icon
class="po-popup-icon-item po-field-icon"
[class.po-field-icon-aa]="size === 'small'"
Expand All @@ -21,8 +21,8 @@
}
<span class="po-item-list-label">{{ label }}</span>

@if (icon && iconPosition === 'right') {
<po-icon class="po-popup-icon-item-right po-field-icon" [p-icon]="icon"></po-icon>
@if (iconArrowRight) {
<po-icon class="po-popup-icon-item-right po-field-icon" [p-icon]="iconArrowRight"></po-icon>
}
</div>
}
Expand Down
89 changes: 51 additions & 38 deletions projects/ui/src/lib/components/po-listbox/po-listbox.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,14 @@
(click)="onSelectItem(item, $event)"
(keydown)="onKeyDown(item, $event)"
>
@if (item.subItems?.length) {
@if (item.subItems?.length || item.$subItemTemplate) {
<po-item-list
[p-label]="item.label"
[p-item]="item"
[p-separator]="item.separator || separator"
[p-icon]="'po-icon-arrow-right'"
[p-icon-position]="'right'"
[p-icon]="item.icon"
[p-icon-arrow-right]="'ICON_ARROW_RIGHT'"
[p-selected]="isSelectedItem(item) || item.selected"
>
</po-item-list>
} @else if (!item.subItems?.length && returnBooleanValue(item, 'visible') !== false) {
Expand Down Expand Up @@ -176,47 +177,59 @@
[cdkOption]="'back-option'"
[attr.aria-label]="literals?.backToPreviousGroup"
(click)="goBack($event)"
(keydown)="onKeydownGoBack($event)"
(keydown)="onKeydownGoBack($event, currentGroup)"
>
<po-icon class="po-field-icon" [p-icon]="'po-icon-arrow-left'"></po-icon>
<po-icon class="po-field-icon" [p-icon]="'ICON_ARROW_LEFT'"></po-icon>
<po-tag [p-value]="currentGroup.label"></po-tag>
</li>

@for (subItem of currentItems; track subItem.label) {
@if (subItem.subItems?.length) {
<li
[cdkOption]="subItem[fieldLabel]"
(click)="onSelectItem(subItem, $event)"
(keydown)="onKeyDown(subItem, $event)"
>
<po-item-list
[p-label]="subItem.label"
[p-item]="subItem"
[p-separator]="subItem.separator || separator"
[p-icon]="'po-icon-arrow-right'"
[p-icon-position]="'right'"
@if (currentGroup.$subItemTemplate) {
<div
(click)="$event.stopPropagation()"
(keydown)="onKeydownTemplate($event)"
(keydown.space)="$event.stopPropagation()"
>
<ng-container
*ngTemplateOutlet="currentGroup.$subItemTemplate; context: { $implicit: currentGroup }"
></ng-container>
</div>
} @else {
@for (subItem of currentItems; track subItem.label) {
@if (subItem.subItems?.length) {
<li
[cdkOption]="subItem[fieldLabel]"
(click)="onSelectItem(subItem, $event)"
(keydown)="onKeyDown(subItem, $event)"
>
</po-item-list>
</li>
} @else if (!subItem.subItems?.length && returnBooleanValue(subItem, 'visible') !== false) {
<li
[cdkOption]="subItem[fieldLabel]"
(click)="onSelectItem(subItem, $event)"
(keydown)="onKeyDown(subItem, $event)"
>
<po-item-list
class="po-listbox-item-sub"
[p-disabled]="returnBooleanValue(subItem, 'disabled')"
[p-visible]="returnBooleanValue(subItem, 'visible')"
[p-label]="subItem[fieldLabel]"
[p-item]="subItem"
[p-icon]="subItem.icon"
[p-separator]="subItem.separator || separator"
[p-danger]="subItem.type === 'danger'"
[p-selected]="isSelectedItem(subItem) || subItem.selected"
<po-item-list
[p-label]="subItem.label"
[p-item]="subItem"
[p-separator]="subItem.separator || separator"
[p-icon]="'ICON_ARROW_RIGHT'"
[p-icon-position]="'right'"
>
</po-item-list>
</li>
} @else if (!subItem.subItems?.length && returnBooleanValue(subItem, 'visible') !== false) {
<li
[cdkOption]="subItem[fieldLabel]"
(click)="onSelectItem(subItem, $event)"
(keydown)="onKeyDown(subItem, $event)"
>
</po-item-list>
</li>
<po-item-list
class="po-listbox-item-sub"
[p-disabled]="returnBooleanValue(subItem, 'disabled')"
[p-visible]="returnBooleanValue(subItem, 'visible')"
[p-label]="subItem[fieldLabel]"
[p-item]="subItem"
[p-icon]="subItem.icon"
[p-separator]="subItem.separator || separator"
[p-danger]="subItem.type === 'danger'"
[p-selected]="isSelectedItem(subItem) || subItem.selected"
>
</po-item-list>
</li>
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,56 @@ describe('PoListBoxComponent', () => {
expect((component as any).navigationStack.length).toBe(2);
});

it('onKeydownTemplate: should emit closeEvent if press Tab out .po-listbox-dropdown', () => {
const emitSpy = spyOn(component.closeEvent, 'emit');
const stopSpy = jasmine.createSpy('stopPropagation');

const event = {
code: 'Tab',
target: document.createElement('div'),
stopPropagation: stopSpy
} as unknown as KeyboardEvent;

component['onKeydownTemplate'](event);

expect(stopSpy).not.toHaveBeenCalled();
expect(emitSpy).toHaveBeenCalled();
});

it('onKeydownTemplate: should call stopPropagation and not emit closeEvent when press Tab in .po-listbox-dropdown', () => {
const emitSpy = spyOn(component.closeEvent, 'emit');
const stopSpy = jasmine.createSpy('stopPropagation');

const dropdown = document.createElement('div');
dropdown.classList.add('po-listbox-dropdown');

const child = document.createElement('div');
dropdown.appendChild(child);
document.body.appendChild(dropdown);

const event = {
code: 'Tab',
target: child,
stopPropagation: stopSpy
} as unknown as KeyboardEvent;

component['onKeydownTemplate'](event);

expect(stopSpy).toHaveBeenCalled();
expect(emitSpy).not.toHaveBeenCalled();

dropdown.remove();
});

it('onKeydownGoBack: should not emit closeEvent if subItemTemplate is true', () => {
const eventTab = new KeyboardEvent('keydown', { code: 'Tab' });

spyOn(component.closeEvent, 'emit');

component.onKeydownGoBack(eventTab, { label: 'item', $subItemTemplate: true as any });
expect(component.closeEvent.emit).not.toHaveBeenCalled();
});

it('onKeydownGoBack should call goBack on Enter, emit closeEvent on Escape or Tab', () => {
const eventEnter = new KeyboardEvent('keydown', { key: 'Enter' });
const eventEscape = new KeyboardEvent('keydown', { code: 'Escape' });
Expand Down
17 changes: 15 additions & 2 deletions projects/ui/src/lib/components/po-listbox/po-listbox.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,25 @@ export class PoListBoxComponent extends PoListBoxBaseComponent implements OnInit
});
}

public onKeydownGoBack(event: KeyboardEvent): void {
public onKeydownGoBack(event: KeyboardEvent, currentGroup?: PoDropdownAction): void {
if (event.key === 'Enter') {
this.goBack(event);
}

if (event?.code === 'Escape' || event.code === 'Tab') {
if (event.code === 'Tab' && !event.shiftKey && currentGroup?.$subItemTemplate) {
return;
}
this.closeEvent.emit();
}
}

protected onKeydownTemplate(event: KeyboardEvent): void {
if (event.code === 'Tab') {
if ((event.target as HTMLElement)?.closest('.po-listbox-dropdown')) {
event.stopPropagation();
return;
}
this.closeEvent.emit();
}
}
Expand Down Expand Up @@ -184,7 +197,7 @@ export class PoListBoxComponent extends PoListBoxBaseComponent implements OnInit
return this.openUrl(itemListAction.url);
}

if (itemListAction?.subItems?.length) {
if (itemListAction?.subItems?.length || itemListAction?.$subItemTemplate) {
this.openGroup(itemListAction, event);
} else if (this.listboxSubitems) {
this.closeEvent.emit();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,7 @@ export interface PoPopupAction {

// id interno
$id?: string;

// template interno
$subItemTemplate?: TemplateRef<any>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ export class PoPopupBaseComponent {
// Indica se há um listbox com subitens
@Input('p-listbox-subitems') listboxSubitems = false;

// template-icon
@Input('p-template-icon') templateIcon = false;

/** Lista de ações que serão exibidas no componente. */
@Input('p-actions') set actions(value: Array<PoPopupAction>) {
this._actions = Array.isArray(value) ? value : [];
Expand Down
12 changes: 12 additions & 0 deletions projects/ui/src/lib/components/po-popup/po-popup.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,18 @@ describe('PoPopupComponent:', () => {
event = { target: {} };
});

it('ngAfterViewInit: should set target if templateIcon is true', () => {
component.templateIcon = true;
component.target = {
iconElement: {
nativeElement: 'test'
}
};
component.ngAfterViewInit();

expect(component.target).toBe('test');
});

it('clickoutListener: should call `closePopupOnClickout` on click in document', () => {
component.open();
fixture.detectChanges();
Expand Down
24 changes: 12 additions & 12 deletions projects/ui/src/lib/components/po-popup/po-popup.component.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
import {
ChangeDetectorRef,
Component,
ElementRef,
Renderer2,
ViewChild,
ViewContainerRef,
inject
} from '@angular/core';
import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, Renderer2, ViewChild, inject } from '@angular/core';
import { Router } from '@angular/router';

import { PoControlPositionService } from '../../services/po-control-position/po-control-position.service';
import { isExternalLink, isTypeof, openExternalLink } from '../../utils/util';
import { isExternalLink, isTypeof, openExternalLink, uuid } from '../../utils/util';

import { PoListBoxComponent } from '../po-listbox';
import { PoPopupAction } from './po-popup-action.interface';
Expand Down Expand Up @@ -44,7 +36,8 @@ import { PoPopupBaseComponent } from './po-popup-base.component';
providers: [PoControlPositionService],
standalone: false
})
export class PoPopupComponent extends PoPopupBaseComponent {
export class PoPopupComponent extends PoPopupBaseComponent implements AfterViewInit {
id = `po-popup[${uuid()}]`;
private renderer = inject(Renderer2);
private router = inject(Router);
private poControlPosition = inject(PoControlPositionService);
Expand All @@ -56,6 +49,12 @@ export class PoPopupComponent extends PoPopupBaseComponent {
//utilizado apenas no theme builder
@ViewChild('poListBoxRef') poListBoxRef: PoListBoxComponent;

ngAfterViewInit() {
if (this.templateIcon && this.target) {
this.target = this.target?.iconElement?.nativeElement;
}
}

/**
* Fecha o componente *popup*.
*
Expand Down Expand Up @@ -92,6 +91,7 @@ export class PoPopupComponent extends PoPopupBaseComponent {
this.param = param;
this.showPopup = true;
this.changeDetector.detectChanges();

this.validateInitialContent();
}

Expand All @@ -117,7 +117,7 @@ export class PoPopupComponent extends PoPopupBaseComponent {
this.clickItem.emit(item);
}

if (item.subItems || item.goBack) {
if (item.subItems || item.$subItemTemplate || item.goBack) {
this.changeDetector.detectChanges();
this.validateInitialContent();
}
Expand Down