Skip to content

Commit

Permalink
search filters: refactoring to be more generic
Browse files Browse the repository at this point in the history
The mocklab service no longer exists. Its data have been migrated
to the new wiremock service.

* Fixes api mock for the tester.
* Changes search filters processing and display.
* Updates search filters on the document route.

Co-Authored-by: Bertrand Zuchuat <[email protected]>
  • Loading branch information
Garfield-fr committed Dec 6, 2023
1 parent ff17035 commit cb3c8ba
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 73 deletions.
4 changes: 2 additions & 2 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@
"localhost": {
"proxyConfig": "proxies/localhost.json"
},
"mocklab": {
"proxyConfig": "proxies/mocklab.json"
"wiremockapi": {
"proxyConfig": "proxies/wiremockapi.json"
},
"production": {
"browserTarget": "ng-core-tester:build:production"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"scripts": {
"ng": "ng",
"serve": "ng serve --configuration mocklab",
"serve": "ng serve --configuration wiremockapi",
"build-lib": "ng build --configuration production @rero/ng-core",
"pack": "ng build --configuration production @rero/ng-core; npm pack dist/rero/ng-core",
"test-lib": "ng test @rero/ng-core",
Expand Down
22 changes: 20 additions & 2 deletions projects/ng-core-tester/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ const routes: Routes = [
url: {
routerLink: ['/record', 'search', 'documents'],
title: 'Link to search document'
}
},
},
{
label: 'Open access',
Expand All @@ -321,12 +321,30 @@ const routes: Routes = [
link: 'https://sonar.rero.ch',
target: '_blank',
title: 'Link to Sonar interface'
}
},
showIfQuery: true
},
{
label: 'Another filter',
filter: 'other',
value: '1'
},
{
label: 'Show Only',
filters: [
{
label: 'Online resources',
filter: 'online',
value: 'true',
showIfQuery: true
},
{
label: 'Physical resources',
filter: 'not_online',
value: 'true',
showIfQuery: true
}
]
}
],
allowEmptySearch: true,
Expand Down
28 changes: 23 additions & 5 deletions projects/rero/ng-core/src/lib/record/record.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* RERO angular core
* Copyright (C) 2020 RERO
* Copyright (C) 2020-2023 RERO
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
Expand All @@ -16,7 +16,7 @@
*/

/**
* Class representing a record set retured by API.
* Class representing a record set returned by API.
*/
export class Record {
aggregations: any;
Expand Down Expand Up @@ -61,13 +61,15 @@ export interface SearchField {
selected?: boolean;
}

/**
* Interface representing a search filter.
*/
/** Interface representing a search filter */
export interface SearchFilter {
filter: string;
label: string;
value: string;
/* Display filter only if there is a query.
This is overridden by the allowEmptySearch parameter
in the resource configuration. */
showIfQuery?: boolean;
/* If you set this value, the url parameter will still be present,
but with different values */
disabledValue?: string;
Expand All @@ -81,6 +83,22 @@ export interface SearchFilter {
};
}

/**
* Interface representing a collection of SearchFilters with label.
* The label adds a title to the interface.
*
* Example on the interface:
*
* Show only (label):
* Filter 1
* Filter 2
* etc…
*/
export interface SearchFilterSection {
label: string;
filters: SearchFilter[];
}

/** Interfaces for an aggregation */
export interface Aggregation {
key: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,17 @@ export class ListFiltersComponent implements OnChanges {
*/
ngOnChanges(changes: SimpleChanges): void {
// hide searchFilters defined on route config from list of filters
this.filtersToHide = [...new Set<string>([
...this.filtersToHide,
...this.searchFilters.map((filter: any) => filter.filter)
])];
const filterSet = [];
this.searchFilters.forEach((filter: any) => {
if (!filter.filters) {
filterSet.push(filter.filter);
} else {
filter.filters.forEach((fSection: any) => {
filterSet.push(fSection.filter);
});
}
});
this.filtersToHide = [...new Set<string>([...this.filtersToHide, ...filterSet])];

if (changes?.aggregationsFilters) {
this._ref.detach();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,54 +141,51 @@ <h5 *ngIf="typesInTabs.length === 1 && showLabel">

<ng-container *ngIf="searchFilters">
<div class="mb-2">
<div *ngFor="let searchfilter of searchFilters">
<!-- filters online and not_online -->
<div class="mt-2" *ngIf="showSearchFilters() && searchfilter.filter === 'online'">{{ 'Show only:' | translate }}</div>
<ng-container *ngIf="searchfilter.filter === 'online' || searchfilter.filter === 'not_online'; else otherSearchFilters">
<div class="custom-control custom-switch" *ngIf="showSearchFilters()">
<input
type="checkbox"
class="custom-control-input"
id="customSwitch-{{ searchfilter.filter }}"
(click)="searchFilter(searchfilter)"
[checked]="isFilterActive(searchfilter)"
>
<label class="custom-control-label" for="customSwitch-{{ searchfilter.filter }}">{{ searchfilter.label | translate }}</label>
</div>
<ng-container *ngFor="let searchFilter of searchFilters">
<ng-container *ngIf="!searchFilter.filters; else searchFilterWithSection">
<ng-container *ngIf="showFilter(searchFilter)" [ngTemplateOutlet]="searchFilterTemplate" [ngTemplateOutletContext]="{filter: searchFilter}"></ng-container>
</ng-container>
<!-- other types of filters -->
<ng-template #otherSearchFilters>
<div class="custom-control custom-switch">
<input
type="checkbox"
class="custom-control-input"
id="customSwitch-{{ searchfilter.filter }}"
(click)="searchFilter(searchfilter)"
[checked]="isFilterActive(searchfilter)"
>
<label class="custom-control-label" for="customSwitch-{{ searchfilter.filter }}">{{ searchfilter.label | translate }}</label>
<ng-container *ngIf="searchfilter.url">
<ng-container *ngIf="searchfilter.url.external; else internal">
<a
class="ml-1 text-dark"
[title]="searchfilter.url.title"
[href]="searchfilter.url.link"
[target]="searchfilter.url.target"
>
<i class="fa fa-info-circle" aria-hidden="true"></i>
</a>
</ng-container>
<ng-template #internal>
<a class="ml-1 text-dark" [title]="searchfilter.url.title" [routerLink]="searchfilter.url.routerLink">
<i class="fa fa-info-circle" aria-hidden="true"></i>
</a>
</ng-template>
<ng-template #searchFilterWithSection>
<ng-container *ngIf="showFilterSection(searchFilter)">
<div class="mt-2" translate>{{ searchFilter.label }}</div>
<ng-container *ngFor="let searchFilterSection of searchFilter.filters">
<ng-container *ngIf="showFilter(searchFilterSection)" [ngTemplateOutlet]="searchFilterTemplate" [ngTemplateOutletContext]="{filter: searchFilterSection}"></ng-container>
</ng-container>
</div>
</ng-container>
</ng-template>
</div>
</ng-container>
</div>
</ng-container>
<ng-template #searchFilterTemplate let-filter="filter">
<div class="custom-control custom-switch">
<input
type="checkbox"
class="custom-control-input"
id="customSwitch-{{ filter.filter }}"
(click)="searchFilter(filter)"
[checked]="isFilterActive(filter)"
>
<label class="custom-control-label" for="customSwitch-{{ filter.filter }}">{{ filter.label | translate }}</label>
<ng-container *ngIf="filter.url">
<ng-container *ngIf="filter.url.external; else internal">
<a
class="ml-1 text-dark"
[title]="filter.url.title"
[href]="filter.url.link"
[target]="filter.url.target"
>
<i class="fa fa-info-circle" aria-hidden="true"></i>
</a>
</ng-container>
<ng-template #internal>
<a class="ml-1 text-dark" [title]="filter.url.title" [routerLink]="filter.url.routerLink">
<i class="fa fa-info-circle" aria-hidden="true"></i>
</a>
</ng-template>
</ng-container>
</div>
</ng-template>

<ng-container *ngIf="!showEmptySearchMessage || q">
<ng-content select="[top-facets]"></ng-content>
<div *ngFor="let item of aggregations">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ import { ApiService } from '../../api/api.service';
import { Error } from '../../error/error';
import { ActionStatus } from '../action-status';
import { JSONSchema7 } from '../editor/editor.component';
import { Aggregation, Record, SearchField, SearchFilter, SearchResult } from '../record';
import { Aggregation, Record, SearchField, SearchFilter, SearchFilterSection, SearchResult } from '../record';
import { RecordUiService } from '../record-ui.service';
import { RecordService } from '../record.service';
import { AggregationsFilter, RecordSearchService } from './record-search.service';
import { createTrue } from 'typescript';

export interface SearchParams {
currentType: string;
Expand All @@ -41,7 +42,7 @@ export interface SearchParams {
aggregationsFilters: Array<AggregationsFilter>;
sort: string;
searchFields: Array<SearchField>;
searchFilters: Array<SearchFilter>;
searchFilters: Array<SearchFilter|SearchFilterSection>;
}

export interface SortOption {
Expand Down Expand Up @@ -126,7 +127,7 @@ export class RecordSearchComponent implements OnInit, OnChanges, OnDestroy {
/** List of fields on which we can do a specific search. */
searchFields: Array<SearchField> = [];
/** List of fields on which we can filter. */
searchFilters: Array<SearchFilter> = [];
searchFilters: Array<SearchFilter|SearchFilterSection> = [];
/** If we need to show the empty search message info. */
showEmptySearchMessage = false;
/** Export formats configuration. */
Expand Down Expand Up @@ -567,7 +568,7 @@ export class RecordSearchComponent implements OnInit, OnChanges, OnDestroy {

/**
* Get component view for the current resource type.
* @retun A component for displaying result item.
* @return A component for displaying result item.
*/
getResultItemComponentView() {
return (this._config.component)
Expand Down Expand Up @@ -888,7 +889,7 @@ export class RecordSearchComponent implements OnInit, OnChanges, OnDestroy {
this._setDefaultSort();

// load search filters
this.searchFilters = this._config.searchFilters ? this._config.searchFilters : [];
this.searchFilters = this._config.searchFilters || [];

// load export options
this.exportOptions = this._exportFormats();
Expand Down Expand Up @@ -996,8 +997,7 @@ export class RecordSearchComponent implements OnInit, OnChanges, OnDestroy {
*/
private _extractPersistentAggregationsFilters(): Array<AggregationsFilter> {
const persistent = [];
this.searchFilters
.filter(filter => filter.persistent === true)
this._flatSearchFilters().filter(filter => filter.persistent === true)
.forEach((filter: SearchFilter) => {
if (this._activatedRoute.snapshot.queryParams.hasOwnProperty(filter.filter)) {
const data = this._activatedRoute.snapshot.queryParams[filter.filter];
Expand All @@ -1010,6 +1010,22 @@ export class RecordSearchComponent implements OnInit, OnChanges, OnDestroy {
return persistent;
}

/**
* Make all search filters on same array level
* @returns - A filters array
*/
private _flatSearchFilters(): SearchFilter[] {
const flatFilters = [];
this.searchFilters.forEach((searchFilter: any) => {
if (searchFilter.filters) {
searchFilter.filters.forEach((filter: any) => flatFilters.push(filter))
} else {
flatFilters.push(searchFilter);
}
});
return flatFilters;
}

/**
* Map aggregation records data to corresponding aggregation.
*
Expand Down Expand Up @@ -1047,7 +1063,7 @@ export class RecordSearchComponent implements OnInit, OnChanges, OnDestroy {
bucket.aggregationKey = aggregationKey;
for (const k of Object.keys(bucket).filter(key => bucket[key].buckets)) {
for (const childBucket of bucket[k].buckets) {
// store the parent: usefull to remove parent filters
// store the parent: useful to remove parent filters
childBucket.parent = bucket;
// store the aggregation key as we re-organize the bucket structure
bucket.indeterminate ||= this._recordSearchService.hasFilter(k, childBucket.key);
Expand Down Expand Up @@ -1089,10 +1105,31 @@ export class RecordSearchComponent implements OnInit, OnChanges, OnDestroy {
}

/**
* Check if to show search filters.
* @returns boolean
* Show the filter's section
* @param searchFilterSection - Collection of filter
* @returns true if the filter's section is show
*/
showFilterSection(searchFilterSection: SearchFilterSection): boolean {
return searchFilterSection.filters.some(
(filter: SearchFilter) => {
return this._config.allowEmptySearch ? true : (this.q && filter.showIfQuery === true) || !filter?.showIfQuery;
}
);
}

/**
* Show the filter
* @param searchFilter - search Filter
* @returns true if the filter is show
*/
showSearchFilters(): boolean {
return !(this._config.allowEmptySearch === false && !this.q);
showFilter(searchFilter: SearchFilter) {
if (this._config.allowEmptySearch) {
return true;
}
if (!this.q) {
return !(searchFilter.showIfQuery === true);
} else {
return (searchFilter.showIfQuery === true || !searchFilter?.showIfQuery);
}
}
}
8 changes: 4 additions & 4 deletions proxies/mocklab.json → proxies/wiremockapi.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
{
"/api/*": {
"target": "https://l985g.mocklab.io",
"target": "https://l985g.wiremockapi.cloud",
"secure": false,
"logLevel": "debug",
"changeOrigin": true,
"pathRewrite": {
"^/api": "https://l985g.mocklab.io/api"
"^/api": "https://l985g.wiremockapi.cloud/api"
}
},
"/schemas/*": {
"target": "https://l985g.mocklab.io",
"target": "https://l985g.wiremockapi.cloud",
"secure": false,
"logLevel": "debug",
"changeOrigin": true,
"pathRewrite": {
"^/schemas": "https://l985g.mocklab.io/schemas"
"^/schemas": "https://l985g.wiremockapi.cloud/schemas"
}
}
}

0 comments on commit cb3c8ba

Please sign in to comment.