Skip to content

Commit

Permalink
feat(search): implementa modo trigger no componente
Browse files Browse the repository at this point in the history
Implementa modo trigger no componente, ativando a busca via `enter` ou `click` no ícone

fixes DTHFUI-7809
  • Loading branch information
jcorrea97 authored and alinelariguet committed Dec 14, 2023
1 parent bc4c627 commit 196ba3e
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ describe('PoSearchBaseComponent', () => {

describe('Properties:', () => {
it('label: should set label when is setted', () => {
component['language'] = 'en';
component.ariaLabel = 'Search';

expect(component.ariaLabel).toBe('Search');
});

it('label: should concat label with literals', () => {
component['language'] = 'en';
component.ariaLabel = 'label button';
expect(component.ariaLabel).toBe('label button Search');
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { PoSearchLiterals } from './literals/po-search-literals';
import { poSearchLiteralsDefault } from './literals/po-search-literals-default';
import { PoSearchFilterMode } from './enum/po-search-filter-mode.enum';

export type searchMode = 'action' | 'trigger';
/**
* @description
*
Expand Down Expand Up @@ -83,8 +84,6 @@ export class PoSearchBaseComponent {
}

/**
* @optional
*
* @description
*
* Deve ser informado o nome da propriedade do objeto que será utilizado para a conversão dos itens apresentados na lista do componente (p-items), esta propriedade será responsável pelo texto de apresentação de cada item da lista.
Expand All @@ -100,6 +99,21 @@ export class PoSearchBaseComponent {
*/
@Input('p-icon') icon: string | TemplateRef<void>;

/**
* @optional
*
* @description
*
* Determina a forma de realizar a pesquisa no componente
*
* Valores aceitos:
* - `action`: Realiza a busca a cada caractere digitado.
* - `trigger`: Realiza a busca ao pressionar `enter` ou clicar no ícone de busca.
*
* @default `action`
*/
@Input('p-search-type') type: searchMode = 'action';

/**
* @optional
*
Expand Down
30 changes: 22 additions & 8 deletions projects/ui/src/lib/components/po-search/po-search.component.html
Original file line number Diff line number Diff line change
@@ -1,29 +1,43 @@
<div class="po-search" [class.po-search-disabled]="disabled">
<div class="po-search-icon">
<div *ngIf="type === 'action'" class="po-search-icon">
<po-icon [p-icon]="icon ? icon : 'po-icon-search'"></po-icon>
</div>

<input
#poSearchInput
type="text"
class="po-search-input"
type="text"
[ariaLabel]="ariaLabel"
[class.po-search-icon-right]="!!poSearchInput.value && !disabled"
[class.po-search-input-clean]="!!poSearchInput.value && !disabled"
[class.po-search-input-trigger]="type === 'trigger'"
[disabled]="disabled"
[placeholder]="literals.search"
(blur)="onBlur()"
(focus)="onFocus()"
(input)="onSearchChange($event.target.value)"
(input)="onSearchChange($event.target.value, type === 'action' ? true : false)"
(keyup.enter)="onSearchChange($event.target.value, type === 'trigger' ? true : false)"
/>

<div *ngIf="!!poSearchInput.value && !disabled" class="po-search-container-clean">
<div class="po-search-buttons">
<button
class="po-search-clean"
*ngIf="!!poSearchInput.value && !disabled"
class="po-search-button po-search-button-clean"
type="button"
[ariaLabel]="literals.clean"
(click)="clearSearch()"
(keydown.enter)="clearSearch()"
>
<po-icon p-icon="po-icon-clear-content"></po-icon>
</button>

<button
*ngIf="type === 'trigger'"
class="po-search-button po-search-button-trigger"
type="button"
[ariaLabel]="literals.search"
(click)="onSearchChange(poSearchInput.value, true)"
(keydown.enter)="onSearchChange(poSearchInput.value, true)"
[disabled]="disabled"
>
<po-icon [p-icon]="icon ? icon : 'po-icon-search'"> </po-icon>
</button>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('PoSearchComponent', () => {
});

it('onSearchChange: should filter items based on search text and emit filtered items', () => {
component.onSearchChange('text');
component.onSearchChange('text', true);

expect(component.filteredItems).toEqual([{ text: 'Text 1' }, { text: 'Text 2' }]);
expect(component.filteredItemsChange.emit).toHaveBeenCalledWith([{ text: 'Text 1' }, { text: 'Text 2' }]);
Expand All @@ -54,7 +54,7 @@ describe('PoSearchComponent', () => {
it('onSearchChange: should filter items based on search text using startsWith', () => {
component.filterType = PoSearchFilterMode.startsWith;

component.onSearchChange('Text');
component.onSearchChange('Text', true);

expect(component.filteredItems).toEqual([{ text: 'Text 1' }, { text: 'Text 2' }]);
expect(component.filteredItemsChange.emit).toHaveBeenCalledWith([{ text: 'Text 1' }, { text: 'Text 2' }]);
Expand All @@ -63,14 +63,14 @@ describe('PoSearchComponent', () => {
it('onSearchChange: should filter items based on search text using endsWith', () => {
component.filterType = PoSearchFilterMode.endsWith;

component.onSearchChange('2');
component.onSearchChange('2', true);

expect(component.filteredItems).toEqual([{ text: 'Text 2' }]);
expect(component.filteredItemsChange.emit).toHaveBeenCalledWith([{ text: 'Text 2' }]);
});

it('onSearchChange: should not filter items if search text is empty', () => {
component.onSearchChange('');
component.onSearchChange('', true);

expect(component.filteredItems).toEqual([{ text: 'Text 1' }, { text: 'Text 2' }, { text: 'Other Text' }]);
expect(component.filteredItemsChange.emit).toHaveBeenCalledWith(component.items);
Expand All @@ -79,7 +79,7 @@ describe('PoSearchComponent', () => {
it('onSearchChange: should return false if filter mode is not recognized', () => {
component.filterType = ('invalidMode' as unknown) as PoSearchFilterMode;

const result = component.onSearchChange('text');
const result = component.onSearchChange('text', true);

expect(result).toBeFalsy();
expect(component.filteredItems).toEqual([]);
Expand All @@ -91,7 +91,7 @@ describe('PoSearchComponent', () => {
component.items = [item];
component.filterKeys = ['value'];

component.onSearchChange('42');
component.onSearchChange('42', true);

expect(component.filteredItems).toEqual([item]);
expect(component.filteredItemsChange.emit).toHaveBeenCalledWith([item]);
Expand All @@ -100,7 +100,7 @@ describe('PoSearchComponent', () => {
it('onSearchChange: should filter items based on search text using contains', () => {
component.filterType = PoSearchFilterMode.contains;

component.onSearchChange('ext');
component.onSearchChange('ext', true);

expect(component.filteredItems).toEqual([{ text: 'Text 1' }, { text: 'Text 2' }, { text: 'Other Text' }]);

Expand All @@ -117,46 +117,17 @@ describe('PoSearchComponent', () => {
component.filterType = PoSearchFilterMode.contains;
component.items = [{ name: null }];

component.onSearchChange(searchText);
component.onSearchChange(searchText, true);

expect(component.filteredItems.length).toBe(0);
});

it('onSearchChange: should reset filteredItems and emit empty array', () => {
component.items = [];
component.filterKeys = ['name'];
component.onSearchChange('item');
component.onSearchChange('item', true);

expect(component.filteredItems).toEqual([]);
expect(component.filteredItemsChange.emit).toHaveBeenCalledWith([]);
});

it('should remove focused class on blur', () => {
const parentElement = document.createElement('div');
const nativeElement = document.createElement('input');

parentElement.appendChild(nativeElement);
parentElement.classList.add('po-search-focused');

component.poSearchInput = new ElementRef(nativeElement);
component.onBlur();

expect(parentElement.classList.contains('po-search-focused')).toBeFalsy();
});

it('should add focused class on focus', () => {
const parentElement = document.createElement('div');
const nativeElement = document.createElement('input');
parentElement.appendChild(nativeElement);

component.poSearchInput = {
nativeElement: {
parentElement: parentElement
}
} as ElementRef;

component.onFocus();

expect(parentElement.classList.contains('po-search-focused')).toBeTruthy();
});
});
62 changes: 28 additions & 34 deletions projects/ui/src/lib/components/po-search/po-search.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,49 +45,43 @@ export class PoSearchComponent extends PoSearchBaseComponent implements OnInit {

clearSearch(): void {
this.poSearchInput.nativeElement.value = '';
this.onSearchChange('');
this.onSearchChange('', true);
this.filteredItemsChange.emit(this.items);
this.poSearchInput.nativeElement.focus();
}

onBlur(): void {
this.renderer.removeClass(this.poSearchInput.nativeElement.parentElement, 'po-search-focused');
}

onFocus(): void {
this.renderer.addClass(this.poSearchInput.nativeElement.parentElement, 'po-search-focused');
}

onSearchChange(searchText: string): void {
searchText = searchText.toLowerCase();
onSearchChange(searchText: string, activated: boolean): void {
if (activated) {
searchText = searchText.toLowerCase();

if (this.items && this.items.length > 0) {
this.filteredItems = this.items.filter(item =>
this.filterKeys.some(key => {
let value = item[key];
if (this.items && this.items.length > 0) {
this.filteredItems = this.items.filter(item =>
this.filterKeys.some(key => {
let value = item[key];

if (typeof value !== 'string') {
value = value != null ? value.toString() : null;
}
if (typeof value !== 'string') {
value = value != null ? value.toString() : null;
}

value = value != null ? value.toLowerCase() : null;
value = value != null ? value.toLowerCase() : null;

if (this.filterType === PoSearchFilterMode.startsWith) {
return value != null && value.startsWith(searchText);
} else if (this.filterType === PoSearchFilterMode.contains) {
return value != null && value.includes(searchText);
} else if (this.filterType === PoSearchFilterMode.endsWith) {
return value != null && value.endsWith(searchText);
}
if (this.filterType === PoSearchFilterMode.startsWith) {
return value != null && value.startsWith(searchText);
} else if (this.filterType === PoSearchFilterMode.contains) {
return value != null && value.includes(searchText);
} else if (this.filterType === PoSearchFilterMode.endsWith) {
return value != null && value.endsWith(searchText);
}

return false;
})
);
this.filteredItemsChange.emit(this.filteredItems);
} else {
this.filteredItems = [];
this.filteredItemsChange.emit(this.filteredItems);
return false;
})
);
this.filteredItemsChange.emit(this.filteredItems);
} else {
this.filteredItems = [];
this.filteredItemsChange.emit(this.filteredItems);
}
this.changeModel.emit(searchText);
}
this.changeModel.emit(searchText);
}
}
3 changes: 2 additions & 1 deletion projects/ui/src/lib/components/po-search/po-search.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ import { PoIconModule } from '../po-icon';
import { PoLoadingModule } from '../po-loading';
import { PoSearchComponent } from './po-search.component';
import { FormsModule } from '@angular/forms';
import { PoAccordionModule } from '../po-accordion/po-accordion.module';

/**
* @description
*
* Módulo do componente po-search.
*/
@NgModule({
imports: [CommonModule, PoCleanModule, PoIconModule, PoLoadingModule, FormsModule],
imports: [CommonModule, PoCleanModule, PoIconModule, PoLoadingModule, PoAccordionModule, FormsModule],
declarations: [PoSearchComponent],
exports: [PoSearchComponent]
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,29 @@
[p-filter-keys]="fieldKeys"
[p-literals]="customLiterals"
[p-filter-type]="filterMode"
[p-search-type]="searchMode"
(p-filtered-items-change)="filter($event)"
(p-change-model)="changeModel($event)"
></po-search>
</div>

<hr />
<div class="po-row">
<po-widget class="po-md-12">
<ul class="po-md-12 row">
<li *ngFor="let item of filteredItems">
<ng-container *ngFor="let key of changeFilter(item)">
<div>
<strong>{{ key }}: </strong> {{ item[key] }} &nbsp;
</div>
</ng-container>
</li>
</ul>
</po-widget>
<po-accordion class="po-md-12">
<po-accordion-item p-label="Itens encontrados: {{ filteredItems.length }}">
<po-widget class="po-md-12">
<ul class="sample-list-search po-md-12 row">
<li *ngFor="let item of filteredItems">
<ng-container *ngFor="let key of changeFilter(item)">
<div>
<strong>{{ key }}: </strong> {{ item[key] }} &nbsp;
</div>
</ng-container>
</li>
</ul>
</po-widget>
</po-accordion-item>
</po-accordion>
</div>
<hr />

Expand Down Expand Up @@ -103,6 +108,16 @@
[p-options]="filterModeOptions"
>
</po-radio-group>

<po-radio-group
class="po-md-6 po-lg-4"
name="searchMode"
[(ngModel)]="searchMode"
p-columns="1"
p-label="Search Mode"
[p-options]="searchModeOptions"
>
</po-radio-group>
</div>

<hr />
Expand Down
Loading

0 comments on commit 196ba3e

Please sign in to comment.