Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Advance search search page #2608

Merged
merged 69 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from 63 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
5a9269e
Update homepage-config.interface.ts
GauravD2t Oct 10, 2022
7336188
Merge branch 'DSpace:main' into main
GauravD2t May 2, 2023
fed9244
advance Search add
GauravD2t May 2, 2023
f2c6f3f
slove error while unti test
GauravD2t May 4, 2023
9910589
write unit test
GauravD2t May 5, 2023
675c354
Ensures select element has an accessible name
GauravD2t May 5, 2023
13f6d4e
resolve conflict
GauravD2t May 15, 2023
5d3af64
Merge branch 'main' into main
GauravD2t May 16, 2023
326777a
change data pass into url
GauravD2t May 16, 2023
22f1844
Merge branch 'main' of https://github.com/GauravD2t/Advanced-search
GauravD2t May 16, 2023
0e28e56
error resolve
GauravD2t May 16, 2023
f6ba079
Merge branch 'main' into main
GauravD2t May 17, 2023
e7f8882
Merge branch 'DSpace:main' into main
GauravD2t May 18, 2023
e0d8566
Merge branch 'DSpace:main' into main
GauravD2t May 19, 2023
8b97c19
Merge branch 'main' into main
GauravD2t May 20, 2023
d6d0d40
Merge branch 'main' into main
GauravD2t May 23, 2023
e6253da
Merge branch 'DSpace:main' into main
GauravD2t May 24, 2023
5ad3f97
Merge branch 'DSpace:main' into main
GauravD2t May 26, 2023
c1acb11
Search.Filters.Applied.F.Title given name as Title
GauravD2t May 26, 2023
79d961f
Advanced filters configurable in the User interface (in config.*.yml)
GauravD2t May 26, 2023
c719782
Merge branch 'DSpace:main' into main
GauravD2t Jun 1, 2023
06be9c1
Merge branch 'DSpace:main' into main
GauravD2t Jun 2, 2023
0f1ce6b
turn on/off and add filters as a list
GauravD2t Jun 2, 2023
e95f8ba
remove currenturl
GauravD2t Jun 2, 2023
3dbf8c4
resolve
GauravD2t Jun 2, 2023
b65f648
change envierment config and url pass data
GauravD2t Jun 3, 2023
11dd84d
Merge branch 'main' into main
GauravD2t Jun 3, 2023
8d87e45
Merge branch 'main' into main
GauravD2t Jun 6, 2023
10f67d9
Merge branch 'main' into main
GauravD2t Jun 7, 2023
e6a00f3
Merge branch 'main' into main
GauravD2t Jun 8, 2023
c107de5
Merge branch 'main' into main
GauravD2t Jun 9, 2023
4bc1e6e
Merge branch 'main' into main
GauravD2t Jun 13, 2023
13079c3
Merge branch 'DSpace:main' into main
GauravD2t Jun 14, 2023
94b6b07
set enabled: false ,and remove debugger ,remove searchConfig
GauravD2t Jun 14, 2023
66f09c4
Merge branch 'main' of https://github.com/GauravD2t/Advanced-search
GauravD2t Jun 15, 2023
b34240c
Merge branch 'main' into main
GauravD2t Jun 16, 2023
02b521b
test case with expect and resolve conflicts
GauravD2t Jun 22, 2023
7ecee60
expect clauses add
GauravD2t Jun 22, 2023
5d3f202
Merge branch 'main' into main
GauravD2t Jun 23, 2023
0f2d4f4
Merge branch 'main' into main
GauravD2t Jun 24, 2023
740074c
Merge branch 'main' into main
GauravD2t Jul 3, 2023
d0f23db
Merge branch 'DSpace:main' into main
GauravD2t Jul 7, 2023
7fe4d5f
Merge branch 'main' into main
GauravD2t Jul 10, 2023
6f49349
Merge branch 'main' into main
GauravD2t Jul 26, 2023
97684d0
Merge branch 'main' into main
GauravD2t Jul 31, 2023
139a70e
Merge branch 'main' of https://github.com/GauravD2t/Advanced-search
GauravD2t Nov 8, 2023
44431d1
Merge branch 'main' of https://github.com/GauravD2t/Advanced-search
GauravD2t Nov 9, 2023
d1dd23a
Merge branch 'main' of https://github.com/GauravD2t/Advanced-search
GauravD2t Nov 20, 2023
df37894
Merge branch 'advance-search-search-page' of https://github.com/Gaura…
GauravD2t Dec 7, 2023
8abcedf
reslove conflict
GauravD2t Dec 27, 2023
6c51436
reslove conflict
GauravD2t Jan 5, 2024
a44bfbe
Merge branch 'advance-search-search-page' of https://github.com/Gaura…
GauravD2t Jan 5, 2024
47675be
merge added
GauravD2t Jan 6, 2024
3c55fbd
Merge branch 'DSpace:main' into advance-search-search-page
GauravD2t Jan 29, 2024
5cc602b
Merge branch 'DSpace:main' into advance-search-search-page
GauravD2t Feb 3, 2024
6591cdd
remove commented and design change advance search
GauravD2t Feb 3, 2024
7282b44
Merge branch 'DSpace:main' into advance-search-search-page
GauravD2t Feb 12, 2024
c225941
moving the "add" button to its own col-lg-12
GauravD2t Feb 12, 2024
b918413
Merge branch 'advance-search-search-page' of https://github.com/Gaura…
GauravD2t Feb 12, 2024
e55f124
Merge branch 'advance-search-search-page' of https://github.com/Gaura…
GauravD2t Feb 16, 2024
25302ce
resolve conflict
GauravD2t Feb 16, 2024
af0199f
Merge branch 'advance-search-search-page' of https://github.com/Gaura…
GauravD2t Feb 22, 2024
8fe2356
reslove error
GauravD2t Feb 22, 2024
beb20ea
Merge branch 'DSpace:main' into advance-search-search-page
GauravD2t Feb 23, 2024
3243c71
merge en.json5 file
GauravD2t Feb 23, 2024
190205a
remove trailing spaces
GauravD2t Feb 23, 2024
7ab9b65
Merge branch 'advance-search-search-page' of https://github.com/Gaura…
GauravD2t Feb 26, 2024
6c8175c
resolve Conflicts
GauravD2t Feb 26, 2024
43f68bd
Fix typo. property is filter not filters
tdonohue Feb 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions config/config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -400,3 +400,12 @@ comcolSelectionSort:
# suggestion:
# - collectionId: 8f7df5ca-f9c2-47a4-81ec-8a6393d6e5af
# source: "openaire"


# Search settings
search:
# Settings to enable/disable or configure advanced search filters.
advancedFilters:
enabled: false
# List of filters to enable in "Advanced Search" dropdown
filters: [ 'title', 'author', 'subject', 'entityType' ]
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"
GauravD2t marked this conversation as resolved.
Show resolved Hide resolved
[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'"
GauravD2t marked this conversation as resolved.
Show resolved Hide resolved
aria-label="operator" name="operator" id="operator" formControlName="operator" required>
<option value="equals">{{'search.filters.operator.equals.text'| translate}}</option>
GauravD2t marked this conversation as resolved.
Show resolved Hide resolved
<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>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import '../search-filters/search-filter/search-filter.component.scss';
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 src/app/shared/search/advanced-search/advanced-search.component.ts
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');

Check warning on line 62 in src/app/shared/search/advanced-search/advanced-search.component.ts

View check run for this annotation

Codecov / codecov/patch

src/app/shared/search/advanced-search/advanced-search.component.ts#L62

Added line #L62 was not covered by tests
}

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);

Check warning on line 82 in src/app/shared/search/advanced-search/advanced-search.component.ts

View check run for this annotation

Codecov / codecov/patch

src/app/shared/search/advanced-search/advanced-search.component.ts#L82

Added line #L82 was not covered by tests
} else {
this.router.navigateByUrl(this.router.url + '&f.' + data.filter + '=' + data.textsearch + ',' + data.operator);

Check warning on line 84 in src/app/shared/search/advanced-search/advanced-search.component.ts

View check run for this annotation

Codecov / codecov/patch

src/app/shared/search/advanced-search/advanced-search.component.ts#L84

Added line #L84 was not covered by tests
}
}

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;

Check warning on line 96 in src/app/shared/search/advanced-search/advanced-search.component.ts

View check run for this annotation

Codecov / codecov/patch

src/app/shared/search/advanced-search/advanced-search.component.ts#L96

Added line #L96 was not covered by tests
}
}
finishSlide(event: any): void {
if (event.fromState === 'collapsed') {
this.closed = false;

Check warning on line 101 in src/app/shared/search/advanced-search/advanced-search.component.ts

View check run for this annotation

Codecov / codecov/patch

src/app/shared/search/advanced-search/advanced-search.component.ts#L101

Added line #L101 was not covered by tests
}
if (event.toState === 'collapsed') {
this.notab = true;
}
}
toggle() {
this.collapsedSearch = !this.collapsedSearch;

Check warning on line 108 in src/app/shared/search/advanced-search/advanced-search.component.ts

View check run for this annotation

Codecov / codecov/patch

src/app/shared/search/advanced-search/advanced-search.component.ts#L108

Added line #L108 was not covered by tests
}
private isCollapsed(): boolean {
return !this.collapsedSearch;
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ <h3>{{"search.filters.head" | translate}}</h3>
<ds-search-filter [scope]="currentScope" [filter]="filter" [inPlaceSearch]="inPlaceSearch" [refreshFilters]="refreshFilters"></ds-search-filter>
</div>
</div>
<ds-advanced-search *ngIf="appConfig.search.advancedFilters.enabled"
[inPlaceSearch]="inPlaceSearch"></ds-advanced-search>
<a class="btn btn-primary" [routerLink]="[searchLink]" [queryParams]="clearParams | async" queryParamsHandling="merge" role="button"><i class="fas fa-undo"></i> {{"search.filters.reset" | translate}}</a>
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { SearchFiltersComponent } from './search-filters.component';
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 { APP_CONFIG } from 'src/config/app-config.interface';
import { environment } from 'src/environments/environment';

describe('SearchFiltersComponent', () => {
let comp: SearchFiltersComponent;
Expand All @@ -20,7 +22,8 @@ describe('SearchFiltersComponent', () => {
getClearFiltersQueryParams: () => {
},
getSearchLink: () => {
}
},
getConfigurationSearchConfig: () => { },
/* eslint-enable no-empty, @typescript-eslint/no-empty-function */
};

Expand All @@ -37,6 +40,7 @@ describe('SearchFiltersComponent', () => {
{ provide: SearchService, useValue: searchServiceStub },
{ provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
{ provide: SearchFilterService, useValue: searchFiltersStub },
{ provide: APP_CONFIG, useValue: environment },

],
schemas: [NO_ERRORS_SCHEMA]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Router } from '@angular/router';

import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface';
import { SearchService } from '../../../core/shared/search/search.service';
import { RemoteData } from '../../../core/data/remote-data';
import { SearchFilterConfig } from '../models/search-filter-config.model';
Expand All @@ -12,6 +12,7 @@ import { SearchFilterService } from '../../../core/shared/search/search-filter.s
import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-page.component';
import { currentPath } from '../../utils/route.utils';
import { hasValue } from '../../empty.util';
import { PaginatedSearchOptions } from '../models/paginated-search-options.model';

@Component({
selector: 'ds-search-filters',
Expand All @@ -28,7 +29,7 @@ export class SearchFiltersComponent implements OnInit, OnDestroy {
* An observable containing configuration about which filters are shown and how they are shown
*/
@Input() filters: Observable<RemoteData<SearchFilterConfig[]>>;

@Input() searchOptions: PaginatedSearchOptions;
/**
* List of all filters that are currently active with their value set to null.
* Used to reset all filters at once
Expand Down Expand Up @@ -70,6 +71,7 @@ export class SearchFiltersComponent implements OnInit, OnDestroy {
* @param {SearchConfigurationService} searchConfigService
*/
constructor(
@Inject(APP_CONFIG) protected appConfig: AppConfig,
private searchService: SearchService,
private filterService: SearchFilterService,
private router: Router,
Expand Down
3 changes: 2 additions & 1 deletion src/app/shared/search/search.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ import { ThemedSearchComponent } from './themed-search.component';
import { ThemedSearchResultsComponent } from './search-results/themed-search-results.component';
import { ThemedSearchSettingsComponent } from './search-settings/themed-search-settings.component';
import { NouisliderModule } from 'ng2-nouislider';
import { AdvancedSearchComponent } from './advanced-search/advanced-search.component';
import { ThemedSearchFiltersComponent } from './search-filters/themed-search-filters.component';
import { ThemedSearchSidebarComponent } from './search-sidebar/themed-search-sidebar.component';

const COMPONENTS = [
SearchComponent,
ThemedSearchComponent,
Expand All @@ -60,6 +60,7 @@ const COMPONENTS = [
ThemedConfigurationSearchPageComponent,
ThemedSearchResultsComponent,
ThemedSearchSettingsComponent,
AdvancedSearchComponent,
ThemedSearchFiltersComponent,
ThemedSearchSidebarComponent,
];
Expand Down
2 changes: 1 addition & 1 deletion src/app/shared/search/search.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export function stripOperatorFromFilterValue(value: string) {
* @param operator
*/
export function addOperatorToFilterValue(value: string, operator: string) {
if (!value.match(new RegExp(`^.+,(equals|query|authority)$`))) {
if (!value.match(new RegExp(`^.+,(equals|query|authority|contains|notcontains|notequals)$`))) {
return `${value},${operator}`;
}
return value;
Expand Down
Loading
Loading