From 555f687e767839cbda2d1d27a0d5a4b9e341a3a2 Mon Sep 17 00:00:00 2001 From: Camille Moinier Date: Thu, 31 Aug 2023 12:31:21 +0200 Subject: [PATCH 1/4] feat: pagination buttons component --- .../dashboard/dashboard-page.component.html | 9 +- .../dashboard-page.component.spec.ts | 12 +- .../pagination-buttons.component.css | 51 ++++++++ .../pagination-buttons.component.html | 31 +++++ .../pagination-buttons.component.spec.ts | 112 ++++++++++++++++++ .../pagination-buttons.component.stories.ts | 47 ++++++++ .../pagination-buttons.component.ts | 68 +++++++++++ .../pagination.component.stories.ts | 2 +- .../ui/elements/src/lib/ui-elements.module.ts | 3 + 9 files changed, 324 insertions(+), 11 deletions(-) create mode 100644 libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.css create mode 100644 libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.html create mode 100644 libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.spec.ts create mode 100644 libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.stories.ts create mode 100644 libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.ts diff --git a/apps/metadata-editor/src/app/dashboard/dashboard-page.component.html b/apps/metadata-editor/src/app/dashboard/dashboard-page.component.html index 97292fae30..dbdc437b41 100644 --- a/apps/metadata-editor/src/app/dashboard/dashboard-page.component.html +++ b/apps/metadata-editor/src/app/dashboard/dashboard-page.component.html @@ -43,12 +43,13 @@

results.records.hits.displayedOn
- --> + + >
diff --git a/apps/metadata-editor/src/app/dashboard/dashboard-page.component.spec.ts b/apps/metadata-editor/src/app/dashboard/dashboard-page.component.spec.ts index b75e7e4160..c1ac85ebe6 100644 --- a/apps/metadata-editor/src/app/dashboard/dashboard-page.component.spec.ts +++ b/apps/metadata-editor/src/app/dashboard/dashboard-page.component.spec.ts @@ -30,12 +30,12 @@ export class RecordTableComponent { } @Component({ // eslint-disable-next-line - selector: 'gn-ui-pagination', + selector: 'gn-ui-pagination-buttons', template: '', }) -export class PaginationComponent { +export class PaginationButtonsComponent { @Input() currentPage = 1 - @Input() nPages = 1 + @Input() totalPages = 1 @Input() hideButton = false @Output() newCurrentPageEvent = new EventEmitter() } @@ -64,7 +64,7 @@ describe('DashboardPageComponent', () => { schemas: [NO_ERRORS_SCHEMA], declarations: [ DashboardPageComponent, - PaginationComponent, + PaginationButtonsComponent, RecordTableComponent, ], providers: [ @@ -117,7 +117,7 @@ describe('DashboardPageComponent', () => { By.directive(RecordTableComponent) ).componentInstance pagination = fixture.debugElement.query( - By.directive(PaginationComponent) + By.directive(PaginationButtonsComponent) ).componentInstance }) it('displays record table', () => { @@ -126,7 +126,7 @@ describe('DashboardPageComponent', () => { it('displays pagination', () => { expect(pagination).toBeTruthy() expect(pagination.currentPage).toEqual(currentPage) - expect(pagination.nPages).toEqual(totalPages) + expect(pagination.totalPages).toEqual(totalPages) }) describe('when click on a record', () => { beforeEach(() => { diff --git a/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.css b/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.css new file mode 100644 index 0000000000..23770d0b94 --- /dev/null +++ b/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.css @@ -0,0 +1,51 @@ +.pagination-container { + display: flex; + align-items: center; + justify-content: center; + width: 200px; + height: 200px; +} + +.pagination-arrow { + border: none; + background: none; + font-size: 1rem; + cursor: pointer; + outline: none; + padding: 5px; + margin: 0 5px; +} + +.text { + margin-left: auto; + margin-right: auto; +} + +.pagination-page { + border: none; + background: none; + cursor: pointer; + outline: none; + padding: 8px; + width: 2.5rem; + border-radius: 20%; +} + +.pagination-page.current { + font-weight: bold; + background-color: #4287f5; +} + +.pagination-page:hover { + font-weight: bold; + background-color: #78a8f5; +} + +.pagination-separator { + margin: 0 5px; +} + +.pagination-arrow:disabled { + cursor: not-allowed; + opacity: 0.5; +} diff --git a/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.html b/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.html new file mode 100644 index 0000000000..774485ef10 --- /dev/null +++ b/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.html @@ -0,0 +1,31 @@ +
+
+ + chevron_left + + + + {{ page }} + + + {{ page }} + + + + chevron_right + +
+
diff --git a/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.spec.ts b/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.spec.ts new file mode 100644 index 0000000000..c2e7e4559f --- /dev/null +++ b/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.spec.ts @@ -0,0 +1,112 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing' +import { PaginationButtonsComponent } from './pagination-buttons.component' + +describe('PaginationButtonsComponent', () => { + let component: PaginationButtonsComponent + let fixture: ComponentFixture + + const mockChangePage = (page) => { + component.setPage(page) + } + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [PaginationButtonsComponent], + }).compileComponents() + + fixture = TestBed.createComponent(PaginationButtonsComponent) + component = fixture.componentInstance + component.currentPage = 3 + component.totalPages = 10 + component.calculateVisiblePages() + component.changePage = mockChangePage + fixture.detectChanges() + }) + + it('should create', () => { + expect(component).toBeTruthy() + }) + describe('when using next arrow', () => { + beforeEach(() => { + component.currentPage = 2 + const paginationButtons = + fixture.nativeElement.querySelectorAll('gn-ui-button') + paginationButtons.forEach((buttonElement) => { + const matIcon = buttonElement.querySelector('mat-icon') + if (matIcon && matIcon.textContent.trim() === 'chevron_right') { + buttonElement.dispatchEvent(new Event('buttonClick')) + } + }) + }) + it('should access next page on click', () => { + expect(component.currentPage).toBe(3) + }) + }) + describe('when using previous arrow', () => { + beforeEach(() => { + component.currentPage = 4 + const paginationButtons = + fixture.nativeElement.querySelectorAll('gn-ui-button') + paginationButtons.forEach((buttonElement) => { + const matIcon = buttonElement.querySelector('mat-icon') + if (matIcon && matIcon.textContent.trim() === 'chevron_left') { + buttonElement.dispatchEvent(new Event('buttonClick')) + } + }) + }) + it('is should access previous page', () => { + expect(component.currentPage).toBe(3) + }) + }) + describe('when accessing first page', () => { + beforeEach(() => { + component.currentPage = 1 + fixture.detectChanges() + }) + it('is should disable the previous arrow', () => { + const paginationButtons = + fixture.nativeElement.querySelectorAll('gn-ui-button') + paginationButtons.forEach((buttonElement) => { + const matIcon = buttonElement.querySelector('mat-icon') + if (matIcon && matIcon.textContent.trim() === 'chevron_left') { + const prevBtn = buttonElement.disabled + expect(prevBtn).toBeTruthy() + } + }) + }) + }) + describe('when accessing last page', () => { + beforeEach(() => { + component.currentPage = 10 + fixture.detectChanges() + }) + it('is should disable the next arrow', () => { + const paginationButtons = + fixture.nativeElement.querySelectorAll('gn-ui-button') + paginationButtons.forEach((buttonElement) => { + const matIcon = buttonElement.querySelector('mat-icon') + if (matIcon && matIcon.textContent.trim() === 'chevron_right') { + const nextBtn = buttonElement.disabled + expect(nextBtn).toBeTruthy() + } + }) + }) + }) + describe('when clicking on page button', () => { + beforeEach(() => { + const paginationButtons = + fixture.nativeElement.querySelectorAll('gn-ui-button') + const pageBtnList = [] + paginationButtons.forEach((buttonElement) => { + const matIcon = buttonElement.querySelector('mat-icon') + if (!matIcon) { + pageBtnList.push(buttonElement) + } + }) + pageBtnList[1].dispatchEvent(new Event('buttonClick')) + }) + it('is should access the requested page', () => { + expect(component.currentPage).toBe(2) + }) + }) +}) diff --git a/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.stories.ts b/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.stories.ts new file mode 100644 index 0000000000..8a382eb52c --- /dev/null +++ b/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.stories.ts @@ -0,0 +1,47 @@ +import { TranslateModule } from '@ngx-translate/core' +import { + componentWrapperDecorator, + 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 { PaginationButtonsComponent } from './pagination-buttons.component' +import { FormsModule } from '@angular/forms' +import { action } from '@storybook/addon-actions' +import { MatIcon } from '@angular/material/icon' + +export default { + title: 'Elements/PaginationButtonsComponent', + component: PaginationButtonsComponent, + decorators: [ + moduleMetadata({ + declarations: [ButtonComponent, MatIcon], + imports: [ + UtilI18nModule, + FormsModule, + TranslateModule.forRoot(TRANSLATE_DEFAULT_CONFIG), + ], + }), + ], + render: (args: PaginationButtonsComponent) => ({ + props: { + ...args, + newCurrentPageEvent: action('newCurrentPageEvent'), + }, + }), +} as Meta + +export const Primary: StoryObj = { + args: { + currentPage: 1, + totalPages: 10, + }, + parameters: { + layout: 'centered', + }, +} diff --git a/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.ts b/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.ts new file mode 100644 index 0000000000..44e2eabc90 --- /dev/null +++ b/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.ts @@ -0,0 +1,68 @@ +import { + Component, + EventEmitter, + Input, + OnChanges, + Output, +} from '@angular/core' + +@Component({ + selector: 'gn-ui-pagination-buttons', + templateUrl: './pagination-buttons.component.html', + styleUrls: ['./pagination-buttons.component.css'], +}) +export class PaginationButtonsComponent implements OnChanges { + @Input() currentPage: number + @Input() totalPages: number + visiblePages: (number | '...')[] = [] + @Output() newCurrentPageEvent = new EventEmitter() + + ngOnChanges(): void { + this.calculateVisiblePages() + } + + calculateVisiblePages(): void { + const maxVisiblePages = 5 + const halfVisible = Math.floor(maxVisiblePages / 2) + const startPage = Math.max(this.currentPage - halfVisible, 1) + const endPage = Math.min(startPage + maxVisiblePages - 1, this.totalPages) + + const visiblePages: (number | '...')[] = [] + if (startPage > 1) { + visiblePages.push(1) + if (startPage > 2) { + visiblePages.push('...') + } + } + for (let page = startPage; page <= endPage; page++) { + visiblePages.push(page) + } + if (endPage < this.totalPages) { + if (endPage < this.totalPages - 1) { + visiblePages.push('...') + } + visiblePages.push(this.totalPages) + } + + this.visiblePages = visiblePages + } + + changePage(page) { + this.setPage(page) + } + + nextPage() { + this.setPage(this.currentPage + 1) + } + + previousPage() { + this.setPage(this.currentPage - 1) + } + + setPage(newPage) { + if (!Number.isInteger(newPage)) return + this.currentPage = newPage + this.calculateVisiblePages() + this.newCurrentPageEvent.emit(this.currentPage) + } +} diff --git a/libs/ui/elements/src/lib/pagination/pagination.component.stories.ts b/libs/ui/elements/src/lib/pagination/pagination.component.stories.ts index 22d4422856..29d9ca1172 100644 --- a/libs/ui/elements/src/lib/pagination/pagination.component.stories.ts +++ b/libs/ui/elements/src/lib/pagination/pagination.component.stories.ts @@ -15,7 +15,7 @@ import { MatIcon } from '@angular/material/icon' import { FormsModule } from '@angular/forms' export default { - title: 'Layout/PaginationComponent', + title: 'Elements/PaginationComponent', component: PaginationComponent, decorators: [ moduleMetadata({ diff --git a/libs/ui/elements/src/lib/ui-elements.module.ts b/libs/ui/elements/src/lib/ui-elements.module.ts index 74a897c6bb..6e0566fa54 100644 --- a/libs/ui/elements/src/lib/ui-elements.module.ts +++ b/libs/ui/elements/src/lib/ui-elements.module.ts @@ -24,6 +24,7 @@ import { FormsModule } from '@angular/forms' import { AvatarComponent } from './avatar/avatar.component' import { UserPreviewComponent } from './user-preview/user-preview.component' import { GnUiLinkifyDirective } from './metadata-info/linkify.directive' +import { PaginationButtonsComponent } from './pagination-buttons/pagination-buttons.component' @NgModule({ imports: [ @@ -55,6 +56,7 @@ import { GnUiLinkifyDirective } from './metadata-info/linkify.directive' AvatarComponent, UserPreviewComponent, GnUiLinkifyDirective, + PaginationButtonsComponent, ], exports: [ MetadataInfoComponent, @@ -71,6 +73,7 @@ import { GnUiLinkifyDirective } from './metadata-info/linkify.directive' ThumbnailComponent, AvatarComponent, UserPreviewComponent, + PaginationButtonsComponent, ], }) export class UiElementsModule {} From 0afcbc0286658950380ee9afae5ef9c8931c1ff3 Mon Sep 17 00:00:00 2001 From: Camille Moinier <132347903+cmoinier@users.noreply.github.com> Date: Thu, 31 Aug 2023 18:36:58 +0200 Subject: [PATCH 2/4] Update libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.html Co-authored-by: Olivia Guyot --- .../lib/pagination-buttons/pagination-buttons.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.html b/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.html index 774485ef10..c2c097dfd9 100644 --- a/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.html +++ b/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.html @@ -9,7 +9,7 @@ - {{ page }} + {{ page }} Date: Thu, 31 Aug 2023 18:37:08 +0200 Subject: [PATCH 3/4] Update libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.ts Co-authored-by: Olivia Guyot --- .../src/lib/pagination-buttons/pagination-buttons.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.ts b/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.ts index 44e2eabc90..38f5f8b768 100644 --- a/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.ts +++ b/libs/ui/elements/src/lib/pagination-buttons/pagination-buttons.component.ts @@ -25,7 +25,7 @@ export class PaginationButtonsComponent implements OnChanges { const maxVisiblePages = 5 const halfVisible = Math.floor(maxVisiblePages / 2) const startPage = Math.max(this.currentPage - halfVisible, 1) - const endPage = Math.min(startPage + maxVisiblePages - 1, this.totalPages) + const endPage = Math.min(this.currentPage + halfVisible, this.totalPages) const visiblePages: (number | '...')[] = [] if (startPage > 1) { From 795c31646254e8a542ad5e4436a3faa21ec63519 Mon Sep 17 00:00:00 2001 From: Camille Moinier Date: Thu, 31 Aug 2023 19:55:16 +0200 Subject: [PATCH 4/4] feat: remove css + correct btn numbers + e2e tests --- .../src/e2e/dashboard.cy.ts | 39 ++++++++++++++ .../dashboard/dashboard-page.component.html | 2 - .../pagination-buttons.component.css | 51 ------------------- 3 files changed, 39 insertions(+), 53 deletions(-) create mode 100644 apps/metadata-editor-e2e/src/e2e/dashboard.cy.ts diff --git a/apps/metadata-editor-e2e/src/e2e/dashboard.cy.ts b/apps/metadata-editor-e2e/src/e2e/dashboard.cy.ts new file mode 100644 index 0000000000..d4004611b1 --- /dev/null +++ b/apps/metadata-editor-e2e/src/e2e/dashboard.cy.ts @@ -0,0 +1,39 @@ +describe('dashboard', () => { + let originalList + let newList + describe('pagination', () => { + it('should display different results on click on arrow', () => { + 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('gn-ui-pagination-buttons').find('gn-ui-button').last().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) + }) + }) + }) + it('should display different results on click on specific page', () => { + cy.get('gn-ui-pagination-buttons').find('gn-ui-button').eq(1).click() + cy.get('gn-ui-record-table') + .find('.record-table-col') + .first() + .invoke('text') + .then((list) => { + newList = list.trim() + expect(newList).to.be(originalList) + }) + }) + }) +}) diff --git a/apps/metadata-editor/src/app/dashboard/dashboard-page.component.html b/apps/metadata-editor/src/app/dashboard/dashboard-page.component.html index dbdc437b41..9774aeceaa 100644 --- a/apps/metadata-editor/src/app/dashboard/dashboard-page.component.html +++ b/apps/metadata-editor/src/app/dashboard/dashboard-page.component.html @@ -43,8 +43,6 @@

results.records.hits.displayedOn
-