From bc30475e201416f35ad3e44481f9d7fafb3f6d26 Mon Sep 17 00:00:00 2001
From: Bastian Jakobi <55296998+bastianjakobi@users.noreply.github.com>
Date: Fri, 15 Mar 2024 14:27:39 +0100
Subject: [PATCH] feat: add ability to dynamically hide/disable action buttons
(#169)
* feat: add ability to dynamically hide/disable table action buttons
* feat: add ability to dynamically hide/disable list action buttons
* refactor: remove whitespace difference
* feat: add ability to dynamically hide/disable grid action buttons
* test: add tests for data table
* test: add tests for data list grid
* test: add tests for hidden action buttons in data table
* test: add smoke tests to parent components
---
.../data-list-grid.component.html | 15 +-
.../data-list-grid.component.spec.ts | 861 ++++++++++++------
.../data-list-grid.component.stories.ts | 153 ++++
.../data-list-grid.component.ts | 41 +-
.../data-table/data-table.component.html | 12 +-
.../data-table/data-table.component.spec.ts | 253 ++++-
.../data-table.component.stories.ts | 75 +-
.../data-table/data-table.component.ts | 11 +
.../data-view/data-view.component.html | 12 +
.../data-view/data-view.component.spec.ts | 161 ++++
.../data-view/data-view.component.ts | 6 +
.../interactive-data-view.component.html | 6 +
.../interactive-data-view.component.spec.ts | 141 +++
.../interactive-data-view.component.ts | 6 +
.../testing/data-list-grid.harness.ts | 35 +-
.../testing/data-table.harness.ts | 28 +-
16 files changed, 1481 insertions(+), 335 deletions(-)
create mode 100644 libs/portal-integration-angular/src/lib/core/components/data-list-grid/data-list-grid.component.stories.ts
diff --git a/libs/portal-integration-angular/src/lib/core/components/data-list-grid/data-list-grid.component.html b/libs/portal-integration-angular/src/lib/core/components/data-list-grid/data-list-grid.component.html
index ec90279a..a0922f2a 100644
--- a/libs/portal-integration-angular/src/lib/core/components/data-list-grid/data-list-grid.component.html
+++ b/libs/portal-integration-angular/src/lib/core/components/data-list-grid/data-list-grid.component.html
@@ -56,10 +56,11 @@
@@ -80,7 +81,7 @@
{{ resolveFieldData(item, titleLineId) || '' }}
-
+
-
+
-
+
diff --git a/libs/portal-integration-angular/src/lib/core/components/data-list-grid/data-list-grid.component.spec.ts b/libs/portal-integration-angular/src/lib/core/components/data-list-grid/data-list-grid.component.spec.ts
index b2a3dac8..8f12db67 100644
--- a/libs/portal-integration-angular/src/lib/core/components/data-list-grid/data-list-grid.component.spec.ts
+++ b/libs/portal-integration-angular/src/lib/core/components/data-list-grid/data-list-grid.component.spec.ts
@@ -1,296 +1,607 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
-import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
+import { NoopAnimationsModule } from '@angular/platform-browser/animations'
import { TranslateModule, TranslateService } from '@ngx-translate/core'
import { DataListGridComponent } from './data-list-grid.component'
-import { PrimeNgModule } from '../../primeng.module';
-import { TranslateTestingModule } from 'ngx-translate-testing';
-import { ColumnType } from '../../../model/column-type.model';
-import { PortalCoreModule } from '../../portal-core.module';
-import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
-import { DataListGridHarness, DataTableHarness } from '../../../../../../../libs/portal-integration-angular/testing';
-import { MockAuthModule } from '../../../mock-auth/mock-auth.module';
-import { ActivatedRoute, RouterModule } from '@angular/router';
+import { PrimeNgModule } from '../../primeng.module'
+import { TranslateTestingModule } from 'ngx-translate-testing'
+import { ColumnType } from '../../../model/column-type.model'
+import { PortalCoreModule } from '../../portal-core.module'
+import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'
+import { DataListGridHarness, DataTableHarness } from '../../../../../../../libs/portal-integration-angular/testing'
+import { MockAuthModule } from '../../../mock-auth/mock-auth.module'
+import { ActivatedRoute, RouterModule } from '@angular/router'
+import { UserService } from '../../../services/user.service'
+import { MockUserService } from '../../../../../mocks/mock-user-service'
describe('DataListGridComponent', () => {
- let fixture: ComponentFixture
- let component: DataListGridComponent
- let translateService: TranslateService
-
- const ENGLISH_LANGUAGE = 'en';
- const ENGLISH_TRANSLATIONS = {
- OCX_DATA_TABLE: {
- SHOWING: "{{first}} - {{last}} of {{totalRecords}}",
- SHOWING_WITH_TOTAL_ON_SERVER: "{{first}} - {{last}} of {{totalRecords}} ({{totalRecordsOnServer}})",
- ALL: "All"
- }
- };
-
- const GERMAN_LANGUAGE = 'de';
- const GERMAN_TRANSLATIONS = {
- OCX_DATA_TABLE: {
- SHOWING: "{{first}} - {{last}} von {{totalRecords}}",
- SHOWING_WITH_TOTAL_ON_SERVER: "{{first}} - {{last}} von {{totalRecords}} ({{totalRecordsOnServer}})",
- ALL: "Alle"
- }
- };
+ let fixture: ComponentFixture
+ let component: DataListGridComponent
+ let translateService: TranslateService
+ let listGrid: DataListGridHarness
- const TRANSLATIONS = {
- [ENGLISH_LANGUAGE]: ENGLISH_TRANSLATIONS,
- [GERMAN_LANGUAGE]: GERMAN_TRANSLATIONS
- };
+ const ENGLISH_LANGUAGE = 'en'
+ const ENGLISH_TRANSLATIONS = {
+ OCX_DATA_TABLE: {
+ SHOWING: '{{first}} - {{last}} of {{totalRecords}}',
+ SHOWING_WITH_TOTAL_ON_SERVER: '{{first}} - {{last}} of {{totalRecords}} ({{totalRecordsOnServer}})',
+ ALL: 'All',
+ },
+ }
- const mockData = [
- {
- version: 0,
- creationDate: '2023-09-12T09:34:11.997048Z',
- creationUser: 'creation user',
- modificationDate: '2023-09-12T09:34:11.997048Z',
- modificationUser: '',
- id: '195ee34e-41c6-47b7-8fc4-3f245dee7651',
- name: 'some name',
- description: '',
- status: 'some status',
- responsible: 'someone responsible',
- endDate: '2023-09-14T09:34:09Z',
- startDate: '2023-09-13T09:34:05Z',
- imagePath: '/path/to/image',
- testNumber: '1',
- },
- {
- version: 0,
- creationDate: '2023-09-12T09:33:58.544494Z',
- creationUser: '',
- modificationDate: '2023-09-12T09:33:58.544494Z',
- modificationUser: '',
- id: '5f8bb05b-d089-485e-a234-0bb6ff25234e',
- name: 'example',
- description: 'example description',
- status: 'status example',
- responsible: '',
- endDate: '2023-09-13T09:33:55Z',
- startDate: '2023-09-12T09:33:53Z',
- imagePath: '',
- testNumber: '3.141',
- },
- {
- version: 0,
- creationDate: '2023-09-12T09:34:27.184086Z',
- creationUser: '',
- modificationDate: '2023-09-12T09:34:27.184086Z',
- modificationUser: '',
- id: 'cf9e7d6b-5362-46af-91f8-62f7ef5c6064',
- name: 'name 1',
- description: '',
- status: 'status name 1',
- responsible: '',
- endDate: '2023-09-15T09:34:24Z',
- startDate: '2023-09-14T09:34:22Z',
- imagePath: '',
- testNumber: '123456789',
- },
- {
- version: 0,
- creationDate: '2023-09-12T09:34:27.184086Z',
- creationUser: '',
- modificationDate: '2023-09-12T09:34:27.184086Z',
- modificationUser: '',
- id: 'cf9e7d6b-5362-46af-91f8-62f7ef5c6064',
- name: 'name 2',
- description: '',
- status: 'status name 2',
- responsible: '',
- endDate: '2023-09-15T09:34:24Z',
- startDate: '2023-09-14T09:34:22Z',
- imagePath: '',
- testNumber: '12345.6789',
- },
+ const GERMAN_LANGUAGE = 'de'
+ const GERMAN_TRANSLATIONS = {
+ OCX_DATA_TABLE: {
+ SHOWING: '{{first}} - {{last}} von {{totalRecords}}',
+ SHOWING_WITH_TOTAL_ON_SERVER: '{{first}} - {{last}} von {{totalRecords}} ({{totalRecordsOnServer}})',
+ ALL: 'Alle',
+ },
+ }
+
+ const TRANSLATIONS = {
+ [ENGLISH_LANGUAGE]: ENGLISH_TRANSLATIONS,
+ [GERMAN_LANGUAGE]: GERMAN_TRANSLATIONS,
+ }
+
+ const mockData = [
+ {
+ version: 0,
+ creationDate: '2023-09-12T09:34:11.997048Z',
+ creationUser: 'creation user',
+ modificationDate: '2023-09-12T09:34:11.997048Z',
+ modificationUser: '',
+ id: '195ee34e-41c6-47b7-8fc4-3f245dee7651',
+ name: 'some name',
+ description: '',
+ status: 'some status',
+ responsible: 'someone responsible',
+ endDate: '2023-09-14T09:34:09Z',
+ startDate: '2023-09-13T09:34:05Z',
+ imagePath: '/path/to/image',
+ testNumber: '1',
+ },
+ {
+ version: 0,
+ creationDate: '2023-09-12T09:33:58.544494Z',
+ creationUser: '',
+ modificationDate: '2023-09-12T09:33:58.544494Z',
+ modificationUser: '',
+ id: '5f8bb05b-d089-485e-a234-0bb6ff25234e',
+ name: 'example',
+ description: 'example description',
+ status: 'status example',
+ responsible: '',
+ endDate: '2023-09-13T09:33:55Z',
+ startDate: '2023-09-12T09:33:53Z',
+ imagePath: '',
+ testNumber: '3.141',
+ },
+ {
+ version: 0,
+ creationDate: '2023-09-12T09:34:27.184086Z',
+ creationUser: '',
+ modificationDate: '2023-09-12T09:34:27.184086Z',
+ modificationUser: '',
+ id: 'cf9e7d6b-5362-46af-91f8-62f7ef5c6064',
+ name: 'name 1',
+ description: '',
+ status: 'status name 1',
+ responsible: '',
+ endDate: '2023-09-15T09:34:24Z',
+ startDate: '2023-09-14T09:34:22Z',
+ imagePath: '',
+ testNumber: '123456789',
+ },
+ {
+ version: 0,
+ creationDate: '2023-09-12T09:34:27.184086Z',
+ creationUser: '',
+ modificationDate: '2023-09-12T09:34:27.184086Z',
+ modificationUser: '',
+ id: 'cf9e7d6b-5362-46af-91f8-62f7ef5c6064',
+ name: 'name 2',
+ description: '',
+ status: 'status name 2',
+ responsible: '',
+ endDate: '2023-09-15T09:34:24Z',
+ startDate: '2023-09-14T09:34:22Z',
+ imagePath: '',
+ testNumber: '12345.6789',
+ },
+ {
+ version: 0,
+ creationDate: '2023-09-12T09:34:27.184086Z',
+ creationUser: '',
+ modificationDate: '2023-09-12T09:34:27.184086Z',
+ modificationUser: '',
+ id: 'cf9e7d6b-5362-46af-91f8-62f7ef5c6064',
+ name: 'name 3',
+ description: '',
+ status: 'status name 3',
+ responsible: '',
+ endDate: '2023-09-15T09:34:24Z',
+ startDate: '2023-09-14T09:34:22Z',
+ imagePath: '',
+ testNumber: '7.1',
+ },
+ ]
+ const mockColumns = [
+ {
+ columnType: ColumnType.STRING,
+ id: 'name',
+ nameKey: 'COLUMN_HEADER_NAME.NAME',
+ filterable: true,
+ sortable: true,
+ predefinedGroupKeys: ['PREDEFINED_GROUP.DEFAULT', 'PREDEFINED_GROUP.EXTENDED', 'PREDEFINED_GROUP.FULL'],
+ },
+ {
+ columnType: ColumnType.STRING,
+ id: 'description',
+ nameKey: 'COLUMN_HEADER_NAME.DESCRIPTION',
+ filterable: true,
+ sortable: true,
+ predefinedGroupKeys: ['PREDEFINED_GROUP.DEFAULT', 'PREDEFINED_GROUP.EXTENDED', 'PREDEFINED_GROUP.FULL'],
+ },
+ {
+ columnType: ColumnType.DATE,
+ id: 'startDate',
+ nameKey: 'COLUMN_HEADER_NAME.START_DATE',
+ filterable: true,
+ sortable: true,
+ predefinedGroupKeys: ['PREDEFINED_GROUP.EXTENDED', 'PREDEFINED_GROUP.FULL'],
+ },
+ {
+ columnType: ColumnType.DATE,
+ id: 'endDate',
+ nameKey: 'COLUMN_HEADER_NAME.END_DATE',
+ filterable: true,
+ sortable: true,
+ predefinedGroupKeys: ['PREDEFINED_GROUP.EXTENDED', 'PREDEFINED_GROUP.FULL'],
+ },
+ {
+ columnType: ColumnType.TRANSLATION_KEY,
+ id: 'status',
+ nameKey: 'COLUMN_HEADER_NAME.STATUS',
+ filterable: true,
+ sortable: true,
+ predefinedGroupKeys: ['PREDEFINED_GROUP.DEFAULT', 'PREDEFINED_GROUP.EXTENDED', 'PREDEFINED_GROUP.FULL'],
+ },
+ {
+ columnType: ColumnType.STRING,
+ id: 'responsible',
+ nameKey: 'COLUMN_HEADER_NAME.RESPONSIBLE',
+ filterable: true,
+ sortable: true,
+ predefinedGroupKeys: ['PREDEFINED_GROUP.DEFAULT', 'PREDEFINED_GROUP.EXTENDED', 'PREDEFINED_GROUP.FULL'],
+ },
+ {
+ columnType: ColumnType.RELATIVE_DATE,
+ id: 'modificationDate',
+ nameKey: 'COLUMN_HEADER_NAME.MODIFICATION_DATE',
+ filterable: true,
+ sortable: true,
+ predefinedGroupKeys: ['PREDEFINED_GROUP.FULL'],
+ },
+ {
+ columnType: ColumnType.STRING,
+ id: 'creationUser',
+ nameKey: 'COLUMN_HEADER_NAME.CREATION_USER',
+ filterable: true,
+ sortable: true,
+ predefinedGroupKeys: ['PREDEFINED_GROUP.FULL'],
+ },
+ {
+ columnType: ColumnType.NUMBER,
+ id: 'testNumber',
+ nameKey: 'COLUMN_HEADER_NAME.TEST_NUMBER',
+ filterable: true,
+ sortable: true,
+ predefinedGroupKeys: ['PREDEFINED_GROUP.EXTENDED', 'PREDEFINED_GROUP.FULL'],
+ },
+ ]
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [DataListGridComponent],
+ imports: [
+ PrimeNgModule,
+ TranslateModule.forRoot(),
+ TranslateTestingModule.withTranslations(TRANSLATIONS),
+ PortalCoreModule,
+ MockAuthModule,
+ RouterModule,
+ NoopAnimationsModule,
+ ],
+ providers: [
{
- version: 0,
- creationDate: '2023-09-12T09:34:27.184086Z',
- creationUser: '',
- modificationDate: '2023-09-12T09:34:27.184086Z',
- modificationUser: '',
- id: 'cf9e7d6b-5362-46af-91f8-62f7ef5c6064',
- name: 'name 3',
- description: '',
- status: 'status name 3',
- responsible: '',
- endDate: '2023-09-15T09:34:24Z',
- startDate: '2023-09-14T09:34:22Z',
- imagePath: '',
- testNumber: '7.1',
+ provide: ActivatedRoute,
+ useValue: {
+ snapshot: {
+ paramMap: {
+ get: () => '1',
+ },
+ },
+ },
},
+ { provide: UserService, useClass: MockUserService },
+ ],
+ }).compileComponents()
+
+ fixture = TestBed.createComponent(DataListGridComponent)
+ component = fixture.componentInstance
+ component.data = mockData
+ component.columns = mockColumns
+ component.paginator = true
+ translateService = TestBed.inject(TranslateService)
+ translateService.use('en')
+ fixture.detectChanges()
+ listGrid = await TestbedHarnessEnvironment.harnessForFixture(fixture, DataListGridHarness)
+ })
+
+ it('should create the data list grid component', () => {
+ expect(component).toBeTruthy()
+ })
+
+ it('loads dataListGrid', async () => {
+ const dataListGrid = await TestbedHarnessEnvironment.harnessForFixture(fixture, DataListGridHarness)
+ expect(dataListGrid).toBeTruthy()
+ })
+
+ describe('should display the paginator currentPageReport -', () => {
+ it('de', async () => {
+ translateService.use('de')
+ const dataListGrid = await TestbedHarnessEnvironment.harnessForFixture(fixture, DataListGridHarness)
+ const paginator = await dataListGrid.getPaginator()
+ const currentPageReport = await paginator.getCurrentPageReportText()
+ expect(currentPageReport).toEqual('1 - 5 von 5')
+ })
+
+ it('en', async () => {
+ translateService.use('en')
+ const dataListGrid = await TestbedHarnessEnvironment.harnessForFixture(fixture, DataListGridHarness)
+ const paginator = await dataListGrid.getPaginator()
+ const currentPageReport = await paginator.getCurrentPageReportText()
+ expect(currentPageReport).toEqual('1 - 5 of 5')
+ })
+ })
+
+ describe('should display the paginator currentPageReport with totalRecordsOnServer -', () => {
+ it('de', async () => {
+ component.totalRecordsOnServer = 10
+ translateService.use('de')
+ const dataListGrid = await TestbedHarnessEnvironment.harnessForFixture(fixture, DataListGridHarness)
+ const paginator = await dataListGrid.getPaginator()
+ const currentPageReport = await paginator.getCurrentPageReportText()
+ expect(currentPageReport).toEqual('1 - 5 von 5 (10)')
+ })
+
+ it('en', async () => {
+ component.totalRecordsOnServer = 10
+ translateService.use('en')
+ const dataListGrid = await TestbedHarnessEnvironment.harnessForFixture(fixture, DataListGridHarness)
+ const paginator = await dataListGrid.getPaginator()
+ const currentPageReport = await paginator.getCurrentPageReportText()
+ expect(currentPageReport).toEqual('1 - 5 of 5 (10)')
+ })
+ })
+
+ describe('should display the paginator rowsPerPageOptions -', () => {
+ it('de', async () => {
+ window.HTMLElement.prototype.scrollIntoView = jest.fn()
+ translateService.use('de')
+ const dataListGrid = await TestbedHarnessEnvironment.harnessForFixture(fixture, DataTableHarness)
+ const paginator = await dataListGrid.getPaginator()
+ const rowsPerPageOptions = await paginator.getRowsPerPageOptions()
+ const rowsPerPageOptionsText = await rowsPerPageOptions.selectedDropdownItemText(3)
+ expect(rowsPerPageOptionsText).toEqual('Alle')
+ })
+
+ it('en', async () => {
+ window.HTMLElement.prototype.scrollIntoView = jest.fn()
+ translateService.use('en')
+ const dataListGrid = await TestbedHarnessEnvironment.harnessForFixture(fixture, DataTableHarness)
+ const paginator = await dataListGrid.getPaginator()
+ const rowsPerPageOptions = await paginator.getRowsPerPageOptions()
+ const rowsPerPageOptionsText = await rowsPerPageOptions.selectedDropdownItemText(3)
+ expect(rowsPerPageOptionsText).toEqual('All')
+ })
+ })
+
+ const setUpListActionButtonMockData = () => {
+ component.columns = [
+ ...mockColumns,
+ {
+ columnType: ColumnType.STRING,
+ id: 'ready',
+ nameKey: 'Ready',
+ },
]
- const mockColumns = [
- {
- columnType: ColumnType.STRING,
- id: 'name',
- nameKey: 'COLUMN_HEADER_NAME.NAME',
- filterable: true,
- sortable: true,
- predefinedGroupKeys: ['PREDEFINED_GROUP.DEFAULT', 'PREDEFINED_GROUP.EXTENDED', 'PREDEFINED_GROUP.FULL'],
- },
- {
- columnType: ColumnType.STRING,
- id: 'description',
- nameKey: 'COLUMN_HEADER_NAME.DESCRIPTION',
- filterable: true,
- sortable: true,
- predefinedGroupKeys: ['PREDEFINED_GROUP.DEFAULT', 'PREDEFINED_GROUP.EXTENDED', 'PREDEFINED_GROUP.FULL'],
- },
- {
- columnType: ColumnType.DATE,
- id: 'startDate',
- nameKey: 'COLUMN_HEADER_NAME.START_DATE',
- filterable: true,
- sortable: true,
- predefinedGroupKeys: ['PREDEFINED_GROUP.EXTENDED', 'PREDEFINED_GROUP.FULL'],
- },
- {
- columnType: ColumnType.DATE,
- id: 'endDate',
- nameKey: 'COLUMN_HEADER_NAME.END_DATE',
- filterable: true,
- sortable: true,
- predefinedGroupKeys: ['PREDEFINED_GROUP.EXTENDED', 'PREDEFINED_GROUP.FULL'],
- },
- {
- columnType: ColumnType.TRANSLATION_KEY,
- id: 'status',
- nameKey: 'COLUMN_HEADER_NAME.STATUS',
- filterable: true,
- sortable: true,
- predefinedGroupKeys: ['PREDEFINED_GROUP.DEFAULT', 'PREDEFINED_GROUP.EXTENDED', 'PREDEFINED_GROUP.FULL'],
- },
- {
- columnType: ColumnType.STRING,
- id: 'responsible',
- nameKey: 'COLUMN_HEADER_NAME.RESPONSIBLE',
- filterable: true,
- sortable: true,
- predefinedGroupKeys: ['PREDEFINED_GROUP.DEFAULT', 'PREDEFINED_GROUP.EXTENDED', 'PREDEFINED_GROUP.FULL'],
- },
- {
- columnType: ColumnType.RELATIVE_DATE,
- id: 'modificationDate',
- nameKey: 'COLUMN_HEADER_NAME.MODIFICATION_DATE',
- filterable: true,
- sortable: true,
- predefinedGroupKeys: ['PREDEFINED_GROUP.FULL'],
- },
- {
- columnType: ColumnType.STRING,
- id: 'creationUser',
- nameKey: 'COLUMN_HEADER_NAME.CREATION_USER',
- filterable: true,
- sortable: true,
- predefinedGroupKeys: ['PREDEFINED_GROUP.FULL'],
- },
- {
- columnType: ColumnType.NUMBER,
- id: 'testNumber',
- nameKey: 'COLUMN_HEADER_NAME.TEST_NUMBER',
- filterable: true,
- sortable: true,
- predefinedGroupKeys: ['PREDEFINED_GROUP.EXTENDED', 'PREDEFINED_GROUP.FULL'],
- },
+
+ component.data = [
+ {
+ version: 0,
+ creationDate: '2023-09-12T09:34:27.184086Z',
+ creationUser: '',
+ modificationDate: '2023-09-12T09:34:27.184086Z',
+ modificationUser: '',
+ id: 'cf9e7d6b-5362-46af-91f8-62f7ef5c6064',
+ name: 'name 3',
+ description: '',
+ status: 'status name 3',
+ responsible: '',
+ endDate: '2023-09-15T09:34:24Z',
+ startDate: '2023-09-14T09:34:22Z',
+ imagePath: '',
+ testNumber: '7.1',
+ ready: false,
+ },
]
- beforeEach(async () => {
- await TestBed.configureTestingModule({
- declarations: [DataListGridComponent],
- imports: [PrimeNgModule, BrowserAnimationsModule, TranslateModule.forRoot(), TranslateTestingModule.withTranslations(TRANSLATIONS),
- PortalCoreModule, MockAuthModule, RouterModule],
- providers: [
- {
- provide: ActivatedRoute,
- useValue: {
- snapshot: {
- paramMap: {
- get: () => '1',
- },
- },
- },
- },
- ],
- }).compileComponents()
-
- fixture = TestBed.createComponent(DataListGridComponent)
- component = fixture.componentInstance
- component.data = mockData
- component.columns = mockColumns
- component.paginator = true
- translateService = TestBed.inject(TranslateService)
- translateService.use('en')
- fixture.detectChanges()
+ component.viewItem.subscribe(() => console.log())
+ component.editItem.subscribe(() => console.log())
+ component.deleteItem.subscribe(() => console.log())
+ component.viewPermission = 'VIEW'
+ component.editPermission = 'EDIT'
+ component.deletePermission = 'DELETE'
+ }
+ describe('Disable list action buttons based on field path', () => {
+ it('should not disable any list action button by default', async () => {
+ component.layout = 'list'
+
+ expect(component.viewItemObserved).toBe(false)
+ expect(component.editItemObserved).toBe(false)
+ expect(component.deleteItemObserved).toBe(false)
+
+ setUpListActionButtonMockData()
+
+ expect(component.viewItemObserved).toBe(true)
+ expect(component.editItemObserved).toBe(true)
+ expect(component.deleteItemObserved).toBe(true)
+
+ const listActions = await listGrid.getActionButtons('list')
+ expect(listActions.length).toBe(3)
+ const expectedIcons = ['pi pi-eye', 'pi pi-trash', 'pi pi-pencil']
+
+ for (const action of listActions) {
+ expect(await listGrid.actionButtonIsDisabled(action, 'list')).toBe(false)
+ const icon = await action.getAttribute('icon')
+ if (icon) {
+ const index = expectedIcons.indexOf(icon)
+ expect(index).toBeGreaterThanOrEqual(0)
+ expectedIcons.splice(index, 1)
+ }
+ }
+
+ expect(expectedIcons.length).toBe(0)
+ })
+
+ it('should dynamically enable/disable an action button based on the contents of a specified field', async () => {
+ component.layout = 'list'
+ setUpListActionButtonMockData()
+ component.viewActionEnabledField = 'ready'
+
+ let listActions = await listGrid.getActionButtons('list')
+ expect(listActions.length).toBe(3)
+
+ for (const action of listActions) {
+ const icon = await action.getAttribute('icon')
+ const isDisabled = await listGrid.actionButtonIsDisabled(action, 'list')
+ if (icon === 'pi pi-eye') {
+ expect(isDisabled).toBe(true)
+ } else {
+ expect(isDisabled).toBe(false)
+ }
+ }
+
+ const tempData = [...component.data]
+
+ tempData[0]['ready'] = true
+
+ component.data = [...tempData]
+
+ listActions = await listGrid.getActionButtons('list')
+
+ for (const action of listActions) {
+ expect(await listGrid.actionButtonIsDisabled(action, 'list')).toBe(false)
+ }
+ })
+ })
+
+ describe('Hide list action buttons based on field path', () => {
+ it('should not hide any list action button by default', async () => {
+ component.layout = 'list'
+
+ expect(component.viewItemObserved).toBe(false)
+ expect(component.editItemObserved).toBe(false)
+ expect(component.deleteItemObserved).toBe(false)
+
+ setUpListActionButtonMockData()
+
+ expect(component.viewItemObserved).toBe(true)
+ expect(component.editItemObserved).toBe(true)
+ expect(component.deleteItemObserved).toBe(true)
+
+ const listActions = await listGrid.getActionButtons('list')
+ expect(listActions.length).toBe(3)
+ const expectedIcons = ['pi pi-eye', 'pi pi-trash', 'pi pi-pencil']
+
+ for (const action of listActions) {
+ const icon = await action.getAttribute('icon')
+ if (icon) {
+ const index = expectedIcons.indexOf(icon)
+ expect(index).toBeGreaterThanOrEqual(0)
+ expectedIcons.splice(index, 1)
+ }
+ }
+
+ expect(expectedIcons.length).toBe(0)
})
- it('should create the data list grid component', () => {
- expect(component).toBeTruthy()
+ it('should dynamically hide/show an action button based on the contents of a specified field', async () => {
+ component.layout = 'list'
+ setUpListActionButtonMockData()
+ component.viewActionVisibleField = 'ready'
+
+ let listActions = await listGrid.getActionButtons('list')
+ expect(listActions.length).toBe(2)
+
+ for (const action of listActions) {
+ const icon = await action.getAttribute('icon')
+ expect(icon === 'pi pi-eye').toBe(false)
+ }
+
+ const tempData = [...component.data]
+
+ tempData[0]['ready'] = true
+
+ component.data = [...tempData]
+
+ listActions = await listGrid.getActionButtons('list')
+
+ expect(listActions.length).toBe(3)
})
+ })
+ const setUpGridActionButtonMockData = () => {
+ component.columns = [
+ ...mockColumns,
+ {
+ columnType: ColumnType.STRING,
+ id: 'ready',
+ nameKey: 'Ready',
+ },
+ ]
+ component.data = [
+ {
+ id: 'Test',
+ imagePath:
+ 'https://images.unsplash.com/photo-1682686581427-7c80ab60e3f3?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
+ property1: 'Card 1',
+ ready: false,
+ },
+ ]
+ component.titleLineId = 'property1'
+ component.viewItem.subscribe(() => console.log())
+ component.editItem.subscribe(() => console.log())
+ component.deleteItem.subscribe(() => console.log())
+ component.viewPermission = 'VIEW'
+ component.editPermission = 'EDIT'
+ component.deletePermission = 'DELETE'
+ }
+ describe('Disable grid action buttons based on field path', () => {
+ it('should not disable any grid action button by default', async () => {
+ component.layout = 'grid'
+ expect(component.viewItemObserved).toBe(false)
+ expect(component.editItemObserved).toBe(false)
+ expect(component.deleteItemObserved).toBe(false)
+
+ setUpGridActionButtonMockData()
+
+ expect(component.viewItemObserved).toBe(true)
+ expect(component.editItemObserved).toBe(true)
+ expect(component.deleteItemObserved).toBe(true)
- it('loads dataListGrid', async () => {
- const dataListGrid = await TestbedHarnessEnvironment.harnessForFixture(fixture, DataListGridHarness)
- expect(dataListGrid).toBeTruthy()
+ const gridMenuButton = await listGrid.getMenuButton()
+
+ await gridMenuButton.click()
+
+ const gridActions = await listGrid.getActionButtons('grid')
+ expect(gridActions.length).toBe(3)
+
+ for (const action of gridActions) {
+ expect(await listGrid.actionButtonIsDisabled(action, 'grid')).toBe(false)
+ }
})
- describe('should display the paginator currentPageReport -', () => {
- it('de', async () => {
- translateService.use('de')
- const dataListGrid = await TestbedHarnessEnvironment.harnessForFixture(fixture, DataListGridHarness)
- const paginator = await dataListGrid.getPaginator()
- const currentPageReport = await paginator.getCurrentPageReportText()
- expect(currentPageReport).toEqual('1 - 5 von 5')
- })
-
- it('en', async () => {
- translateService.use('en')
- const dataListGrid = await TestbedHarnessEnvironment.harnessForFixture(fixture, DataListGridHarness)
- const paginator = await dataListGrid.getPaginator()
- const currentPageReport = await paginator.getCurrentPageReportText()
- expect(currentPageReport).toEqual('1 - 5 of 5')
- })
+ it('should dynamically enable/disable an action button based on the contents of a specified field', async () => {
+ component.layout = 'grid'
+ setUpGridActionButtonMockData()
+ component.viewActionEnabledField = 'ready'
+ const gridMenuButton = await listGrid.getMenuButton()
+
+ await gridMenuButton.click()
+
+ let gridActions = await listGrid.getActionButtons('grid')
+ expect(gridActions.length).toBe(3)
+
+ for (const action of gridActions) {
+ const isDisabled = await listGrid.actionButtonIsDisabled(action, 'grid')
+ const text = await action.text()
+ if (gridActions.indexOf(action) === 0) {
+ expect(text).toBe('OCX_DATA_LIST_GRID.MENU.VIEW')
+ expect(isDisabled).toBe(true)
+ } else {
+ expect(text === 'OCX_DATA_LIST_GRID.MENU.VIEW').toBe(false)
+ expect(isDisabled).toBe(false)
+ }
+ }
+
+ const tempData = [...component.data]
+
+ tempData[0]['ready'] = true
+
+ component.data = [...tempData]
+
+ await gridMenuButton.click()
+ await gridMenuButton.click()
+
+ gridActions = await listGrid.getActionButtons('grid')
+
+ for (const action of gridActions) {
+ expect(await listGrid.actionButtonIsDisabled(action, 'grid')).toBe(false)
+ }
})
-
- describe('should display the paginator currentPageReport with totalRecordsOnServer -', () => {
- it('de', async () => {
- component.totalRecordsOnServer = 10
- translateService.use('de')
- const dataListGrid = await TestbedHarnessEnvironment.harnessForFixture(fixture, DataListGridHarness)
- const paginator = await dataListGrid.getPaginator()
- const currentPageReport = await paginator.getCurrentPageReportText()
- expect(currentPageReport).toEqual('1 - 5 von 5 (10)')
- })
-
- it('en', async () => {
- component.totalRecordsOnServer = 10
- translateService.use('en')
- const dataListGrid = await TestbedHarnessEnvironment.harnessForFixture(fixture, DataListGridHarness)
- const paginator = await dataListGrid.getPaginator()
- const currentPageReport = await paginator.getCurrentPageReportText()
- expect(currentPageReport).toEqual('1 - 5 of 5 (10)')
- })
+ })
+
+ describe('Hide grid action buttons based on field path', () => {
+ it('should not hide any grid action button by default', async () => {
+ component.layout = 'grid'
+ expect(component.viewItemObserved).toBe(false)
+ expect(component.editItemObserved).toBe(false)
+ expect(component.deleteItemObserved).toBe(false)
+
+ setUpGridActionButtonMockData()
+
+ expect(component.viewItemObserved).toBe(true)
+ expect(component.editItemObserved).toBe(true)
+ expect(component.deleteItemObserved).toBe(true)
+
+ const gridMenuButton = await listGrid.getMenuButton()
+
+ await gridMenuButton.click()
+
+ const gridActions = await listGrid.getActionButtons('grid')
+ expect(gridActions.length).toBe(3)
})
-
- describe('should display the paginator rowsPerPageOptions -', () => {
- it('de', async () => {
- window.HTMLElement.prototype.scrollIntoView = jest.fn()
- translateService.use('de')
- const dataListGrid = await TestbedHarnessEnvironment.harnessForFixture(fixture, DataTableHarness)
- const paginator = await dataListGrid.getPaginator()
- const rowsPerPageOptions = await paginator.getRowsPerPageOptions()
- const rowsPerPageOptionsText = await rowsPerPageOptions.selectedDropdownItemText(3)
- expect(rowsPerPageOptionsText).toEqual('Alle')
- })
-
- it('en', async () => {
- window.HTMLElement.prototype.scrollIntoView = jest.fn()
- translateService.use('en')
- const dataListGrid = await TestbedHarnessEnvironment.harnessForFixture(fixture, DataTableHarness)
- const paginator = await dataListGrid.getPaginator()
- const rowsPerPageOptions = await paginator.getRowsPerPageOptions()
- const rowsPerPageOptionsText = await rowsPerPageOptions.selectedDropdownItemText(3)
- expect(rowsPerPageOptionsText).toEqual('All')
- })
+
+ it('should dynamically hide/show an action button based on the contents of a specified field', async () => {
+ component.layout = 'grid'
+ setUpGridActionButtonMockData()
+ component.viewActionVisibleField = 'ready'
+ const gridMenuButton = await listGrid.getMenuButton()
+
+ await gridMenuButton.click()
+
+ let gridActions = await listGrid.getActionButtons('grid')
+ expect(gridActions.length).toBe(2)
+
+ let hiddenGridActions = await listGrid.getActionButtons('grid-hidden')
+ expect(hiddenGridActions.length).toBe(1)
+ expect(await hiddenGridActions[0].text()).toBe('OCX_DATA_LIST_GRID.MENU.VIEW')
+
+ for (const action of gridActions) {
+ const text = await action.text()
+ expect(text === 'OCX_DATA_LIST_GRID.MENU.VIEW').toBe(false)
+ }
+
+ const tempData = [...component.data]
+
+ tempData[0]['ready'] = true
+
+ component.data = [...tempData]
+
+ await gridMenuButton.click()
+ await gridMenuButton.click()
+ gridActions = await listGrid.getActionButtons('grid')
+ expect(gridActions.length).toBe(3)
+ hiddenGridActions = await listGrid.getActionButtons('grid-hidden')
+ expect(hiddenGridActions.length).toBe(0)
})
-
+ })
})
diff --git a/libs/portal-integration-angular/src/lib/core/components/data-list-grid/data-list-grid.component.stories.ts b/libs/portal-integration-angular/src/lib/core/components/data-list-grid/data-list-grid.component.stories.ts
new file mode 100644
index 00000000..b9b0b70c
--- /dev/null
+++ b/libs/portal-integration-angular/src/lib/core/components/data-list-grid/data-list-grid.component.stories.ts
@@ -0,0 +1,153 @@
+import { StorybookTranslateModule } from './../../storybook-translate.module';
+import { Meta, moduleMetadata, applicationConfig, StoryFn } from '@storybook/angular';
+import { DataListGridComponent } from './data-list-grid.component'
+import { ButtonModule } from 'primeng/button';
+import { MultiSelectModule } from 'primeng/multiselect';
+import { importProvidersFrom } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { MockAuthModule } from '../../../mock-auth/mock-auth.module';
+import { IfPermissionDirective } from '../../directives/if-permission.directive';
+import { UserService } from '../../../services/user.service';
+import { MockUserService } from '../../../../../mocks/mock-user-service'
+import { DataViewModule } from 'primeng/dataview';
+import { MenuModule } from 'primeng/menu';
+import { RouterModule } from '@angular/router';
+
+const DataListGridComponentSBConfig: Meta = {
+ title: 'DataListGridComponent',
+ component: DataListGridComponent,
+ decorators: [
+ applicationConfig({
+ providers: [
+ importProvidersFrom(BrowserModule),
+ importProvidersFrom(BrowserAnimationsModule),
+ { provide: UserService, useClass: MockUserService },
+ importProvidersFrom(RouterModule.forRoot([], { useHash: true })),
+ ],
+ }),
+ moduleMetadata({
+ declarations: [DataListGridComponent, IfPermissionDirective],
+ imports: [
+ DataViewModule,
+ MenuModule,
+ ButtonModule,
+ MultiSelectModule,
+ StorybookTranslateModule,
+ MockAuthModule
+ ],
+ })
+ ]
+}
+const Template: StoryFn = (args) => ({
+ props: args,
+})
+
+const defaultComponentArgs = {
+ data: [
+ {
+ id: "Test",
+ imagePath: "https://images.unsplash.com/photo-1682686581427-7c80ab60e3f3?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDF8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
+ property1: "Card 1",
+ available: true
+ },
+ {
+ id: "Test2",
+ imagePath: "https://images.unsplash.com/photo-1710092662335-065cdbfb9781?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
+ property1: "Card 2",
+ available: false
+ },
+ ],
+ emptyResultsMessage: "No results",
+ titleLineId: "property1",
+ layout: "list",
+ deleteItem: ($event: any) => console.log("Delete table row ", $event),
+ editItem: ($event: any) => console.log("Edit table row ", $event),
+ viewItem: ($event: any) => console.log("View table row ", $event),
+ deletePermission: 'TEST_MGMT#TEST_DELETE',
+ editPermission: 'TEST_MGMT#TEST_EDIT',
+ viewPermission: 'TEST_MGMT#TEST_VIEW',
+}
+const defaultArgTypes = {
+ deleteItem: {action: 'deleteItem'},
+ editItem: {action: 'deleteItem'},
+ viewItem: {action: 'deleteItem'}
+}
+
+export const ListWithMockData = {
+ render: Template,
+ argTypes: defaultArgTypes,
+ args: defaultComponentArgs,
+}
+
+export const ListWithNoData = {
+ render: Template,
+ argTypes: defaultArgTypes,
+ args: {
+ ...defaultComponentArgs,
+ data: [],
+ }
+}
+
+export const ListWithConditionallyDisabledActionButtons = {
+ argTypes: defaultArgTypes,
+ render: Template,
+ args: {
+ ...defaultComponentArgs,
+ deleteActionEnabledField: "available",
+ editActionEnabledField: "available",
+ },
+}
+
+export const ListWithConditionallyHiddenActionButtons = {
+ argTypes: defaultArgTypes,
+ render: Template,
+ args: {
+ ...defaultComponentArgs,
+ deleteActionVisibleField: "available",
+ editActionVisibleField: "available",
+ },
+}
+
+export const GridWithMockData = {
+ render: Template,
+ argTypes: defaultArgTypes,
+ args: {
+ ...defaultComponentArgs,
+ layout: "grid"
+ },
+}
+
+export const GridWithNoData = {
+ render: Template,
+ argTypes: defaultArgTypes,
+ args: {
+ ...defaultComponentArgs,
+ data: [],
+ layout: "grid"
+ }
+}
+
+export const GridWithConditionallyDisabledActionButtons = {
+ argTypes: defaultArgTypes,
+ render: Template,
+ args: {
+ ...defaultComponentArgs,
+ deleteActionEnabledField: "available",
+ editActionEnabledField: "available",
+ layout: "grid"
+ },
+}
+
+export const GridWithConditionallyHiddenActionButtons = {
+ argTypes: defaultArgTypes,
+ render: Template,
+ args: {
+ ...defaultComponentArgs,
+ deleteActionVisibleField: "available",
+ editActionVisibleField: "available",
+ layout: "grid"
+ },
+}
+
+export default DataListGridComponentSBConfig;
\ No newline at end of file
diff --git a/libs/portal-integration-angular/src/lib/core/components/data-list-grid/data-list-grid.component.ts b/libs/portal-integration-angular/src/lib/core/components/data-list-grid/data-list-grid.component.ts
index c2c162b7..ade8a260 100644
--- a/libs/portal-integration-angular/src/lib/core/components/data-list-grid/data-list-grid.component.ts
+++ b/libs/portal-integration-angular/src/lib/core/components/data-list-grid/data-list-grid.component.ts
@@ -56,6 +56,12 @@ export class DataListGridComponent extends DataSortBase implements OnInit, DoChe
@Input() viewPermission: string | undefined
@Input() editPermission: string | undefined
@Input() deletePermission: string | undefined
+ @Input() deleteActionVisibleField: string | undefined
+ @Input() deleteActionEnabledField: string | undefined
+ @Input() viewActionVisibleField: string | undefined
+ @Input() viewActionEnabledField: string | undefined
+ @Input() editActionVisibleField: string | undefined
+ @Input() editActionEnabledField: string | undefined
@Input() viewMenuItemKey: string | undefined
@Input() editMenuItemKey: string | undefined
@Input() deleteMenuItemKey: string | undefined
@@ -246,7 +252,25 @@ export class DataListGridComponent extends DataSortBase implements OnInit, DoChe
: `./onecx-portal-lib/assets/images/${this.fallbackImage}`
}
- updateGridMenuItems(): void {
+ updateGridMenuItems(useSelectedItem = false): void {
+ let deleteDisabled = false;
+ let editDisabled = false;
+ let viewDisabled = false;
+
+ let deleteVisible = true;
+ let editVisible = true;
+ let viewVisible = true;
+
+ if(useSelectedItem && this.selectedItem) {
+ viewDisabled = !!this.viewActionEnabledField && !this.fieldIsTruthy(this.selectedItem, this.viewActionEnabledField);
+ editDisabled = !!this.editActionEnabledField && !this.fieldIsTruthy(this.selectedItem, this.editActionEnabledField);
+ deleteDisabled = !!this.deleteActionEnabledField && !this.fieldIsTruthy(this.selectedItem, this.deleteActionEnabledField);
+
+ viewVisible = (!this.viewActionVisibleField || this.fieldIsTruthy(this.selectedItem, this.viewActionVisibleField))
+ editVisible = (!this.editActionVisibleField || this.fieldIsTruthy(this.selectedItem, this.editActionVisibleField))
+ deleteVisible = (!this.deleteActionVisibleField || this.fieldIsTruthy(this.selectedItem, this.deleteActionVisibleField))
+ }
+
this.translateService
.get([
this.viewMenuItemKey || 'OCX_DATA_LIST_GRID.MENU.VIEW',
@@ -256,11 +280,16 @@ export class DataListGridComponent extends DataSortBase implements OnInit, DoChe
])
.subscribe((translations) => {
let menuItems: MenuItem[] = []
+ const automationId = 'data-grid-action-button'
+ const automationIdHidden = 'data-grid-action-button-hidden'
if (this.viewItem.observed && this.userService.hasPermission(this.viewPermission || '')) {
menuItems.push({
label: translations[this.viewMenuItemKey || 'OCX_DATA_LIST_GRID.MENU.VIEW'],
icon: PrimeIcons.EYE,
command: () => this.viewItem.emit(this.selectedItem),
+ disabled: viewDisabled,
+ visible: viewVisible,
+ automationId: viewVisible ? automationId : automationIdHidden
})
}
if (this.editItem.observed && this.userService.hasPermission(this.editPermission || '')) {
@@ -268,6 +297,9 @@ export class DataListGridComponent extends DataSortBase implements OnInit, DoChe
label: translations[this.editMenuItemKey || 'OCX_DATA_LIST_GRID.MENU.EDIT'],
icon: PrimeIcons.PENCIL,
command: () => this.editItem.emit(this.selectedItem),
+ disabled: editDisabled,
+ visible: editVisible,
+ automationId: editVisible ? automationId : automationIdHidden
})
}
if (this.deleteItem.observed && this.userService.hasPermission(this.deletePermission || '')) {
@@ -275,6 +307,9 @@ export class DataListGridComponent extends DataSortBase implements OnInit, DoChe
label: translations[this.deleteMenuItemKey || 'OCX_DATA_LIST_GRID.MENU.DELETE'],
icon: PrimeIcons.TRASH,
command: () => this.deleteItem.emit(this.selectedItem),
+ disabled: deleteDisabled,
+ visible: deleteVisible,
+ automationId: deleteVisible ? automationId : automationIdHidden
})
}
menuItems = menuItems.concat(
@@ -305,4 +340,8 @@ export class DataListGridComponent extends DataSortBase implements OnInit, DoChe
this.page = page
this.pageChanged.emit(page)
}
+
+ fieldIsTruthy(object: any, key: any) {
+ return !!this.resolveFieldData(object, key)
+ }
}
diff --git a/libs/portal-integration-angular/src/lib/core/components/data-table/data-table.component.html b/libs/portal-integration-angular/src/lib/core/components/data-table/data-table.component.html
index 1f47d1dd..2cb95ca3 100644
--- a/libs/portal-integration-angular/src/lib/core/components/data-table/data-table.component.html
+++ b/libs/portal-integration-angular/src/lib/core/components/data-table/data-table.component.html
@@ -9,37 +9,43 @@
[ngClass]="(frozenActionColumn && actionColumnPosition === 'left') ? 'border-right-1' : (frozenActionColumn && actionColumnPosition === 'right') ? 'border-left-1' : ''"
>