@pega/angular-sdk-overrides 25.1.13 → 26.1.10

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.
Files changed (153) hide show
  1. package/lib/designSystemExtension/alert/alert.component.html +4 -1
  2. package/lib/designSystemExtension/alert/alert.component.scss +9 -10
  3. package/lib/designSystemExtension/alert/alert.component.ts +6 -2
  4. package/lib/designSystemExtension/field-group/field-group.component.ts +11 -3
  5. package/lib/designSystemExtension/material-case-summary/material-case-summary.component.html +51 -36
  6. package/lib/designSystemExtension/material-case-summary/material-case-summary.component.scss +12 -0
  7. package/lib/designSystemExtension/material-case-summary/material-case-summary.component.ts +11 -0
  8. package/lib/designSystemExtension/operator/operator.component.html +5 -3
  9. package/lib/designSystemExtension/operator/operator.component.scss +7 -0
  10. package/lib/designSystemExtension/operator/operator.component.ts +2 -1
  11. package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.html +100 -10
  12. package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.scss +141 -1
  13. package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.spec.ts +1 -1
  14. package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.ts +240 -72
  15. package/lib/field/auto-complete/auto-complete.component.html +46 -5
  16. package/lib/field/auto-complete/auto-complete.component.scss +92 -0
  17. package/lib/field/auto-complete/auto-complete.component.ts +350 -23
  18. package/lib/field/cancel-alert/cancel-alert.component.ts +38 -15
  19. package/lib/field/check-box/check-box.component.html +8 -6
  20. package/lib/field/check-box/check-box.component.ts +9 -2
  21. package/lib/field/currency/currency.component.html +2 -1
  22. package/lib/field/currency/currency.component.ts +10 -1
  23. package/lib/field/date/date.component.html +2 -1
  24. package/lib/field/date/date.component.ts +2 -0
  25. package/lib/field/date-time/date-time.component.html +2 -1
  26. package/lib/field/date-time/date-time.component.scss +46 -40
  27. package/lib/field/date-time/date-time.component.ts +29 -0
  28. package/lib/field/decimal/decimal.component.html +2 -1
  29. package/lib/field/decimal/decimal.component.ts +2 -0
  30. package/lib/field/dropdown/dropdown.component.html +4 -1
  31. package/lib/field/dropdown/dropdown.component.ts +18 -19
  32. package/lib/field/email/email.component.html +2 -1
  33. package/lib/field/email/email.component.ts +2 -2
  34. package/lib/field/embedded-data-multi/embedded-data-multi.component.html +3 -0
  35. package/lib/field/embedded-data-multi/embedded-data-multi.component.scss +0 -0
  36. package/lib/field/embedded-data-multi/embedded-data-multi.component.ts +173 -0
  37. package/lib/field/field.base.ts +27 -1
  38. package/lib/field/group/group.component.html +1 -1
  39. package/lib/field/group/group.component.ts +15 -5
  40. package/lib/field/integer/integer.component.html +2 -1
  41. package/lib/field/integer/integer.component.ts +4 -2
  42. package/lib/field/location/location.component.html +2 -1
  43. package/lib/field/location/location.component.ts +3 -1
  44. package/lib/field/multiselect/multiselect.component.html +1 -1
  45. package/lib/field/multiselect/multiselect.component.ts +23 -4
  46. package/lib/field/multiselect/utils.ts +5 -3
  47. package/lib/field/object-reference/object-reference.component.html +30 -15
  48. package/lib/field/object-reference/object-reference.component.ts +818 -94
  49. package/lib/field/percentage/percentage.component.html +2 -1
  50. package/lib/field/percentage/percentage.component.ts +10 -1
  51. package/lib/field/phone/phone.component.html +2 -1
  52. package/lib/field/phone/phone.component.ts +2 -1
  53. package/lib/field/radio-buttons/radio-buttons.component.html +2 -1
  54. package/lib/field/radio-buttons/radio-buttons.component.ts +10 -1
  55. package/lib/field/rich-text/rich-text.component.scss +1 -166
  56. package/lib/field/rich-text/rich-text.component.spec.ts +1 -1
  57. package/lib/field/rich-text/rich-text.component.ts +1 -1
  58. package/lib/field/selectable-card/selectable-card.component.html +1 -0
  59. package/lib/field/selectable-card/selectable-card.component.ts +31 -16
  60. package/lib/field/semantic-link/semantic-link.component.html +1 -1
  61. package/lib/field/semantic-link/semantic-link.component.scss +4 -0
  62. package/lib/field/semantic-link/semantic-link.component.ts +13 -7
  63. package/lib/field/text-area/text-area.component.html +2 -1
  64. package/lib/field/text-area/text-area.component.ts +2 -1
  65. package/lib/field/text-input/text-input.component.html +2 -1
  66. package/lib/field/text-input/text-input.component.ts +2 -1
  67. package/lib/field/time/time.component.html +2 -1
  68. package/lib/field/time/time.component.ts +2 -1
  69. package/lib/field/url/url.component.html +2 -1
  70. package/lib/field/url/url.component.ts +2 -1
  71. package/lib/field/user-reference/user-reference.component.html +4 -2
  72. package/lib/field/user-reference/user-reference.component.ts +11 -6
  73. package/lib/infra/Containers/flow-container/flow-container.component.html +1 -1
  74. package/lib/infra/Containers/flow-container/flow-container.component.ts +35 -46
  75. package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.html +12 -0
  76. package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.spec.ts +130 -0
  77. package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.ts +77 -0
  78. package/lib/infra/Containers/modal-view-container/modal-view-container.component.html +35 -8
  79. package/lib/infra/Containers/modal-view-container/modal-view-container.component.ts +185 -194
  80. package/lib/infra/Containers/view-container/view-container.component.ts +32 -35
  81. package/lib/infra/assignment/assignment.component.html +2 -1
  82. package/lib/infra/assignment/assignment.component.ts +42 -15
  83. package/lib/infra/defer-load/defer-load.component.ts +6 -3
  84. package/lib/infra/multi-step/multi-step.component.html +25 -33
  85. package/lib/infra/multi-step/multi-step.component.scss +67 -125
  86. package/lib/infra/multi-step/multi-step.component.ts +11 -0
  87. package/lib/infra/navbar/navbar.component.ts +33 -36
  88. package/lib/infra/reference/reference.component.ts +1 -2
  89. package/lib/infra/root-container/root-container.component.ts +33 -38
  90. package/lib/infra/view/view.component.ts +21 -40
  91. package/lib/template/advanced-search/advanced-search.component.ts +0 -1
  92. package/lib/template/advanced-search/search-groups/search-groups.component.ts +2 -3
  93. package/lib/template/app-shell/app-shell.component.ts +13 -13
  94. package/lib/template/base/details-template-base.ts +41 -14
  95. package/lib/template/case-summary/case-summary.component.ts +15 -1
  96. package/lib/template/case-view/case-view.component.html +13 -1
  97. package/lib/template/case-view/case-view.component.spec.ts +119 -10
  98. package/lib/template/case-view/case-view.component.ts +52 -3
  99. package/lib/template/data-reference/data-reference.component.ts +2 -4
  100. package/lib/template/data-reference/search-form/search-form.component.html +1 -1
  101. package/lib/template/data-reference/search-form/search-form.component.ts +9 -1
  102. package/lib/template/data-reference/search-form/tabsData.ts +0 -1
  103. package/lib/template/default-form/default-form.component.html +19 -3
  104. package/lib/template/default-form/default-form.component.ts +13 -3
  105. package/lib/template/details/details.component.html +23 -3
  106. package/lib/template/details/details.component.scss +9 -2
  107. package/lib/template/details/details.component.ts +3 -70
  108. package/lib/template/details-narrow-wide/details-narrow-wide.component.html +21 -2
  109. package/lib/template/details-narrow-wide/details-narrow-wide.component.scss +8 -1
  110. package/lib/template/details-narrow-wide/details-narrow-wide.component.ts +3 -39
  111. package/lib/template/details-one-column/details-one-column.component.html +23 -1
  112. package/lib/template/details-one-column/details-one-column.component.scss +14 -0
  113. package/lib/template/details-one-column/details-one-column.component.ts +3 -40
  114. package/lib/template/details-three-column/details-three-column.component.html +21 -2
  115. package/lib/template/details-three-column/details-three-column.component.scss +8 -1
  116. package/lib/template/details-three-column/details-three-column.component.ts +3 -48
  117. package/lib/template/details-two-column/details-two-column.component.html +21 -2
  118. package/lib/template/details-two-column/details-two-column.component.scss +8 -1
  119. package/lib/template/details-two-column/details-two-column.component.ts +3 -46
  120. package/lib/template/details-wide-narrow/details-wide-narrow.component.html +21 -2
  121. package/lib/template/details-wide-narrow/details-wide-narrow.component.scss +8 -1
  122. package/lib/template/details-wide-narrow/details-wide-narrow.component.ts +3 -43
  123. package/lib/template/field-group-template/field-group-template.component.ts +19 -6
  124. package/lib/template/field-value-list/field-value-list.component.scss +17 -0
  125. package/lib/template/list-view/list-view.component.ts +47 -11
  126. package/lib/template/self-service-case-view/self-service-case-view.component.html +13 -1
  127. package/lib/template/self-service-case-view/self-service-case-view.component.spec.ts +96 -11
  128. package/lib/template/self-service-case-view/self-service-case-view.component.ts +52 -3
  129. package/lib/template/simple-table/simple-table.component.ts +6 -0
  130. package/lib/template/simple-table-manual/helpers.ts +84 -16
  131. package/lib/template/simple-table-manual/simple-table-manual.component.html +30 -14
  132. package/lib/template/simple-table-manual/simple-table-manual.component.scss +8 -2
  133. package/lib/template/simple-table-manual/simple-table-manual.component.ts +131 -43
  134. package/lib/template/single-reference-readonly/single-reference-readonly.component.html +0 -0
  135. package/lib/template/single-reference-readonly/single-reference-readonly.component.scss +2 -3
  136. package/lib/template/single-reference-readonly/single-reference-readonly.component.ts +7 -8
  137. package/lib/template/wss-nav-bar/wss-nav-bar.component.ts +19 -21
  138. package/lib/widget/attachment/AttachmentUtils.ts +19 -11
  139. package/lib/widget/attachment/attachment.component.html +16 -6
  140. package/lib/widget/attachment/attachment.component.ts +14 -4
  141. package/lib/widget/case-history/case-history.component.ts +6 -2
  142. package/lib/widget/feed-container/feed-container.component.ts +1 -199
  143. package/lib/widget/file-utility/file-utility.component.ts +26 -30
  144. package/lib/widget/todo/todo.component.ts +7 -2
  145. package/package.json +2 -2
  146. package/lib/designSystemExtension/material-details/material-details.component.html +0 -40
  147. package/lib/designSystemExtension/material-details/material-details.component.scss +0 -116
  148. package/lib/designSystemExtension/material-details/material-details.component.spec.ts +0 -24
  149. package/lib/designSystemExtension/material-details/material-details.component.ts +0 -31
  150. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.html +0 -29
  151. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.scss +0 -34
  152. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.spec.ts +0 -22
  153. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.ts +0 -37
@@ -9,6 +9,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
9
9
  import { AngularPConnectData, AngularPConnectService } from '@pega/angular-sdk-components';
10
10
  import { Utils } from '@pega/angular-sdk-components';
11
11
  import { ComponentMapperComponent } from '@pega/angular-sdk-components';
12
+ import { FieldWarningDirective } from '@pega/angular-sdk-components';
12
13
  import { PConnFieldProps } from '@pega/angular-sdk-components';
13
14
  import { map, Observable, startWith } from 'rxjs';
14
15
  import { handleEvent } from '@pega/angular-sdk-components';
@@ -38,6 +39,7 @@ interface UserReferenceProps extends Omit<PConnFieldProps, 'value'> {
38
39
  MatOptionModule,
39
40
  MatInputModule,
40
41
  MatAutocompleteModule,
42
+ FieldWarningDirective,
41
43
  forwardRef(() => ComponentMapperComponent)
42
44
  ]
43
45
  })
@@ -54,6 +56,7 @@ export class UserReferenceComponent implements OnInit, OnDestroy {
54
56
  options$: any;
55
57
  bReadonly$: boolean;
56
58
  bRequired$: boolean;
59
+ bFieldMessageVisible$ = false;
57
60
  showAsFormattedText$?: boolean;
58
61
  displayAs$?: string;
59
62
  testId: string;
@@ -158,12 +161,17 @@ export class UserReferenceComponent implements OnInit, OnDestroy {
158
161
  this.testId = props.testId;
159
162
  this.onRecordChange = props?.onRecordChange;
160
163
 
161
- const { label, displayAs, value, showAsFormattedText, helperText, placeholder, displayMode } = props;
164
+ const { label, displayAs, value, showAsFormattedText, helperText, placeholder, displayMode, messageConfig = {}, showFieldMessage } = props;
165
+ const { readOnly, required } = props;
166
+ [this.bReadonly$, this.bRequired$] = [readOnly, required].map(prop => prop === true || (typeof prop === 'string' && prop === 'true'));
167
+
168
+ const isMessageVisible = this.utils.getBooleanValue(messageConfig.visibility);
169
+ this.bFieldMessageVisible$ = this.utils.getBooleanValue(showFieldMessage) && isMessageVisible && !this.bReadonly$;
162
170
 
163
171
  this.label$ = label;
164
172
  this.showAsFormattedText$ = showAsFormattedText;
165
173
  this.displayAs$ = displayAs;
166
- this.helperText = helperText;
174
+ this.helperText = this.bFieldMessageVisible$ ? (messageConfig.content ?? '') : helperText;
167
175
  this.placeholder = placeholder || '';
168
176
  this.displayMode$ = displayMode;
169
177
 
@@ -173,9 +181,6 @@ export class UserReferenceComponent implements OnInit, OnDestroy {
173
181
  this.value$ = value || '';
174
182
  }
175
183
 
176
- const { readOnly, required } = props;
177
- [this.bReadonly$, this.bRequired$] = [readOnly, required].map(prop => prop === true || (typeof prop === 'string' && prop === 'true'));
178
-
179
184
  this.actionsApi = this.pConn$.getActionsApi();
180
185
  this.propName = this.pConn$.getStateProps().value;
181
186
 
@@ -229,7 +234,7 @@ export class UserReferenceComponent implements OnInit, OnDestroy {
229
234
  let key = '';
230
235
  if (event?.target?.value) {
231
236
  const index = this.options$?.findIndex(element => element.value === event.target.value);
232
- key = index > -1 ? (key = this.options$[index].key) : event.target.value;
237
+ key = index > -1 ? this.options$[index].key : event.target.value;
233
238
  }
234
239
  const value = key;
235
240
  handleEvent(this.actionsApi, 'changeNblur', this.propName, value);
@@ -1,7 +1,7 @@
1
1
  <div style="text-align: left" class="psdk-flow-container-top">
2
2
  <div *ngIf="!bShowConfirm">
3
3
  <div *ngIf="!todo_showTodo$">
4
- <h2 *ngIf="!isMultiStep">{{ containerName$ }}</h2>
4
+ <h2 *ngIf="containerName$">{{ containerName$ }}</h2>
5
5
  <div *ngIf="banners.length">
6
6
  <component-mapper name="AlertBanner" [props]="{ banners }"></component-mapper>
7
7
  </div>
@@ -1,4 +1,4 @@
1
- import { Component, OnInit, Input, ChangeDetectorRef, NgZone, forwardRef, OnDestroy, Injector } from '@angular/core';
1
+ import { Component, OnInit, Input, ChangeDetectorRef, forwardRef, OnDestroy, Injector } from '@angular/core';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import { FormBuilder, FormGroup } from '@angular/forms';
4
4
  import { MatCardModule } from '@angular/material/card';
@@ -75,7 +75,6 @@ export class FlowContainerComponent extends FlowContainerBaseComponent implement
75
75
  private cdRef: ChangeDetectorRef,
76
76
  private psService: ProgressSpinnerService,
77
77
  private fb: FormBuilder,
78
- private ngZone: NgZone,
79
78
  private utils: Utils
80
79
  ) {
81
80
  super(injector);
@@ -123,6 +122,7 @@ export class FlowContainerComponent extends FlowContainerBaseComponent implement
123
122
  'clearBannerMessages',
124
123
  () => {
125
124
  this.banners = [];
125
+ this.cdRef.markForCheck();
126
126
  },
127
127
  'clearBannerMessages'
128
128
  );
@@ -188,10 +188,9 @@ export class FlowContainerComponent extends FlowContainerBaseComponent implement
188
188
  }
189
189
 
190
190
  showPageMessages(completeProps: FlowContainerProps) {
191
- this.ngZone.run(() => {
192
- const pageMessages = completeProps.pageMessages;
193
- this.banners = [{ messages: pageMessages?.map(msg => this.localizedVal(msg.message, 'Messages')), variant: 'urgent' }];
194
- });
191
+ const pageMessages = completeProps.pageMessages;
192
+ this.banners = [{ messages: pageMessages?.map(msg => this.localizedVal(msg.message, 'Messages')), variant: 'urgent' }];
193
+ this.cdRef.markForCheck();
195
194
  }
196
195
 
197
196
  getTodoVisibilty() {
@@ -279,12 +278,8 @@ export class FlowContainerComponent extends FlowContainerBaseComponent implement
279
278
 
280
279
  // this.containerName$ = oWorkMeta["name"];
281
280
  if (bLoadChildren && oWorkData) {
282
- this.containerName$ = this.localizedVal(
283
- this.getActiveViewLabel() || oWorkData.caseInfo.assignments?.[0]?.name,
284
- undefined,
285
- this.localeReference
286
- );
287
- this.isMultiStep = this.utils.getBooleanValue(oWorkData.caseInfo.assignments?.[0]?.isMultiStep);
281
+ this.containerName$ = this.localizedVal(this.getActiveViewLabel() || oWorkData.caseInfo.assignments?.[0].name, undefined, this.localeReference);
282
+ this.isMultiStep = this.utils.getBooleanValue(oWorkData.caseInfo.assignments?.[0].isMultiStep);
288
283
  }
289
284
 
290
285
  // turn off spinner
@@ -388,8 +383,7 @@ export class FlowContainerComponent extends FlowContainerBaseComponent implement
388
383
  const { CASE_INFO: CASE_CONSTS } = PCore.getConstants();
389
384
 
390
385
  setTimeout(() => {
391
- this.ngZone.run(() => {
392
- /*
386
+ /*
393
387
  *** renove this commmented out code when React/WC is updated
394
388
  *** this code is replace with the call to "getToDoAssigments" function below
395
389
 
@@ -407,32 +401,32 @@ export class FlowContainerComponent extends FlowContainerBaseComponent implement
407
401
  const caseActions = localPConn.getValue(CASE_CONSTS.CASE_INFO_ACTIONS);
408
402
  */
409
403
 
410
- const todoAssignments = getToDoAssignments(this.pConn$);
404
+ const todoAssignments = getToDoAssignments(this.pConn$);
411
405
 
412
- if (todoAssignments && todoAssignments.length > 0) {
413
- this.todo_caseInfoID$ = this.pConn$.getValue(CASE_CONSTS.CASE_INFO_ID);
414
- this.todo_datasource$ = { source: todoAssignments };
415
- }
406
+ if (todoAssignments && todoAssignments.length > 0) {
407
+ this.todo_caseInfoID$ = this.pConn$.getValue(CASE_CONSTS.CASE_INFO_ID);
408
+ this.todo_datasource$ = { source: todoAssignments };
409
+ }
416
410
 
417
- /* remove this commented out code when update React/WC */
418
- // let kid = this.pConn$.getChildren()[0];
411
+ /* remove this commented out code when update React/WC */
412
+ // let kid = this.pConn$.getChildren()[0];
419
413
 
420
- // kid.getPConnect() can be a Reference component. So normalize it just in case
421
- // let todoKid = kid.getPConnect().getChildren()[0];
414
+ // kid.getPConnect() can be a Reference component. So normalize it just in case
415
+ // let todoKid = kid.getPConnect().getChildren()[0];
422
416
 
423
- // this.todo_pConn$ = todoKid.getPConnect();
417
+ // this.todo_pConn$ = todoKid.getPConnect();
424
418
 
425
- /* code change here to note for React/WC */
426
- // todo now needs pConn to open the work item on click "go"
427
- this.todo_pConn$ = this.pConn$;
419
+ /* code change here to note for React/WC */
420
+ // todo now needs pConn to open the work item on click "go"
421
+ this.todo_pConn$ = this.pConn$;
428
422
 
429
- // still needs the context of the original work item
430
- this.todo_context$ = localPConn.getContextName();
423
+ // still needs the context of the original work item
424
+ this.todo_context$ = localPConn.getContextName();
431
425
 
432
- this.todo_showTodo$ = true;
426
+ this.todo_showTodo$ = true;
433
427
 
434
- this.psService.sendMessage(false);
435
- });
428
+ this.psService.sendMessage(false);
429
+ this.cdRef.markForCheck();
436
430
  });
437
431
  }
438
432
 
@@ -537,24 +531,19 @@ export class FlowContainerComponent extends FlowContainerBaseComponent implement
537
531
  const normalizedConfigObjectAsPConnect = normalizedConfigObject.getComponent();
538
532
 
539
533
  // makes sure Angular tracks these changes
540
- this.ngZone.run(() => {
541
- this.buildName$ = this.getBuildName();
542
- // what comes back now in configObject is the children of the flowContainer
534
+ this.buildName$ = this.getBuildName();
535
+ // what comes back now in configObject is the children of the flowContainer
543
536
 
544
- this.arChildren$ = [];
545
- this.arChildren$.push(normalizedConfigObjectAsPConnect);
537
+ this.arChildren$ = [];
538
+ this.arChildren$.push(normalizedConfigObjectAsPConnect);
546
539
 
547
- this.psService.sendMessage(false);
540
+ this.psService.sendMessage(false);
548
541
 
549
- const oWorkItem = configObject.getPConnect();
550
- const oWorkData: any = oWorkItem.getDataObject();
542
+ const oWorkItem = configObject.getPConnect();
543
+ const oWorkData: any = oWorkItem.getDataObject();
551
544
 
552
- this.containerName$ = this.localizedVal(
553
- this.getActiveViewLabel() || oWorkData.caseInfo.assignments?.[0]?.name,
554
- undefined,
555
- this.localeReference
556
- );
557
- });
545
+ this.containerName$ = this.localizedVal(this.getActiveViewLabel() || oWorkData.caseInfo.assignments?.[0]?.name, undefined, this.localeReference);
546
+ this.cdRef.markForCheck();
558
547
  }
559
548
 
560
549
  getBuildName(): string {
@@ -0,0 +1,12 @@
1
+ <mat-grid-list cols="2" rowHeight="4.25rem">
2
+ <mat-grid-tile>
3
+ <button mat-raised-button variant="contained" color="secondary" [disabled]="bDisabled$" (click)="onCancel()">
4
+ {{ localizedVal('Cancel', localeCategory) }}
5
+ </button>
6
+ </mat-grid-tile>
7
+ <mat-grid-tile>
8
+ <button mat-raised-button variant="contained" color="primary" [disabled]="bDisabled$" (click)="onSubmit()">
9
+ {{ localizedVal(primaryLabel$, localeCategory) }}
10
+ </button>
11
+ </mat-grid-tile>
12
+ </mat-grid-list>
@@ -0,0 +1,130 @@
1
+ import { provideZonelessChangeDetection } from '@angular/core';
2
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
3
+
4
+ import { DataViewActionButtonsComponent } from './data-view-action-buttons.component';
5
+
6
+ const RESOURCE_STATUS = { CREATE: 'CREATE', UPDATE: 'UPDATE', OPEN_FLOW_ACTION: 'OPEN_FLOW_ACTION' };
7
+ const DATA_EVENTS = { DATA_OBJECT_CREATED: 'DataObjectCreated', DATA_OBJECT_UPDATED: 'DataObjectUpdated' };
8
+
9
+ describe('DataViewActionButtonsComponent', () => {
10
+ let component: DataViewActionButtonsComponent;
11
+ let fixture: ComponentFixture<DataViewActionButtonsComponent>;
12
+ let actionsApi: {
13
+ createDataObject: jasmine.Spy;
14
+ updateDataObject: jasmine.Spy;
15
+ submitDataObjectAction: jasmine.Spy;
16
+ cancelDataObject: jasmine.Spy;
17
+ };
18
+ let publishSpy: jasmine.Spy;
19
+
20
+ beforeEach(async () => {
21
+ publishSpy = jasmine.createSpy('publish');
22
+ // PCore is read in a field initializer, so it must exist before the component is created.
23
+ (globalThis as any).PCore = {
24
+ getLocaleUtils: () => ({ getLocaleValue: (value: string) => value }),
25
+ getConstants: () => ({ RESOURCE_STATUS, PUB_SUB_EVENTS: { DATA_EVENTS } }),
26
+ getPubSubUtils: () => ({ publish: publishSpy })
27
+ };
28
+
29
+ await TestBed.configureTestingModule({
30
+ imports: [DataViewActionButtonsComponent],
31
+ providers: [provideZonelessChangeDetection()]
32
+ }).compileComponents();
33
+
34
+ fixture = TestBed.createComponent(DataViewActionButtonsComponent);
35
+ component = fixture.componentInstance;
36
+
37
+ actionsApi = {
38
+ createDataObject: jasmine.createSpy('createDataObject').and.returnValue(Promise.resolve()),
39
+ updateDataObject: jasmine.createSpy('updateDataObject').and.returnValue(Promise.resolve()),
40
+ submitDataObjectAction: jasmine.createSpy('submitDataObjectAction').and.returnValue(Promise.resolve()),
41
+ cancelDataObject: jasmine.createSpy('cancelDataObject').and.returnValue({})
42
+ };
43
+
44
+ component.pConn$ = { getActionsApi: () => actionsApi } as any;
45
+ component.context$ = 'app/modal_1';
46
+ component.dataRecordKeys$ = '{"pyGUID":"abc-123"}';
47
+ component.classID$ = 'My-Data-Class';
48
+ });
49
+
50
+ it('should create', () => {
51
+ expect(component).toBeTruthy();
52
+ });
53
+
54
+ it('labels the primary button "Update" when editing an existing record', () => {
55
+ component.dataObjectAction$ = RESOURCE_STATUS.UPDATE;
56
+ expect(component.primaryLabel$).toBe('Update');
57
+ });
58
+
59
+ it('labels the primary button "Submit" when creating a record', () => {
60
+ component.dataObjectAction$ = RESOURCE_STATUS.CREATE;
61
+ expect(component.primaryLabel$).toBe('Submit');
62
+ });
63
+
64
+ it('labels the primary button "Submit" for a record action', () => {
65
+ component.dataObjectAction$ = RESOURCE_STATUS.OPEN_FLOW_ACTION;
66
+ expect(component.primaryLabel$).toBe('Submit');
67
+ });
68
+
69
+ it('creates the record and publishes the created event', async () => {
70
+ component.dataObjectAction$ = RESOURCE_STATUS.CREATE;
71
+
72
+ component.onSubmit();
73
+ await fixture.whenStable();
74
+
75
+ expect(actionsApi.createDataObject).toHaveBeenCalledWith('app/modal_1');
76
+ expect(publishSpy).toHaveBeenCalledWith(DATA_EVENTS.DATA_OBJECT_CREATED, { classId: 'My-Data-Class', data: undefined });
77
+ });
78
+
79
+ it('parses the serialised record keys before updating', async () => {
80
+ component.dataObjectAction$ = RESOURCE_STATUS.UPDATE;
81
+
82
+ component.onSubmit();
83
+ await fixture.whenStable();
84
+
85
+ expect(actionsApi.updateDataObject).toHaveBeenCalledWith('app/modal_1', { pyGUID: 'abc-123' });
86
+ expect(publishSpy).toHaveBeenCalledWith(DATA_EVENTS.DATA_OBJECT_UPDATED, { classId: 'My-Data-Class' });
87
+ });
88
+
89
+ it('submits a record action with its parsed keys and action ID', async () => {
90
+ component.dataObjectAction$ = RESOURCE_STATUS.OPEN_FLOW_ACTION;
91
+ component.actionID$ = 'updateCVV';
92
+
93
+ component.onSubmit();
94
+ await fixture.whenStable();
95
+
96
+ expect(actionsApi.submitDataObjectAction).toHaveBeenCalledWith('app/modal_1', { pyGUID: 'abc-123' }, 'updateCVV');
97
+ });
98
+
99
+ it('disables both buttons while a save is in flight', () => {
100
+ let resolveSave: () => void = () => {};
101
+ actionsApi.createDataObject.and.returnValue(
102
+ new Promise<void>(resolve => {
103
+ resolveSave = resolve;
104
+ })
105
+ );
106
+ component.dataObjectAction$ = RESOURCE_STATUS.CREATE;
107
+
108
+ component.onSubmit();
109
+
110
+ expect(component.bDisabled$).toBeTrue();
111
+ resolveSave();
112
+ });
113
+
114
+ it('re-enables the buttons and publishes nothing when the save is rejected', async () => {
115
+ actionsApi.createDataObject.and.returnValue(Promise.reject(new Error('rejected')));
116
+ component.dataObjectAction$ = RESOURCE_STATUS.CREATE;
117
+
118
+ component.onSubmit();
119
+ await fixture.whenStable();
120
+
121
+ expect(publishSpy).not.toHaveBeenCalled();
122
+ expect(component.bDisabled$).toBeFalse();
123
+ });
124
+
125
+ it('discards the edit through the engine on cancel', () => {
126
+ component.onCancel();
127
+
128
+ expect(actionsApi.cancelDataObject).toHaveBeenCalledWith('app/modal_1');
129
+ });
130
+ });
@@ -0,0 +1,77 @@
1
+ import { CommonModule } from '@angular/common';
2
+ import { ChangeDetectorRef, Component, Input } from '@angular/core';
3
+ import { MatButtonModule } from '@angular/material/button';
4
+ import { MatGridListModule } from '@angular/material/grid-list';
5
+
6
+ @Component({
7
+ selector: 'app-data-view-action-buttons',
8
+ templateUrl: './data-view-action-buttons.component.html',
9
+ imports: [CommonModule, MatGridListModule, MatButtonModule]
10
+ })
11
+ export class DataViewActionButtonsComponent {
12
+ @Input() pConn$: typeof PConnect;
13
+ @Input() context$: string;
14
+ @Input() dataObjectAction$: string;
15
+ @Input() actionID$ = '';
16
+ // The container item publishes the record keys JSON-serialised.
17
+ @Input() dataRecordKeys$ = '';
18
+ @Input() classID$ = '';
19
+
20
+ localizedVal = PCore.getLocaleUtils().getLocaleValue;
21
+ localeCategory = 'Data Object';
22
+ bDisabled$ = false;
23
+
24
+ constructor(private cdRef: ChangeDetectorRef) {}
25
+
26
+ get primaryLabel$(): string {
27
+ return this.dataObjectAction$ === PCore.getConstants().RESOURCE_STATUS.UPDATE ? 'Update' : 'Submit';
28
+ }
29
+
30
+ onCancel() {
31
+ // The engine drops the container item, which collapses the modal.
32
+ this.pConn$.getActionsApi().cancelDataObject(this.context$);
33
+ }
34
+
35
+ onSubmit() {
36
+ const { RESOURCE_STATUS, PUB_SUB_EVENTS } = PCore.getConstants();
37
+ const { DATA_OBJECT_CREATED, DATA_OBJECT_UPDATED } = PUB_SUB_EVENTS.DATA_EVENTS;
38
+ const actionsApi = this.pConn$.getActionsApi();
39
+ const { publish } = PCore.getPubSubUtils();
40
+
41
+ let submitAction: Promise<any>;
42
+ let publishCompleted: (data?: any) => void;
43
+
44
+ switch (this.dataObjectAction$) {
45
+ case RESOURCE_STATUS.UPDATE:
46
+ submitAction = actionsApi.updateDataObject(this.context$, this.getRecordKeys());
47
+ publishCompleted = () => publish(DATA_OBJECT_UPDATED, { classId: this.classID$ });
48
+ break;
49
+ case RESOURCE_STATUS.OPEN_FLOW_ACTION:
50
+ submitAction = actionsApi.submitDataObjectAction(this.context$, this.getRecordKeys(), this.actionID$);
51
+ publishCompleted = () => publish(DATA_OBJECT_UPDATED, { classId: this.classID$, actionID: this.actionID$ });
52
+ break;
53
+ default:
54
+ submitAction = actionsApi.createDataObject(this.context$);
55
+ publishCompleted = data => publish(DATA_OBJECT_CREATED, { classId: this.classID$, data });
56
+ }
57
+
58
+ this.bDisabled$ = true;
59
+
60
+ submitAction
61
+ .then(data => {
62
+ publishCompleted(data);
63
+ })
64
+ .catch(() => {
65
+ // Keep the modal open so the server error banner is visible and the edit can be corrected.
66
+ })
67
+ .finally(() => {
68
+ this.bDisabled$ = false;
69
+ // Change detection is zoneless, so this async reset needs to be flagged explicitly.
70
+ this.cdRef.markForCheck();
71
+ });
72
+ }
73
+
74
+ private getRecordKeys(): Record<string, any> {
75
+ return this.dataRecordKeys$ ? JSON.parse(this.dataRecordKeys$) : {};
76
+ }
77
+ }
@@ -1,15 +1,36 @@
1
- <div id="dialog" *ngIf="bShowModal$" class="psdk-dialog-background">
2
- <div class="psdk-modal-view-container-top" id="{{ buildName$ }}">
3
- <h3 *ngIf="title$ != ''">{{ title$ }}</h3>
1
+ <div *ngFor="let modal of modalStack; trackBy: trackByModalKey" id="dialog" class="psdk-dialog-background">
2
+ <div class="psdk-modal-view-container-top" id="{{ modal.key }}">
3
+ <h3 *ngIf="modal.title != ''">{{ modal.title }}</h3>
4
4
  <component-mapper
5
5
  name="Assignment"
6
- [props]="{ pConn$: createdViewPConn$, formGroup$, arChildren$, itemKey$, isCreateStage$: true, updateToken$, isInModal$: true, banners }"
6
+ [props]="{
7
+ pConn$: modal.createdViewPConn$,
8
+ formGroup$,
9
+ arChildren$: modal.arChildren$,
10
+ itemKey$: modal.key,
11
+ isCreateStage$: true,
12
+ updateToken$: modal.updateToken,
13
+ isInModal$: true
14
+ }"
7
15
  ></component-mapper>
8
- <div *ngIf="isMultiRecord">
16
+ <div *ngIf="modal.isMultiRecordData">
9
17
  <component-mapper
10
18
  name="ListViewActionButtons"
11
- [props]="{ pConn$: createdViewPConn$, context$ }"
12
- [outputEvents]="{ closeActionsDialog: closeActionsDialog }"
19
+ [props]="{ pConn$: modal.createdViewPConn$, context$: modal.context }"
20
+ [outputEvents]="{ closeActionsDialog: closeModalFor(modal.key) }"
21
+ ></component-mapper>
22
+ </div>
23
+ <div *ngIf="modal.isDataObjectModal">
24
+ <component-mapper
25
+ name="DataViewActionButtons"
26
+ [props]="{
27
+ pConn$: modal.createdViewPConn$,
28
+ context$: modal.context,
29
+ classID$: modal.dataObjectClassId,
30
+ dataObjectAction$: modal.dataObjectAction,
31
+ dataRecordKeys$: modal.dataRecordKeys,
32
+ actionID$: modal.dataObjectActionID
33
+ }"
13
34
  ></component-mapper>
14
35
  </div>
15
36
  </div>
@@ -18,7 +39,13 @@
18
39
  <div *ngIf="bShowCancelAlert$">
19
40
  <component-mapper
20
41
  name="CancelAlert"
21
- [props]="{ bShowAlert$: bShowCancelAlert$, pConn$: cancelPConn$ }"
42
+ [props]="{
43
+ bShowAlert$: bShowCancelAlert$,
44
+ pConn$: cancelPConn$,
45
+ hideDelete: cancelHideDelete$,
46
+ isDataObject: cancelIsDataObject$,
47
+ skipReleaseLockRequest: cancelSkipReleaseLockRequest$
48
+ }"
22
49
  [parent]="this"
23
50
  [outputEvents]="{ onAlertState$: onAlertState }"
24
51
  ></component-mapper>