forked from DSpace/dspace-angular
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Advance search search page (DSpace#2608)
* Update homepage-config.interface.ts change comment of homepage-config.interface.ts * advance Search add * slove error while unti test * write unit test * Ensures select element has an accessible name * change data pass into url * error resolve * Search.Filters.Applied.F.Title given name as Title * Advanced filters configurable in the User interface (in config.*.yml) * turn on/off and add filters as a list * remove currenturl * resolve * change envierment config and url pass data * set enabled: false ,and remove debugger ,remove searchConfig * expect clauses add * reslove conflict * merge added * remove commented and design change advance search * moving the "add" button to its own col-lg-12 * resolve conflict * reslove error * merge en.json5 file * remove trailing spaces * resolve Conflicts * Fix typo. property is filter not filters --------- Co-authored-by: Tim Donohue <[email protected]>
- Loading branch information
Showing
16 changed files
with
326 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
src/app/shared/search/advanced-search/advanced-search.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<div class="facet-filter d-block mb-3 p-3" [ngClass]="{ 'focus': focusBox }" role="region"> | ||
<button (click)="toggle()" (focusin)="focusBox = true" (focusout)="focusBox = false" class="filter-name d-flex" | ||
[attr.aria-expanded]="false" | ||
[attr.aria-label]="((collapsedSearch ? 'search.filters.filter.expand' : 'search.filters.filter.collapse') | translate) + ' ' + (('search.advanced.filters.head') | translate | lowercase)" | ||
[attr.data-test]="'filter-toggle' | dsBrowserOnly"> | ||
<span class="h4 d-inline-block text-left mt-auto mb-auto"> | ||
{{'search.advanced.filters.head' | translate}} | ||
</span> | ||
<i class="filter-toggle flex-grow-1 fas p-auto" aria-hidden="true" [ngClass]="collapsedSearch ? 'fa-plus' : 'fa-minus'" | ||
[title]="(collapsedSearch ? 'search.filters.filter.expand' : 'search.filters.filter.collapse') | translate"> | ||
</i> | ||
</button> | ||
<div [@slide]="collapsedSearch ? 'collapsed' : 'expanded'" (@slide.start)="startSlide($event)" | ||
(@slide.done)="finishSlide($event)" class="search-filter-wrapper" | ||
[ngClass]="{ 'closed' : closed, 'notab': notab }"> | ||
<form [class]="'ng-invalid'" [formGroup]="advSearchForm" (ngSubmit)="onSubmit(advSearchForm.value)"> | ||
<div class="row"> | ||
<div class="col-lg-12"> | ||
<select | ||
[className]="(filter.invalid) && (filter.dirty || filter.touched) ? 'form-control is-invalid' :'form-control'" | ||
aria-label="filter" name="filter" id="filter" placeholder="select operator" | ||
formControlName="filter" required> | ||
<ng-container *ngFor="let filter of appConfig.search.advancedFilters.filter;"> | ||
<option [value]="filter"> | ||
{{'search.filters.filter.' + filter + '.text'| translate}} | ||
</option> | ||
</ng-container> | ||
</select> | ||
</div> | ||
<div class="col-lg-12 mt-1"> | ||
<select | ||
[className]="(operator.invalid) && (operator.dirty || operator.touched) ? 'form-control is-invalid' :'form-control'" | ||
aria-label="operator" name="operator" id="operator" formControlName="operator" required> | ||
<option value="equals">{{'search.filters.operator.equals.text'| translate}}</option> | ||
<option value="notequals">{{'search.filters.operator.notequals.text'| translate}}</option> | ||
<option value="contains">{{'search.filters.operator.contains.text'| translate}}</option> | ||
<option value="notcontains">{{'search.filters.operator.notcontains.text'| translate}}</option> | ||
</select> | ||
</div> | ||
<div class="col-lg-12 mt-1"> | ||
<input type="text" aria-label="textsearch" class="form-control" id="textsearch" name="textsearch" | ||
formControlName="textsearch" #text [placeholder]="('filter.search.text.placeholder' | translate)" required> | ||
</div> | ||
<div class="col-lg-12 mt-1"> | ||
<button class="form-control btn w-50 float-right btn-primary" type="submit" | ||
[disabled]="advSearchForm.invalid">{{'advancesearch.form.submit'| translate}}</button> | ||
</div> | ||
</div> | ||
</form> | ||
</div> | ||
</div> |
1 change: 1 addition & 0 deletions
1
src/app/shared/search/advanced-search/advanced-search.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
@import '../search-filters/search-filter/search-filter.component.scss'; |
74 changes: 74 additions & 0 deletions
74
src/app/shared/search/advanced-search/advanced-search.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||
import { FormBuilderService } from '../../../shared/form/builder/form-builder.service'; | ||
import { AdvancedSearchComponent } from './advanced-search.component'; | ||
import { getMockFormBuilderService } from '../../../shared/mocks/form-builder-service.mock'; | ||
import { SearchService } from '../../../core/shared/search/search.service'; | ||
import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-page.component'; | ||
import { SearchConfigurationServiceStub } from '../../testing/search-configuration-service.stub'; | ||
import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service'; | ||
import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; | ||
import { APP_CONFIG } from '../../../../config/app-config.interface'; | ||
import { environment } from '../../../../environments/environment'; | ||
import { RouterStub } from '../../testing/router.stub'; | ||
import { Router } from '@angular/router'; | ||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; | ||
import { BrowserOnlyMockPipe } from '../../testing/browser-only-mock.pipe'; | ||
import { RouterTestingModule } from '@angular/router/testing'; | ||
import { TranslateModule } from '@ngx-translate/core'; | ||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; | ||
describe('AdvancedSearchComponent', () => { | ||
let component: AdvancedSearchComponent; | ||
let fixture: ComponentFixture<AdvancedSearchComponent>; | ||
let builderService: FormBuilderService = getMockFormBuilderService(); | ||
let searchService: SearchService; | ||
let router; | ||
const searchServiceStub = { | ||
/* eslint-disable no-empty,@typescript-eslint/no-empty-function */ | ||
getClearFiltersQueryParams: () => { | ||
}, | ||
getSearchLink: () => { | ||
}, | ||
getConfigurationSearchConfig: () => { }, | ||
/* eslint-enable no-empty, @typescript-eslint/no-empty-function */ | ||
}; | ||
beforeEach(async () => { | ||
await TestBed.configureTestingModule({ | ||
declarations: [AdvancedSearchComponent, BrowserOnlyMockPipe], | ||
imports: [FormsModule, RouterTestingModule, TranslateModule.forRoot(), BrowserAnimationsModule, ReactiveFormsModule], | ||
providers: [ | ||
FormBuilder, | ||
{ provide: APP_CONFIG, useValue: environment }, | ||
{ provide: FormBuilderService, useValue: builderService }, | ||
{ provide: Router, useValue: new RouterStub() }, | ||
{ provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() }, | ||
{ provide: RemoteDataBuildService, useValue: {} }, | ||
{ provide: SearchService, useValue: searchServiceStub }, | ||
], | ||
schemas: [NO_ERRORS_SCHEMA] | ||
}).overrideComponent(AdvancedSearchComponent, { | ||
set: { changeDetection: ChangeDetectionStrategy.Default } | ||
}).compileComponents(); | ||
}); | ||
|
||
beforeEach(() => { | ||
fixture = TestBed.createComponent(AdvancedSearchComponent); | ||
component = fixture.componentInstance; | ||
router = TestBed.inject(Router); | ||
fixture.detectChanges(); | ||
}); | ||
describe('when the getSearchLink method is called', () => { | ||
const data = { filter: 'title', textsearch: 'demo', operator: 'equals' }; | ||
it('should call navigate on the router with the right searchlink and parameters when the filter is provided with a valid operator', () => { | ||
component.advSearchForm.get('textsearch').patchValue('1'); | ||
component.advSearchForm.get('filter').patchValue('1'); | ||
component.advSearchForm.get('operator').patchValue('1'); | ||
|
||
component.onSubmit(data); | ||
expect(router.navigate).toHaveBeenCalledWith([undefined], { | ||
queryParams: { ['f.' + data.filter]: data.textsearch + ',' + data.operator }, | ||
queryParamsHandling: 'merge' | ||
}); | ||
|
||
}); | ||
}); | ||
}); |
115 changes: 115 additions & 0 deletions
115
src/app/shared/search/advanced-search/advanced-search.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import { Component, Inject, Input, OnInit } from '@angular/core'; | ||
import { Router } from '@angular/router'; | ||
import { slide } from '../../animations/slide'; | ||
import { FormBuilder } from '@angular/forms'; | ||
import { FormControl, FormGroup, Validators } from '@angular/forms'; | ||
import { SearchService } from '../../../core/shared/search/search.service'; | ||
import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; | ||
import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-page.component'; | ||
import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface'; | ||
@Component({ | ||
selector: 'ds-advanced-search', | ||
templateUrl: './advanced-search.component.html', | ||
styleUrls: ['./advanced-search.component.scss'], | ||
animations: [slide], | ||
}) | ||
/** | ||
* This component represents the part of the search sidebar that contains advanced filters. | ||
*/ | ||
export class AdvancedSearchComponent implements OnInit { | ||
/** | ||
* True when the search component should show results on the current page | ||
*/ | ||
@Input() inPlaceSearch; | ||
|
||
|
||
/** | ||
* Link to the search page | ||
*/ | ||
notab: boolean; | ||
|
||
closed: boolean; | ||
collapsedSearch = false; | ||
focusBox = false; | ||
|
||
advSearchForm: FormGroup; | ||
constructor( | ||
@Inject(APP_CONFIG) protected appConfig: AppConfig, | ||
private formBuilder: FormBuilder, | ||
protected searchService: SearchService, | ||
protected router: Router, | ||
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService) { | ||
} | ||
|
||
ngOnInit(): void { | ||
|
||
this.advSearchForm = this.formBuilder.group({ | ||
textsearch: new FormControl('', { | ||
validators: [Validators.required], | ||
}), | ||
filter: new FormControl('title', { | ||
validators: [Validators.required], | ||
}), | ||
operator: new FormControl('equals', | ||
{ validators: [Validators.required], }), | ||
|
||
}); | ||
this.collapsedSearch = this.isCollapsed(); | ||
|
||
} | ||
|
||
get textsearch() { | ||
return this.advSearchForm.get('textsearch'); | ||
} | ||
|
||
get filter() { | ||
return this.advSearchForm.get('filter'); | ||
} | ||
|
||
get operator() { | ||
return this.advSearchForm.get('operator'); | ||
} | ||
paramName(filter) { | ||
return 'f.' + filter; | ||
} | ||
onSubmit(data) { | ||
if (this.advSearchForm.valid) { | ||
let queryParams = { [this.paramName(data.filter)]: data.textsearch + ',' + data.operator }; | ||
if (!this.inPlaceSearch) { | ||
this.router.navigate([this.searchService.getSearchLink()], { queryParams: queryParams, queryParamsHandling: 'merge' }); | ||
} else { | ||
if (!this.router.url.includes('?')) { | ||
this.router.navigateByUrl(this.router.url + '?f.' + data.filter + '=' + data.textsearch + ',' + data.operator); | ||
} else { | ||
this.router.navigateByUrl(this.router.url + '&f.' + data.filter + '=' + data.textsearch + ',' + data.operator); | ||
} | ||
} | ||
|
||
this.advSearchForm.reset({ operator: data.operator, filter: data.filter, textsearch: '' }); | ||
} | ||
} | ||
startSlide(event: any): void { | ||
if (event.toState === 'collapsed') { | ||
this.closed = true; | ||
} | ||
if (event.fromState === 'collapsed') { | ||
this.notab = false; | ||
} | ||
} | ||
finishSlide(event: any): void { | ||
if (event.fromState === 'collapsed') { | ||
this.closed = false; | ||
} | ||
if (event.toState === 'collapsed') { | ||
this.notab = true; | ||
} | ||
} | ||
toggle() { | ||
this.collapsedSearch = !this.collapsedSearch; | ||
} | ||
private isCollapsed(): boolean { | ||
return !this.collapsedSearch; | ||
} | ||
|
||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export interface AdvancedSearchConfig { | ||
enabled: boolean; | ||
filter: string[]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.