Skip to content

Commit

Permalink
feat: allow hiding of selectAll checkbox & fix initial selection of d…
Browse files Browse the repository at this point in the history
…isabled table rows (#479)

* refactor: dynamically hide selectAll & add selectionEnabledField input to parents of ocx-data-table

* feat: allow initial specification of selected + disabled row

* fix: fix linter errors

* refactor: split selection change handler into multiple methods

---------

Co-authored-by: SchettlerKoehler <[email protected]>
  • Loading branch information
bastianjakobi and SchettlerKoehler authored Sep 20, 2024
1 parent 236e2c4 commit 9097001
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import { BreadcrumbModule } from 'primeng/breadcrumb'
import { SkeletonModule } from 'primeng/skeleton'
import { MessageModule } from 'primeng/message'
import { SharedModule } from 'primeng/api'
import { CheckboxModule } from 'primeng/checkbox'

@NgModule({
imports: [
BreadcrumbModule,
CheckboxModule,
DropdownModule,
ButtonModule,
DialogModule,
Expand All @@ -33,6 +35,7 @@ import { SharedModule } from 'primeng/api'
],
exports: [
BreadcrumbModule,
CheckboxModule,
DropdownModule,
ButtonModule,
DialogModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
<ng-template pTemplate="header">
<tr>
<th style="width: 4rem" scope="col" *ngIf="selectionChangedObserved">
<p-tableHeaderCheckbox></p-tableHeaderCheckbox>
<p-tableHeaderCheckbox *ngIf="allowSelectAll"></p-tableHeaderCheckbox>
</th>
<ng-container *ngIf="actionColumnPosition === 'left';">
<ng-container *ngTemplateOutlet="actionColumnHeader"></ng-container>
Expand Down Expand Up @@ -194,10 +194,18 @@
<ng-template pTemplate="body" let-rowObject>
<tr>
<td *ngIf="selectionChangedObserved">
<p-tableCheckbox
[value]="rowObject"
[disabled]="!!selectionEnabledField && !fieldIsTruthy(rowObject, selectionEnabledField)"
></p-tableCheckbox>
<p-checkbox
*ngIf="(!!selectionEnabledField && !fieldIsTruthy(rowObject, selectionEnabledField)) && isSelected(rowObject); else defaultCheckbox"
[(ngModel)]="checked"
[binary]="true"
[disabled]="true"
></p-checkbox>
<ng-template #defaultCheckbox>
<p-tableCheckbox
[value]="rowObject"
[disabled]="!!selectionEnabledField && !fieldIsTruthy(rowObject, selectionEnabledField)"
></p-tableCheckbox>
</ng-template>
</td>
<ng-container *ngIf="actionColumnPosition === 'left';">
<ng-container *ngTemplateOutlet="actionColumn; context: {localRowObject: rowObject}"></ng-container>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { HAS_PERMISSION_CHECKER, IfPermissionDirective } from '../../directives/
import { ColumnType } from '../../model/column-type.model'
import { MenuModule } from 'primeng/menu'
import { DynamicLocaleId } from '../../utils/dynamic-locale-id'
import { CheckboxModule } from 'primeng/checkbox'
import { FormsModule } from '@angular/forms'

type DataTableInputTypes = Pick<DataTableComponent, 'rows' | 'columns' | 'emptyResultsMessage' | 'selectedRows'>

Expand All @@ -36,7 +38,7 @@ const DataTableComponentSBConfig: Meta<DataTableComponent> = {
}),
moduleMetadata({
declarations: [DataTableComponent, IfPermissionDirective],
imports: [TableModule, ButtonModule, MultiSelectModule, StorybookTranslateModule, MockAuthModule, MenuModule],
imports: [TableModule, ButtonModule, MultiSelectModule, StorybookTranslateModule, MockAuthModule, MenuModule, CheckboxModule, FormsModule],
}),
],
}
Expand All @@ -52,6 +54,7 @@ const dataTableActionsArgTypes = {

const dataTableSelectionArgTypes = {
selectionChanged: { action: 'selectionChanged' },
componentStateChanged: { action: 'componentStateChanged' },
}

const defaultComponentArgs: DataTableInputTypes = {
Expand Down Expand Up @@ -142,6 +145,16 @@ export const WithRowSelectionAndDefaultSelection = {
},
}

export const WithRowSelectionAndDisabledDefaultSelection = {
argTypes: dataTableSelectionArgTypes,
render: Template,
args: {
...defaultComponentArgs,
selectedRows: [1],
selectionEnabledField: 'available'
},
}

const extendedComponentArgs: DataTableInputTypes = {
columns: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,17 @@ import { isValidDate } from '@onecx/accelerator'
import { UserService } from '@onecx/angular-integration-interface'
import { MenuItem, PrimeTemplate, SelectItem } from 'primeng/api'
import { Menu } from 'primeng/menu'
import { BehaviorSubject, Observable, combineLatest, debounceTime, first, map, mergeMap, of } from 'rxjs'
import {
BehaviorSubject,
Observable,
combineLatest,
debounceTime,
first,
map,
mergeMap,
of,
withLatestFrom,
} from 'rxjs'
import { ColumnType } from '../../model/column-type.model'
import { DataAction } from '../../model/data-action'
import { DataSortDirection } from '../../model/data-sort-direction'
Expand Down Expand Up @@ -64,7 +74,7 @@ export interface DataTableComponentState {
})
export class DataTableComponent extends DataSortBase implements OnInit, AfterContentInit {
TemplateType = TemplateType

checked = true
_rows$ = new BehaviorSubject<Row[]>([])
@Input()
get rows(): Row[] {
Expand All @@ -74,14 +84,19 @@ export class DataTableComponent extends DataSortBase implements OnInit, AfterCon
!this._rows$.getValue().length ?? this.resetPage()
this._rows$.next(value)
}
_selection$ = new BehaviorSubject<Row[]>([])
_selectionIds$ = new BehaviorSubject<(string | number)[]>([])
@Input()
get selectedRows(): Row[] {
return this._selection$.getValue()
}
set selectedRows(value: Row[]) {
this._selection$.next(value)
set selectedRows(value: Row[] | string[] | number[]) {
this._selectionIds$.next(
value.map((row) => {
if (typeof row === 'object') {
return row.id
}
return row
})
)
}

_filters$ = new BehaviorSubject<Filter[]>([])
@Input()
get filters(): Filter[] {
Expand Down Expand Up @@ -109,15 +124,15 @@ export class DataTableComponent extends DataSortBase implements OnInit, AfterCon
}
columnTemplates$: Observable<Record<string, TemplateRef<any> | null>> | undefined
_columns$ = new BehaviorSubject<DataTableColumn[]>([])
@Input()
@Input()
get columns(): DataTableColumn[] {
return this._columns$.getValue()
}
set columns(value: DataTableColumn[]) {
this._columns$.next(value)
const obs = value.map((c) => this.getTemplate(c, TemplateType.CELL))
this.columnTemplates$ = combineLatest(obs).pipe(
map(values => Object.fromEntries(value.map((c, i) => [c.id, values[i]])))
map((values) => Object.fromEntries(value.map((c, i) => [c.id, values[i]])))
)
}
@Input() clientSideFiltering = true
Expand Down Expand Up @@ -155,6 +170,7 @@ export class DataTableComponent extends DataSortBase implements OnInit, AfterCon
@Input() editActionVisibleField: string | undefined
@Input() editActionEnabledField: string | undefined
@Input() selectionEnabledField: string | undefined
@Input() allowSelectAll = true
@Input() paginator = true
@Input() page = 0
@Input()
Expand Down Expand Up @@ -187,13 +203,13 @@ export class DataTableComponent extends DataSortBase implements OnInit, AfterCon
return this.numberCellTemplate || this.numberCellChildTemplate
}

/**
/**
* @deprecated Will be removed and instead to change the template of a specific column
* use the new approach instead by following the naming convention column id + IdCell
* e.g. for a column with the id 'status' use pTemplate="statusIdCell"
*/
@Input() customCellTemplate: TemplateRef<any> | undefined
/**
/**
* @deprecated Will be removed and instead to change the template of a specific column
* use the new approach instead by following the naming convention column id + IdCell
* e.g. for a column with the id 'status' use pTemplate="statusIdCell"
Expand Down Expand Up @@ -448,20 +464,21 @@ export class DataTableComponent extends DataSortBase implements OnInit, AfterCon
}

emitComponentStateChanged(state: DataTableComponentState = {}) {
combineLatest([this.displayedPageSize$, this._selection$]).pipe(first()).subscribe(([pageSize, selectedRows]) => {
this.componentStateChanged.emit({
filters: this.filters,
sorting: {
sortColumn: this.sortColumn,
sortDirection: this.sortDirection
},
pageSize,
activePage: this.page,
selectedRows,
...state
})
})

this.displayedPageSize$
.pipe(withLatestFrom(this._selectionIds$, this._rows$), first())
.subscribe(([pageSize, selectedIds, rows]) => {
this.componentStateChanged.emit({
filters: this.filters,
sorting: {
sortColumn: this.sortColumn,
sortDirection: this.sortDirection,
},
pageSize,
activePage: this.page,
selectedRows: rows.filter((row) => selectedIds.includes(row.id)),
...state,
})
})
}

ngAfterContentInit() {
Expand Down Expand Up @@ -523,8 +540,8 @@ export class DataTableComponent extends DataSortBase implements OnInit, AfterCon
this.emitComponentStateChanged({
sorting: {
sortColumn: sortColumn,
sortDirection: newSortDirection
}
sortDirection: newSortDirection,
},
})
}

Expand Down Expand Up @@ -564,7 +581,7 @@ export class DataTableComponent extends DataSortBase implements OnInit, AfterCon
}
this.filtered.emit(filters)
this.emitComponentStateChanged({
filters
filters,
})
this.resetPage()
}
Expand Down Expand Up @@ -593,21 +610,55 @@ export class DataTableComponent extends DataSortBase implements OnInit, AfterCon
}

mapSelectionToRows() {
this.selectedRows$ = combineLatest([this._selection$, this._rows$]).pipe(
map(([selectedRows, rows]) => {
return selectedRows.map((row) => {
return rows.find((r) => r.id === row.id)
this.selectedRows$ = combineLatest([this._selectionIds$, this._rows$]).pipe(
map(([selectedRowIds, rows]) => {
return selectedRowIds.map((rowId) => {
return rows.find((r) => r.id === rowId)
})
})
)
}

onSelectionChange(event: Row[]) {
this.selectedRows = event;
this.selectionChanged.emit(event)
this.emitComponentStateChanged({
selectedRows: event
onSelectionChange(selection: Row[]) {
let newSelectionIds = selection.map((row) => row.id)
const rows = this._rows$.getValue()

if (this.selectionEnabledField) {
const disabledRowIds = rows.filter((r) => !this.fieldIsTruthy(r, this.selectionEnabledField)).map((row) => row.id)
if (disabledRowIds.length > 0) {
newSelectionIds = this.mergeWithDisabledKeys(newSelectionIds, disabledRowIds)
}
}

this._selectionIds$.next(newSelectionIds)
this.selectionChanged.emit(this._rows$.getValue().filter((row) => newSelectionIds.includes(row.id)))
this.emitComponentStateChanged()
}

mergeWithDisabledKeys(newSelectionIds: (string | number)[], disabledRowIds: (string | number)[]) {
const previousSelectionIds = this._selectionIds$.getValue()
const previouslySelectedAndDisabled = previousSelectionIds.filter((id) => disabledRowIds.includes(id))
const disabledAndPreviouslyDeselected = disabledRowIds.filter((id) => !previousSelectionIds.includes(id))
const updatedSelection = [...newSelectionIds]

previouslySelectedAndDisabled.forEach((id) => {
if (!updatedSelection.includes(id)) {
updatedSelection.push(id)
}
})

disabledAndPreviouslyDeselected.forEach((id) => {
const index = updatedSelection.indexOf(id)
if (index > -1) {
updatedSelection.splice(index, 1)
}
})

return updatedSelection
}

isSelected(row: Row) {
return this._selectionIds$.getValue().includes(row.id)
}

onPageChange(event: any) {
Expand All @@ -618,7 +669,7 @@ export class DataTableComponent extends DataSortBase implements OnInit, AfterCon
this.pageSizeChanged.emit(event.rows)
this.emitComponentStateChanged({
activePage: page,
pageSize: event.rows
pageSize: event.rows,
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@
[currentPageShowingKey]="currentPageShowingKey"
[currentPageShowingWithTotalOnServerKey]="currentPageShowingWithTotalOnServerKey"
[parentTemplates]="templatesForChildren$ | async"
[allowSelectAll]="tableAllowSelectAll"
[selectionEnabledField]="tableSelectionEnabledField"
>
</ocx-data-table>
<ng-template #stringCell let-rowObject="rowObject" let-column="column">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ export class DataViewComponent implements DoCheck, OnInit, AfterContentInit {
@Input() viewActionEnabledField: string | undefined
@Input() editActionVisibleField: string | undefined
@Input() editActionEnabledField: string | undefined
@Input() tableSelectionEnabledField: string | undefined
@Input() tableAllowSelectAll = true
@Input() data: RowListGridData[] = []
@Input() name = 'Data table'
@Input() titleLineId: string | undefined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@
[currentPageShowingWithTotalOnServerKey]="currentPageShowingWithTotalOnServerKey"
(componentStateChanged)="dataViewComponentState$.next($event)"
[parentTemplates]="templates$ | async"
[tableAllowSelectAll]="tableAllowSelectAll"
[tableSelectionEnabledField]="tableSelectionEnabledField"
>
</ocx-data-view>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ export class InteractiveDataViewComponent implements OnInit, AfterContentInit {
@Input() viewActionEnabledField: string | undefined
@Input() editActionVisibleField: string | undefined
@Input() editActionEnabledField: string | undefined
@Input() tableSelectionEnabledField: string | undefined
@Input() tableAllowSelectAll = true
@Input() name = 'Data'
@Input() titleLineId: string | undefined
@Input() subtitleLineIds: string[] = []
Expand Down

0 comments on commit 9097001

Please sign in to comment.