]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.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)
committerNizamudeen A <nia@redhat.com>
Thu, 12 Jan 2023 10:29:21 +0000 (15:59 +0530)
Add custom pagination component based on patternfly design

Fixes: https://tracker.ceph.com/issues/55876
Signed-off-by: nsedrickm <nsedrick101@gmail.com>
(cherry picked from commit 363352163dd13c2ebe2a03865eda6cf5319d2536)

src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/crud-table/crud-table.component.spec.ts

  - Deleted the CrudTable component since its not backported yet
src/pybind/mgr/dashboard/frontend/src/app/shared/datatable/datatable.module.ts
  - Deleted the import of crud-table

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/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 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 2c653f809081f7e54d8c274994c23979e57aa5b5..576a1cbe76181ca035f44fb837fdf86dd06fdb8c 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 98ed0bf63a164652309f770e289fc3e22344a2f1..53c246d6e0bc66a42843d6cfabda3da323ad3f6a 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,