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..89ef14fff9 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 @@ -3,7 +3,7 @@ import { FormsModule } from '@angular/forms'; import { RouterTestingModule } from '@angular/router/testing'; import { Routes } from '@angular/router'; import { TitleCasePipe } from '@angular/common'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { NO_ERRORS_SCHEMA, SimpleChanges } from '@angular/core'; import { PoDynamicFieldType, PoDynamicModule } from '@po-ui/ng-components'; @@ -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,103 @@ describe('PoPageDynamicSearchComponent:', () => { expect(component.changeDisclaimers.emit).toHaveBeenCalledWith(currentDisclaimers); }); - it(`onRemoveAllDisclaimers: should call 'changeDisclaimers.emit' if all disclaimers are removed`, () => { + it(`onRemoveDisclaimer: should include fixed filters from 'this.filters' into currentDisclaimers`, () => { + component.filters = [ + { property: 'status', fixed: true, initValue: 'Active', label: 'Status' }, + { property: 'category', fixed: true, initValue: 'Finance', label: 'Category' }, + { property: 'name', fixed: false, initValue: 'Test', label: 'Name' } + ]; + const currentDisclaimers = [{ property: 'name', label: 'Name: Test', value: 'Test', hideClose: false }]; + const removedDisclaimer = { property: 'name', label: 'Name: Test', value: 'Test', hideClose: false }; + + const expectedDisclaimers = [ + { property: 'name', label: 'Name: Test', value: 'Test', hideClose: false }, + { property: 'status', label: 'Status: Active', value: 'Active', hideClose: true }, + { property: 'category', label: 'Category: Finance', value: 'Finance', hideClose: true } + ]; + + spyOn(component.changeDisclaimers, 'emit'); + + component['onRemoveDisclaimer']({ removedDisclaimer, currentDisclaimers }); + + expect(component.changeDisclaimers.emit).toHaveBeenCalledWith(expectedDisclaimers); + }); + + it(`onRemoveDisclaimer: should not duplicate fixed filters already present in currentDisclaimers`, () => { + component.filters = [ + { property: 'status', fixed: true, initValue: 'Active', label: 'Status' }, + { property: 'category', fixed: true, initValue: 'Finance', label: 'Category' } + ]; + const currentDisclaimers = [{ property: 'status', label: 'Status: Active', value: 'Active', hideClose: true }]; + const removedDisclaimer = { property: 'category', label: 'Category: Finance', value: 'Finance', hideClose: true }; + + const expectedDisclaimers = [ + { property: 'status', label: 'Status: Active', value: 'Active', hideClose: true }, + { property: 'category', label: 'Category: Finance', value: 'Finance', hideClose: true } + ]; + + spyOn(component.changeDisclaimers, 'emit'); + + component['onRemoveDisclaimer']({ removedDisclaimer, currentDisclaimers }); + + expect(component.changeDisclaimers.emit).toHaveBeenCalledWith(expectedDisclaimers); + }); + + it(`onRemoveDisclaimer: should not include fixed filters without 'initValue'`, () => { + component.filters = [ + { property: 'status', fixed: true, label: 'Status' }, + { property: 'category', fixed: true, initValue: 'Finance', label: 'Category' } + ]; + const currentDisclaimers = [ + { property: 'category', label: 'Category: Finance', value: 'Finance', hideClose: true } + ]; + const removedDisclaimer = { property: 'category', label: 'Category: Finance', value: 'Finance', hideClose: true }; + + const expectedDisclaimers = [ + { property: 'category', label: 'Category: Finance', value: 'Finance', hideClose: true } + ]; + spyOn(component.changeDisclaimers, 'emit'); + component['onRemoveDisclaimer']({ removedDisclaimer, currentDisclaimers }); + + expect(component.changeDisclaimers.emit).toHaveBeenCalledWith(expectedDisclaimers); + }); + + 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([]); @@ -671,6 +799,87 @@ describe('PoPageDynamicSearchComponent:', () => { expect(result).toBe(123); }); + it('should return an array of disclaimers for filters with fixed property, initValue defined, and no duplicates', () => { + component.filters = [ + { property: 'status', fixed: true, initValue: 'Active', label: 'Status' }, + { property: 'category', fixed: true, initValue: 'Finance', label: 'Category' }, + { property: 'name', fixed: false, initValue: 'Test', label: 'Name' } + ]; + + const currentDisclaimers = [ + { property: 'category', label: 'Category: Finance', value: 'Finance', hideClose: true } + ]; + + const expectedDisclaimers = [{ property: 'status', label: 'Status: Active', value: 'Active', hideClose: true }]; + + const result = component['getFixedFiltersDisclaimers'](currentDisclaimers); + + expect(result).toEqual(expectedDisclaimers); + }); + + it('should return an empty array if there are no fixed filters', () => { + component.filters = [ + { property: 'name', fixed: false, initValue: 'Test', label: 'Name' }, + { property: 'age', fixed: false, initValue: 30, label: 'Age' } + ]; + + const result = component['getFixedFiltersDisclaimers'](); + + expect(result).toEqual([]); + }); + + it('should ignore filters without initValue or with null/undefined initValue', () => { + component.filters = [ + { property: 'status', fixed: true, label: 'Status' }, + { property: 'category', fixed: true, initValue: null, label: 'Category' }, + { property: 'name', fixed: true, initValue: undefined, label: 'Name' } + ]; + + const result = component['getFixedFiltersDisclaimers'](); + + expect(result).toEqual([]); + }); + + it('should not add disclaimers if they are already present in currentDisclaimers', () => { + component.filters = [ + { property: 'status', fixed: true, initValue: 'Active', label: 'Status' }, + { property: 'category', fixed: true, initValue: 'Finance', label: 'Category' } + ]; + + const currentDisclaimers = [ + { property: 'status', label: 'Status: Active', value: 'Active', hideClose: true }, + { property: 'category', label: 'Category: Finance', value: 'Finance', hideClose: true } + ]; + + const result = component['getFixedFiltersDisclaimers'](currentDisclaimers); + + expect(result).toEqual([]); + }); + + it('should correctly handle an empty currentDisclaimers array', () => { + component.filters = [ + { property: 'status', fixed: true, initValue: 'Active', label: 'Status' }, + { property: 'category', fixed: true, initValue: 'Finance', label: 'Category' } + ]; + + const expectedDisclaimers = [ + { property: 'status', label: 'Status: Active', value: 'Active', hideClose: true }, + { property: 'category', label: 'Category: Finance', value: 'Finance', hideClose: true } + ]; + + const result = component['getFixedFiltersDisclaimers']([]); + + expect(result).toEqual(expectedDisclaimers); + }); + + it('should return an empty array if filters array is empty', () => { + component.filters = []; + + const result = component['getFixedFiltersDisclaimers'](); + + expect(result).toEqual([]); + }); + describe('ngOnInit:', () => { it('should call setAdvancedFilterLiterals with component.literals', () => { spyOn(component, 'setAdvancedFilterLiterals'); @@ -736,91 +945,157 @@ describe('PoPageDynamicSearchComponent:', () => { expect(component['onAction']).toHaveBeenCalledWith('jhon', true); }); }); - }); - describe('Integration:', () => { - it(`shouldn't call 'changeDisclaimers.emit' if disclaimers have been added because of quickSearch`, () => { - component.filters = [{ property: 'city', initValue: 'Ontario' }]; + 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.changeDisclaimers, 'emit'); + spyOn(component, 'onChangeFilters'); + component.ngAfterViewInit(); - component.literals.quickSearchLabel = 'Search'; - component.onAction('Chicago'); + expect(component.onChangeFilters).toHaveBeenCalledWith(filters); + expect(component.previousFilters).toEqual(filters); + }); - expect(component.changeDisclaimers.emit).not.toHaveBeenCalled(); - }); + it('should not call `onChangeFilters` if `filters` have not changed', () => { + const filters = [{ property: 'city', label: 'City' }]; + component.filters = filters; + component.previousFilters = [...filters]; - it(`should add quickSearch and advanced filter in disclaimers if concat-filters is true and advanced filter is defined`, () => { - component.concatFilters = true; + spyOn(component, 'onChangeFilters'); - component.filters = [{ property: 'city', initValue: 'Ontario' }]; + component.ngAfterViewInit(); - component.literals.quickSearchLabel = 'Search'; - component.onAction('Chicago'); + expect(component.onChangeFilters).not.toHaveBeenCalled(); + expect(component.previousFilters).toEqual(filters); + }); + }); + }); - const currentDisclaimers = [ - { label: 'City: Ontario', value: 'Ontario', property: 'city', hideClose: false }, - { property: 'search', label: `Search Chicago`, value: 'Chicago', hideClose: false } - ]; + describe('ngOnChanges:', () => { + it('should call `onChangeFilters` and update `previousFilters` when `visibleFixedFilters` changes and there are fixed filters', () => { + const changes: SimpleChanges = { + visibleFixedFilters: { + currentValue: true, + previousValue: false, + firstChange: false, + isFirstChange: () => false + } + }; - expect(component.disclaimerGroup.disclaimers).toEqual(currentDisclaimers); - }); + spyOn(component, 'onChangeFilters'); // Espiar o método - 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: 'status', fixed: true, initValue: 'Active' }, + { property: 'category', fixed: false, initValue: 'Finance' } + ]; - component.filters = [{ property: 'city', initValue: 'Ontario' }]; + component.ngOnChanges(changes); - component.literals.quickSearchLabel = 'Search'; - component.onAction('Chicago'); + expect(component.onChangeFilters).toHaveBeenCalledWith(component.filters); + expect(component.previousFilters).toEqual(component.filters); + }); - component.onAction('Test'); + it('should not call `onChangeFilters` if `visibleFixedFilters` did not change', () => { + const changes: SimpleChanges = { + visibleFixedFilters: { + currentValue: true, + previousValue: true, + firstChange: false, + isFirstChange: () => false + } + }; - const currentDisclaimers = [ - { label: 'City: Ontario', value: 'Ontario', property: 'city', hideClose: false }, - { property: 'search', label: `Search Test`, value: 'Test', hideClose: false } + spyOn(component, 'onChangeFilters'); // Espiar o método + + component.filters = [ + { property: 'status', fixed: true, initValue: 'Active' }, + { property: 'category', fixed: false, initValue: 'Finance' } ]; - expect(component.disclaimerGroup.disclaimers).toEqual(currentDisclaimers); + component.ngOnChanges(changes); + + expect(component.onChangeFilters).not.toHaveBeenCalled(); + expect(component.previousFilters).toEqual([]); }); - it(`should add advanced search and remove quickSearch in disclaimers if concat-filters is false`, () => { - component.concatFilters = false; + it('should not throw an error if `filters` is empty', () => { + const changes: SimpleChanges = { + visibleFixedFilters: { + currentValue: true, + previousValue: false, + firstChange: false, + isFirstChange: () => false + } + }; - component.literals.quickSearchLabel = 'Search'; - component.onAction('Chicago'); - const disclaimersWithQuickFilter = [ - { property: 'search', label: `Search Chicago`, value: 'Chicago', hideClose: false } - ]; + spyOn(component, 'onChangeFilters'); // Espiar o método - expect(component.disclaimerGroup.disclaimers).toEqual(disclaimersWithQuickFilter); + component.filters = []; - const disclaimersWithAdvancedSearch = [ - { label: 'City: Ontario', value: 'Ontario', property: 'city', hideClose: false } - ]; + component.ngOnChanges(changes); + + expect(component.onChangeFilters).not.toHaveBeenCalled(); + expect(component.previousFilters).toEqual([]); + }); + }); + describe('Integration:', () => { + it(`shouldn't call 'changeDisclaimers.emit' if disclaimers have been added because of quickSearch`, () => { component.filters = [{ property: 'city', initValue: 'Ontario' }]; - expect(component.disclaimerGroup.disclaimers).toEqual(disclaimersWithAdvancedSearch); + spyOn(component.changeDisclaimers, 'emit'); + + component.literals.quickSearchLabel = 'Search'; + component.onAction('Chicago'); + + expect(component.changeDisclaimers.emit).not.toHaveBeenCalled(); }); - it(`should add advanced search and remove quickSearch in disclaimers if concat-filters is true`, () => { + it(`should remove previous quickSearch disclaimer when adding a new quickSearch`, () => { component.concatFilters = true; + component.literals.quickSearchLabel = 'Search'; + component.onAction('Chicago'); + + expect(component.disclaimerGroup.disclaimers).toEqual([ + { + property: 'search', + label: 'Search Chicago', + value: 'Chicago', + hideClose: false + } + ]); + + component.onAction('Test'); + + 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); + component.filters = [{ property: 'city', initValue: 'Ontario' }]; + 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..6154811b99 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,12 @@ -import { Component, ViewChild, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core'; +import { + Component, + ViewChild, + OnInit, + OnDestroy, + ChangeDetectorRef, + AfterViewInit, + SimpleChanges +} from '@angular/core'; import { Observable, Subscription } from 'rxjs'; import { @@ -43,7 +51,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 +118,27 @@ export class PoPageDynamicSearchComponent extends PoPageDynamicSearchBaseCompone } } + ngAfterViewInit(): void { + if (this.stringify(this.filters) !== this.stringify(this.previousFilters)) { + this.onChangeFilters(this.filters); + + this.previousFilters = [...this.filters]; + } + } + + ngOnChanges(changes: SimpleChanges) { + const visibleFixedFilters = changes.visibleFixedFilters; + + if ( + visibleFixedFilters && + visibleFixedFilters.currentValue !== visibleFixedFilters.previousValue && + this.filters.some(filter => filter.fixed) + ) { + this.onChangeFilters(this.filters); + this.previousFilters = [...this.filters]; + } + } + onChangeFilters(filters: Array) { const filterObjectWithValue = filters .filter(filter => filter.initValue) @@ -152,7 +184,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); @@ -262,41 +299,73 @@ export class PoPageDynamicSearchComponent extends PoPageDynamicSearchBaseCompone return value; } - private onRemoveDisclaimer(removeData: PoDisclaimerGroupRemoveAction) { - const { currentDisclaimers } = removeData; - - this.emitChangesDisclaimers(currentDisclaimers); - } - private emitChangesDisclaimers(currentDisclaimers: any) { this.changeDisclaimers.emit(currentDisclaimers); this.setFilters(this.formatArrayToObjectKeyValue(currentDisclaimers)); } private onRemoveAllDisclaimers() { - this.emitChangesDisclaimers([]); + const disclaimersToKeep = this.getFixedFiltersDisclaimers(); + this.emitChangesDisclaimers(disclaimersToKeep); + } + + private onRemoveDisclaimer(removeData: PoDisclaimerGroupRemoveAction) { + const { currentDisclaimers } = removeData; + + const updatedDisclaimers = [...currentDisclaimers, ...this.getFixedFiltersDisclaimers(currentDisclaimers)]; + + this.emitChangesDisclaimers(updatedDisclaimers); } - private setDisclaimers(filters, optionsServiceObjects?: Array) { + private getFixedFiltersDisclaimers(currentDisclaimers?: Array): Array { + const fixedFilters = this.filters.filter( + filter => + filter.fixed === true && + filter.hasOwnProperty('initValue') && + filter.initValue !== undefined && + filter.initValue !== null + ); + + return fixedFilters + .map(filter => ({ + property: filter.property, + value: filter.initValue, + label: `${filter.label}: ${filter.initValue}`, + hideClose: true + })) + .filter( + fixedFilter => + !currentDisclaimers || !currentDisclaimers.some(disclaimer => disclaimer.property === fixedFilter.property) + ); + } + + 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,