Skip to content

Commit c6ae123

Browse files
devin-ai-integration[bot]anabye
authored andcommitted
feat(page): adiciona tipos de header e layout de ações
Inclui propriedades `p-header-type` e `p-actions-layout`. Adiciona propriedade `kind` em `PoPageAction`. fixes DTHFUI-12541
1 parent 6de0ea2 commit c6ae123

19 files changed

Lines changed: 923 additions & 177 deletions

projects/portal/src/styles.css

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,6 @@ html.potheme-light-AAA #logo-totvs {
1919
content: url('./assets/graphics/logo-totvs-preto.png');
2020
}
2121

22-
html.potheme-dark-AA .po-header-nav,
23-
html.potheme-dark-AAA .po-header-nav {
24-
--stroke-color: var(--color-brand-01-dark);
25-
}
26-
2722
.dot {
2823
border-radius: 50%;
2924
display: inline-block;

projects/ui/src/lib/components/po-dropdown/po-dropdown-base.component.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { Directive, HostBinding, HostListener, Input } from '@angular/core';
22

33
import { convertToBoolean, getDefaultSizeFn, validateSizeFn } from './../../utils/util';
4+
import { PO_CONTROL_POSITIONS } from './../../services/po-control-position/po-control-position.constants';
45

56
import { PoFieldSize } from '../../enums/po-field-size.enum';
67
import { PoDropdownAction } from './po-dropdown-action.interface';
78

9+
const poDropdownDefaultPosition = 'bottom-left';
10+
811
/**
912
* @description
1013
*
@@ -77,6 +80,7 @@ export class PoDropdownBaseComponent {
7780
private _disabled: boolean = false;
7881
private _size?: string = undefined;
7982
private _initialSize?: string = undefined;
83+
private _position: string = poDropdownDefaultPosition;
8084

8185
/** Lista de ações que serão exibidas no componente. */
8286
@Input('p-actions') set actions(value: Array<PoDropdownAction>) {
@@ -129,6 +133,52 @@ export class PoDropdownBaseComponent {
129133
return this._size ?? getDefaultSizeFn(PoFieldSize);
130134
}
131135

136+
/**
137+
* @optional
138+
*
139+
* @description
140+
*
141+
* Define a posição preferencial de abertura do popup do dropdown em relação ao botão.
142+
*
143+
* Posições válidas:
144+
* - `right`: No lado direito.
145+
* - `right-bottom`: No lado direito inferior.
146+
* - `right-top`: No lado direito superior.
147+
* - `bottom`: Abaixo.
148+
* - `bottom-left`: Abaixo e à esquerda (padrão).
149+
* - `bottom-right`: Abaixo e à direita.
150+
* - `left`: No lado esquerdo.
151+
* - `left-top`: No lado esquerdo superior.
152+
* - `left-bottom`: No lado esquerdo inferior.
153+
* - `top`: Acima.
154+
* - `top-right`: Acima e à direita.
155+
* - `top-left`: Acima e à esquerda.
156+
*
157+
* > O popup será rotacionado automaticamente caso não caiba na posição definida.
158+
*
159+
* @default `bottom-left`
160+
*/
161+
@Input('p-position') set position(value: string) {
162+
this._position = PO_CONTROL_POSITIONS.includes(value) ? value : poDropdownDefaultPosition;
163+
}
164+
165+
get position(): string {
166+
return this._position;
167+
}
168+
169+
get popupCustomPositions(): Array<string> {
170+
if (this._position === 'bottom-right') {
171+
return ['bottom-right', 'top-right'];
172+
}
173+
if (this._position === 'top-left') {
174+
return ['top-left', 'bottom-left'];
175+
}
176+
if (this._position === 'top-right') {
177+
return ['top-right', 'bottom-right'];
178+
}
179+
return ['bottom-left', 'top-left'];
180+
}
181+
132182
@HostListener('window:PoUiThemeChange')
133183
protected onThemeChange(): void {
134184
this.applySizeBasedOnA11y();

projects/ui/src/lib/components/po-dropdown/po-dropdown-base.componente.spec.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,5 +98,44 @@ describe('PoDropdownBaseComponent:', () => {
9898
expect((component as any).applySizeBasedOnA11y).toHaveBeenCalled();
9999
});
100100
});
101+
102+
describe('p-position:', () => {
103+
it('should set position to valid value', () => {
104+
component.position = 'bottom-right';
105+
expect(component.position).toBe('bottom-right');
106+
});
107+
108+
it('should default to bottom-left with invalid value', () => {
109+
component.position = 'invalid';
110+
expect(component.position).toBe('bottom-left');
111+
});
112+
113+
it('should default to bottom-left with empty string', () => {
114+
component.position = '';
115+
expect(component.position).toBe('bottom-left');
116+
});
117+
});
118+
119+
describe('popupCustomPositions:', () => {
120+
it('should return bottom-right and top-right when position is bottom-right', () => {
121+
component.position = 'bottom-right';
122+
expect(component.popupCustomPositions).toEqual(['bottom-right', 'top-right']);
123+
});
124+
125+
it('should return top-left and bottom-left when position is top-left', () => {
126+
component.position = 'top-left';
127+
expect(component.popupCustomPositions).toEqual(['top-left', 'bottom-left']);
128+
});
129+
130+
it('should return top-right and bottom-right when position is top-right', () => {
131+
component.position = 'top-right';
132+
expect(component.popupCustomPositions).toEqual(['top-right', 'bottom-right']);
133+
});
134+
135+
it('should return bottom-left and top-left as default', () => {
136+
component.position = 'bottom-left';
137+
expect(component.popupCustomPositions).toEqual(['bottom-left', 'top-left']);
138+
});
139+
});
101140
});
102141
});

projects/ui/src/lib/components/po-dropdown/po-dropdown.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
#popupRef
2020
p-hide-arrow
2121
p-is-corner-align
22-
p-position="bottom-left"
22+
[p-position]="position"
2323
[p-actions]="actions"
24-
[p-custom-positions]="['bottom-left', 'top-left']"
24+
[p-custom-positions]="popupCustomPositions"
2525
[p-size]="size"
2626
[p-target]="dropdownRef"
2727
[p-listbox-subitems]="true"

projects/ui/src/lib/components/po-icon/po-icon-dictionary.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export const AnimaliaIconDictionary: { [key: string]: string } = {
66
ICON_ALIGN_LEFT: 'an an-text-align-left',
77
ICON_ALIGN_RIGHT: 'an an-text-align-right',
88
ICON_ARROW_ARC_LEFT: 'an an-arrow-arc-left',
9+
ICON_ARROW_BACK: 'an an-arrow-left',
910
ICON_ARROW_DOWN: 'an an-caret-down',
1011
ICON_OTHER_ARROW_DOWN: 'an an-arrow-down',
1112
ICON_ARROW_LEFT: 'an an-caret-left',

projects/ui/src/lib/components/po-page/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
export * from './po-page-default/enums/po-page-actions-layout.enum';
2+
export * from './po-page-default/enums/po-page-header-type.enum';
13
export * from './po-page-default/po-page-default.component';
24
export * from './po-page-default/po-page-default.interface';
35
export * from './po-page-default/po-page-default-literals.interface';

projects/ui/src/lib/components/po-page/interfaces/po-page-action.interface.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,38 @@ import { PoDropdownAction } from '../../po-dropdown';
22

33
/**
44
* @description
5-
* Interface para as ações dos componentes po-page-default e po-page-list.
65
*
7-
* > Quando o array de actions possui quatro ou mais registros, os dois últimos e os seguintes são automaticamente agrupados no po-dropdown.
8-
* A partir desse ponto, as propriedades `selected`, `separator`, `type` e `subItems` passam a ter efeito apenas nas ações exibidas dentro do dropdown, ou seja, a partir da terceira ação.
9-
* Dessa forma, o uso de subItems (agrupadores dentro do dropdown) só terá efeito quando houver pelo menos quatro ações definidas.
6+
* Interface para as ações dos componentes `po-page-default` e `po-page-list`.
107
*
11-
* @docsExtends PoDropdownAction
8+
* As ações são exibidas como botões no cabeçalho e, caso excedam o limite de exibição ou o layout
9+
* seja configurado para tal, são agrupadas automaticamente em um *dropdown*.
10+
*
11+
* > As propriedades `separator`, `selected` e `subItems` possuem efeito apenas quando
12+
* a ação é exibida dentro do *dropdown*.
1213
*
1314
* @ignoreExtendedDescription
1415
*
1516
* @usedBy PoPageDefaultComponent, PoPageListComponent
1617
*/
17-
export interface PoPageAction extends PoDropdownAction {}
18+
export interface PoPageAction extends PoDropdownAction {
19+
/**
20+
* @description
21+
*
22+
* Define o estilo visual da ação quando ela é exibida como botão fora do *dropdown*.
23+
*
24+
* Valores permitidos:
25+
* - `primary`: Botão com maior destaque visual.
26+
* - `secondary`: Estilo padrão para a maioria das ações.
27+
*
28+
* > Valores inválidos são ignorados, mantendo o valor padrão da posição da ação.
29+
*
30+
* > Aplicável apenas a ações exibidas como botões (fora do *dropdown*). Ações dentro do *dropdown* não utilizam esta propriedade.
31+
*
32+
* > Funciona independentemente da posição da ação e com qualquer `PoPageHeaderType` ou `PoPageActionsLayout`.
33+
*
34+
* **Valores padrão por posição (quando `kind` não é definido):**
35+
* - Layout `default`: primeira ação = `primary`, demais = `secondary`.
36+
* - Layout `mixed`: primeira ação = `primary` (header primary) ou `secondary` (header secondary/tertiary).
37+
*/
38+
kind?: string;
39+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* @usedBy PoPageDefaultComponent
3+
*
4+
* @description
5+
*
6+
* Enum que define os layouts de ações disponíveis no `po-page-default`.
7+
*
8+
* > Compatível com todos os valores de `PoPageHeaderType` (`primary`, `secondary` e `tertiary`).
9+
*
10+
* Define os layouts de exibição das ações no cabeçalho.
11+
*/
12+
export enum PoPageActionsLayout {
13+
/**
14+
* Comportamento padrão: as ações são exibidas como botões (até 3 em desktop e 2 em mobile)
15+
* e as demais são agrupadas no *dropdown*.
16+
*/
17+
default = 'default',
18+
19+
/** Todas as ações são agrupadas exclusivamente dentro do menu *dropdown*. */
20+
dropdown = 'dropdown',
21+
22+
/**
23+
* A primeira ação é exibida como um botão de destaque e todas as demais são movidas para o *dropdown*.
24+
*/
25+
mixed = 'mixed'
26+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* @usedBy PoPageDefaultComponent
3+
*
4+
* @description
5+
*
6+
* Define os tipos de cabeçalho disponíveis, alterando a hierarquia visual e os elementos de navegação.
7+
*/
8+
export enum PoPageHeaderType {
9+
/**
10+
* Layout padrão que permite o uso de `p-breadcrumb`. As ações seguem o estilo definido
11+
* em suas propriedades individuais (padrão: `primary` para a primeira ação).
12+
*/
13+
primary = 'primary',
14+
15+
/**
16+
* Exibe um botão de retorno (voltar) ao lado do título e oculta o `p-breadcrumb`.
17+
* Por padrão, as ações assumem o estilo `secondary`.
18+
*/
19+
secondary = 'secondary',
20+
21+
/**
22+
* Layout simplificado sem botões de navegação e sem `p-breadcrumb`.
23+
* Assim como no tipo `secondary`, as ações assumem o estilo `secondary` por padrão.
24+
*/
25+
tertiary = 'tertiary'
26+
}

projects/ui/src/lib/components/po-page/po-page-default/po-page-default-base.component.spec.ts

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ describe('PoPageDefaultBaseComponent:', () => {
1919
let languageService: PoLanguageService;
2020
let component: PoPageDefaultComponent;
2121

22-
beforeEach(() => {
22+
beforeEach(async () => {
23+
await TestBed.configureTestingModule({}).compileComponents();
24+
2325
languageService = new PoLanguageService();
2426

25-
component = new PoPageDefaultComponent(languageService);
27+
component = TestBed.runInInjectionContext(() => new PoPageDefaultComponent(languageService));
2628
});
2729

2830
it('should be created', () => {
@@ -118,6 +120,58 @@ describe('PoPageDefaultBaseComponent:', () => {
118120
expectPropertiesValues(component, 'actions', validValues, validValues);
119121
});
120122

123+
describe('p-page-header-type:', () => {
124+
it('should set `pageHeaderType` to `primary` when receiving `primary`.', () => {
125+
component.pageHeaderType = 'primary';
126+
expect(component.pageHeaderType).toBe('primary');
127+
});
128+
129+
it('should set `pageHeaderType` to `secondary` when receiving `secondary`.', () => {
130+
component.pageHeaderType = 'secondary';
131+
expect(component.pageHeaderType).toBe('secondary');
132+
});
133+
134+
it('should set `pageHeaderType` to `tertiary` when receiving `tertiary`.', () => {
135+
component.pageHeaderType = 'tertiary';
136+
expect(component.pageHeaderType).toBe('tertiary');
137+
});
138+
139+
it('should default `pageHeaderType` to `primary` with invalid values.', () => {
140+
const invalidValues = ['invalid', '', null, undefined, 'other'];
141+
142+
invalidValues.forEach(value => {
143+
component.pageHeaderType = value;
144+
expect(component.pageHeaderType).toBe('primary');
145+
});
146+
});
147+
});
148+
149+
describe('p-page-actions-layout:', () => {
150+
it('should set `pageActionsLayout` to `default` when receiving `default`.', () => {
151+
component.pageActionsLayout = 'default';
152+
expect(component.pageActionsLayout).toBe('default');
153+
});
154+
155+
it('should set `pageActionsLayout` to `dropdown` when receiving `dropdown`.', () => {
156+
component.pageActionsLayout = 'dropdown';
157+
expect(component.pageActionsLayout).toBe('dropdown');
158+
});
159+
160+
it('should set `pageActionsLayout` to `mixed` when receiving `mixed`.', () => {
161+
component.pageActionsLayout = 'mixed';
162+
expect(component.pageActionsLayout).toBe('mixed');
163+
});
164+
165+
it('should default `pageActionsLayout` to `default` with invalid values.', () => {
166+
const invalidValues = ['invalid', '', null, undefined, 'other'];
167+
168+
invalidValues.forEach(value => {
169+
component.pageActionsLayout = value;
170+
expect(component.pageActionsLayout).toBe('default');
171+
});
172+
});
173+
});
174+
121175
describe('p-components-size', () => {
122176
beforeEach(() => {
123177
document.documentElement.removeAttribute('data-a11y');

0 commit comments

Comments
 (0)