@pega/angular-sdk-overrides 0.26.5 → 0.26.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,12 +2,8 @@
2
2
  <label [ngClass]="{ 'label-required': required === true }" class="rich-text-label">{{ label }}</label>
3
3
 
4
4
  <div class="rich-text-editor" *ngIf="!readonly">
5
- <div
6
- class="tiptap-container"
7
- [class.disabled]="disabled"
8
- [class.tiptap-container-invalid]="richText.invalid && (error || richText.touched || richText.dirty)"
9
- >
10
- <div #editorEl class="tiptap-editor-area" [attr.aria-invalid]="richText.invalid && (error || richText.touched || richText.dirty)"></div>
5
+ <div class="tiptap-container" [class.disabled]="disabled" [class.tiptap-container-invalid]="showError">
6
+ <div #editorEl class="tiptap-editor-area" [attr.aria-invalid]="showError"></div>
11
7
 
12
8
  <div class="tiptap-toolbar" *ngIf="!disabled" (mousedown)="$event.preventDefault()">
13
9
  <mat-form-field class="tiptap-block-select" appearance="outline" subscriptSizing="dynamic">
@@ -105,7 +101,7 @@
105
101
  </div>
106
102
  </div>
107
103
 
108
- <p *ngIf="richText.invalid && (error || richText.touched || richText.dirty)" [ngClass]="'text-editor-error'">{{ info }}</p>
104
+ <p *ngIf="showError" [ngClass]="'text-editor-error'">{{ errorMessage }}</p>
109
105
  </div>
110
106
 
111
107
  <div *ngIf="readonly">
@@ -86,7 +86,7 @@ export class RichTextEditorComponent implements OnChanges, AfterViewInit, OnDest
86
86
  this.initializeEditor();
87
87
  }
88
88
 
89
- if (this.editor && changes['value'] && this.value !== this.editor.getHTML()) {
89
+ if (this.editor && changes['value'] && (this.value ?? '') !== this.getEditorValue(this.editor)) {
90
90
  this.editor.commands.setContent(this.value ?? '', { emitUpdate: false });
91
91
  }
92
92
  if (this.editor && changes['disabled']) {
@@ -113,7 +113,7 @@ export class RichTextEditorComponent implements OnChanges, AfterViewInit, OnDest
113
113
  content: this.value || '',
114
114
  editable: !this.disabled,
115
115
  onUpdate: ({ editor }) => {
116
- const html = editor.getHTML();
116
+ const html = this.getEditorValue(editor);
117
117
  this.richText.setValue(html, { emitEvent: false });
118
118
  this.richText.markAsDirty();
119
119
  this.onChange.emit(html);
@@ -121,7 +121,7 @@ export class RichTextEditorComponent implements OnChanges, AfterViewInit, OnDest
121
121
  },
122
122
  onBlur: ({ editor }) => {
123
123
  this.richText.markAsTouched();
124
- this.onBlur.emit(editor.getHTML());
124
+ this.onBlur.emit(this.getEditorValue(editor));
125
125
  this.cdr.markForCheck();
126
126
  },
127
127
  onSelectionUpdate: ({ editor }) => {
@@ -132,6 +132,22 @@ export class RichTextEditorComponent implements OnChanges, AfterViewInit, OnDest
132
132
  this.updateCurrentBlock(this.editor);
133
133
  }
134
134
 
135
+ // Tiptap reports an empty document as "<p></p>", which would satisfy Validators.required and the engine's required check.
136
+ private getEditorValue(editor: Editor): string {
137
+ return editor.isEmpty ? '' : editor.getHTML();
138
+ }
139
+
140
+ get showError(): boolean {
141
+ return !!this.error || (this.richText.invalid && (this.richText.touched || this.richText.dirty));
142
+ }
143
+
144
+ get errorMessage(): string {
145
+ if (!this.error && this.richText.hasError('required')) {
146
+ return 'You must enter a value';
147
+ }
148
+ return this.info;
149
+ }
150
+
135
151
  private updateCurrentBlock(editor: Editor): void {
136
152
  if (editor.isActive('heading', { level: 1 })) this.currentBlock = 'h1';
137
153
  else if (editor.isActive('heading', { level: 2 })) this.currentBlock = 'h2';
@@ -415,7 +415,7 @@ export class AutoCompleteComponent extends FieldBase implements OnInit {
415
415
  let key = '';
416
416
  if (val) {
417
417
  const index = this.options$?.findIndex(element => element.value === val);
418
- key = index > -1 ? (key = this.options$[index].key) : val;
418
+ key = index > -1 ? this.options$[index].key : val;
419
419
  }
420
420
  const value = key;
421
421
  handleEvent(this.actionsApi, 'changeNblur', this.propName, value);
@@ -12,13 +12,13 @@
12
12
  <div [formGroup]="formGroup$" *ngIf="bVisible$">
13
13
  <div class="mat-form-field-infix" *ngIf="showLabel$">
14
14
  <span>
15
- <label class="mat-form-field-label psdk-label-readonly" [ngClass]="{ 'label-required': bRequired$ && selectionMode === 'multi' }">{{
16
- label$
17
- }}</label>
15
+ <label class="mat-form-field-label psdk-label-readonly" [ngClass]="{ 'label-required': bRequired$ && selectionMode === 'multi' }">
16
+ {{ label$ }}</label
17
+ >
18
18
  </span>
19
19
  </div>
20
20
  <div *ngIf="selectionMode === 'multi'; else single">
21
- <mat-option *ngFor="let item of listOfCheckboxes">
21
+ <div class="mat-mdc-option" *ngFor="let item of listOfCheckboxes">
22
22
  <mat-checkbox
23
23
  [labelPosition]="'after'"
24
24
  [checked]="item.selected"
@@ -28,7 +28,7 @@
28
28
  (blur)="fieldOnBlur($event)"
29
29
  >{{ item.text ?? item.value }}
30
30
  </mat-checkbox>
31
- </mat-option>
31
+ </div>
32
32
  </div>
33
33
  <ng-template #single>
34
34
  <mat-checkbox
@@ -3,7 +3,6 @@ import { CommonModule } from '@angular/common';
3
3
  import { ReactiveFormsModule } from '@angular/forms';
4
4
  import { MatCheckboxModule } from '@angular/material/checkbox';
5
5
  import { MatFormFieldModule } from '@angular/material/form-field';
6
- import { MatOptionModule } from '@angular/material/core';
7
6
 
8
7
  import { FieldBase } from '@pega/angular-sdk-components';
9
8
  import { FieldWarningDirective } from '@pega/angular-sdk-components';
@@ -39,7 +38,6 @@ interface CheckboxProps extends Omit<PConnFieldProps, 'value'> {
39
38
  ReactiveFormsModule,
40
39
  MatCheckboxModule,
41
40
  MatFormFieldModule,
42
- MatOptionModule,
43
41
  FieldWarningDirective,
44
42
  forwardRef(() => ComponentMapperComponent)
45
43
  ]
@@ -97,6 +97,12 @@ export class DateTimeComponent extends FieldBase implements OnInit, OnDestroy {
97
97
  }
98
98
 
99
99
  fieldOnDateChange(event: any) {
100
+ // owl picker emits null when the typed text can't be parsed; clear the input instead of defaulting to now
101
+ if (!event.value) {
102
+ this.fieldControl.setValue('');
103
+ handleEvent(this.actionsApi, 'changeNblur', this.propName, '');
104
+ return;
105
+ }
100
106
  // this comes from the date pop up
101
107
  if (typeof event.value === 'object') {
102
108
  // convert date to pega "date" format
@@ -106,4 +112,25 @@ export class DateTimeComponent extends FieldBase implements OnInit, OnDestroy {
106
112
  }
107
113
  handleEvent(this.actionsApi, 'changeNblur', this.propName, event.value);
108
114
  }
115
+
116
+ override getErrorMessage() {
117
+ // look for validation messages for json, pre-defined or just an error pushed from workitem (400)
118
+ if (this.fieldControl.hasError('message')) {
119
+ return this.angularPConnectData.validateMessage ?? '';
120
+ }
121
+
122
+ if (this.fieldControl.hasError('required')) {
123
+ return 'You must enter a value';
124
+ }
125
+
126
+ if (this.fieldControl.hasError('owlDateTimeParse')) {
127
+ return `${this.fieldControl.errors?.['owlDateTimeParse'].text} is not a valid date time value`;
128
+ }
129
+
130
+ if (this.fieldControl.errors) {
131
+ return 'Invalid date time value';
132
+ }
133
+
134
+ return '';
135
+ }
109
136
  }
@@ -90,7 +90,7 @@ export class MultiselectComponent extends FieldBase {
90
90
  key: 'true'
91
91
  }
92
92
  ];
93
- let secondaryColumns: any = [];
93
+ let secondaryColumns: any;
94
94
  // Read columnsFormatter from raw (unresolved) metadata to get field property paths
95
95
  // (e.g. "@P .pyLabel") instead of resolved data values (e.g. "New Complex Fields").
96
96
  // This matches Constellation's approach: pConn.getRawMetadata()?.config?.columnsFormatter
@@ -234,7 +234,7 @@ export class UserReferenceComponent implements OnInit, OnDestroy {
234
234
  let key = '';
235
235
  if (event?.target?.value) {
236
236
  const index = this.options$?.findIndex(element => element.value === event.target.value);
237
- key = index > -1 ? (key = this.options$[index].key) : event.target.value;
237
+ key = index > -1 ? this.options$[index].key : event.target.value;
238
238
  }
239
239
  const value = key;
240
240
  handleEvent(this.actionsApi, 'changeNblur', this.propName, value);
@@ -166,10 +166,6 @@ export class ViewContainerComponent implements OnInit, OnDestroy {
166
166
  const { accessedOrder, items } = routingInfo;
167
167
  if (accessedOrder && items) {
168
168
  const key = accessedOrder[accessedOrder.length - 1];
169
- let componentVisible = accessedOrder.length > 0;
170
- const { visible } = this.state;
171
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
172
- componentVisible = visible || componentVisible;
173
169
  if (items[key] && items[key].view && Object.keys(items[key].view).length > 0) {
174
170
  const latestItem = items[key];
175
171
  const rootView = latestItem.view;
@@ -37,7 +37,7 @@ export function flattenObj(obj: any): any {
37
37
  export const initializeSearchFields = (searchFields, getPConnect, referenceListClassID, searchFieldRestoreValues = {}) => {
38
38
  const filtersProperties = {};
39
39
  searchFields?.forEach(field => {
40
- let val = '';
40
+ let val;
41
41
  const { value, defaultValue = '' } = field.config;
42
42
  const propPath = PCore.getAnnotationUtils().getPropertyName(value);
43
43
 
@@ -265,7 +265,7 @@ export class DataReferenceComponent implements OnInit, OnDestroy {
265
265
  if (this.pConn$.getContextName().includes('modal') || this.pConn$.getContextName().includes('workarea')) {
266
266
  if (hasAssociatedViewConfigured || this.allowImplicitRefresh) {
267
267
  const pageReference = this.pConn$.getPageReference();
268
- let pgRef: any = null;
268
+ let pgRef: string;
269
269
  if (pageReference.startsWith('objectInfo')) {
270
270
  pgRef = pageReference.replace('objectInfo.content', '');
271
271
  } else {
@@ -386,7 +386,7 @@ export class ListViewComponent implements OnInit, OnDestroy {
386
386
  }
387
387
 
388
388
  getValue(col) {
389
- return this.fieldDefs.find(f => f.name === col)?.label;
389
+ return this.fields$.find(f => f.config.name === col)?.config.label;
390
390
  }
391
391
 
392
392
  getListData() {
@@ -625,11 +625,10 @@ export class ListViewComponent implements OnInit, OnDestroy {
625
625
  }
626
626
 
627
627
  _getIconStyle(level): string {
628
- let sReturn = '';
629
628
  let nLevel = parseInt(level, 10);
630
629
  nLevel--;
631
630
  nLevel *= 15;
632
- sReturn = `padding-left: ${nLevel}px; vertical-align: middle`;
631
+ const sReturn = `padding-left: ${nLevel}px; vertical-align: middle`;
633
632
 
634
633
  return sReturn;
635
634
  }
@@ -1002,16 +1001,14 @@ export class ListViewComponent implements OnInit, OnDestroy {
1002
1001
  let bKeep = true;
1003
1002
  for (const filterObj of this.filterByColumns) {
1004
1003
  if (filterObj.containsFilterValue != '' || filterObj.containsFilter == 'null' || filterObj.containsFilter == 'notnull') {
1005
- let filterValue: any;
1006
-
1007
1004
  switch (filterObj.type) {
1008
1005
  case 'Date':
1009
1006
  case 'DateTime':
1010
1007
  case 'Time':
1011
- bKeep = this.filterDataWithDate(item, filterObj, filterValue);
1008
+ bKeep = this.filterDataWithDate(item, filterObj);
1012
1009
  break;
1013
1010
  default:
1014
- bKeep = this.filterDataWithCommonTypes(item, filterObj, filterValue);
1011
+ bKeep = this.filterDataWithCommonTypes(item, filterObj);
1015
1012
  break;
1016
1013
  }
1017
1014
  }
@@ -1025,10 +1022,10 @@ export class ListViewComponent implements OnInit, OnDestroy {
1025
1022
  return bKeep;
1026
1023
  }
1027
1024
 
1028
- filterDataWithDate(item, filterObj, filterValue) {
1025
+ filterDataWithDate(item, filterObj) {
1029
1026
  let bKeep;
1030
1027
  let value = item[filterObj.ref] != null || item[filterObj.ref] != '' ? getSeconds(item[filterObj.ref]) : null;
1031
- filterValue = filterObj.containsFilterValue != null && filterObj.containsFilterValue != '' ? getSeconds(filterObj.containsFilterValue) : null;
1028
+ let filterValue = filterObj.containsFilterValue != null && filterObj.containsFilterValue != '' ? getSeconds(filterObj.containsFilterValue) : null;
1032
1029
 
1033
1030
  switch (filterObj.containsFilter) {
1034
1031
  case 'notequal':
@@ -1071,10 +1068,10 @@ export class ListViewComponent implements OnInit, OnDestroy {
1071
1068
  return bKeep;
1072
1069
  }
1073
1070
 
1074
- filterDataWithCommonTypes(item, filterObj, filterValue) {
1071
+ filterDataWithCommonTypes(item, filterObj) {
1075
1072
  let bKeep;
1076
1073
  const value = item[filterObj.ref].toLowerCase();
1077
- filterValue = filterObj.containsFilterValue.toLowerCase();
1074
+ const filterValue = filterObj.containsFilterValue.toLowerCase();
1078
1075
 
1079
1076
  switch (filterObj.containsFilter) {
1080
1077
  case 'contains':
@@ -216,6 +216,56 @@ export function getFieldLabel(fieldConfig) {
216
216
  return fieldLabel;
217
217
  }
218
218
 
219
+ function trimAnnotation(anno) {
220
+ if (typeof anno !== 'string' || !anno.startsWith('@')) return anno;
221
+ const spaceIndex = anno.indexOf(' ');
222
+ if (spaceIndex === -1) return anno;
223
+ return anno.substring(spaceIndex + 1);
224
+ }
225
+
226
+ /**
227
+ * Resolves an "@FL" label from the data model and localizes it using the property's locale reference.
228
+ */
229
+ function getLocalizedFieldLabel(fieldConfig, pConnect) {
230
+ const { classID, label, caption } = fieldConfig;
231
+ let fieldLabel = getPropertyNameFromConfigValue(label || caption);
232
+ const fieldMetaData: any = PCore.getMetadataUtils().getEmbeddedPropertyMetadata(fieldLabel, classID) ?? {};
233
+ fieldLabel = fieldMetaData.label ?? fieldMetaData.caption ?? fieldLabel;
234
+ const localeValue = pConnect.getLocalizationService(fieldMetaData.localeReference).getLocalizedText(fieldLabel);
235
+ return localeValue || fieldLabel;
236
+ }
237
+
238
+ export function resolveFieldLabel(fieldViewMetadata, pConnect, classID?) {
239
+ const fieldClassID = classID || fieldViewMetadata.config.classID;
240
+ if (fieldClassID && pConnect) {
241
+ const fieldConfig: any = {
242
+ meta: fieldViewMetadata,
243
+ options: {
244
+ ...pConnect.options,
245
+ hasForm: false,
246
+ localeReference: pConnect.getLocaleRuleName()
247
+ },
248
+ useCustomContext: { classID: fieldClassID }
249
+ };
250
+ const fieldPConnect = PCore.createPConnect(fieldConfig).getPConnect();
251
+ const resolvedProps: any = fieldPConnect.resolveConfigProps({
252
+ label: fieldViewMetadata.config.label || fieldViewMetadata.config.caption
253
+ });
254
+ if (resolvedProps.label) return resolvedProps.label;
255
+ }
256
+
257
+ let label = fieldViewMetadata.config.label || fieldViewMetadata.config.caption;
258
+ if (isFLProperty(label)) {
259
+ label = getLocalizedFieldLabel(fieldViewMetadata.config, pConnect);
260
+ } else {
261
+ label = trimAnnotation(label);
262
+ }
263
+ if (pConnect) {
264
+ label = pConnect.getLocalizedValue(label);
265
+ }
266
+ return label;
267
+ }
268
+
219
269
  export const updateFieldLabels = (fields, configFields, primaryFieldsViewIndex, pConnect, options) => {
220
270
  const labelsOfFields: any = [];
221
271
  const { columnsRawConfig = [] } = options;
@@ -255,12 +305,9 @@ export const updateFieldLabels = (fields, configFields, primaryFieldsViewIndex,
255
305
  };
256
306
 
257
307
  export const buildFieldsForTable = (configFields, pConnect, showActionColumn, options) => {
258
- const { primaryFieldsViewIndex, fields } = options;
308
+ const { classID } = options;
259
309
 
260
- // get resolved field labels for primary fields raw config included in configFields
261
- const fieldsLabels = updateFieldLabels(fields, configFields, primaryFieldsViewIndex, pConnect, {
262
- columnsRawConfig: pConnect.getRawConfigProps()?.children?.find(item => item?.name === 'Columns')?.children
263
- });
310
+ const fieldsLabels = configFields?.map(field => resolveFieldLabel(field, pConnect, classID)) ?? [];
264
311
 
265
312
  const fieldDefs = configFields?.map((field, index) => {
266
313
  return {
@@ -363,7 +363,8 @@ export class SimpleTableManualComponent implements OnInit, OnDestroy {
363
363
  // Nebula and we may not end up using it all.
364
364
  this.fieldDefs = buildFieldsForTable(configFields, this.pConn$, this.showActionColumn, {
365
365
  primaryFieldsViewIndex,
366
- fields: resolvedFields
366
+ fields: resolvedFields,
367
+ classID: contextClass
367
368
  });
368
369
  this.fieldDefs = this.fieldDefs?.filter(field => !(field.meta?.config?.hide === true));
369
370
  this.initializeDefaultPageInstructions();
@@ -971,11 +972,10 @@ export class SimpleTableManualComponent implements OnInit, OnDestroy {
971
972
  }
972
973
 
973
974
  _getIconStyle(level): string {
974
- let sReturn = '';
975
975
  let nLevel = parseInt(level, 10);
976
976
  nLevel--;
977
977
  nLevel *= 15;
978
- sReturn = `padding-left: ${nLevel}px; vertical-align: middle`;
978
+ const sReturn = `padding-left: ${nLevel}px; vertical-align: middle`;
979
979
 
980
980
  return sReturn;
981
981
  }
@@ -59,7 +59,7 @@ export class SingleReferenceReadonlyComponent implements OnInit, OnDestroy {
59
59
  const label = this.configProps.label;
60
60
  const showLabel = this.configProps.showLabel;
61
61
  const inheritedProps = this.pConn$.getInheritedProps();
62
- const propsToUse = { label: label || inheritedProps.label, showLabel: inheritedProps.showLabel || showLabel };
62
+ const propsToUse = { label: label || inheritedProps['label'], showLabel: inheritedProps['showLabel'] || showLabel };
63
63
  const type = (rawViewMetadata?.config as any)?.componentType ?? rawViewMetadata?.type;
64
64
  this.displayMode = this.configProps.displayMode;
65
65
  const targetObjectType = this.configProps.targetObjectType;
@@ -49,7 +49,7 @@ export const fileDownloadVar = (content: { data: string; headers: Record<string,
49
49
  if (!/^(http|https):\/\//.test(data)) {
50
50
  data = `//${data}`;
51
51
  }
52
- window.open(content.data, '_blank');
52
+ window.open(data, '_blank');
53
53
  } else if (type === 'EMAIL') {
54
54
  // Temp Fix: for EMAIL type attachment
55
55
  fileDownload(content.data, name, 'html', content.headers);
@@ -194,8 +194,8 @@ export class AttachmentComponent implements OnInit, OnDestroy {
194
194
  error: errorFile.props.error || null
195
195
  };
196
196
  });
197
- let key = '';
198
- let updatedAttachments: any = [];
197
+ let key: string;
198
+ let updatedAttachments: any;
199
199
  if (this.allowMultiple$ || this.isOldAttachment) {
200
200
  key = this.isOldAttachment ? `${this.valueRef}.pxResults` : this.valueRef;
201
201
  const existingAttachments = PCore.getStoreValue(`.${key}`, this.pConn$.getPageReference(), this.pConn$.getContextName()) || [];
@@ -347,7 +347,7 @@ export class FeedContainerComponent implements OnInit, OnDestroy {
347
347
  }
348
348
 
349
349
  likeClick(messageID: string, rMessageID: string, bLikedByMe: boolean, level: string) {
350
- let pulseMessage = {};
350
+ let pulseMessage: object;
351
351
 
352
352
  if (level === 'top') {
353
353
  pulseMessage = {
@@ -523,7 +523,7 @@ export class FileUtilityComponent implements OnInit, OnDestroy {
523
523
  if (!/^(http|https):\/\//.test(data)) {
524
524
  data = `//${data}`;
525
525
  }
526
- window.open(content.data, '_blank');
526
+ window.open(data, '_blank');
527
527
  }
528
528
  })
529
529
  .catch(console.error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pega/angular-sdk-overrides",
3
- "version": "0.26.5",
3
+ "version": "0.26.6",
4
4
  "description": "Angular SDK - Code for overriding components",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"