-
Notifications
You must be signed in to change notification settings - Fork 32
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
Editor: Add a search filters summary component #1037
base: me-date-filter
Are you sure you want to change the base?
Changes from all commits
6dcda70
4d12246
52cadbd
d2ad356
4cd5186
da0c15c
3076468
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<div *ngIf="(fieldValues$ | async)?.length > 0"> | ||
<div | ||
*ngIf="fieldType === 'dateRange'; else valuesBadge" | ||
class="flex flex-row items-center gap-2" | ||
> | ||
<span class="text-gray-800" translate | ||
>search.filters.summaryLabel.{{ fieldName }}</span | ||
> | ||
<gn-ui-badge | ||
*ngFor="let fieldValue of dateRange$ | async" | ||
[style.--gn-ui-badge-text-color]="'black'" | ||
[style.--gn-ui-badge-background-color]="'#F2F2F2'" | ||
[style.--gn-ui-badge-font-weight]="'700'" | ||
[removable]="true" | ||
(badgeRemoveClicked)="removeFilterValue(fieldValue)" | ||
>{{ fieldValue['start'] | date: 'dd.MM.yyyy' }} - | ||
{{ fieldValue['end'] | date: 'dd.MM.yyyy' }}</gn-ui-badge | ||
> | ||
</div> | ||
<ng-template #valuesBadge> | ||
<div class="flex flex-row items-center gap-2"> | ||
<span class="text-gray-800" translate | ||
>search.filters.summaryLabel.{{ fieldName }}</span | ||
> | ||
<gn-ui-badge | ||
*ngFor="let fieldValue of values$ | async" | ||
[style.--gn-ui-badge-text-color]="'black'" | ||
[style.--gn-ui-badge-background-color]="'#F2F2F2'" | ||
[style.--gn-ui-badge-font-weight]="'700'" | ||
[removable]="true" | ||
(badgeRemoveClicked)="removeFilterValue(fieldValue)" | ||
>{{ fieldValue }}</gn-ui-badge | ||
> | ||
</div> | ||
</ng-template> | ||
</div> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { ComponentFixture, TestBed } from '@angular/core/testing' | ||
|
||
import { SearchFiltersSummaryItemComponent } from './search-filters-summary-item.component' | ||
|
||
describe('SearchFiltersSummaryComponent', () => { | ||
let component: SearchFiltersSummaryItemComponent | ||
let fixture: ComponentFixture<SearchFiltersSummaryItemComponent> | ||
|
||
beforeEach(() => { | ||
TestBed.configureTestingModule({ | ||
imports: [SearchFiltersSummaryItemComponent], | ||
}) | ||
fixture = TestBed.createComponent(SearchFiltersSummaryItemComponent) | ||
component = fixture.componentInstance | ||
fixture.detectChanges() | ||
}) | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy() | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core' | ||
import { CommonModule } from '@angular/common' | ||
import { | ||
FieldsService, | ||
FieldType, | ||
FieldValue, | ||
FieldValues, | ||
SearchFacade, | ||
SearchService, | ||
} from '@geonetwork-ui/feature/search' | ||
import { | ||
catchError, | ||
filter, | ||
firstValueFrom, | ||
map, | ||
Observable, | ||
startWith, | ||
switchMap, | ||
tap, | ||
} from 'rxjs' | ||
import { BadgeComponent } from '@geonetwork-ui/ui/inputs' | ||
import { TranslateModule } from '@ngx-translate/core' | ||
import { DateRange } from '@geonetwork-ui/api/repository' | ||
|
||
@Component({ | ||
selector: 'md-editor-search-filters-summary-item', | ||
standalone: true, | ||
imports: [CommonModule, TranslateModule, BadgeComponent], | ||
templateUrl: './search-filters-summary-item.component.html', | ||
styleUrls: ['./search-filters-summary-item.component.css'], | ||
}) | ||
export class SearchFiltersSummaryItemComponent implements OnInit { | ||
@Input() fieldName: string | ||
fieldType: FieldType | ||
|
||
fieldValues$ = this.searchFacade.searchFilters$.pipe( | ||
switchMap((filters) => | ||
this.fieldsService.readFieldValuesFromFilters(filters) | ||
), | ||
tap((fieldValues) => console.log(fieldValues)), | ||
map((fieldValues) => | ||
Array.isArray(fieldValues[this.fieldName]) //TODO: handle date ranges as arrays everywhere? | ||
? fieldValues[this.fieldName] | ||
: [fieldValues[this.fieldName]] | ||
), | ||
tap((fieldValues) => console.log(fieldValues)) | ||
// startWith([]), | ||
// catchError(() => of([])) | ||
) as Observable<FieldValue[] | DateRange[]> | ||
|
||
dateRange$ = this.fieldValues$.pipe( | ||
filter(() => this.fieldType === 'dateRange'), | ||
map((fieldValues) => fieldValues as DateRange[]) | ||
) | ||
|
||
values$ = this.fieldValues$.pipe( | ||
filter(() => this.fieldType === 'values'), | ||
map((fieldValues) => fieldValues as FieldValue[]) | ||
) | ||
|
||
constructor( | ||
private searchFacade: SearchFacade, | ||
private searchService: SearchService, | ||
private fieldsService: FieldsService | ||
) {} | ||
|
||
ngOnInit() { | ||
this.fieldType = this.fieldsService.getFieldType(this.fieldName) | ||
} | ||
|
||
async removeFilterValue(fieldValue: FieldValue | DateRange) { | ||
const currentFieldValues: (FieldValue | DateRange)[] = await firstValueFrom( | ||
this.fieldValues$ | ||
) | ||
const updatedFieldValues = currentFieldValues.filter( | ||
(value: string | DateRange) => value !== fieldValue | ||
) | ||
this.fieldsService | ||
.buildFiltersFromFieldValues({ | ||
[this.fieldName]: updatedFieldValues as FieldValue[], | ||
}) | ||
.subscribe((filters) => this.searchService.updateFilters(filters)) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<div | ||
class="flex flex-row py-3 px-4 gap-4 overflow-hidden grow border-[1px] border-gray-200" | ||
*ngIf="searchFilterActive$ | async" | ||
> | ||
<div class="flex flex-row flex-wrap gap-2"> | ||
<md-editor-search-filters-summary-item | ||
*ngFor="let field of searchFields; let i = index" | ||
[fieldName]="field" | ||
></md-editor-search-filters-summary-item> | ||
</div> | ||
<button | ||
class="bg-white text-black font-bold ml-auto" | ||
(click)="clearFilters()" | ||
> | ||
{{ 'search.filters.clear' | translate }} | ||
</button> | ||
</div> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { ComponentFixture, TestBed } from '@angular/core/testing' | ||
import { SearchFiltersSummaryComponent } from './search-filters-summary.component' | ||
|
||
describe('SearchFiltersSummaryComponent', () => { | ||
let component: SearchFiltersSummaryComponent | ||
let fixture: ComponentFixture<SearchFiltersSummaryComponent> | ||
|
||
beforeEach(async () => { | ||
await TestBed.configureTestingModule({ | ||
imports: [SearchFiltersSummaryComponent], | ||
}).compileComponents() | ||
|
||
fixture = TestBed.createComponent(SearchFiltersSummaryComponent) | ||
component = fixture.componentInstance | ||
fixture.detectChanges() | ||
}) | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy() | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { Component, Input } from '@angular/core' | ||
import { CommonModule } from '@angular/common' | ||
import { SearchFacade, SearchService } from '@geonetwork-ui/feature/search' | ||
import { map } from 'rxjs' | ||
import { SearchFiltersSummaryItemComponent } from '../search-filters-summary-item/search-filters-summary-item.component' | ||
import { TranslateModule } from '@ngx-translate/core' | ||
|
||
@Component({ | ||
selector: 'md-editor-search-filters-summary', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this can maybe go in the feature-search lib ? |
||
imports: [CommonModule, SearchFiltersSummaryItemComponent, TranslateModule], | ||
templateUrl: './search-filters-summary.component.html', | ||
styleUrls: ['./search-filters-summary.component.css'], | ||
standalone: true, | ||
}) | ||
export class SearchFiltersSummaryComponent { | ||
@Input() searchFields: string[] = [] | ||
|
||
searchFilterActive$ = this.searchFacade.searchFilters$.pipe( | ||
map((filters) => this.hasNonEmptyValues(filters)) | ||
) | ||
Comment on lines
+16
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm wondering if instead of taking a list of filters as input we can simply listen for the search filters and show everyone of them. This means that if another filter (which does not appear in the advanced filters above) is active then it will also show up. What do you think? |
||
|
||
constructor( | ||
private searchFacade: SearchFacade, | ||
private searchService: SearchService | ||
) {} | ||
|
||
hasNonEmptyValues(filters: any): boolean { | ||
return Object.values(filters).some( | ||
(value) => | ||
value !== undefined && | ||
(typeof value !== 'object' || | ||
(typeof value === 'object' && Object.keys(value).length > 0)) | ||
) | ||
} | ||
|
||
clearFilters() { | ||
this.searchService.setFilters({}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,20 @@ | ||
<div | ||
class="flex flex-row py-3 px-4 gap-4 shadow-md shadow-gray-300 border-[1px] border-gray-200 overflow-hidden rounded bg-white grow mx-[32px] my-[16px] text-sm" | ||
class="rounded bg-white shadow-md shadow-gray-300 border-[1px] border-gray-200 mx-[32px] my-[16px] text-sm" | ||
> | ||
<mat-icon class="material-symbols-outlined">filter_list</mat-icon> | ||
<gn-ui-filter-dropdown | ||
*ngFor="let filter of searchConfig; let i = index" | ||
[fieldName]="filter.fieldName" | ||
[title]="filter.title | translate" | ||
[style.--gn-ui-button-height]="'32px'" | ||
[style.--gn-ui-multiselect-counter-text-color]="'var(--color-primary)'" | ||
[style.--gn-ui-multiselect-counter-background-color]="'white'" | ||
></gn-ui-filter-dropdown> | ||
<div | ||
class="flex flex-row py-3 px-4 gap-4 overflow-hidden grow border-[1px] border-gray-200" | ||
> | ||
<mat-icon class="material-symbols-outlined">filter_list</mat-icon> | ||
<gn-ui-filter-dropdown | ||
*ngFor="let filter of searchConfig; let i = index" | ||
[fieldName]="filter.fieldName" | ||
[title]="filter.title | translate" | ||
[style.--gn-ui-button-height]="'32px'" | ||
[style.--gn-ui-multiselect-counter-text-color]="'var(--color-primary)'" | ||
[style.--gn-ui-multiselect-counter-background-color]="'white'" | ||
></gn-ui-filter-dropdown> | ||
</div> | ||
<md-editor-search-filters-summary | ||
[searchFields]="searchFields" | ||
></md-editor-search-filters-summary> | ||
</div> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,6 +39,9 @@ marker('search.filters.publisherOrg') | |
marker('search.filters.user') | ||
marker('search.filters.changeDate') | ||
|
||
marker('search.filters.summaryLabel.user') | ||
marker('search.filters.summaryLabel.changeDate') | ||
Comment on lines
+42
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how about marking labels for all fields? It might make more sense taken from outside I think. Another approach would be to use the field normal label if a summaryLabel is not available. |
||
|
||
@Injectable({ | ||
providedIn: 'root', | ||
}) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about directly modifying the current filters instead of going from filters to values and then values to filters? simply deleting the corresponding key in the filters should be enough