Skip to content

Commit

Permalink
Merge pull request #608 from geonetwork/ME/sorting
Browse files Browse the repository at this point in the history
Metadata-editor : Sorting catalog
  • Loading branch information
cmoinier committed Sep 11, 2023
2 parents f46783c + 19a5b34 commit 7a84a5b
Show file tree
Hide file tree
Showing 17 changed files with 277 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ <h2 class="text-xl mr-4 font-title" translate>
*ngIf="!isOpen"
(buttonClick)="open()"
type="outline"
extraClass="!p-[8px]"
extraClass="!px-[8px]"
data-cy="filters-expand"
>
<mat-icon>more_horiz</mat-icon>
Expand Down
33 changes: 31 additions & 2 deletions apps/metadata-editor-e2e/src/e2e/dashboard.cy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
describe('dashboard', () => {
let originalList
let newList
describe('pagination', () => {
let originalList
let newList
it('should display different results on click on arrow', () => {
cy.visit('/')
cy.get('gn-ui-record-table')
Expand Down Expand Up @@ -36,4 +36,33 @@ describe('dashboard', () => {
})
})
})

// NEEDS TO WAIT UNTIL STYLE IS DONE
describe('sorting', () => {
let originalList
let newList
it('should order the result list on click', () => {
cy.visit('/')
cy.get('gn-ui-record-table')
.find('.record-table-col')
.first()
.as('pageOne')

cy.get('@pageOne')
.invoke('text')
.then((list) => {
originalList = list.trim()
cy.get('.record-table-header').first().click()
cy.get('gn-ui-sort').find('gn-ui-button').first().click()
cy.get('gn-ui-record-table')
.find('.record-table-col')
.first()
.invoke('text')
.then((list) => {
newList = list.trim()
expect(newList).not.to.be(originalList)
})
})
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ <h1 class="text-[56px] font-title grow">{{ title }}</h1>
[records]="results"
[totalHits]="searchFacade.resultsHits$ | async"
(recordSelect)="editRecord($event)"
(sortByChange)="setSortBy($event)"
[sortBy]="searchFacade.sortBy$ | async"
></gn-ui-record-table>
<div
class="px-5 py-5 flex justify-center gap-8 items-baseline"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ describe('RecordsListComponent', () => {
let fixture: ComponentFixture<RecordsListComponent>
let router: Router
let searchService: SearchService
let searchFacade: SearchFacade

beforeEach(() => {
TestBed.configureTestingModule({
Expand Down Expand Up @@ -89,6 +90,7 @@ describe('RecordsListComponent', () => {
})
router = TestBed.inject(Router)
searchService = TestBed.inject(SearchService)
searchFacade = TestBed.inject(SearchFacade)
fixture = TestBed.createComponent(RecordsListComponent)
component = fixture.componentInstance
fixture.detectChanges()
Expand Down Expand Up @@ -116,6 +118,12 @@ describe('RecordsListComponent', () => {
expect(pagination.currentPage).toEqual(currentPage)
expect(pagination.totalPages).toEqual(totalPages)
})
it('orders the completion column', () => {
expect(searchFacade.setSortBy).toHaveBeenCalledWith([
'desc',
'changeDate',
])
})
describe('when click on a record', () => {
beforeEach(() => {
table.recordSelect.emit({ uniqueIdentifier: 123 })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { CatalogRecord } from '@geonetwork-ui/common/domain/record'
import { SearchFacade, SearchService } from '@geonetwork-ui/feature/search'
import { UiSearchModule } from '@geonetwork-ui/ui/search'
import { UiElementsModule } from '@geonetwork-ui/ui/elements'
import { SortByField } from '@geonetwork-ui/common/domain/search'

const includes = [
'uuid',
Expand Down Expand Up @@ -49,4 +50,8 @@ export class RecordsListComponent {
editRecord(record: CatalogRecord) {
this.router.navigate(['/edit', record.uniqueIdentifier])
}

setSortBy(newSortBy: SortByField) {
this.searchFacade.setSortBy(newSortBy)
}
}
4 changes: 4 additions & 0 deletions apps/metadata-editor/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ body {
.menu-title {
@apply text-xl px-9 py-3;
}

.mat-mdc-button-base {
line-height: normal;
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { TranslateModule } from '@ngx-translate/core'
import {
componentWrapperDecorator,
Meta,
moduleMetadata,
StoryObj,
} from '@storybook/angular'
import { Meta, moduleMetadata, StoryObj } from '@storybook/angular'
import {
TRANSLATE_DEFAULT_CONFIG,
UtilI18nModule,
} from '@geonetwork-ui/util/i18n'
import { UiInputsModule, ButtonComponent } from '@geonetwork-ui/ui/inputs'
import { ButtonComponent } from '@geonetwork-ui/ui/inputs'
import { PaginationButtonsComponent } from './pagination-buttons.component'
import { FormsModule } from '@angular/forms'
import { action } from '@storybook/addon-actions'
Expand Down
5 changes: 5 additions & 0 deletions libs/ui/inputs/src/lib/button/button.component.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/* makes sure icons will not make the buttons grow vertically */
mat-icon.mat-icon {
margin-top: -0.3em;
margin-bottom: -0.3em;
}
21 changes: 19 additions & 2 deletions libs/ui/inputs/src/lib/button/button.component.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
TRANSLATE_DEFAULT_CONFIG,
UtilI18nModule,
} from '@geonetwork-ui/util/i18n'
import { MatIconModule } from '@angular/material/icon'

export default {
title: 'Inputs/ButtonComponent',
Expand All @@ -14,6 +15,7 @@ export default {
imports: [
UtilI18nModule,
TranslateModule.forRoot(TRANSLATE_DEFAULT_CONFIG),
MatIconModule,
],
}),
],
Expand All @@ -36,7 +38,22 @@ export const Primary: StoryObj<ButtonComponentWithContent> = {
},
render: (args) => ({
props: args,
template:
'<gn-ui-button [type]="type" [disabled]="disabled" [extraClass]="extraClass">{{ content }}</gn-ui-button>',
template: `<div class='flex flex-row gap-5'>
<gn-ui-button [type]="type" [disabled]="disabled" [extraClass]="extraClass">
{{ content }}
</gn-ui-button>
<gn-ui-button [type]="type" [disabled]="disabled" [extraClass]="extraClass">
with an icon&nbsp;<mat-icon class="material-symbols-outlined">downloading</mat-icon>
</gn-ui-button>
<gn-ui-button class="text-[1.5em]" [type]="type" [disabled]="disabled" [extraClass]="extraClass">
<mat-icon class='material-symbols-outlined'>globe_asia</mat-icon>&nbsp;bigger
</gn-ui-button>
<gn-ui-button class="text-[0.7em]" [type]="type" [disabled]="disabled" [extraClass]="extraClass">
<mat-icon class='material-symbols-outlined'>pest_control</mat-icon>&nbsp;smaller
</gn-ui-button>
<gn-ui-button [type]="type" [disabled]="disabled" [extraClass]="extraClass + ' !px-[3em] !py-[0.5em]'">
different&nbsp;<mat-icon class="material-symbols-outlined">waves</mat-icon>&nbsp;shape
</gn-ui-button>
</div>`,
}),
}
2 changes: 1 addition & 1 deletion libs/ui/inputs/src/lib/button/button.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export class ButtonComponent {
case 'primary':
return 'focus:ring-4 focus:ring-primary-lightest'
case 'outline':
return 'border border-gray-300 hover:border-primary-lighter focus:border-primary-lighter focus:ring-4 focus:ring-primary-lightest active:border-primary-darker'
return 'border border-gray-300 -m-[1px] hover:border-primary-lighter focus:border-primary-lighter focus:ring-4 focus:ring-primary-lightest active:border-primary-darker'
case 'light':
return 'focus:ring-4 focus:ring-gray-300'
}
Expand Down
91 changes: 82 additions & 9 deletions libs/ui/search/src/lib/record-table/record-table.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,97 @@
>
results.records.hits.displayedOn
</div>

<div
class="grid grid-cols-[repeat(5,minmax(0,max-content))] gap-x-4 gap-y-1"
>
<div class="contents text-sm">
<div translate="" class="record-table-header text-gray-400">
record.metadata.title
<div class="record-table-header text-gray-400 flex gap-1">
<gn-ui-button
type="light"
extraClass="px-3 py-2 space-x-1"
(buttonClick)="setSortBy('resourceTitleObject.default.keyword')"
>
<span translate>record.metadata.title</span>
<mat-icon
class="material-symbols-outlined"
*ngIf="isSortedBy('resourceTitleObject.default.keyword', 'desc')"
>
expand_more</mat-icon
>
<mat-icon
class="material-symbols-outlined"
*ngIf="isSortedBy('resourceTitleObject.default.keyword', 'asc')"
>
expand_less</mat-icon
>
</gn-ui-button>
</div>
<div translate="" class="record-table-header text-gray-400">
<div translate="" class="record-table-header text-gray-400 flex gap-1">
record.metadata.formats
</div>
<div translate="" class="record-table-header text-gray-400">
record.metadata.author
<div class="record-table-header text-gray-400 flex gap-1">
<gn-ui-button
type="light"
extraClass="px-3 py-2 space-x-1"
(buttonClick)="setSortBy('recordOwner')"
>
<span translate>record.metadata.author</span>
<mat-icon
class="material-symbols-outlined"
*ngIf="isSortedBy('recordOwner', 'desc')"
>
expand_more</mat-icon
>
<mat-icon
class="material-symbols-outlined"
*ngIf="isSortedBy('recordOwner', 'asc')"
>
expand_less</mat-icon
>
</gn-ui-button>
</div>
<div translate="" class="record-table-header text-gray-400">
record.metadata.completion
<div class="record-table-header text-gray-400 flex gap-1">
<gn-ui-button
type="light"
extraClass="px-3 py-2 space-x-1"
(buttonClick)="setSortBy('changeDate')"
>
<span translate>record.metadata.updatedOn</span>
<mat-icon
class="material-symbols-outlined"
*ngIf="isSortedBy('changeDate', 'desc')"
>
expand_more</mat-icon
>
<mat-icon
class="material-symbols-outlined"
*ngIf="isSortedBy('changeDate', 'asc')"
>
expand_less</mat-icon
>
</gn-ui-button>
</div>
<div translate="" class="record-table-header text-gray-400">
record.metadata.createdOn
<div class="record-table-header text-gray-400 flex gap-1">
<gn-ui-button
type="light"
extraClass="px-3 py-2 space-x-1"
(buttonClick)="setSortBy('createDate')"
>
<span translate>record.metadata.createdOn</span>
<mat-icon
class="material-symbols-outlined"
*ngIf="isSortedBy('createDate', 'desc')"
>
expand_more</mat-icon
>
<mat-icon
class="material-symbols-outlined"
*ngIf="isSortedBy('createDate', 'asc')"
>
expand_less</mat-icon
>
</gn-ui-button>
</div>
</div>
<div
Expand Down
38 changes: 38 additions & 0 deletions libs/ui/search/src/lib/record-table/record-table.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'
import { DATASET_RECORDS } from '@geonetwork-ui/common/fixtures'

import { RecordTableComponent } from './record-table.component'
import { SortByField } from '@geonetwork-ui/common/domain/search'

describe('RecordTableComponent', () => {
let component: RecordTableComponent
Expand Down Expand Up @@ -39,4 +40,41 @@ describe('RecordTableComponent', () => {
).toEqual('#1e5180') // geojson
})
})

describe('sorting', () => {
describe('#setSortBy', () => {
let newSortBy: SortByField
beforeEach(() => {
newSortBy = null
component.sortByChange.subscribe((v) => (newSortBy = v))
})
it('initially sorts by ascending order', () => {
component.setSortBy('title')
expect(newSortBy).toEqual(['asc', 'title'])
})
it('changes the order if already sorted', () => {
component.sortBy = ['asc', 'title']
component.setSortBy('title')
expect(newSortBy).toEqual(['desc', 'title'])
})
})
describe('#isSortedBy', () => {
it('returns false if not sorted by this column', () => {
component.sortBy = ['desc', 'owner']
expect(component.isSortedBy('title', 'desc')).toBe(false)
})
it('returns true if the current sortBy is for this column', () => {
component.sortBy = ['desc', 'title']
expect(component.isSortedBy('title', 'desc')).toBe(true)
})
it('returns true if the current sortBy is for this column (multiple sorts)', () => {
component.sortBy = [
['asc', 'score'],
['desc', 'title'],
]
expect(component.isSortedBy('title', 'desc')).toBe(true)
expect(component.isSortedBy('title', 'asc')).toBe(false)
})
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { Meta, StoryObj } from '@storybook/angular'
import { moduleMetadata } from '@storybook/angular'
import { RecordTableComponent } from './record-table.component'
import { DATASET_RECORDS } from '@geonetwork-ui/common/fixtures'
import { action } from '@storybook/addon-actions'
import { MatIconModule } from '@angular/material/icon'
import { UiInputsModule } from '@geonetwork-ui/ui/inputs'

const meta: Meta<RecordTableComponent> = {
component: RecordTableComponent,
title: 'Search/RecordTableComponent',
decorators: [
moduleMetadata({
declarations: [RecordTableComponent],
imports: [UiInputsModule, MatIconModule],
}),
],
render: (args: RecordTableComponent) => ({
props: {
...args,
recordSelect: action('recordSelect'),
sortByChange: action('sortByChange'),
},
}),
}
export default meta
type Story = StoryObj<RecordTableComponent>

export const Primary: Story = {
args: {
records: DATASET_RECORDS.concat(DATASET_RECORDS, DATASET_RECORDS),
totalHits: 1234,
},
}
Loading

0 comments on commit 7a84a5b

Please sign in to comment.