]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/dashboard: Improve level A accessibility for pagination component
authornsedrickm <nsedrick101@gmail.com>
Wed, 3 Aug 2022 11:14:27 +0000 (12:14 +0100)
committernsedrickm <nsedrick101@gmail.com>
Wed, 24 Aug 2022 15:52:46 +0000 (16:52 +0100)
Add custom pagination component based on patternfly design

Fixes: https://tracker.ceph.com/issues/55876
Signed-off-by: nsedrickm <nsedrick101@gmail.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-daemon-list/rgw-daemon-list.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/crud-table/crud-table.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/datatable.module.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-key-value/table-key-value.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-pagination/table-pagination.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-pagination/table-pagination.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-pagination/table-pagination.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-pagination/table-pagination.component.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.html
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table/table.component.spec.ts

index ecf0bedf961d0d5fb5942e91bcd453daffd93554..c8cddfdc8a9eb06469d02494709fe8d187ff022e 100644 (file)
@@ -77,7 +77,7 @@ describe('RgwDaemonListComponent', () => {
     expect(listDaemonsSpy).toHaveBeenCalledTimes(1);
     expect(component.daemons).toEqual([daemon]);
     expect(fixture.debugElement.query(By.css('cd-table')).nativeElement.textContent).toContain(
-      'total 1'
+      'total of 1'
     );
 
     fixture.destroy();
index df10e2a97e43548be2f1ac31018db5ce317517d7..f641cbba4c1b6b71f708eaf250b58549feccd1f7 100644 (file)
@@ -12,6 +12,7 @@ import { ComponentsModule } from '~/app/shared/components/components.module';
 import { PipesModule } from '~/app/shared/pipes/pipes.module';
 import { configureTestBed } from '~/testing/unit-test-helper';
 import { TableKeyValueComponent } from '../table-key-value/table-key-value.component';
+import { TablePaginationComponent } from '../table-pagination/table-pagination.component';
 import { TableComponent } from '../table/table.component';
 import { CRUDTableComponent } from './crud-table.component';
 
@@ -20,7 +21,12 @@ describe('CRUDTableComponent', () => {
   let fixture: ComponentFixture<CRUDTableComponent>;
 
   configureTestBed({
-    declarations: [CRUDTableComponent, TableComponent, TableKeyValueComponent],
+    declarations: [
+      CRUDTableComponent,
+      TableComponent,
+      TableKeyValueComponent,
+      TablePaginationComponent
+    ],
     imports: [
       NgxDatatableModule,
       FormsModule,
index 5cd3363320054691877493fafdb9bdd9bb7ccd6f..9edd6624fed6aeef79b0c9a1f8ad14c00337fa70 100644 (file)
@@ -12,6 +12,7 @@ import { PipesModule } from '../pipes/pipes.module';
 import { CRUDTableComponent } from './crud-table/crud-table.component';
 import { TableActionsComponent } from './table-actions/table-actions.component';
 import { TableKeyValueComponent } from './table-key-value/table-key-value.component';
+import { TablePaginationComponent } from './table-pagination/table-pagination.component';
 import { TableComponent } from './table/table.component';
 
 @NgModule({
@@ -26,13 +27,20 @@ import { TableComponent } from './table/table.component';
     ComponentsModule,
     RouterModule
   ],
-  declarations: [TableComponent, TableKeyValueComponent, TableActionsComponent, CRUDTableComponent],
+  declarations: [
+    TableComponent,
+    TableKeyValueComponent,
+    TableActionsComponent,
+    CRUDTableComponent,
+    TablePaginationComponent
+  ],
   exports: [
     TableComponent,
     NgxDatatableModule,
     TableKeyValueComponent,
     TableActionsComponent,
-    CRUDTableComponent
+    CRUDTableComponent,
+    TablePaginationComponent
   ]
 })
 export class DataTableModule {}
index 150d44241051e23a09a0216ebd59a7205dca495a..af493513eec099b6f49e3e440cecd93b8fff8354 100644 (file)
@@ -12,6 +12,7 @@ import { CdTableColumn } from '~/app/shared/models/cd-table-column';
 import { CdDatePipe } from '~/app/shared/pipes/cd-date.pipe';
 import { PipesModule } from '~/app/shared/pipes/pipes.module';
 import { configureTestBed } from '~/testing/unit-test-helper';
+import { TablePaginationComponent } from '../table-pagination/table-pagination.component';
 import { TableComponent } from '../table/table.component';
 import { TableKeyValueComponent } from './table-key-value.component';
 
@@ -20,7 +21,7 @@ describe('TableKeyValueComponent', () => {
   let fixture: ComponentFixture<TableKeyValueComponent>;
 
   configureTestBed({
-    declarations: [TableComponent, TableKeyValueComponent],
+    declarations: [TableComponent, TableKeyValueComponent, TablePaginationComponent],
     imports: [
       FormsModule,
       NgxDatatableModule,
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-pagination/table-pagination.component.html b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-pagination/table-pagination.component.html
new file mode 100644 (file)
index 0000000..7582c76
--- /dev/null
@@ -0,0 +1,58 @@
+<nav class="pagination"
+     aria-label="Pagination"
+     i18n-aria-label>
+  <button
+    class="pagination__btn pagination__btn_first"
+    aria-label="Go to first page"
+    i18n-aria-label
+    [disabled]="!canPrevious()"
+    (click)="selectPage(1)"
+  >
+    <i class="fa fa-angle-double-left"
+       aria-hidden="true"></i>
+  </button>
+  <button
+    class="pagination__btn pagination__btn_prev"
+    aria-label="Go to previous page"
+    i18n-aria-label
+    [disabled]="!canPrevious()"
+    (click)="prevPage()"
+  >
+    <i class="fa fa-angle-left"
+       aria-hidden="true"></i>
+  </button>
+  <div class="pagination__pages">
+    <input
+      #pageNumber
+      class="pagination__page_input"
+      aria-label="Current page"
+      i18n-aria-label
+      type="number"
+      min="1"
+      [max]="totalPages"
+      [value]="page"
+      (input)="selectPage(pageNumber.valueAsNumber)"
+    />
+    <span aria-hidden="true"> of {{ totalPages }} </span>
+  </div>
+  <button
+    class="pagination__btn pagination__btn_next"
+    aria-label="Go to next page"
+    i18n-aria-label
+    (click)="nextPage()"
+    [disabled]="!canNext()"
+  >
+    <i class="fa fa-angle-right"
+       aria-hidden="true"></i>
+  </button>
+  <button
+    class="pagination__btn pagination__btn_last"
+    aria-label="Go to last page"
+    i18n-aria-label
+    [disabled]="!canNext()"
+    (click)="selectPage(totalPages)"
+  >
+    <i class="fa fa-angle-double-right"
+       aria-hidden="true"></i>
+  </button>
+</nav>
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-pagination/table-pagination.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-pagination/table-pagination.component.scss
new file mode 100644 (file)
index 0000000..4455ded
--- /dev/null
@@ -0,0 +1,21 @@
+@use './src/styles/vendor/variables' as vv;
+
+.pagination {
+  align-items: center;
+  display: flex;
+}
+
+.pagination__btn {
+  background: none;
+  border: 0;
+
+  &:disabled {
+    color: vv.$gray-500;
+  }
+}
+
+.pagination__page_input {
+  border: 1px solid vv.$gray-500;
+  border-radius: 0.25rem;
+  padding-left: 0.25rem;
+}
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-pagination/table-pagination.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-pagination/table-pagination.component.spec.ts
new file mode 100644 (file)
index 0000000..b220b59
--- /dev/null
@@ -0,0 +1,54 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TablePaginationComponent } from './table-pagination.component';
+
+describe('TablePaginationComponent', () => {
+  let component: TablePaginationComponent;
+  let fixture: ComponentFixture<TablePaginationComponent>;
+  let element: HTMLElement;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [TablePaginationComponent]
+    }).compileComponents();
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(TablePaginationComponent);
+    component = fixture.componentInstance;
+    element = fixture.debugElement.nativeElement;
+    component.page = 1;
+    component.size = 10;
+    component.count = 100;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+
+  it('should contain valid inputs', () => {
+    expect(component.page).toEqual(1);
+    expect(component.size).toEqual(10);
+    expect(component.count).toEqual(100);
+  });
+
+  it('should change page', () => {
+    const input = element.querySelector('input');
+    input.value = '5';
+    input.dispatchEvent(new Event('input'));
+    expect(component.page).toEqual(5);
+  });
+
+  it('should disable prev button', () => {
+    const prev: HTMLButtonElement = element.querySelector('.pagination__btn_prev');
+    expect(prev.disabled).toBeTruthy();
+  });
+
+  it('should disable next button', () => {
+    const next: HTMLButtonElement = element.querySelector('.pagination__btn_next');
+    component.size = 100;
+    fixture.detectChanges();
+    expect(next.disabled).toBeTruthy();
+  });
+});
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-pagination/table-pagination.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/table-pagination/table-pagination.component.ts
new file mode 100644 (file)
index 0000000..5080f05
--- /dev/null
@@ -0,0 +1,110 @@
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+
+@Component({
+  selector: 'cd-table-pagination',
+  templateUrl: './table-pagination.component.html',
+  styleUrls: ['./table-pagination.component.scss']
+})
+export class TablePaginationComponent {
+  private _size = 0;
+  private _count = 0;
+  private _page = 1;
+  pages: any;
+
+  @Input()
+  set size(value: number) {
+    this._size = value;
+    this.pages = this.calcPages();
+  }
+
+  get size(): number {
+    return this._size;
+  }
+
+  @Input()
+  set page(value: number) {
+    this._page = value;
+  }
+
+  get page(): number {
+    return this._page;
+  }
+
+  @Input()
+  set count(value: number) {
+    this._count = value;
+  }
+
+  get count(): number {
+    return this._count;
+  }
+
+  get totalPages(): number {
+    const count = this.size < 1 ? 1 : Math.ceil(this._count / this._size);
+    return Math.max(count || 0, 1);
+  }
+
+  @Output() pageChange: EventEmitter<any> = new EventEmitter();
+
+  canPrevious(): boolean {
+    return this._page > 1;
+  }
+
+  canNext(): boolean {
+    return this._page < this.totalPages;
+  }
+
+  prevPage(): void {
+    this.selectPage(this._page - 1);
+  }
+
+  nextPage(): void {
+    this.selectPage(this._page + 1);
+  }
+
+  selectPage(page: number): void {
+    if (page > 0 && page <= this.totalPages && page !== this.page) {
+      this._page = page;
+      this.pageChange.emit({
+        page
+      });
+    } else if (page > 0 && page >= this.totalPages) {
+      this._page = this.totalPages;
+      this.pageChange.emit({
+        page: this.totalPages
+      });
+    }
+  }
+
+  calcPages(page?: number): any[] {
+    const pages = [];
+    let startPage = 1;
+    let endPage = this.totalPages;
+    const maxSize = 5;
+    const isMaxSized = maxSize < this.totalPages;
+
+    page = page || this.page;
+
+    if (isMaxSized) {
+      startPage = page - Math.floor(maxSize / 2);
+      endPage = page + Math.floor(maxSize / 2);
+
+      if (startPage < 1) {
+        startPage = 1;
+        endPage = Math.min(startPage + maxSize - 1, this.totalPages);
+      } else if (endPage > this.totalPages) {
+        startPage = Math.max(this.totalPages - maxSize + 1, 1);
+        endPage = this.totalPages;
+      }
+    }
+
+    for (let num = startPage; num <= endPage; num++) {
+      pages.push({
+        number: num,
+        text: <string>(<any>num)
+      });
+    }
+
+    return pages;
+  }
+}
index d7dd7e5c703ffbbf4b8a69e13fe82200a351cea2..b92fb655d553ace9d915085eeb2fabd4f85095f4 100644 (file)
             {{ rowCount }} <ng-container i18n="X total">total</ng-container>
           </ng-template>
         </div>
-        <datatable-pager [pagerLeftArrowIcon]="paginationClasses.pagerPrevious"
-                         [pagerRightArrowIcon]="paginationClasses.pagerNext"
-                         [pagerPreviousIcon]="paginationClasses.pagerLeftArrow"
-                         [pagerNextIcon]="paginationClasses.pagerRightArrow"
-                         [page]="curPage"
-                         [size]="pageSize"
-                         [count]="rowCount"
-                         [hidden]="!((rowCount / pageSize) > 1)"
-                         (change)="table.onFooterPage($event)">
-        </datatable-pager>
+        <cd-table-pagination [page]="curPage"
+                             [size]="pageSize"
+                             [count]="rowCount"
+                             [hidden]="!((rowCount / pageSize) > 1)"
+                             (pageChange)="table.onFooterPage($event)"></cd-table-pagination>
       </ng-template>
     </ngx-datatable-footer>
   </ngx-datatable>
index f0f649780afed5270d91d24d2e183ab16b58937c..f8178be26d8a8e0053a37ad0d9704cb03c5f285c 100644 (file)
@@ -16,6 +16,7 @@ import { CdTableFetchDataContext } from '~/app/shared/models/cd-table-fetch-data
 import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
 import { PipesModule } from '~/app/shared/pipes/pipes.module';
 import { configureTestBed } from '~/testing/unit-test-helper';
+import { TablePaginationComponent } from '../table-pagination/table-pagination.component';
 import { TableComponent } from './table.component';
 
 describe('TableComponent', () => {
@@ -39,7 +40,7 @@ describe('TableComponent', () => {
   };
 
   configureTestBed({
-    declarations: [TableComponent],
+    declarations: [TableComponent, TablePaginationComponent],
     imports: [
       BrowserAnimationsModule,
       NgxDatatableModule,