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

Editor: Add a date range search filter #1030

Merged
merged 43 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
0b5ba02
feat(filters): first draft to filter results by date range
tkohr Oct 25, 2024
d4722db
fix(fields): return one value instead of array for date range filter
tkohr Oct 29, 2024
c4c898f
fix(fields): handle stringified range filters
tkohr Oct 29, 2024
7cb12dc
refactor(es): check on filter type dateRange instead of name changeDate
tkohr Oct 29, 2024
0cef9b7
refactor(es): add types for FilterQuery
tkohr Oct 29, 2024
bd48cb1
refactor(es): move utility functions
tkohr Oct 29, 2024
fc9d05b
refactor(fields): clarify curently used types for date range
tkohr Oct 29, 2024
a36690c
feat(router): add utilitis to serialize DateRange objects in URL
tkohr Oct 30, 2024
eb1e459
feat(router): use and adapt query-params.utils for serialization
tkohr Oct 31, 2024
adce683
refactor(date-range): keep one isDateRange() function
tkohr Oct 31, 2024
1dfeaed
refactor(fields): clean getFiltersForValues() and pass DateRange as a…
tkohr Oct 31, 2024
1849410
feat(filter-dropdown): display date in date picker input
tkohr Oct 31, 2024
ac463c0
fix(filter-dropdown): ignore timezones to set exact dates
tkohr Oct 31, 2024
8cc8b61
feat(all-records): remove filter publicationYear
tkohr Oct 31, 2024
006f639
feat(date-range): add a date-range-dropdown.component to follow mockups
tkohr Nov 5, 2024
b22225e
test(filters): add e2e tests for date range search filter
tkohr Nov 5, 2024
56ae253
test(filters): add unit tests and fix code
tkohr Nov 6, 2024
32b7c5c
fix(date-range): define and export type once
tkohr Nov 6, 2024
1a3f86a
review(search filters): address comments
tkohr Nov 13, 2024
bf9cae0
feat(date-range): synchronize date-range-dropdown with selected date …
tkohr Nov 13, 2024
e4db9ea
chore(router): clean commented type
tkohr Nov 26, 2024
b966143
feat(search-filters): add search-filters-summary
tkohr Nov 8, 2024
57d9dea
refactor(search-filters): refactor summary component
tkohr Nov 8, 2024
33b2788
refactor(search-filters-summary): rename component into search-filter…
tkohr Nov 8, 2024
a3757ea
feat(search-filters): display summary and filter labels only if value…
tkohr Nov 13, 2024
e80aa11
feat(search-filters): introduce intermediate search-filters-summary.c…
tkohr Nov 13, 2024
b8a9090
feat(search-filter-summary): allow to remove one filter value and cle…
tkohr Nov 13, 2024
4a4a20a
feat(search-filters-summary): adapt UI
tkohr Nov 14, 2024
527c253
refactor(search-filters-summary-item): implement a getReadableValues …
tkohr Nov 19, 2024
4ee157c
refactor(search-filter-summary): move components for feature-search lib
tkohr Nov 19, 2024
a46f4e5
fix(search-filters-summary): fix error when used in myRecords
tkohr Nov 19, 2024
d3ef2ad
feat(search-filter-summary): format userInfo in summary badges
tkohr Nov 21, 2024
11a2ed3
feat(search-filter-summary): provide filter label as fallback label i…
tkohr Nov 21, 2024
66f26a5
refactor(fields): remove FieldType 'userInfo'
tkohr Nov 25, 2024
85353ee
test(search-filter-summary): add unit tests
tkohr Nov 26, 2024
df492b9
rebase(search-filters): clean after rebase
tkohr Nov 26, 2024
e3e2f71
test(search-filter-summary): add e2e tests
tkohr Nov 26, 2024
ebe1355
fix(dateRange/e2e): handle different timezones
tkohr Nov 26, 2024
8302b76
feat(search-filters-summary): allow providing filters to ignore in fi…
tkohr Dec 4, 2024
508983b
fix(fields): ignore empty values in getFiltersForValues
tkohr Dec 5, 2024
5ee6f32
chore(date-range-dropdown): replace left over mat-icon with ng-icon
tkohr Dec 5, 2024
75ca16b
fix(search-filters-summary): ignore filters in FILTER_SUMMARY_IGNORE_…
tkohr Dec 10, 2024
3d963c2
Merge pull request #1037 from geonetwork/me-search-filters-summary
tkohr Dec 11, 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
204 changes: 196 additions & 8 deletions apps/metadata-editor-e2e/src/e2e/dashboard.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,16 +296,48 @@ describe('dashboard (authenticated)', () => {
})
})
describe('search filters', () => {
function selectUser(index = 0, openDropdown = true) {
if (openDropdown) {
cy.get('md-editor-search-filters').find('gn-ui-button').first().click()
}
cy.get('.cdk-overlay-container')
.find('input[type="checkbox"]')
.eq(index)
.check()
}
function selectDateRange() {
cy.get('mat-calendar-header').find('button').first().click()
cy.get('mat-multi-year-view').contains('button', '2024').click()
cy.get('mat-year-view').contains('button', 'AUG').click()
cy.get('mat-month-view').contains('button', '1').click()
cy.get('mat-month-view').contains('button', '30').click()
}
function closeDropDown() {
cy.get('body').click(0, 0)
}
function checkFilterByChangeDate() {
cy.get('gn-ui-interactive-table')
.find('[data-cy="table-row"]')
.should('have.length', '1')
cy.get('gn-ui-results-table')
.find('[data-cy="resultItemTitle"]')
.each(($resultItemTitle) => {
cy.wrap($resultItemTitle)
.invoke('text')
.should('eq', 'Accroches vélos MEL')
})
}
describe('allRecords search filter', () => {
beforeEach(() => {
cy.visit('/catalog/search')
})
it('should filter the record list by editor (Barbara Roberts)', () => {
cy.get('md-editor-search-filters').find('gn-ui-button').first().click()
cy.get('.cdk-overlay-container')
.find('input[type="checkbox"]')
.eq(1)
.check()
it('should contain filter component with two search filters', () => {
cy.get('md-editor-search-filters')
.find('gn-ui-button')
.should('have.length', 2)
tkohr marked this conversation as resolved.
Show resolved Hide resolved
})
it('should filter the record list by user (Barbara Roberts)', () => {
selectUser(1)
cy.get('gn-ui-interactive-table')
.find('[data-cy="table-row"]')
.should('have.length', '5')
Expand All @@ -315,15 +347,171 @@ describe('dashboard (authenticated)', () => {
cy.wrap($ownerInfo).invoke('text').should('eq', 'Barbara Roberts')
})
})
it('should filter the record list by last update (changeDate)', () => {
cy.get('md-editor-search-filters').find('gn-ui-button').eq(1).click()
selectDateRange()
checkFilterByChangeDate()
})
it('should display the expand icon for the date range dropdown correctly', () => {
cy.get('md-editor-search-filters')
.find('gn-ui-date-range-dropdown')
.find('ng-icon')
.should('have.attr', 'ng-reflect-name', 'matExpandMore')
cy.get('md-editor-search-filters').find('gn-ui-button').eq(1).click()
cy.get('md-editor-search-filters')
.find('gn-ui-date-range-dropdown')
.find('ng-icon')
.should('have.attr', 'ng-reflect-name', 'matExpandLess')
})
})
describe('myRecords search filters', () => {
beforeEach(() => {
cy.visit('/my-space/my-records')
})
it('should contain filter component with no search filter for now', () => {
it('should contain filter component with one search filter', () => {
cy.get('md-editor-search-filters')
.find('gn-ui-button')
.should('not.exist')
.should('have.length', 1)
})
it('should filter the record list by last update (changeDate)', () => {
cy.get('md-editor-search-filters').find('gn-ui-button').first().click()
selectDateRange()
checkFilterByChangeDate()
})
})
describe('allRecord search filters summary', () => {
beforeEach(() => {
cy.visit('/catalog/search')
})
it('should not display anything without selected filters', () => {
cy.get('gn-ui-search-filters-summary-item').should('not.exist')
})
describe('selecting users', () => {
beforeEach(() => {
selectUser(1)
})
it('should display a label for badges of selected users', () => {
cy.get('gn-ui-search-filters-summary')
.find('[data-cy="filterSummaryLabel"]')
.invoke('text')
.should('eq', 'Modified by: ')
})
it('should display the badge for a selected user', () => {
cy.get('gn-ui-search-filters-summary')
.find('gn-ui-badge')
.should('have.length', 1)
cy.get('gn-ui-search-filters-summary')
.find('gn-ui-badge')
.invoke('text')
.should('eq', 'Barbara Roberts')
})
it('should display a second badge for a second selected user', () => {
selectUser(0, false)
cy.get('gn-ui-search-filters-summary')
.find('gn-ui-badge')
.should('have.length', 2)
cy.get('gn-ui-search-filters-summary')
.find('gn-ui-badge')
.eq(1)
.invoke('text')
.should('eq', 'admin admin')
})
it('should remove one of two badges when a badge cross is clicked', () => {
selectUser(0, false)
closeDropDown()
cy.get('gn-ui-search-filters-summary')
.find('gn-ui-badge')
.eq(0)
.find('ng-icon')
.click()
cy.get('gn-ui-search-filters-summary')
.find('gn-ui-badge')
.should('have.length', 1)
})
})
describe('selecting date range', () => {
beforeEach(() => {
cy.get('md-editor-search-filters').find('gn-ui-button').eq(1).click()
selectDateRange()
})
it('should display a label for the date range', () => {
cy.get('gn-ui-search-filters-summary')
.find('[data-cy="filterSummaryLabel"]')
.invoke('text')
.should('eq', 'Modified on: ')
})
it('should display the badge for the selected date range', () => {
cy.get('gn-ui-search-filters-summary')
.find('gn-ui-badge')
.invoke('text')
.should('eq', '01.08.2024 - 30.08.2024')
})
it('should remove the badge when the badge cross is clicked', () => {
closeDropDown()
cy.get('gn-ui-search-filters-summary')
.find('gn-ui-badge')
.find('ng-icon')
.click()
cy.get('gn-ui-search-filters-summary')
.find('gn-ui-badge')
.should('not.exist')
})
})
describe('selecting multiple filters (users and date range)', () => {
beforeEach(() => {
selectUser(0)
closeDropDown()
cy.get('md-editor-search-filters').find('gn-ui-button').eq(1).click()
selectDateRange()
})
it('should display both badges', () => {
cy.get('gn-ui-search-filters-summary')
.find('gn-ui-badge')
.should('have.length', 2)
})
it('should clear all filters when the clear button is clicked', () => {
cy.get('gn-ui-search-filters-summary').find('button').last().click()
cy.get('gn-ui-search-filters-summary')
.find('gn-ui-badge')
.should('have.length', 0)
cy.get('gn-ui-search-filters-summary-item').should('not.exist')
})
})
})
describe('myRecords search filters summary', () => {
beforeEach(() => {
cy.visit('/my-space/my-records')
})
it('should not display anything without selected filters', () => {
cy.get('gn-ui-search-filters-summary-item').should('not.exist')
})
describe('selecting date range', () => {
beforeEach(() => {
cy.get('md-editor-search-filters').find('gn-ui-button').eq(0).click()
selectDateRange()
})
it('should display a label for the date range', () => {
cy.get('gn-ui-search-filters-summary')
.find('[data-cy="filterSummaryLabel"]')
.invoke('text')
.should('eq', 'Modified on: ')
})
it('should display the badge for the selected date range', () => {
cy.get('gn-ui-search-filters-summary')
.find('gn-ui-badge')
.invoke('text')
.should('eq', '01.08.2024 - 30.08.2024')
})
it('should remove the badge when the badge cross is clicked', () => {
closeDropDown()
cy.get('gn-ui-search-filters-summary')
.find('gn-ui-badge')
.find('ng-icon')
.click()
cy.get('gn-ui-search-filters-summary')
.find('gn-ui-badge')
.should('not.exist')
})
})
})
})
Expand Down
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"
>
<ng-icon class="mt-0.5" name="iconoirFilterList"></ng-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"
>
<ng-icon class="mt-0.5" name="iconoirFilterList"></ng-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>
<gn-ui-search-filters-summary
[searchFields]="searchFields"
></gn-ui-search-filters-summary>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'
import { SearchFiltersComponent } from './search-filters.component'
import { MockBuilder } from 'ng-mocks'
import { TranslateModule } from '@ngx-translate/core'
import { By } from '@angular/platform-browser'
import { SearchFiltersSummaryComponent } from '@geonetwork-ui/feature/search'

describe('SearchFiltersComponent', () => {
let component: SearchFiltersComponent
let fixture: ComponentFixture<SearchFiltersComponent>

beforeEach(() => {
return MockBuilder(SearchFiltersComponent)
return MockBuilder(SearchFiltersComponent).mock(
SearchFiltersSummaryComponent
)
})

beforeEach(async () => {
Expand Down Expand Up @@ -40,5 +44,15 @@ describe('SearchFiltersComponent', () => {
fixture.detectChanges()
expect(component.searchConfig).toEqual([])
})

it('should pass searchFields to SearchFiltersSummaryComponent', () => {
const searchFields = ['user', 'publisherOrg', 'format', 'isSpatial']
component.searchFields = searchFields
fixture.detectChanges()
const summaryComponent = fixture.debugElement.query(
By.directive(SearchFiltersSummaryComponent)
).componentInstance
expect(summaryComponent.searchFields).toEqual(searchFields)
})
})
})
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { Component, Input, OnInit } from '@angular/core'
import { CommonModule } from '@angular/common'
import { TranslateModule } from '@ngx-translate/core'
import { FeatureSearchModule } from '@geonetwork-ui/feature/search'
import {
NgIconComponent,
provideIcons,
provideNgIconsConfig,
} from '@ng-icons/core'
import { iconoirFilterList } from '@ng-icons/iconoir'
import {
FeatureSearchModule,
SearchFiltersSummaryComponent,
} from '@geonetwork-ui/feature/search'

@Component({
selector: 'md-editor-search-filters',
Expand All @@ -17,6 +20,7 @@ import { iconoirFilterList } from '@ng-icons/iconoir'
TranslateModule,
FeatureSearchModule,
NgIconComponent,
SearchFiltersSummaryComponent,
],
providers: [
provideIcons({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export class AllRecordsComponent {
importRecordButton!: ElementRef
@ViewChild('template') template!: TemplateRef<any>
private overlayRef!: OverlayRef
searchFields = ['user']
searchFields = ['user', 'changeDate']
searchText$: Observable<string | null> =
this.searchFacade.searchFilters$.pipe(
map((filters) => ('any' in filters ? (filters['any'] as string) : null))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { RecordsListComponent } from '../records-list.component'
import {
FeatureSearchModule,
FieldsService,
ResultsTableContainerComponent,
FILTER_SUMMARY_IGNORE_LIST,
SearchFacade,
} from '@geonetwork-ui/feature/search'
import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform.service.interface'
Expand All @@ -38,6 +38,8 @@ import {
iconoirPagePlus,
} from '@ng-icons/iconoir'

const FILTER_OWNER = 'owner'

@Component({
selector: 'md-editor-my-records',
templateUrl: './my-records.component.html',
Expand All @@ -47,7 +49,6 @@ import {
CommonModule,
TranslateModule,
RecordsListComponent,
ResultsTableContainerComponent,
UiElementsModule,
RecordsCountComponent,
ButtonComponent,
Expand All @@ -66,14 +67,15 @@ import {
provideNgIconsConfig({
size: '1.5rem',
}),
{ provide: FILTER_SUMMARY_IGNORE_LIST, useValue: [FILTER_OWNER] },
],
})
export class MyRecordsComponent implements OnInit {
@ViewChild('importRecordButton', { read: ElementRef })
private importRecordButton!: ElementRef
@ViewChild('template') template!: TemplateRef<any>
private overlayRef!: OverlayRef
searchFields = []
searchFields = ['changeDate']
searchText$: Observable<string | null>

isImportMenuOpen = false
Expand All @@ -93,7 +95,7 @@ export class MyRecordsComponent implements OnInit {

this.platformService.getMe().subscribe((user) => {
this.fieldsService
.buildFiltersFromFieldValues({ owner: user.id })
.buildFiltersFromFieldValues({ [FILTER_OWNER]: user.id })
.subscribe((filters) => {
this.searchFacade.updateFilters(filters)
})
Expand Down
Loading
Loading