From 5ed825378ddf13097d7e0ed476beeb4c6d210798 Mon Sep 17 00:00:00 2001 From: renanarosario Date: Tue, 3 Dec 2024 17:43:56 -0300 Subject: [PATCH] feat(dynamic-table): cria nova propriedade visibleFixedFilters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Foi criado a nova propriedade para visibleFixedFilters opcional do tipo boolean com valor default 'true' que permite exibir ou false para não exibir o filtro fixo nos disclaimer Fixes dynamic-table/DTHFUI-7112 --- ...page-dynamic-search-base.component.spec.ts | 22 +++ .../po-page-dynamic-search-base.component.ts | 40 ++++- ...o-page-dynamic-search-filters.interface.ts | 5 + .../po-page-dynamic-search.component.spec.ts | 163 ++++++++++++------ .../po-page-dynamic-search.component.ts | 78 ++++++--- .../po-page-dynamic-table.component.html | 1 + .../po-page-dynamic-table.component.spec.ts | 12 ++ .../po-page-dynamic-table.component.ts | 20 +++ 8 files changed, 262 insertions(+), 79 deletions(-) diff --git a/projects/templates/src/lib/components/po-page-dynamic-search/po-page-dynamic-search-base.component.spec.ts b/projects/templates/src/lib/components/po-page-dynamic-search/po-page-dynamic-search-base.component.spec.ts index e40457f077..8d650644f9 100644 --- a/projects/templates/src/lib/components/po-page-dynamic-search/po-page-dynamic-search-base.component.spec.ts +++ b/projects/templates/src/lib/components/po-page-dynamic-search/po-page-dynamic-search-base.component.spec.ts @@ -183,4 +183,26 @@ describe('PoPageDynamicSearchBaseComponent:', () => { expect(component.advancedFilterLiterals).toEqual(expectedValue); }); }); + + describe('p-visible-fixed-filters:', () => { + it('should update `_visibleFixedFilters` when `visibleFixedFilters` is set', () => { + component.visibleFixedFilters = true; + expect(component['_visibleFixedFilters']).toBe(true); + + component.visibleFixedFilters = false; + expect(component['_visibleFixedFilters']).toBe(false); + }); + + it('should return `_visibleFixedFilters` when `visibleFixedFilters` is accessed', () => { + component['_visibleFixedFilters'] = true; + expect(component.visibleFixedFilters).toBe(true); + + component['_visibleFixedFilters'] = false; + expect(component.visibleFixedFilters).toBe(false); + }); + + it('should have a default value of `true` for `visibleFixedFilters` if not set', () => { + expect(component.visibleFixedFilters).toBeTrue(); + }); + }); }); diff --git a/projects/templates/src/lib/components/po-page-dynamic-search/po-page-dynamic-search-base.component.ts b/projects/templates/src/lib/components/po-page-dynamic-search/po-page-dynamic-search-base.component.ts index 6c03109119..8da5688e4e 100644 --- a/projects/templates/src/lib/components/po-page-dynamic-search/po-page-dynamic-search-base.component.ts +++ b/projects/templates/src/lib/components/po-page-dynamic-search/po-page-dynamic-search-base.component.ts @@ -154,6 +154,34 @@ export abstract class PoPageDynamicSearchBaseComponent { */ @Input('p-quick-search-value') quickSearchValue: string; + _visibleFixedFilters = true; + + /** + * @optional + * + * @description + * + * Controla a visibilidade dos filtros fixos na página. + * + * - Quando `true` (default), todos os filtros, incluindo os fixos, são exibidos, permitindo que o usuário visualize os filtros aplicados. + * - Quando `false`, os filtros fixos são ocultados, não sendo exibidos na interface, mas ainda sendo aplicados como filtros nas requisições. + * + * Esta propriedade trabalha em conjunto com a propriedade `fixed` dos filtros individuais. Filtros marcados como `fixed: true` não serão exibidos na interface do filtro avançado quando `visibleFixedFilters` for `false`, mas continuarão a ser aplicados de forma transparente ao usuário. Dessa forma, permite-se maior flexibilidade no controle de quais filtros devem ser visíveis ao usuário ou devem ser aplicados permanentemente sem interferência. + * + * **Exemplo de uso:** + * ```html + * + * + * ``` + */ + @Input('p-visible-fixed-filters') set visibleFixedFilters(visible: boolean) { + this._visibleFixedFilters = visible; + } + + get visibleFixedFilters(): boolean { + return this._visibleFixedFilters; + } + /** * @optional * @@ -189,10 +217,10 @@ export abstract class PoPageDynamicSearchBaseComponent { private _hideCloseDisclaimers: Array = []; private _literals: PoPageDynamicSearchLiterals; private _quickSearchWidth: number; - - private previousFilters: Array; private language: string; + previousFilters: Array; + /** * @optional * @@ -258,12 +286,6 @@ export abstract class PoPageDynamicSearchBaseComponent { */ @Input('p-filters') set filters(filters: Array) { this._filters = Array.isArray(filters) ? [...filters] : []; - - if (this.stringify(this._filters) !== this.stringify(this.previousFilters)) { - this.onChangeFilters(this.filters); - - this.previousFilters = [...this._filters]; - } } get filters(): Array { @@ -320,7 +342,7 @@ export abstract class PoPageDynamicSearchBaseComponent { }; } - private stringify(columns: Array) { + stringify(columns: Array) { // não faz o stringify da propriedade searchService, pois pode conter objeto complexo e disparar um erro. return JSON.stringify(columns, (key, value) => { if (key !== 'searchService') { diff --git a/projects/templates/src/lib/components/po-page-dynamic-search/po-page-dynamic-search-filters.interface.ts b/projects/templates/src/lib/components/po-page-dynamic-search/po-page-dynamic-search-filters.interface.ts index 8b7bc0dd15..e825ff9e14 100644 --- a/projects/templates/src/lib/components/po-page-dynamic-search/po-page-dynamic-search-filters.interface.ts +++ b/projects/templates/src/lib/components/po-page-dynamic-search/po-page-dynamic-search-filters.interface.ts @@ -12,4 +12,9 @@ export interface PoPageDynamicSearchFilters extends PoDynamicFormField { * Define um valor inicial para um filtro de busca avançada. */ initValue?: any; + + /** + * Define um valor fixed para um filtro de busca avançada. + */ + fixed?: boolean; } diff --git a/projects/templates/src/lib/components/po-page-dynamic-search/po-page-dynamic-search.component.spec.ts b/projects/templates/src/lib/components/po-page-dynamic-search/po-page-dynamic-search.component.spec.ts index a93c5d5b5b..a12e553c43 100644 --- a/projects/templates/src/lib/components/po-page-dynamic-search/po-page-dynamic-search.component.spec.ts +++ b/projects/templates/src/lib/components/po-page-dynamic-search/po-page-dynamic-search.component.spec.ts @@ -218,17 +218,51 @@ describe('PoPageDynamicSearchComponent:', () => { const filter = { property: 'value1' }; const optionsService = undefined; + const visibleFilters = + component.visibleFixedFilters === false + ? component.filters.filter(filter => !('fixed' in filter) || !filter.fixed) + : component.filters; + spyOn(component, 'setDisclaimers'); spyOn(component.advancedSearch, 'emit'); spyOn(component, 'setFilters'); component.onAdvancedSearch({ filter, optionsService }); - expect(component['setDisclaimers']).toHaveBeenCalledWith(filter, optionsService); + expect(component['setDisclaimers']).toHaveBeenCalledWith(filter, optionsService, visibleFilters); expect(component['setFilters']).toHaveBeenCalledBefore(component.advancedSearch.emit); expect(component.advancedSearch.emit).toHaveBeenCalledWith(filter); }); + it('onAdvancedSearch: should correctly filter out fixed filters when visibleFixedFilters is false', () => { + component.visibleFixedFilters = false; + component.filters = [ + { property: 'city', fixed: true, initValue: 'Toronto' }, + { property: 'name', fixed: false, initValue: 'John Doe' }, + { property: 'country', initValue: 'Canada' } + ]; + + const filteredItems = { filter: { city: 'Toronto' }, optionsService: undefined }; + + const setDisclaimersSpy = spyOn(component as any, 'setDisclaimers').and.callThrough(); + spyOn(component, 'setFilters').and.callThrough(); + spyOn(component.advancedSearch, 'emit'); + + component.onAdvancedSearch(filteredItems); + + const actualVisibleFilters = setDisclaimersSpy.calls.mostRecent().args[2] as Array; + + expect(actualVisibleFilters.length).toBe(2); + expect(actualVisibleFilters).toEqual( + jasmine.arrayContaining([ + jasmine.objectContaining({ property: 'name', fixed: false }), + jasmine.objectContaining({ property: 'country' }) + ]) + ); + expect(component['setFilters']).toHaveBeenCalledWith(filteredItems.filter); + expect(component.advancedSearch.emit).toHaveBeenCalledWith(filteredItems.filter); + }); + it(`setFilters: should call 'convertToFilters'`, () => { const filters = [{ property: 'value1' }]; @@ -340,9 +374,40 @@ describe('PoPageDynamicSearchComponent:', () => { expect(component.changeDisclaimers.emit).toHaveBeenCalledWith(currentDisclaimers); }); - it(`onRemoveAllDisclaimers: should call 'changeDisclaimers.emit' if all disclaimers are removed`, () => { + it(`onRemoveAllDisclaimers: should call 'changeDisclaimers.emit' with disclaimers that need to be kept`, () => { + component.filters = [ + { property: 'status', fixed: true, initValue: 'Ativo', label: 'Status' }, + { property: 'category', fixed: true, initValue: 'Financeiro', label: 'Categoria' }, + { property: 'name', fixed: false } + ]; + const expectedDisclaimersToKeep = [ + { + property: 'status', + value: 'Ativo', + label: 'Status: Ativo', + hideClose: true + }, + { + property: 'category', + value: 'Financeiro', + label: 'Categoria: Financeiro', + hideClose: true + } + ]; + spyOn(component.changeDisclaimers, 'emit'); + component['onRemoveAllDisclaimers'](); + + expect(component.changeDisclaimers.emit).toHaveBeenCalledWith(expectedDisclaimersToKeep); + }); + it(`onRemoveAllDisclaimers: should call 'changeDisclaimers.emit' with an empty array if there are no fixed filters`, () => { + component.filters = [ + { property: 'name', fixed: false, initValue: 'John Doe' }, + { property: 'age', fixed: false, initValue: 30 } + ]; + + spyOn(component.changeDisclaimers, 'emit'); component['onRemoveAllDisclaimers'](); expect(component.changeDisclaimers.emit).toHaveBeenCalledWith([]); @@ -736,6 +801,33 @@ describe('PoPageDynamicSearchComponent:', () => { expect(component['onAction']).toHaveBeenCalledWith('jhon', true); }); }); + + describe('ngAfterViewInit:', () => { + it('should call `onChangeFilters` and update `previousFilters` if `filters` have changed', () => { + const filters = [{ property: 'city', label: 'City' }]; + component.filters = filters; + component.previousFilters = [{ property: 'city', label: 'Previous City' }]; + + spyOn(component, 'onChangeFilters'); + component.ngAfterViewInit(); + + expect(component.onChangeFilters).toHaveBeenCalledWith(filters); + expect(component.previousFilters).toEqual(filters); + }); + + it('should not call `onChangeFilters` if `filters` have not changed', () => { + const filters = [{ property: 'city', label: 'City' }]; + component.filters = filters; + component.previousFilters = [...filters]; + + spyOn(component, 'onChangeFilters'); + + component.ngAfterViewInit(); + + expect(component.onChangeFilters).not.toHaveBeenCalled(); + expect(component.previousFilters).toEqual(filters); + }); + }); }); describe('Integration:', () => { @@ -750,77 +842,48 @@ describe('PoPageDynamicSearchComponent:', () => { expect(component.changeDisclaimers.emit).not.toHaveBeenCalled(); }); - it(`should add quickSearch and advanced filter in disclaimers if concat-filters is true and advanced filter is defined`, () => { + it(`should remove previous quickSearch disclaimer when adding a new quickSearch`, () => { component.concatFilters = true; - - component.filters = [{ property: 'city', initValue: 'Ontario' }]; - component.literals.quickSearchLabel = 'Search'; component.onAction('Chicago'); - const currentDisclaimers = [ - { label: 'City: Ontario', value: 'Ontario', property: 'city', hideClose: false }, - { property: 'search', label: `Search Chicago`, value: 'Chicago', hideClose: false } - ]; - - expect(component.disclaimerGroup.disclaimers).toEqual(currentDisclaimers); - }); - - it(`should add advanced filter and quickSearch updated in disclaimers if concat-filters is true and advanced filter is defined`, () => { - component.concatFilters = true; - - component.filters = [{ property: 'city', initValue: 'Ontario' }]; - - component.literals.quickSearchLabel = 'Search'; - component.onAction('Chicago'); + expect(component.disclaimerGroup.disclaimers).toEqual([ + { + property: 'search', + label: 'Search Chicago', + value: 'Chicago', + hideClose: false + } + ]); component.onAction('Test'); - const currentDisclaimers = [ - { label: 'City: Ontario', value: 'Ontario', property: 'city', hideClose: false }, - { property: 'search', label: `Search Test`, value: 'Test', hideClose: false } - ]; - - expect(component.disclaimerGroup.disclaimers).toEqual(currentDisclaimers); + expect(component.disclaimerGroup.disclaimers).toEqual([ + { + property: 'search', + label: 'Search Test', + value: 'Test', + hideClose: false + } + ]); }); it(`should add advanced search and remove quickSearch in disclaimers if concat-filters is false`, () => { component.concatFilters = false; - component.literals.quickSearchLabel = 'Search'; component.onAction('Chicago'); + const disclaimersWithQuickFilter = [ { property: 'search', label: `Search Chicago`, value: 'Chicago', hideClose: false } ]; - expect(component.disclaimerGroup.disclaimers).toEqual(disclaimersWithQuickFilter); - const disclaimersWithAdvancedSearch = [ - { label: 'City: Ontario', value: 'Ontario', property: 'city', hideClose: false } - ]; - component.filters = [{ property: 'city', initValue: 'Ontario' }]; - - expect(component.disclaimerGroup.disclaimers).toEqual(disclaimersWithAdvancedSearch); - }); - - it(`should add advanced search and remove quickSearch in disclaimers if concat-filters is true`, () => { - component.concatFilters = true; - - component.literals.quickSearchLabel = 'Search'; - component.onAction('Chicago'); - const disclaimersWithQuickFilter = [ - { property: 'search', label: `Search Chicago`, value: 'Chicago', hideClose: false } - ]; - - expect(component.disclaimerGroup.disclaimers).toEqual(disclaimersWithQuickFilter); + component.onAdvancedSearch({ filter: { city: 'Ontario' } }); const disclaimersWithAdvancedSearch = [ { label: 'City: Ontario', value: 'Ontario', property: 'city', hideClose: false } ]; - - component.filters = [{ property: 'city', initValue: 'Ontario' }]; - expect(component.disclaimerGroup.disclaimers).toEqual(disclaimersWithAdvancedSearch); }); }); diff --git a/projects/templates/src/lib/components/po-page-dynamic-search/po-page-dynamic-search.component.ts b/projects/templates/src/lib/components/po-page-dynamic-search/po-page-dynamic-search.component.ts index 58a267189f..692f7feb78 100644 --- a/projects/templates/src/lib/components/po-page-dynamic-search/po-page-dynamic-search.component.ts +++ b/projects/templates/src/lib/components/po-page-dynamic-search/po-page-dynamic-search.component.ts @@ -1,4 +1,4 @@ -import { Component, ViewChild, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core'; +import { Component, ViewChild, OnInit, OnDestroy, ChangeDetectorRef, AfterViewInit } from '@angular/core'; import { Observable, Subscription } from 'rxjs'; import { @@ -43,7 +43,10 @@ type UrlOrPoCustomizationFunction = string | (() => PoPageDynamicSearchOptions); selector: 'po-page-dynamic-search', templateUrl: './po-page-dynamic-search.component.html' }) -export class PoPageDynamicSearchComponent extends PoPageDynamicSearchBaseComponent implements OnInit, OnDestroy { +export class PoPageDynamicSearchComponent + extends PoPageDynamicSearchBaseComponent + implements OnInit, OnDestroy, AfterViewInit +{ @ViewChild(PoAdvancedFilterComponent, { static: true }) poAdvancedFilter: PoAdvancedFilterComponent; @ViewChild(PoPageListComponent, { static: true }) poPageList: PoPageListComponent; @@ -107,6 +110,14 @@ export class PoPageDynamicSearchComponent extends PoPageDynamicSearchBaseCompone } } + ngAfterViewInit(): void { + if (this.stringify(this.filters) !== this.stringify(this.previousFilters)) { + this.onChangeFilters(this.filters); + + this.previousFilters = [...this.filters]; + } + } + onChangeFilters(filters: Array) { const filterObjectWithValue = filters .filter(filter => filter.initValue) @@ -152,7 +163,12 @@ export class PoPageDynamicSearchComponent extends PoPageDynamicSearchBaseCompone onAdvancedSearch(filteredItems, isAdvancedSearch?) { const { filter, optionsService } = filteredItems; - this._disclaimerGroup.disclaimers = this.setDisclaimers(filter, optionsService); + const visibleFilters = + this.visibleFixedFilters === false + ? this.filters.filter(filter => !('fixed' in filter) || !filter.fixed) + : this.filters; + + this._disclaimerGroup.disclaimers = this.setDisclaimers(filter, optionsService, visibleFilters); this.setFilters(filter); @@ -274,29 +290,51 @@ export class PoPageDynamicSearchComponent extends PoPageDynamicSearchBaseCompone } private onRemoveAllDisclaimers() { - this.emitChangesDisclaimers([]); + const fixedFilters = this.filters.filter( + filter => + filter.fixed === true && + filter.hasOwnProperty('initValue') && + filter.initValue !== undefined && + filter.initValue !== null + ); + + const disclaimersToKeep = fixedFilters.map(filter => ({ + property: filter.property, + value: filter.initValue, + label: `${filter.label}: ${filter.initValue}`, + hideClose: true + })); + + this.emitChangesDisclaimers(disclaimersToKeep); } - private setDisclaimers(filters, optionsServiceObjects?: Array) { + private setDisclaimers( + filters, + optionsServiceObjects?: Array, + visibleFilters?: Array + ) { const disclaimers = []; const properties = Object.keys(filters); + const visibleProperties = visibleFilters ? visibleFilters.map(filter => filter.property) : properties; properties.forEach(property => { - const field = this.getFieldByProperty(this.filters, property); - const label = field.label || capitalizeFirstLetter(field.property); - const value = filters[property]; - const hideClose = - this.hideCloseDisclaimers.some(hideCloseDisclaimer => hideCloseDisclaimer === property) || false; - - const valueDisplayedOnTheDisclaimerLabel = this.getFilterValueToDisclaimer(field, value, optionsServiceObjects); - - if (valueDisplayedOnTheDisclaimerLabel !== '') { - disclaimers.push({ - label: `${label}: ${valueDisplayedOnTheDisclaimerLabel}`, - property, - value, - hideClose - }); + if (visibleProperties.includes(property)) { + const field = this.getFieldByProperty(this.filters, property); + const label = field.label || capitalizeFirstLetter(field.property); + const value = filters[property]; + const hideClose = + this.hideCloseDisclaimers.some(hideCloseDisclaimer => hideCloseDisclaimer === property) || false; + + const valueDisplayedOnTheDisclaimerLabel = this.getFilterValueToDisclaimer(field, value, optionsServiceObjects); + + if (valueDisplayedOnTheDisclaimerLabel !== '') { + disclaimers.push({ + label: `${label}: ${valueDisplayedOnTheDisclaimerLabel}`, + property, + value, + hideClose + }); + } } }); diff --git a/projects/templates/src/lib/components/po-page-dynamic-table/po-page-dynamic-table.component.html b/projects/templates/src/lib/components/po-page-dynamic-table/po-page-dynamic-table.component.html index a264fb61c8..0fde4aa9ff 100644 --- a/projects/templates/src/lib/components/po-page-dynamic-table/po-page-dynamic-table.component.html +++ b/projects/templates/src/lib/components/po-page-dynamic-table/po-page-dynamic-table.component.html @@ -9,6 +9,7 @@ [p-hide-remove-all-disclaimer]="hideRemoveAllDisclaimer" [p-quick-search-width]="quickSearchWidth" [p-title]="title" + [p-visible-fixed-filters]="visibleFixedFilters" (p-advanced-search)="onAdvancedSearch($event)" (p-change-disclaimers)="onChangeDisclaimers($event)" (p-quick-search)="onQuickSearch($event)" diff --git a/projects/templates/src/lib/components/po-page-dynamic-table/po-page-dynamic-table.component.spec.ts b/projects/templates/src/lib/components/po-page-dynamic-table/po-page-dynamic-table.component.spec.ts index 118015dce0..d9f6c3e60c 100644 --- a/projects/templates/src/lib/components/po-page-dynamic-table/po-page-dynamic-table.component.spec.ts +++ b/projects/templates/src/lib/components/po-page-dynamic-table/po-page-dynamic-table.component.spec.ts @@ -182,6 +182,18 @@ describe('PoPageDynamicTableComponent:', () => { component.virtualScroll = true; expect(component.virtualScroll).toBe(true); }); + + it('p-visible-fixed-filters: should update property `p-visible-filter-disclaimers` to `false` when valid boolean value is given', () => { + component.visibleFixedFilters = false; + + expect(component.visibleFixedFilters).toBe(false); + }); + + it('p-visible-fixed-filters: should update property `p-visible-filter-disclaimers` to `true` when valid boolean value is given', () => { + component.visibleFixedFilters = true; + + expect(component.visibleFixedFilters).toBe(true); + }); }); describe('Methods:', () => { diff --git a/projects/templates/src/lib/components/po-page-dynamic-table/po-page-dynamic-table.component.ts b/projects/templates/src/lib/components/po-page-dynamic-table/po-page-dynamic-table.component.ts index a890c1531a..b4164bc0af 100644 --- a/projects/templates/src/lib/components/po-page-dynamic-table/po-page-dynamic-table.component.ts +++ b/projects/templates/src/lib/components/po-page-dynamic-table/po-page-dynamic-table.component.ts @@ -595,6 +595,26 @@ export class PoPageDynamicTableComponent extends PoPageDynamicListBaseComponent return this._virtualScroll; } + /** + * @optional + * + * @description + * + * Controla a visibilidade dos filtros fixos na página. + * + * - Quando `true` (default), todos os filtros, incluindo os fixos, são exibidos, permitindo que o usuário visualize os filtros aplicados. + * - Quando `false`, os filtros fixos são ocultados, não sendo exibidos na interface, mas ainda sendo aplicados como filtros nas requisições. + * + * Esta propriedade trabalha em conjunto com a propriedade `fixed` dos filtros individuais. Filtros marcados como `fixed: true` não serão exibidos na interface do filtro avançado quando `visibleFixedFilters` for `false`, mas continuarão a ser aplicados de forma transparente ao usuário. Dessa forma, permite-se maior flexibilidade no controle de quais filtros devem ser visíveis ao usuário ou devem ser aplicados permanentemente sem interferência. + * + * **Exemplo de uso:** + * ```html + * + * + * ``` + */ + @Input('p-visible-fixed-filters') visibleFixedFilters: boolean = true; + constructor( private router: Router, private activatedRoute: ActivatedRoute,