From 912a0a40aabd2b2c1d1b7aaec2ed171e9f6223a6 Mon Sep 17 00:00:00 2001 From: Bastian Jakobi <55296998+bastianjakobi@users.noreply.github.com> Date: Tue, 16 Jul 2024 12:51:12 +0200 Subject: [PATCH] feat: fix minor bugs + implement pageSizes for data components (#309) * fix: remove empty div if !objectDetails * fix: display action column if only overflowActions have been specified * feat: improve data table paginator * feat: implement pageSizes input for pagination * fix: fix broken tests * feat: add initial storybook for interactivedataview * fix: fix pageSize input in parent components * fix: improve if chec for objectDetails * fix: implement css only fix * fix: implement css only fix * refactor: revert formatting changes * refactor: revert formatting changes --- .../data-list-grid.component.html | 12 +- .../data-list-grid.component.spec.ts | 10 +- .../data-list-grid.component.stories.ts | 133 ++++++++++++++++++ .../data-list-grid.component.ts | 29 +++- .../data-table/data-table.component.html | 15 +- .../data-table/data-table.component.spec.ts | 6 + .../data-table.component.stories.ts | 40 ++++++ .../data-table/data-table.component.ts | 28 +++- .../data-view/data-view.component.ts | 2 +- ...interactive-data-view.component.stories.ts | 123 ++++++++++++++++ .../interactive-data-view.component.ts | 2 +- .../page-header/page-header.component.scss | 2 +- 12 files changed, 379 insertions(+), 23 deletions(-) create mode 100644 libs/angular-accelerator/src/lib/components/interactive-data-view/interactive-data-view.component.stories.ts diff --git a/libs/angular-accelerator/src/lib/components/data-list-grid/data-list-grid.component.html b/libs/angular-accelerator/src/lib/components/data-list-grid/data-list-grid.component.html index 7cec25f3..088c690d 100644 --- a/libs/angular-accelerator/src/lib/components/data-list-grid/data-list-grid.component.html +++ b/libs/angular-accelerator/src/lib/components/data-list-grid/data-list-grid.component.html @@ -1,19 +1,17 @@ - - {{ item.value === data?.length ? ("OCX_DATA_TABLE.ALL" | translate) : item.value }} - { it('de', async () => { window.HTMLElement.prototype.scrollIntoView = jest.fn() translateService.use('de') + fixture = TestBed.createComponent(DataListGridComponent) + component = fixture.componentInstance + component.data = mockData + component.columns = mockColumns + component.paginator = true + fixture.detectChanges() const dataListGrid = await TestbedHarnessEnvironment.harnessForFixture(fixture, DataTableHarness) const paginator = await dataListGrid.getPaginator() const rowsPerPageOptions = await paginator.getRowsPerPageOptions() - const rowsPerPageOptionsText = await rowsPerPageOptions.selectedDropdownItemText(3) + const rowsPerPageOptionsText = await rowsPerPageOptions.selectedDropdownItemText(0) expect(rowsPerPageOptionsText).toEqual('Alle') }) @@ -299,7 +305,7 @@ describe('DataListGridComponent', () => { const dataListGrid = await TestbedHarnessEnvironment.harnessForFixture(fixture, DataTableHarness) const paginator = await dataListGrid.getPaginator() const rowsPerPageOptions = await paginator.getRowsPerPageOptions() - const rowsPerPageOptionsText = await rowsPerPageOptions.selectedDropdownItemText(3) + const rowsPerPageOptionsText = await rowsPerPageOptions.selectedDropdownItemText(0) expect(rowsPerPageOptionsText).toEqual('All') }) }) diff --git a/libs/angular-accelerator/src/lib/components/data-list-grid/data-list-grid.component.stories.ts b/libs/angular-accelerator/src/lib/components/data-list-grid/data-list-grid.component.stories.ts index 1be68664..66e8c7d8 100644 --- a/libs/angular-accelerator/src/lib/components/data-list-grid/data-list-grid.component.stories.ts +++ b/libs/angular-accelerator/src/lib/components/data-list-grid/data-list-grid.component.stories.ts @@ -68,6 +68,78 @@ const defaultArgTypes = { editItem: { action: 'deleteItem' }, viewItem: { action: 'deleteItem' }, } +const extendedMockData = [ + { + 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, + }, + { + 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, + }, + { + 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, + }, + { + 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, + }, + { + 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, + }, +] export const ListWithMockData = { render: Template, @@ -187,6 +259,55 @@ export const ListWithAdditionalOverflowActions = { }, } +export const ListWithOnlyAdditionalOverflowActions = { + argTypes: defaultArgTypes, + render: Template, + args: { + ...defaultComponentArgs, + deleteItem: null, + editItem: null, + viewItem: null, + deletePermission: null, + editPermission: null, + viewPermission: null, + additionalActions: [ + { + id: '1', + labelKey: 'Additional Action', + icon: 'pi pi-plus', + permission: 'TEST_MGMT#TEST_VIEW', + showAsOverflow: true, + }, + { + id: '2', + labelKey: 'Conditionally Hidden', + icon: 'pi pi-plus', + permission: 'TEST_MGMT#TEST_VIEW', + showAsOverflow: true, + actionVisibleField: 'available', + }, + { + id: '3', + labelKey: 'Conditionally Enabled', + icon: 'pi pi-plus', + permission: 'TEST_MGMT#TEST_VIEW', + showAsOverflow: true, + actionEnabledField: 'available', + }, + ] + }, +} + +export const ListWithPageSizes = { + argTypes: defaultArgTypes, + render: Template, + args: { + ...defaultComponentArgs, + pageSizes: [2, 15, 25], + data: extendedMockData + }, +} + export const GridWithMockData = { render: Template, argTypes: defaultArgTypes, @@ -280,4 +401,16 @@ export const GridWithConditionallyVisibleAdditionalActions = { ] }, } + +export const GridWithPageSizes = { + argTypes: defaultArgTypes, + render: Template, + args: { + ...defaultComponentArgs, + layout: 'grid', + pageSizes: [2, 15, 25], + data: extendedMockData, + }, +} + export default DataListGridComponentSBConfig diff --git a/libs/angular-accelerator/src/lib/components/data-list-grid/data-list-grid.component.ts b/libs/angular-accelerator/src/lib/components/data-list-grid/data-list-grid.component.ts index 7291ba2b..eb4d3a33 100644 --- a/libs/angular-accelerator/src/lib/components/data-list-grid/data-list-grid.component.ts +++ b/libs/angular-accelerator/src/lib/components/data-list-grid/data-list-grid.component.ts @@ -48,8 +48,27 @@ export class DataListGridComponent extends DataSortBase implements OnInit, DoChe @Input() clientSideSorting = true @Input() clientSideFiltering = true @Input() sortStates: DataSortDirection[] = [] - @Input() pageSizes: number[] = [10, 25, 50] - @Input() pageSize: number = this.pageSizes[0] || 50 + + displayedPageSizes$: Observable<(number | { showAll: string })[]> + _pageSizes$ = new BehaviorSubject<(number | { showAll: string })[]>([10, 25, 50]) + @Input() + get pageSizes(): (number | { showAll: string })[] { + return this._pageSizes$.getValue() + } + set pageSizes(value: (number | { showAll: string })[]) { + this._pageSizes$.next(value) + } + + displayedPageSize$: Observable + _pageSize$ = new BehaviorSubject(undefined) + @Input() + get pageSize(): number | undefined { + return this._pageSize$.getValue() + } + set pageSize(value: number | undefined) { + this._pageSize$.next(value) + } + @Input() emptyResultsMessage: string | undefined @Input() fallbackImage = 'placeholder.png' @Input() layout: 'grid' | 'list' = 'grid' @@ -208,6 +227,12 @@ export class DataListGridComponent extends DataSortBase implements OnInit, DoChe this.fallbackImagePath$ = this.appStateService.currentMfe$.pipe( map((currentMfe) => this.getFallbackImagePath(currentMfe)) ) + this.displayedPageSizes$ = combineLatest([this._pageSizes$, this.translateService.get('OCX_DATA_TABLE.ALL')]).pipe( + map(([pageSizes, translation]) => pageSizes.concat({ showAll: translation })) + ) + this.displayedPageSize$ = combineLatest([this._pageSize$, this._pageSizes$]).pipe( + map(([pageSize, pageSizes]) => pageSize ?? pageSizes.find((val): val is number => typeof val === 'number') ?? 50) + ) } ngDoCheck(): void { diff --git a/libs/angular-accelerator/src/lib/components/data-table/data-table.component.html b/libs/angular-accelerator/src/lib/components/data-table/data-table.component.html index 2ef352f3..9ea0f4ed 100644 --- a/libs/angular-accelerator/src/lib/components/data-table/data-table.component.html +++ b/libs/angular-accelerator/src/lib/components/data-table/data-table.component.html @@ -1,5 +1,5 @@ - + - + diff --git a/libs/angular-accelerator/src/lib/components/data-table/data-table.component.spec.ts b/libs/angular-accelerator/src/lib/components/data-table/data-table.component.spec.ts index 4be80c68..88fc8b7d 100644 --- a/libs/angular-accelerator/src/lib/components/data-table/data-table.component.spec.ts +++ b/libs/angular-accelerator/src/lib/components/data-table/data-table.component.spec.ts @@ -276,6 +276,12 @@ describe('DataTableComponent', () => { it('de', async () => { window.HTMLElement.prototype.scrollIntoView = jest.fn() translateService.use('de') + fixture = TestBed.createComponent(DataTableComponent) + component = fixture.componentInstance + component.rows = mockData + component.columns = mockColumns + component.paginator = true + fixture.detectChanges() const dataTable = await TestbedHarnessEnvironment.harnessForFixture(fixture, DataTableHarness) const paginator = await dataTable.getPaginator() const rowsPerPageOptions = await paginator.getRowsPerPageOptions() diff --git a/libs/angular-accelerator/src/lib/components/data-table/data-table.component.stories.ts b/libs/angular-accelerator/src/lib/components/data-table/data-table.component.stories.ts index 5a5d53e6..5c237d2f 100644 --- a/libs/angular-accelerator/src/lib/components/data-table/data-table.component.stories.ts +++ b/libs/angular-accelerator/src/lib/components/data-table/data-table.component.stories.ts @@ -399,4 +399,44 @@ export const WithAdditionalOverflowActions = { }, } +export const WithOnlyOverflowActions = { + render: Template, + args: { + ...defaultComponentArgs, + additionalActions: [ + { + id: '1', + labelKey: 'Additional Action', + icon: 'pi pi-plus', + permission: 'TEST_MGMT#TEST_VIEW', + showAsOverflow: true, + }, + { + id: '2', + labelKey: 'Conditionally Hidden', + icon: 'pi pi-plus', + permission: 'TEST_MGMT#TEST_VIEW', + showAsOverflow: true, + actionVisibleField: 'available', + }, + { + id: '3', + labelKey: 'Conditionally Enabled', + icon: 'pi pi-plus', + permission: 'TEST_MGMT#TEST_VIEW', + showAsOverflow: true, + actionEnabledField: 'available', + }, + ] + }, +} + +export const WithPageSizes = { + render: Template, + args: { + ...defaultComponentArgs, + pageSizes: [2, 15, 25] + }, +} + export default DataTableComponentSBConfig diff --git a/libs/angular-accelerator/src/lib/components/data-table/data-table.component.ts b/libs/angular-accelerator/src/lib/components/data-table/data-table.component.ts index 8c3349c4..e1a7a479 100644 --- a/libs/angular-accelerator/src/lib/components/data-table/data-table.component.ts +++ b/libs/angular-accelerator/src/lib/components/data-table/data-table.component.ts @@ -81,8 +81,26 @@ export class DataTableComponent extends DataSortBase implements OnInit { @Input() clientSideFiltering = true @Input() clientSideSorting = true @Input() sortStates: DataSortDirection[] = [DataSortDirection.ASCENDING, DataSortDirection.DESCENDING] - @Input() pageSizes: number[] = [10, 25, 50] - @Input() pageSize: number = this.pageSizes[0] || 50 + + displayedPageSizes$: Observable<(number | { showAll: string })[]> + _pageSizes$ = new BehaviorSubject<(number | { showAll: string })[]>([10, 25, 50]) + @Input() + get pageSizes(): (number | { showAll: string })[] { + return this._pageSizes$.getValue() + } + set pageSizes(value: (number | { showAll: string })[]) { + this._pageSizes$.next(value) + } + displayedPageSize$: Observable + _pageSize$ = new BehaviorSubject(undefined) + @Input() + get pageSize(): number | undefined { + return this._pageSize$.getValue() + } + set pageSize(value: number | undefined) { + this._pageSize$.next(value) + } + @Input() emptyResultsMessage: string | undefined @Input() name = '' @Input() deletePermission: string | undefined @@ -215,6 +233,12 @@ export class DataTableComponent extends DataSortBase implements OnInit { ) { super(locale, translateService) this.name = this.name || this.router.url.replace(/[^A-Za-z0-9]/, '_') + this.displayedPageSizes$ = combineLatest([this._pageSizes$, this.translateService.get('OCX_DATA_TABLE.ALL')]).pipe( + map(([pageSizes, translation]) => pageSizes.concat({ showAll: translation })) + ) + this.displayedPageSize$ = combineLatest([this._pageSize$, this._pageSizes$]).pipe( + map(([pageSize, pageSizes]) => pageSize ?? pageSizes.find((val): val is number => typeof val === 'number') ?? 50) + ) } ngOnInit(): void { diff --git a/libs/angular-accelerator/src/lib/components/data-view/data-view.component.ts b/libs/angular-accelerator/src/lib/components/data-view/data-view.component.ts index 9290c014..2c6e8524 100644 --- a/libs/angular-accelerator/src/lib/components/data-view/data-view.component.ts +++ b/libs/angular-accelerator/src/lib/components/data-view/data-view.component.ts @@ -84,7 +84,7 @@ export class DataViewComponent implements DoCheck, OnInit { @Input() sortStates: DataSortDirection[] = [DataSortDirection.ASCENDING, DataSortDirection.DESCENDING] @Input() pageSizes: number[] = [10, 25, 50] - @Input() pageSize: number = this.pageSizes?.[0] || 50 + @Input() pageSize: number | undefined @Input() stringTableCellTemplate: TemplateRef | undefined @ContentChild('stringTableCell') stringTableCellChildTemplate: TemplateRef | undefined diff --git a/libs/angular-accelerator/src/lib/components/interactive-data-view/interactive-data-view.component.stories.ts b/libs/angular-accelerator/src/lib/components/interactive-data-view/interactive-data-view.component.stories.ts new file mode 100644 index 00000000..936df6b1 --- /dev/null +++ b/libs/angular-accelerator/src/lib/components/interactive-data-view/interactive-data-view.component.stories.ts @@ -0,0 +1,123 @@ +import { importProvidersFrom } from '@angular/core' +import { BrowserModule } from '@angular/platform-browser' +import { BrowserAnimationsModule } from '@angular/platform-browser/animations' +import { ActivatedRoute } from '@angular/router' +import { UserService } from '@onecx/angular-integration-interface' +import { MockUserService } from '@onecx/angular-integration-interface/mocks' +import { Meta, StoryFn, applicationConfig, moduleMetadata } from '@storybook/angular' +import { ButtonModule } from 'primeng/button' +import { DataViewModule } from 'primeng/dataview' +import { DialogModule } from 'primeng/dialog' +import { DropdownModule } from 'primeng/dropdown' +import { MenuModule } from 'primeng/menu' +import { MultiSelectModule } from 'primeng/multiselect' +import { PickListModule } from 'primeng/picklist' +import { SelectButtonModule } from 'primeng/selectbutton' +import { TableModule } from 'primeng/table' +import { IfPermissionDirective } from '../../directives/if-permission.directive' +import { MockAuthModule } from '../../mock-auth/mock-auth.module' +import { ColumnType } from '../../model/column-type.model' +import { StorybookTranslateModule } from '../../storybook-translate.module' +import { ColumnGroupSelectionComponent } from '../column-group-selection/column-group-selection.component' +import { CustomGroupColumnSelectorComponent } from '../custom-group-column-selector/custom-group-column-selector.component' +import { DataLayoutSelectionComponent } from '../data-layout-selection/data-layout-selection.component' +import { DataListGridSortingComponent } from '../data-list-grid-sorting/data-list-grid-sorting.component' +import { DataListGridComponent } from '../data-list-grid/data-list-grid.component' +import { DataTableComponent } from '../data-table/data-table.component' +import { DataViewComponent } from '../data-view/data-view.component' +import { InteractiveDataViewComponent } from './interactive-data-view.component' + + +type InteractiveDataViewInputTypes = Pick +const InteractiveDataViewComponentSBConfig: Meta = { + title: 'InteractiveDataViewComponent', + component: InteractiveDataViewComponent, + decorators: [ + applicationConfig({ + providers: [ + importProvidersFrom(BrowserModule), + importProvidersFrom(BrowserAnimationsModule), + { provide: UserService, useClass: MockUserService }, + { + provide: ActivatedRoute, + useValue: { + snapshot: { + paramMap: { + get: () => '1', + }, + }, + }, + }, + ], + }), + moduleMetadata({ + declarations: [InteractiveDataViewComponent, IfPermissionDirective, CustomGroupColumnSelectorComponent, ColumnGroupSelectionComponent, DataViewComponent, DataTableComponent, DataLayoutSelectionComponent, DataListGridComponent, DataListGridSortingComponent], + imports: [TableModule, ButtonModule, MultiSelectModule, StorybookTranslateModule, MockAuthModule, MenuModule, PickListModule, SelectButtonModule, DialogModule, DataViewModule, DropdownModule], + }), + ], + } + const Template: StoryFn = (args) => ({ + props: args, + }) + + const defaultComponentArgs: InteractiveDataViewInputTypes = { + columns: [ + { + id: 'product', + columnType: ColumnType.STRING, + nameKey: 'Product', + sortable: false, + }, + { + id: 'amount', + columnType: ColumnType.NUMBER, + nameKey: 'Amount', + sortable: true, + }, + { + id: 'available', + columnType: ColumnType.STRING, + nameKey: 'Available', + sortable: false, + }, + ], + data: [ + { + id: 1, + product: 'Apples', + amount: 2, + available: false, + imagePath: '' + }, + { + id: 2, + product: 'Bananas', + amount: 10, + available: true, + imagePath: '' + }, + { + id: 3, + product: 'Strawberries', + amount: 5, + available: false, + imagePath: '' + }, + ], + emptyResultsMessage: 'No results', + } + + export const WithMockData = { + render: Template, + args: defaultComponentArgs, + } + + export const WithPageSizes = { + render: Template, + args: { + ...defaultComponentArgs, + pageSizes: [2, 15, 25] + }, + } + +export default InteractiveDataViewComponentSBConfig \ No newline at end of file diff --git a/libs/angular-accelerator/src/lib/components/interactive-data-view/interactive-data-view.component.ts b/libs/angular-accelerator/src/lib/components/interactive-data-view/interactive-data-view.component.ts index c87b6787..fc38ce51 100644 --- a/libs/angular-accelerator/src/lib/components/interactive-data-view/interactive-data-view.component.ts +++ b/libs/angular-accelerator/src/lib/components/interactive-data-view/interactive-data-view.component.ts @@ -53,7 +53,7 @@ export class InteractiveDataViewComponent implements OnInit { DataSortDirection.NONE, ] @Input() pageSizes: number[] = [10, 25, 50] - @Input() pageSize: number = this.pageSizes[0] || 50 + @Input() pageSize: number | undefined; @Input() totalRecordsOnServer: number | undefined @Input() layout: 'grid' | 'list' | 'table' = 'table' @Input() defaultGroupKey = '' diff --git a/libs/angular-accelerator/src/lib/components/page-header/page-header.component.scss b/libs/angular-accelerator/src/lib/components/page-header/page-header.component.scss index 18795602..8d849ef2 100644 --- a/libs/angular-accelerator/src/lib/components/page-header/page-header.component.scss +++ b/libs/angular-accelerator/src/lib/components/page-header/page-header.component.scss @@ -86,7 +86,7 @@ padding: 1rem; &:empty { - display: none; + display: none !important; } } }