[ADF-1039] Search results highlighting (#2080)

* Provide a way to highlight the searched word

* Add unit test
This commit is contained in:
Maurizio Vitale
2017-07-13 15:07:48 +01:00
committed by Eugenio Romano
parent 55cd57b49d
commit 8a1281475c
10 changed files with 79 additions and 1 deletions

View File

@@ -57,6 +57,7 @@ import { DataColumnListComponent } from './src/components/data-column/data-colum
import { DataColumnComponent } from './src/components/data-column/data-column.component';
import { UploadDirective } from './src/directives/upload.directive';
import { FileSizePipe } from './src/pipes/file-size.pipe';
import { HighlightPipe } from './src/pipes/text-highlight.pipe';
import { AlfrescoMdlButtonDirective } from './src/components/material/mdl-button.directive';
import { AlfrescoMdlMenuDirective } from './src/components/material/mdl-menu.directive';
@@ -131,6 +132,7 @@ export function createTranslateLoader(http: Http, logService: LogService) {
DataColumnComponent,
DataColumnListComponent,
FileSizePipe,
HighlightPipe,
AdfToolbarComponent
],
providers: providers(),
@@ -150,6 +152,7 @@ export function createTranslateLoader(http: Http, logService: LogService) {
DataColumnComponent,
DataColumnListComponent,
FileSizePipe,
HighlightPipe,
AdfToolbarComponent
]
})

View File

@@ -0,0 +1,40 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'highlight'
})
export class HighlightPipe implements PipeTransform {
constructor() { }
transform(text: string, search: string): any {
if (search && text) {
let pattern = search.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
pattern = pattern.split(' ').filter((t) => {
return t.length > 0;
}).join('|');
const regex = new RegExp(pattern, 'gi');
let result = text.replace(regex, (match) => `<span class="highlight">${match}</span>`);
return result;
} else {
return text;
}
}
}

View File

@@ -58,6 +58,7 @@ results component embedded inside the same component.
| inputType | string | "text" | Type of the input field to render, e.g. "search" or "text" (default) |
| expandable | boolean | true | Whether to use an expanding search control, if false then a regular input is used. |
| autocomplete | boolean | true | Whether the browser should offer field auto-completion for the input field to the user. |
| highlight | boolean | false | Use the true value if you want to see the searched word highlighted. |
| liveSearchEnabled | boolean | true | Whether find-as-you-type suggestions should be offered for matching content items. Set to false to disable. |
| liveSearchRoot | string | "-root-" | NodeRef or node name where the search should start. |
| liveSearchResultType | string | | Node type to filter live search results by, e.g. 'cm:content'. |

View File

@@ -37,6 +37,10 @@
text-overflow: ellipsis;
}
:host /deep/ .highlight {
color: #33afdf;
}
@media screen and (max-width: 400px) {
:host {
right: 0;

View File

@@ -11,7 +11,10 @@
attr.data-automation-id="autocomplete_result_for_{{result.entry.name}}">
<td class="img-td"><img src="{{getMimeTypeIcon(result)}}" alt="{{result.entry.name}}"/></td>
<td>
<div id="result_name_{{idx}}" class="truncate"><b>{{result.entry.name}}</b></div>
<div id="result_name_{{idx}}" *ngIf="highlight; else elseBlock" class="truncate" [innerHtml]="result.entry.name | highlight: searchTerm"></div>
<ng-template #elseBlock>
<div id="result_name_{{idx}}" class="truncate" [innerHtml]="result.entry.name"></div>
</ng-template>
<div id="result_user_{{idx}}" class="truncate">{{result.entry.createdByUser.displayName}}</div>
</td>
</tr>

View File

@@ -110,6 +110,25 @@ describe('AlfrescoSearchAutocompleteComponent', () => {
updateSearchTerm('searchTerm');
});
it('should highlight the searched word', (done) => {
component.highlight = true;
spyOn(searchService, 'getQueryNodesPromise')
.and.returnValue(Promise.resolve(results));
component.resultsLoad.subscribe(() => {
fixture.detectChanges();
let el: any = element.querySelectorAll('table[data-automation-id="autocomplete_results"] tbody tr')[1].children[1].children[0];
expect(el.innerText).toEqual('MyDoc');
let spanHighlight = el.children[0];
expect(spanHighlight.classList[0]).toEqual('highlight');
expect(spanHighlight.innerText).toEqual('My');
done();
});
updateSearchTerm('My');
});
it('should limit the number of returned search results to the configured maximum', (done) => {
spyOn(searchService, 'getQueryNodesPromise')

View File

@@ -52,6 +52,9 @@ export class AlfrescoSearchAutocompleteComponent implements OnInit, OnChanges {
@Input()
resultType: string = null;
@Input()
highlight: boolean = false;
@Output()
fileSelect: EventEmitter<any> = new EventEmitter();

View File

@@ -28,6 +28,7 @@
[resultType]="liveSearchResultType"
[resultSort]="liveSearchResultSort"
[maxResults]="liveSearchMaxResults"
[highlight]="highlight"
[ngClass]="{active: searchActive, valid: searchValid}"
(fileSelect)="onFileClicked($event)"
(searchFocus)="onAutoCompleteFocus($event)"

View File

@@ -41,6 +41,9 @@ export class AlfrescoSearchControlComponent implements OnInit, OnDestroy {
@Input()
expandable: boolean = true;
@Input()
highlight: boolean = false;
@Output()
searchChange = new EventEmitter();