Skip to content

Commit

Permalink
Added multi select combobox and checkbox support (#167)
Browse files Browse the repository at this point in the history
Co-authored-by: mohas22 <[email protected]>
  • Loading branch information
samhere06 and mohas22 authored Jun 20, 2024
1 parent d49bf07 commit 692356c
Show file tree
Hide file tree
Showing 11 changed files with 892 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import { InlineDashboardPageComponent } from '../../_components/template/inline-
import { ListPageComponent } from '../../_components/template/list-page/list-page.component';
import { ListViewComponent } from '../../_components/template/list-view/list-view.component';
import { MultiReferenceReadonlyComponent } from '../../_components/template/multi-reference-readonly/multi-reference-readonly.component';
import { MultiselectComponent } from '../../_components/field/multiselect/multiselect.component';
import { NarrowWideFormComponent } from '../../_components/template/narrow-wide-form/narrow-wide-form.component';
import { OneColumnComponent } from '../../_components/template/one-column/one-column.component';
import { OneColumnPageComponent } from '../../_components/template/one-column-page/one-column-page.component';
Expand Down Expand Up @@ -185,6 +186,7 @@ const pegaSdkComponentMap = {
MaterialUtility: MaterialUtilityComponent,
ModalViewContainer: ModalViewContainerComponent,
MultiReferenceReadOnly: MultiReferenceReadonlyComponent,
Multiselect: MultiselectComponent,
MultiStep: MultiStepComponent,
// 'NarrowWide': NarrowWideFormComponent,
NarrowWideDetails: DetailsNarrowWideComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,37 @@
></component-mapper>
</div>
<ng-template #noDisplayMode>
<div *ngIf="!bReadonly$ && bHasForm$; else noEdit">
<div *ngIf="bHasForm$; else noEdit">
<div [formGroup]="formGroup$" *ngIf="bVisible$">
<div class="mat-form-field-infix" *ngIf="showLabel$">
<span>
<label class="mat-form-field-label psdk-label-readonly">{{ label$ }}</label>
</span>
<mat-checkbox
[labelPosition]="'after'"
[checked]="isChecked$"
[disabled]="bDisabled$"
[attr.data-test-id]="testId"
[formControl]="fieldControl"
(change)="fieldOnChange($event)"
(blur)="fieldOnBlur($event)"
>{{ caption$ }}</mat-checkbox
>
</div>
<div class="mat-form-field-infix" *ngIf="!bReadonly$ && !showLabel$">
<div *ngIf="selectionMode === 'multi'; else single">
<mat-option *ngFor="let item of listOfCheckboxes" (click)="handleChangeMultiMode($event, item)">
<mat-checkbox
[labelPosition]="'after'"
[checked]="item.selected"
[attr.data-test-id]="testId + ':' + item.value"
(change)="handleChangeMultiMode($event, item)"
(blur)="fieldOnBlur($event)"
>{{ item.text ?? item.value }}
</mat-checkbox>
</mat-option>
</div>
<ng-template #single>
<mat-checkbox
[labelPosition]="'after'"
[checked]="isChecked$"
[disabled]="bDisabled$"
[attr.data-test-id]="testId"
[formControl]="fieldControl"
(change)="fieldOnChange($event)"
(blur)="fieldOnBlur($event)"
>{{ caption$ }}</mat-checkbox
>
</div>
<mat-hint *ngIf="helperText">{{ helperText }}</mat-hint>
<p *ngIf="helperText">{{ helperText }}</p>
</ng-template>
<mat-error *ngIf="fieldControl.invalid">{{ getErrorMessage() }}</mat-error>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
top: 0rem;
margin-top: 0.625rem;
font-size: 0.875rem;
display: block;
transform: translateY(-1.28125em) scale(0.75) perspective(100px) translateZ(0.001px);
-ms-transform: translateY(-1.28125em) scale(0.75);
width: 133.33333%;
color: rgba(0, 0, 0, 0.6);
}

.psdk-data-readonly {
Expand All @@ -22,3 +22,16 @@
::ng-deep .mat-mdc-form-field-infix {
width: auto;
}

p {
font-size: 0.75rem;
color: rgba(0, 0, 0, 0.58);
}

mat-checkbox {
margin-left: -11px;
}

.mat-mdc-option {
margin-left: -16px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import { CommonModule } from '@angular/common';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatOptionModule } from '@angular/material/core';
import { interval } from 'rxjs';
import { AngularPConnectData, AngularPConnectService } from '../../../_bridge/angular-pconnect';
import { Utils } from '../../../_helpers/utils';
import { ComponentMapperComponent } from '../../../_bridge/component-mapper/component-mapper.component';
import { PConnFieldProps } from '../../../_types/PConnProps.interface';
import { deleteInstruction, insertInstruction, updateNewInstructions } from '../../../_helpers/instructions-utils';
import { handleEvent } from '../../../_helpers/event-util';

interface CheckboxProps extends Omit<PConnFieldProps, 'value'> {
// If any, enter additional props that only exist on Checkbox here
Expand All @@ -15,14 +19,21 @@ interface CheckboxProps extends Omit<PConnFieldProps, 'value'> {
caption?: string;
trueLabel?: string;
falseLabel?: string;
selectionMode?: string;
datasource?: any;
selectionKey?: string;
selectionList?: any;
primaryField: string;
readonlyContextList: any;
referenceList: string;
}

@Component({
selector: 'app-check-box',
templateUrl: './check-box.component.html',
styleUrls: ['./check-box.component.scss'],
standalone: true,
imports: [CommonModule, ReactiveFormsModule, MatCheckboxModule, MatFormFieldModule, forwardRef(() => ComponentMapperComponent)]
imports: [CommonModule, ReactiveFormsModule, MatCheckboxModule, MatFormFieldModule, MatOptionModule, forwardRef(() => ComponentMapperComponent)]
})
export class CheckBoxComponent implements OnInit, OnDestroy {
@Input() pConn$: typeof PConnect;
Expand Down Expand Up @@ -50,6 +61,17 @@ export class CheckBoxComponent implements OnInit, OnDestroy {
trueLabel$?: string;
falseLabel$?: string;

selectionMode?: string;
datasource?: any;
selectionKey?: string;
selectionList?: any;
primaryField: string;
selectedvalues: any;
referenceList: string;
listOfCheckboxes: any[] = [];
actionsApi: any;
propName: any;

fieldControl = new FormControl('', null);

constructor(
Expand All @@ -69,6 +91,11 @@ export class CheckBoxComponent implements OnInit, OnDestroy {
// this.updateSelf();
this.checkAndUpdate();

if (this.selectionMode === 'multi' && this.referenceList?.length > 0) {
this.pConn$.setReferenceList(this.selectionList);
updateNewInstructions(this.pConn$, this.selectionList);
}

if (this.formGroup$) {
// add control to formGroup
this.formGroup$.addControl(this.controlName$, this.fieldControl);
Expand Down Expand Up @@ -111,68 +138,124 @@ export class CheckBoxComponent implements OnInit, OnDestroy {
// moved this from ngOnInit() and call this from there instead...
this.configProps$ = this.pConn$.resolveConfigProps(this.pConn$.getConfigProps()) as CheckboxProps;

if (this.configProps$.value != undefined) {
this.value$ = this.configProps$.value;
}
this.testId = this.configProps$.testId;
this.label$ = this.configProps$.label;
this.displayMode$ = this.configProps$.displayMode;
this.label$ = this.configProps$.label;
if (this.label$ != '') {
this.showLabel$ = true;
}

this.caption$ = this.configProps$.caption;
this.helperText = this.configProps$.helperText;
this.trueLabel$ = this.configProps$.trueLabel;
this.falseLabel$ = this.configProps$.falseLabel;

// timeout and detectChanges to avoid ExpressionChangedAfterItHasBeenCheckedError
setTimeout(() => {
if (this.configProps$.required != null) {
this.bRequired$ = this.utils.getBooleanValue(this.configProps$.required);
this.actionsApi = this.pConn$.getActionsApi();
this.propName = (this.pConn$.getStateProps() as any).value;

// multi case
this.selectionMode = this.configProps$.selectionMode;
if (this.selectionMode === 'multi') {
this.referenceList = this.configProps$.referenceList;
this.selectionList = this.configProps$.selectionList;
this.selectedvalues = this.configProps$.readonlyContextList;
this.primaryField = this.configProps$.primaryField;

this.datasource = this.configProps$.datasource;
this.selectionKey = this.configProps$.selectionKey;
const listSourceItems = this.datasource?.source ?? [];
const dataField: any = this.selectionKey?.split?.('.')[1];
const listToDisplay: any[] = [];
listSourceItems.forEach(element => {
element.selected = this.selectedvalues?.some?.(data => data[dataField] === element.key);
listToDisplay.push(element);
});
this.listOfCheckboxes = listToDisplay;
} else {
if (this.configProps$.value != undefined) {
this.value$ = this.configProps$.value;
}
this.cdRef.detectChanges();
});

if (this.configProps$.visibility != null) {
this.bVisible$ = this.utils.getBooleanValue(this.configProps$.visibility);
}

// disabled
if (this.configProps$.disabled != undefined) {
this.bDisabled$ = this.utils.getBooleanValue(this.configProps$.disabled);
}
this.caption$ = this.configProps$.caption;
this.helperText = this.configProps$.helperText;
this.trueLabel$ = this.configProps$.trueLabel;
this.falseLabel$ = this.configProps$.falseLabel;

// timeout and detectChanges to avoid ExpressionChangedAfterItHasBeenCheckedError
setTimeout(() => {
if (this.configProps$.required != null) {
this.bRequired$ = this.utils.getBooleanValue(this.configProps$.required);
}
this.cdRef.detectChanges();
});

if (this.configProps$.visibility != null) {
this.bVisible$ = this.utils.getBooleanValue(this.configProps$.visibility);
}

if (this.bDisabled$) {
this.fieldControl.disable();
} else {
this.fieldControl.enable();
}
// disabled
if (this.configProps$.disabled != undefined) {
this.bDisabled$ = this.utils.getBooleanValue(this.configProps$.disabled);
}

if (this.configProps$.readOnly != null) {
this.bReadonly$ = this.utils.getBooleanValue(this.configProps$.readOnly);
}
if (this.bDisabled$) {
this.fieldControl.disable();
} else {
this.fieldControl.enable();
}

this.componentReference = (this.pConn$.getStateProps() as any).value;
if (this.configProps$.readOnly != null) {
this.bReadonly$ = this.utils.getBooleanValue(this.configProps$.readOnly);
this.fieldControl.disable();
}

if (this.label$ != '') {
this.showLabel$ = true;
}
this.componentReference = (this.pConn$.getStateProps() as any).value;

// eslint-disable-next-line sonarjs/no-redundant-boolean
if (this.value$ === 'true' || this.value$ == true) {
this.isChecked$ = true;
} else {
this.isChecked$ = false;
// eslint-disable-next-line sonarjs/no-redundant-boolean
if (this.value$ === 'true' || this.value$ == true) {
this.isChecked$ = true;
} else {
this.isChecked$ = false;
}
// trigger display of error message with field control
if (this.angularPConnectData.validateMessage != null && this.angularPConnectData.validateMessage != '') {
const timer = interval(100).subscribe(() => {
this.fieldControl.setErrors({ message: true });
this.fieldControl.markAsTouched();

timer.unsubscribe();
});
}
}
}

fieldOnChange(event: any) {
event.value = event.checked;

this.angularPConnectData.actions?.onChange(this, event);
handleEvent(this.actionsApi, 'changeNblur', this.propName, event.checked);
}

fieldOnBlur(event: any) {
event.value = event.checked;
this.angularPConnectData.actions?.onBlur(this, event);
if (this.selectionMode === 'multi') {
this.pConn$.getValidationApi().validate(this.selectedvalues, this.selectionList);
} else {
event.value = event.checked;
this.angularPConnectData.actions?.onBlur(this, event);
}
}

handleChangeMultiMode(event, element) {
if (!element.selected) {
insertInstruction(this.pConn$, this.selectionList, this.selectionKey, this.primaryField, {
id: element.key,
primary: element.text ?? element.value
});
} else {
deleteInstruction(this.pConn$, this.selectionList, this.selectionKey, {
id: element.key,
primary: element.text ?? element.value
});
}
this.pConn$.clearErrorMessages({
property: this.selectionList,
category: '',
context: ''
});
}

getErrorMessage() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<div [formGroup]="formGroup$">
<mat-form-field class="psdk-full-width" subscriptSizing="dynamic">
<mat-label>{{ label$ }}</mat-label>
<mat-chip-grid #chipGrid>
<ng-container *ngFor="let select of selectedItems">
<mat-chip-row (removed)="removeChip(select)">
{{ select.primary }}
<button matChipRemove>
<mat-icon>cancel</mat-icon>
</button>
</mat-chip-row>
</ng-container>
</mat-chip-grid>
<input
matInput
[placeholder]="placeholder"
[formControl]="fieldControl"
[value]="value$"
[required]="bRequired$"
[matAutocomplete]="auto"
(input)="fieldOnChange($event)"
[matChipInputFor]="chipGrid"
#trigger="matAutocompleteTrigger"
/>
<mat-autocomplete #auto="matAutocomplete">
<mat-option *ngFor="let item of itemsTree" [value]="item.primary" (click)="optionClicked($event, item, trigger)">
<mat-checkbox [checked]="item.selected" (click)="optionClicked($event, item, trigger)">
<span>{{ item.primary }}</span>
</mat-checkbox>
</mat-option>
</mat-autocomplete>
<mat-error *ngIf="fieldControl.invalid">{{ getErrorMessage() }}</mat-error>
</mat-form-field>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.psdk-full-width {
width: 100%;
}

::ng-deep .mat-mdc-form-field-infix {
padding-left: 10px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { MultiselectComponent } from './multiselect.component';

describe('MultiselectComponent', () => {
let component: MultiselectComponent;
let fixture: ComponentFixture<MultiselectComponent>;

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [MultiselectComponent]
});
fixture = TestBed.createComponent(MultiselectComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Loading

0 comments on commit 692356c

Please sign in to comment.