+
diff --git a/projects/ui/src/lib/components/po-search/enum/po-search-filter-mode.enum.ts b/projects/ui/src/lib/components/po-search/enum/po-search-filter-mode.enum.ts
index 17824803da..c427944b5b 100644
--- a/projects/ui/src/lib/components/po-search/enum/po-search-filter-mode.enum.ts
+++ b/projects/ui/src/lib/components/po-search/enum/po-search-filter-mode.enum.ts
@@ -3,7 +3,7 @@
*
* @description
*
- * Define o tipo de busca usado no po-multiselect.
+ * Define o tipo de busca usado no po-search.
*/
export enum PoSearchFilterMode {
/** Verifica se o texto *inicia* com o valor pesquisado. */
diff --git a/projects/ui/src/lib/components/po-search/index.ts b/projects/ui/src/lib/components/po-search/index.ts
index 0986a9393c..2f9ad82470 100644
--- a/projects/ui/src/lib/components/po-search/index.ts
+++ b/projects/ui/src/lib/components/po-search/index.ts
@@ -1,3 +1,4 @@
export * from './po-search.component';
export * from './po-search.module';
-export * from './po-search-filter-mode.enum';
+export * from './enum/po-search-filter-mode.enum';
+export * from './literals/po-search-literals';
diff --git a/projects/ui/src/lib/components/po-search/literals/po-search-literals-default.ts b/projects/ui/src/lib/components/po-search/literals/po-search-literals-default.ts
new file mode 100644
index 0000000000..277929b3c3
--- /dev/null
+++ b/projects/ui/src/lib/components/po-search/literals/po-search-literals-default.ts
@@ -0,0 +1,20 @@
+import { PoSearchLiterals } from './po-search-literals';
+
+export const poSearchLiteralsDefault = {
+ en: {
+ search: 'Search',
+ clean: 'Clean'
+ },
+ es: {
+ search: 'Buscar',
+ clean: 'limpiar'
+ },
+ pt: {
+ search: 'Pesquisar',
+ clean: 'Apagar'
+ },
+ ru: {
+ search: 'Поиск',
+ clean: 'чистый'
+ }
+};
diff --git a/projects/ui/src/lib/components/po-search/literals/po-search-literals.ts b/projects/ui/src/lib/components/po-search/literals/po-search-literals.ts
index b5eb073f8b..016ecd3ab6 100644
--- a/projects/ui/src/lib/components/po-search/literals/po-search-literals.ts
+++ b/projects/ui/src/lib/components/po-search/literals/po-search-literals.ts
@@ -6,7 +6,18 @@ export interface PoSearchLiterals {
*
* @description
*
- * search: Texto exibido no *placeholder* do filtro do componente menu.
+ * search: Texto exibido no *placeholder* do filtro do componente `po-search`.
*/
search?: string;
+
+ /**
+ * @usedBy PoSearchComponent
+ *
+ * @optional
+ *
+ * @description
+ *
+ * search: Texto usado no leitor de tela para acessibilidade.
+ */
+ clean?: string;
}
diff --git a/projects/ui/src/lib/components/po-search/po-search-base.component.spec.ts b/projects/ui/src/lib/components/po-search/po-search-base.component.spec.ts
index 7e6ea14cf4..1e09bc84ea 100644
--- a/projects/ui/src/lib/components/po-search/po-search-base.component.spec.ts
+++ b/projects/ui/src/lib/components/po-search/po-search-base.component.spec.ts
@@ -1,13 +1,53 @@
import { PoSearchBaseComponent } from './po-search-base.component';
+import { PoLanguageService } from '../../services/po-language/po-language.service';
+import { poSearchLiteralsDefault } from './literals/po-search-literals-default';
+import { poLocaleDefault } from '../../services/po-language/po-language.constant';
+import * as Utils from '../../utils/util';
describe('PoSearchBaseComponent', () => {
let component: PoSearchBaseComponent;
beforeEach(() => {
- component = new PoSearchBaseComponent();
+ component = new PoSearchBaseComponent(new PoLanguageService());
});
it('should create an instance', () => {
expect(component).toBeTruthy();
});
+
+ describe('Properties:', () => {
+ it('label: should set label when is setted', () => {
+ component.ariaLabel = 'Search';
+
+ expect(component.ariaLabel).toBe('Search');
+ });
+
+ it('label: should concat label with literals', () => {
+ component.ariaLabel = 'label button';
+ expect(component.ariaLabel).toBe('label button Search');
+ });
+
+ it('should be set `literals` with browser language if `literals` is `undefined`', () => {
+ component['language'] = Utils.browserLanguage();
+ component.literals = undefined;
+
+ expect(component.literals).toEqual(poSearchLiteralsDefault[Utils.browserLanguage()]);
+ });
+
+ it('should be in portuguese if browser is set with `pt`.', () => {
+ component['language'] = 'pt';
+
+ component.literals = {};
+
+ expect(component.literals).toEqual(poSearchLiteralsDefault[poLocaleDefault]);
+ });
+
+ it('should be in english if browser is set with `en`.', () => {
+ component['language'] = 'en';
+
+ component.literals = {};
+
+ expect(component.literals).toEqual(poSearchLiteralsDefault.en);
+ });
+ });
});
diff --git a/projects/ui/src/lib/components/po-search/po-search-base.component.ts b/projects/ui/src/lib/components/po-search/po-search-base.component.ts
index 2ec1f4d76d..d0b9c1d68e 100644
--- a/projects/ui/src/lib/components/po-search/po-search-base.component.ts
+++ b/projects/ui/src/lib/components/po-search/po-search-base.component.ts
@@ -1,21 +1,187 @@
-import { PoFilterMode } from './po-search-filter-mode.enum';
+import { PoLanguageService } from '../../services/po-language/po-language.service';
+import { poLocaleDefault } from '../../services/po-language/po-language.constant';
-import { Directive, EventEmitter, Input, Output } from '@angular/core';
+import { Directive, EventEmitter, Input, Output, TemplateRef } from '@angular/core';
import { convertToBoolean } from '../../utils/util';
+import { PoSearchLiterals } from './literals/po-search-literals';
+import { poSearchLiteralsDefault } from './literals/po-search-literals-default';
+import { PoSearchFilterMode } from './enum/po-search-filter-mode.enum';
+/**
+ * @description
+ *
+ * O componente search, também conhecido como barra de pesquisa, é utilizado para ajudar os usuários a localizar um determinado conteúdo
+ *
+ * Normalmente localizado no canto superior direito, junto com o ícone de lupa, uma vez que este ícone é amplamente reconhecido.
+ *
+ * Portanto, é de extrema importância que, ao utilizar este componente, as pessoas responsáveis por seu desenvolvimento considerem os seguintes critérios.
+ *
+ * #### Boas práticas
+ *
+ * Foram estruturados os padrões de usabilidade para auxiliar na utilização do componente e garantir uma boa experiência aos usuários. Por isso, é muito importante que ao utilizar este componente, as pessoas que o projetarem devem levar em consideração os seguintes critérios:
+ * - Utilize labels para apresentar resultados que estão sendo exibidos e apresente os resultados mais relevantes primeiro.
+ * - Exiba uma mensagem clara quando não forem encontrados resultados para busca e sempre que possível ofereça outras sugestões de busca.
+ * - Mantenha o texto original no campo de input, que facilita a ação do usuário caso queira fazer uma nova busca com alguma modificação na pesquisa.
+ * - Caso seja possível detectar um erro de digitação, mostre os resultados para a palavra "corrigida", isso evita a frustração de não obter resultados e não força o usuário a realizar uma nova busca.
+ * - Quando apropriado, destaque os termos da busca nos resultados.
+ * - A entrada do campo de pesquisa deve caber em uma linha. Não use entradas de pesquisa de várias linhas.
+ * - Recomenda-se ter apenas uma pesquisa por página. Se você precisar de várias pesquisas, rotule-as claramente para indicar sua finalidade.
+ * - Se possível, forneça sugestões de pesquisa, seja em um helptext ou sugestão de pesquisa que é um autocomplete. Isso ajuda os usuários a encontrar o que estão procurando, especialmente se os itens pesquisáveis forem complexos.
+ *
+ * #### Acessibilidade tratada no componente
+ *
+ * Algumas diretrizes de acessibilidade já são tratadas no componente, internamente, e não podem ser alteradas pelo proprietário do conteúdo. São elas:
+ *
+ * - Permitir a interação via teclado (2.1.1: Keyboard (A));
+ * - Alteração entre os estados precisa ser indicada por mais de um elemento além da cor (1.4.1: Use of Color);
+ */
@Directive()
export class PoSearchBaseComponent {
- @Input({ alias: 'p-disabled', transform: convertToBoolean }) disabled?: boolean;
+ private _literals?: PoSearchLiterals;
+ private _ariaLabel?: string;
+ private language: string;
- @Input('p-loading') loading: boolean;
+ /**
+ * @optional
+ *
+ * @description
+ *
+ * Desabilita o po-search e não permite que o usuário interaja com o mesmo.
+ *
+ * @default `false`
+ */
+ @Input({ alias: 'p-disabled', transform: convertToBoolean }) disabled?: boolean;
+ /**
+ * @optional
+ *
+ * @description
+ *
+ * Lista de itens que serão utilizados para pesquisa
+ */
@Input('p-items') items: Array = [];
+ /**
+ * @optional
+ *
+ * @description
+ *
+ * Define um aria-label para o po-search.
+ *
+ * > Devido o componente não possuir uma label assim como outros campos de texto, o `aria-label` é utilizado para acessibilidade.
+ */
+ @Input('p-aria-label') set ariaLabel(value: string) {
+ this._ariaLabel = value;
+
+ if (value !== this.literals.search) {
+ this._ariaLabel = `${this._ariaLabel} ${this.literals.search}`;
+ }
+ }
+
+ get ariaLabel(): string {
+ return this._ariaLabel;
+ }
+
+ /**
+ * @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.
+ */
@Input('p-filter-keys') filterKeys: Array = [];
- @Input('p-filter-type') filterType: PoFilterMode = PoFilterMode.startsWith;
+ /**
+ * @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.
+ */
+ @Input('p-icon') icon: string | TemplateRef;
+ /**
+ * @optional
+ *
+ * @description
+ *
+ * Objeto com as literais usadas no `po-search`.
+ *
+ * Para utilizar basta passar a literal que deseja customizar:
+ *
+ * ```
+ * const customLiterals: PoSearchLiterals = {
+ * search: 'Pesquisar',
+ * clean: 'Limpar',
+ * };
+ * ```
+ *
+ * E para carregar a literal customizada, basta apenas passar o objeto para o componente.
+ *
+ * ```
+ *
+ *
+ * ```
+ *
+ * > O objeto padrão de literais será traduzido de acordo com o idioma do
+ * [`PoI18nService`](/documentation/po-i18n) ou do browser.
+ */
+ @Input('p-literals') set literals(value: PoSearchLiterals) {
+ if (value instanceof Object && !(value instanceof Array)) {
+ this._literals = {
+ ...poSearchLiteralsDefault[poLocaleDefault],
+ ...poSearchLiteralsDefault[this.language],
+ ...value
+ };
+ } else {
+ this._literals = poSearchLiteralsDefault[this.language];
+ }
+ }
+
+ get literals() {
+ return this._literals || poSearchLiteralsDefault[this.language];
+ }
+
+ /**
+ * @optional
+ *
+ * @description
+ *
+ * Define o modo de pesquisa utilizado no campo de busca, quando habilitado. Valores definidos no enum: PoSearchFilterMode
+ *
+ * @default `startsWith`
+ */
+ @Input('p-filter-type') filterType: PoSearchFilterMode = PoSearchFilterMode.startsWith;
+
+ /**
+ * @optional
+ *
+ * @description
+ *
+ * Evento disparado ao alterar valor do model.
+ */
+ @Output('p-change-model') changeModel: EventEmitter = new EventEmitter();
+
+ /**
+ * @optional
+ *
+ * @description
+ *
+ * Pode ser informada uma função que será disparada quando houver alterações no input.
+ */
@Output('p-filtered-items-change') filteredItemsChange = new EventEmitter>();
+ /**
+ * @optional
+ *
+ * @description
+ *
+ * Pode ser informada uma função que será disparada quando houver alterações nos filtros.
+ */
@Output('p-filter') filter: EventEmitter = new EventEmitter();
+
+ constructor(languageService: PoLanguageService) {
+ this.language = languageService.getShortLanguage();
+ }
}
diff --git a/projects/ui/src/lib/components/po-search/po-search-filter-mode.enum.ts b/projects/ui/src/lib/components/po-search/po-search-filter-mode.enum.ts
deleted file mode 100644
index ac0870791e..0000000000
--- a/projects/ui/src/lib/components/po-search/po-search-filter-mode.enum.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * @usedBy PoSearchComponent
- *
- * @description
- *
- * Define o tipo de busca usado no po-seach.
- */
-export enum PoFilterMode {
- /** Verifica se o texto *inicia* com o valor pesquisado. */
- startsWith,
- /** Verifica se o texto *contém* o valor pesquisado. */
- contains,
- /** Verifica se o texto *finaliza* com o valor pesquisado. */
- endsWith
-}
diff --git a/projects/ui/src/lib/components/po-search/po-search.component.html b/projects/ui/src/lib/components/po-search/po-search.component.html
index f6eb7b38e3..5bb831edbd 100644
--- a/projects/ui/src/lib/components/po-search/po-search.component.html
+++ b/projects/ui/src/lib/components/po-search/po-search.component.html
@@ -1,12 +1,14 @@
-
+
-
-
+
-
-
+
+
diff --git a/projects/ui/src/lib/components/po-search/po-search.component.spec.ts b/projects/ui/src/lib/components/po-search/po-search.component.spec.ts
index 80f604f0ed..268740e0d3 100644
--- a/projects/ui/src/lib/components/po-search/po-search.component.spec.ts
+++ b/projects/ui/src/lib/components/po-search/po-search.component.spec.ts
@@ -1,8 +1,8 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { ElementRef, EventEmitter } from '@angular/core';
+import { ElementRef } from '@angular/core';
import { PoSearchComponent } from './po-search.component';
-import { PoFilterMode } from './po-search-filter-mode.enum';
+import { PoSearchFilterMode } from './enum/po-search-filter-mode.enum';
describe('PoSearchComponent', () => {
let component: PoSearchComponent;
@@ -32,15 +32,16 @@ describe('PoSearchComponent', () => {
});
it('clearSearch: should clear the search', () => {
- component.clearSearch();
+ const inputElement = document.createElement('input');
+ document.body.appendChild(inputElement);
- expect(component.poSearchInput.nativeElement.value).toBe('');
+ component.poSearchInput = { nativeElement: inputElement };
- spyOn(component, 'onSearchChange');
component.clearSearch();
- expect(component.onSearchChange).toHaveBeenCalledWith('');
- expect(component.filteredItemsChange.emit).toHaveBeenCalledWith(component.items);
+ expect(inputElement.value).toBe('');
+
+ document.body.removeChild(inputElement);
});
it('onSearchChange: should filter items based on search text and emit filtered items', () => {
@@ -51,7 +52,7 @@ describe('PoSearchComponent', () => {
});
it('onSearchChange: should filter items based on search text using startsWith', () => {
- component.filterType = PoFilterMode.startsWith;
+ component.filterType = PoSearchFilterMode.startsWith;
component.onSearchChange('Text');
@@ -60,7 +61,7 @@ describe('PoSearchComponent', () => {
});
it('onSearchChange: should filter items based on search text using endsWith', () => {
- component.filterType = PoFilterMode.endsWith;
+ component.filterType = PoSearchFilterMode.endsWith;
component.onSearchChange('2');
@@ -76,7 +77,7 @@ describe('PoSearchComponent', () => {
});
it('onSearchChange: should return false if filter mode is not recognized', () => {
- component.filterType = ('invalidMode' as unknown) as PoFilterMode;
+ component.filterType = ('invalidMode' as unknown) as PoSearchFilterMode;
const result = component.onSearchChange('text');
@@ -97,7 +98,7 @@ describe('PoSearchComponent', () => {
});
it('onSearchChange: should filter items based on search text using contains', () => {
- component.filterType = PoFilterMode.contains;
+ component.filterType = PoSearchFilterMode.contains;
component.onSearchChange('ext');
@@ -113,7 +114,7 @@ describe('PoSearchComponent', () => {
it('onSearchChange: should handle null value', () => {
const searchText = 'example';
component.filterKeys = ['name'];
- component.filterType = PoFilterMode.contains;
+ component.filterType = PoSearchFilterMode.contains;
component.items = [{ name: null }];
component.onSearchChange(searchText);
diff --git a/projects/ui/src/lib/components/po-search/po-search.component.ts b/projects/ui/src/lib/components/po-search/po-search.component.ts
index c7aaa3a677..b9cb2f2f7b 100644
--- a/projects/ui/src/lib/components/po-search/po-search.component.ts
+++ b/projects/ui/src/lib/components/po-search/po-search.component.ts
@@ -1,17 +1,31 @@
-import { PoFilterMode } from './po-search-filter-mode.enum';
-
import { Component, ElementRef, OnInit, Renderer2, ViewChild } from '@angular/core';
import { PoLanguageService } from '../../services/po-language/po-language.service';
-import { PoSearchLiterals } from './literals/po-search-literals';
import { PoSearchBaseComponent } from './po-search-base.component';
-
-export const poSearchLiteralsDefault = {
- en: { search: 'Search' },
- es: { search: 'Buscar' },
- pt: { search: 'Pesquisar' },
- ru: { search: 'Поиск' }
-};
-
+import { PoSearchFilterMode } from './enum/po-search-filter-mode.enum';
+
+/**
+ * @docsExtends PoSearchBaseComponent
+ *
+ * @example
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ */
@Component({
selector: 'po-search',
templateUrl: './po-search.component.html'
@@ -21,18 +35,11 @@ export class PoSearchComponent extends PoSearchBaseComponent implements OnInit {
filteredItems: Array = [];
- public literals?: any;
-
constructor(public languageService: PoLanguageService, private renderer: Renderer2) {
- super();
+ super(languageService);
}
ngOnInit(): void {
- this.literals = {
- ...poSearchLiteralsDefault[this.languageService?.getLanguageDefault()],
- ...poSearchLiteralsDefault[this.languageService?.getShortLanguage()]
- };
-
this.filteredItems = this.items;
}
@@ -40,6 +47,7 @@ export class PoSearchComponent extends PoSearchBaseComponent implements OnInit {
this.poSearchInput.nativeElement.value = '';
this.onSearchChange('');
this.filteredItemsChange.emit(this.items);
+ this.poSearchInput.nativeElement.focus();
}
onBlur(): void {
@@ -64,22 +72,22 @@ export class PoSearchComponent extends PoSearchBaseComponent implements OnInit {
value = value != null ? value.toLowerCase() : null;
- if (this.filterType === PoFilterMode.startsWith) {
+ if (this.filterType === PoSearchFilterMode.startsWith) {
return value != null && value.startsWith(searchText);
- } else if (this.filterType === PoFilterMode.contains) {
+ } else if (this.filterType === PoSearchFilterMode.contains) {
return value != null && value.includes(searchText);
- } else if (this.filterType === PoFilterMode.endsWith) {
+ } 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);
}
+ this.changeModel.emit(searchText);
}
}
diff --git a/projects/ui/src/lib/components/po-search/samples/sample-po-search-basic/sample-po-search-basic.component.html b/projects/ui/src/lib/components/po-search/samples/sample-po-search-basic/sample-po-search-basic.component.html
new file mode 100644
index 0000000000..b1bba60709
--- /dev/null
+++ b/projects/ui/src/lib/components/po-search/samples/sample-po-search-basic/sample-po-search-basic.component.html
@@ -0,0 +1 @@
+
diff --git a/projects/ui/src/lib/components/po-search/samples/sample-po-search-basic/sample-po-search-basic.component.ts b/projects/ui/src/lib/components/po-search/samples/sample-po-search-basic/sample-po-search-basic.component.ts
new file mode 100644
index 0000000000..da5aa3d572
--- /dev/null
+++ b/projects/ui/src/lib/components/po-search/samples/sample-po-search-basic/sample-po-search-basic.component.ts
@@ -0,0 +1,7 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'sample-po-search-basic',
+ templateUrl: './sample-po-search-basic.component.html'
+})
+export class SamplePoSearchBasicComponent {}
diff --git a/projects/ui/src/lib/components/po-search/samples/sample-po-search-find-people/sample-po-search-find-people.component.html b/projects/ui/src/lib/components/po-search/samples/sample-po-search-find-people/sample-po-search-find-people.component.html
new file mode 100644
index 0000000000..b03744e12b
--- /dev/null
+++ b/projects/ui/src/lib/components/po-search/samples/sample-po-search-find-people/sample-po-search-find-people.component.html
@@ -0,0 +1,31 @@
+