@pega/angular-sdk-overrides 0.26.1 → 0.26.3

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 (133) 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 +12 -3
  6. package/lib/designSystemExtension/operator/operator.component.ts +2 -1
  7. package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.html +104 -10
  8. package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.scss +141 -1
  9. package/lib/designSystemExtension/rich-text-editor/rich-text-editor.component.ts +224 -72
  10. package/lib/field/auto-complete/auto-complete.component.html +46 -5
  11. package/lib/field/auto-complete/auto-complete.component.scss +92 -0
  12. package/lib/field/auto-complete/auto-complete.component.ts +349 -22
  13. package/lib/field/cancel-alert/cancel-alert.component.ts +38 -15
  14. package/lib/field/check-box/check-box.component.html +3 -1
  15. package/lib/field/check-box/check-box.component.ts +10 -1
  16. package/lib/field/currency/currency.component.html +2 -1
  17. package/lib/field/currency/currency.component.ts +10 -1
  18. package/lib/field/date/date.component.html +2 -1
  19. package/lib/field/date/date.component.ts +2 -0
  20. package/lib/field/date-time/date-time.component.html +2 -1
  21. package/lib/field/date-time/date-time.component.scss +46 -40
  22. package/lib/field/date-time/date-time.component.ts +2 -0
  23. package/lib/field/decimal/decimal.component.html +2 -1
  24. package/lib/field/decimal/decimal.component.ts +2 -0
  25. package/lib/field/dropdown/dropdown.component.html +2 -1
  26. package/lib/field/dropdown/dropdown.component.ts +10 -1
  27. package/lib/field/email/email.component.html +2 -1
  28. package/lib/field/email/email.component.ts +2 -2
  29. package/lib/field/field.base.ts +27 -1
  30. package/lib/field/group/group.component.html +1 -1
  31. package/lib/field/group/group.component.ts +1 -3
  32. package/lib/field/integer/integer.component.html +2 -1
  33. package/lib/field/integer/integer.component.ts +4 -2
  34. package/lib/field/location/location.component.html +2 -1
  35. package/lib/field/location/location.component.ts +3 -1
  36. package/lib/field/object-reference/object-reference.component.html +1 -1
  37. package/lib/field/object-reference/object-reference.component.ts +3 -0
  38. package/lib/field/percentage/percentage.component.html +2 -1
  39. package/lib/field/percentage/percentage.component.ts +10 -1
  40. package/lib/field/phone/phone.component.html +2 -1
  41. package/lib/field/phone/phone.component.ts +2 -1
  42. package/lib/field/radio-buttons/radio-buttons.component.html +2 -1
  43. package/lib/field/radio-buttons/radio-buttons.component.ts +10 -1
  44. package/lib/field/rich-text/rich-text.component.scss +1 -166
  45. package/lib/field/rich-text/rich-text.component.ts +1 -1
  46. package/lib/field/semantic-link/semantic-link.component.html +1 -1
  47. package/lib/field/semantic-link/semantic-link.component.scss +4 -0
  48. package/lib/field/semantic-link/semantic-link.component.ts +9 -5
  49. package/lib/field/text-area/text-area.component.html +2 -1
  50. package/lib/field/text-area/text-area.component.ts +2 -1
  51. package/lib/field/text-input/text-input.component.html +2 -1
  52. package/lib/field/text-input/text-input.component.ts +2 -1
  53. package/lib/field/time/time.component.html +2 -1
  54. package/lib/field/time/time.component.ts +2 -1
  55. package/lib/field/url/url.component.html +2 -1
  56. package/lib/field/url/url.component.ts +2 -1
  57. package/lib/field/user-reference/user-reference.component.html +4 -2
  58. package/lib/field/user-reference/user-reference.component.ts +10 -5
  59. package/lib/infra/Containers/flow-container/flow-container.component.html +1 -1
  60. package/lib/infra/Containers/flow-container/flow-container.component.ts +33 -40
  61. package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.html +12 -0
  62. package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.spec.ts +130 -0
  63. package/lib/infra/Containers/modal-view-container/data-view-action-buttons/data-view-action-buttons.component.ts +77 -0
  64. package/lib/infra/Containers/modal-view-container/modal-view-container.component.html +35 -8
  65. package/lib/infra/Containers/modal-view-container/modal-view-container.component.spec.ts +24 -11
  66. package/lib/infra/Containers/modal-view-container/modal-view-container.component.ts +185 -194
  67. package/lib/infra/Containers/view-container/view-container.component.ts +32 -31
  68. package/lib/infra/assignment/assignment.component.html +2 -1
  69. package/lib/infra/assignment/assignment.component.ts +30 -13
  70. package/lib/infra/defer-load/defer-load.component.ts +6 -3
  71. package/lib/infra/multi-step/multi-step.component.html +25 -33
  72. package/lib/infra/multi-step/multi-step.component.scss +48 -0
  73. package/lib/infra/multi-step/multi-step.component.ts +11 -0
  74. package/lib/infra/navbar/navbar.component.ts +33 -35
  75. package/lib/infra/root-container/root-container.component.ts +33 -38
  76. package/lib/infra/view/view.component.ts +17 -39
  77. package/lib/template/app-shell/app-shell.component.ts +12 -11
  78. package/lib/template/base/details-template-base.ts +35 -53
  79. package/lib/template/case-summary/case-summary.component.ts +15 -1
  80. package/lib/template/case-view/case-view.component.html +13 -1
  81. package/lib/template/case-view/case-view.component.spec.ts +119 -10
  82. package/lib/template/case-view/case-view.component.ts +52 -3
  83. package/lib/template/data-reference/data-reference.component.ts +27 -18
  84. package/lib/template/data-reference/search-form/search-form.component.html +1 -1
  85. package/lib/template/data-reference/search-form/search-form.component.ts +2 -0
  86. package/lib/template/default-form/default-form.component.html +19 -3
  87. package/lib/template/default-form/default-form.component.ts +13 -3
  88. package/lib/template/details/details.component.html +23 -3
  89. package/lib/template/details/details.component.scss +9 -2
  90. package/lib/template/details/details.component.ts +3 -32
  91. package/lib/template/details-narrow-wide/details-narrow-wide.component.html +21 -2
  92. package/lib/template/details-narrow-wide/details-narrow-wide.component.scss +8 -1
  93. package/lib/template/details-narrow-wide/details-narrow-wide.component.ts +3 -37
  94. package/lib/template/details-one-column/details-one-column.component.html +23 -1
  95. package/lib/template/details-one-column/details-one-column.component.scss +14 -0
  96. package/lib/template/details-one-column/details-one-column.component.ts +3 -38
  97. package/lib/template/details-three-column/details-three-column.component.html +21 -2
  98. package/lib/template/details-three-column/details-three-column.component.scss +8 -1
  99. package/lib/template/details-three-column/details-three-column.component.ts +3 -46
  100. package/lib/template/details-two-column/details-two-column.component.html +21 -2
  101. package/lib/template/details-two-column/details-two-column.component.scss +8 -1
  102. package/lib/template/details-two-column/details-two-column.component.ts +3 -44
  103. package/lib/template/details-wide-narrow/details-wide-narrow.component.html +21 -2
  104. package/lib/template/details-wide-narrow/details-wide-narrow.component.scss +8 -1
  105. package/lib/template/details-wide-narrow/details-wide-narrow.component.ts +3 -41
  106. package/lib/template/field-value-list/field-value-list.component.scss +17 -0
  107. package/lib/template/list-view/list-view.component.ts +39 -0
  108. package/lib/template/self-service-case-view/self-service-case-view.component.html +13 -1
  109. package/lib/template/self-service-case-view/self-service-case-view.component.spec.ts +96 -11
  110. package/lib/template/self-service-case-view/self-service-case-view.component.ts +52 -3
  111. package/lib/template/simple-table-manual/helpers.ts +30 -9
  112. package/lib/template/simple-table-manual/simple-table-manual.component.html +30 -14
  113. package/lib/template/simple-table-manual/simple-table-manual.component.scss +4 -0
  114. package/lib/template/simple-table-manual/simple-table-manual.component.ts +118 -29
  115. package/lib/template/single-reference-readonly/single-reference-readonly.component.html +1 -1
  116. package/lib/template/single-reference-readonly/single-reference-readonly.component.scss +2 -2
  117. package/lib/template/single-reference-readonly/single-reference-readonly.component.ts +4 -4
  118. package/lib/template/wss-nav-bar/wss-nav-bar.component.ts +18 -20
  119. package/lib/widget/attachment/AttachmentUtils.ts +18 -10
  120. package/lib/widget/attachment/attachment.component.html +16 -6
  121. package/lib/widget/attachment/attachment.component.ts +12 -2
  122. package/lib/widget/case-history/case-history.component.ts +6 -2
  123. package/lib/widget/file-utility/file-utility.component.ts +25 -29
  124. package/lib/widget/todo/todo.component.ts +7 -2
  125. package/package.json +1 -1
  126. package/lib/designSystemExtension/material-details/material-details.component.html +0 -40
  127. package/lib/designSystemExtension/material-details/material-details.component.scss +0 -116
  128. package/lib/designSystemExtension/material-details/material-details.component.spec.ts +0 -24
  129. package/lib/designSystemExtension/material-details/material-details.component.ts +0 -31
  130. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.html +0 -41
  131. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.scss +0 -34
  132. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.spec.ts +0 -22
  133. package/lib/designSystemExtension/material-details-fields/material-details-fields.component.ts +0 -61
@@ -11,7 +11,10 @@ export class DetailsTemplateBase implements OnInit, OnDestroy {
11
11
  protected angularPConnectData: AngularPConnectData = {};
12
12
  protected angularPConnect;
13
13
 
14
- childrenMetadataOld;
14
+ children: any[] = [];
15
+ propsToUse: any = {};
16
+ showHighlightedData: boolean;
17
+ highlightedDataArr: any = [];
15
18
 
16
19
  constructor(injector: Injector) {
17
20
  this.angularPConnect = injector.get(AngularPConnectService);
@@ -39,23 +42,13 @@ export class DetailsTemplateBase implements OnInit, OnDestroy {
39
42
  const bUpdateSelf = this.angularPConnect.shouldComponentUpdate(this);
40
43
 
41
44
  // Only call updateSelf when the component should update
42
- if (bUpdateSelf || this.hasRawMetadataChanged()) {
45
+ if (bUpdateSelf) {
43
46
  this.updateSelf();
44
47
  }
45
48
  }
46
49
 
47
- // this method will get overriden by the child component
48
- updateSelf() {}
49
-
50
- hasRawMetadataChanged(): boolean {
51
- const newChildrenMetadata = this.fetchChildrenMetadata();
52
-
53
- if (!PCore.isDeepEqual(newChildrenMetadata, this.childrenMetadataOld)) {
54
- this.childrenMetadataOld = newChildrenMetadata;
55
- return true;
56
- }
57
-
58
- return false;
50
+ updateSelf() {
51
+ this.updateDetailsProps();
59
52
  }
60
53
 
61
54
  fetchChildrenMetadata() {
@@ -67,46 +60,35 @@ export class DetailsTemplateBase implements OnInit, OnDestroy {
67
60
  });
68
61
  }
69
62
 
70
- processDetailFields(kid: any): any[] {
71
- const pKid = kid.getPConnect();
72
- const fields = pKid.getChildren();
73
- const processedFields: any[] = [];
74
-
75
- fields?.forEach(field => {
76
- const thePConn = field.getPConnect();
77
- const theCompType = thePConn.getComponentName().toLowerCase();
78
- if (theCompType === 'reference' || theCompType === 'group') {
79
- const configProps = thePConn.getConfigProps();
80
- configProps.readOnly = true;
81
- configProps.displayMode = 'DISPLAY_ONLY';
82
- const propToUse = { ...thePConn.getInheritedProps() };
83
- configProps.label = propToUse?.label;
84
- const options = {
85
- context: thePConn.getContextName(),
86
- pageReference: thePConn.getPageReference(),
87
- referenceList: thePConn.getReferenceList()
88
- };
89
- const viewContConfig = {
90
- meta: {
91
- ...thePConn.getMetadata(),
92
- type: theCompType,
93
- config: configProps
94
- },
95
- options
96
- };
97
- const theViewCont = PCore.createPConnect(viewContConfig);
98
- processedFields.push({
99
- type: theCompType,
100
- pConn: theViewCont?.getPConnect()
101
- });
102
- } else {
103
- processedFields.push({
104
- type: theCompType,
105
- config: thePConn.getConfigProps()
106
- });
63
+ updateDetailsProps() {
64
+ const { label, showLabel, showHighlightedData } = this.pConn$.resolveConfigProps(this.pConn$.getConfigProps());
65
+ this.propsToUse = { label, showLabel, ...this.pConn$.getInheritedProps() };
66
+ this.showHighlightedData = showHighlightedData;
67
+
68
+ this.pConn$.setInheritedProp('displayMode', 'DISPLAY_ONLY');
69
+ this.pConn$.setInheritedProp('readOnly', true);
70
+
71
+ this.children = this.pConn$.getChildren() as any[];
72
+
73
+ // Process highlighted fields for display in the highlighted section
74
+ this.highlightedDataArr = this.processHighlightedFields();
75
+ }
76
+
77
+ processHighlightedFields(): any[] {
78
+ if (!this.showHighlightedData) return [];
79
+
80
+ const { highlightedData = [] } = (this.pConn$.getRawMetadata() as any).config;
81
+
82
+ return (highlightedData ?? []).map(field => {
83
+ field.config.displayMode = 'STACKED_LARGE_VAL';
84
+ field.config.readOnly = true;
85
+
86
+ if (field.config.value === '@P .pyStatusWork') {
87
+ field.type = 'TextInput';
88
+ field.config.displayAsStatus = true;
107
89
  }
108
- });
109
90
 
110
- return processedFields;
91
+ return this.pConn$.createComponent(field, '', 0, {}).getPConnect();
92
+ });
111
93
  }
112
94
  }
@@ -3,6 +3,7 @@ import { FormGroup } from '@angular/forms';
3
3
  import { AngularPConnectData, AngularPConnectService } from '@pega/angular-sdk-components';
4
4
  import { ComponentMapperComponent } from '@pega/angular-sdk-components';
5
5
  import { Utils } from '@pega/angular-sdk-components';
6
+ import { getAllFields } from '@pega/angular-sdk-components';
6
7
 
7
8
  interface CaseSummaryProps {
8
9
  // If any, enter additional props that only exist on this component
@@ -73,7 +74,20 @@ export class CaseSummaryComponent implements OnInit, OnDestroy, OnChanges {
73
74
  }
74
75
 
75
76
  updateSelf() {
77
+ this.configProps$ = this.pConn$.resolveConfigProps(this.pConn$.getConfigProps()) as CaseSummaryProps;
76
78
  this.generatePrimaryAndSecondaryFields();
79
+ this.status$ = this.configProps$.status;
80
+ this.bShowStatus$ = this.configProps$.showStatus;
81
+ }
82
+
83
+ additionalProps() {
84
+ const allFields = getAllFields(this.pConn$);
85
+ const fields = {
86
+ primaryFields: allFields[0],
87
+ secondaryFields: allFields[1]
88
+ };
89
+
90
+ return this.pConn$.resolveConfigProps(fields);
77
91
  }
78
92
 
79
93
  ngOnChanges() {
@@ -86,7 +100,7 @@ export class CaseSummaryComponent implements OnInit, OnDestroy, OnChanges {
86
100
 
87
101
  for (const oField of this.arChildren$[0].getPConnect().getChildren()) {
88
102
  const kid = oField.getPConnect();
89
- this.primaryFields$.push(kid.resolveConfigProps(kid.getRawMetadata()));
103
+ this.primaryFields$.push({ ...kid.resolveConfigProps(kid.getRawMetadata()), kid });
90
104
  }
91
105
 
92
106
  const secondarySummaryFields = this.prepareCaseSummaryData(this.arChildren$[1].getPConnect());
@@ -17,7 +17,9 @@
17
17
 
18
18
  <div class="psdk-case-view-buttons">
19
19
  <button *ngIf="editAction" mat-raised-button color="secondary" (click)="_editClick()">{{ localizedVal('Edit', localeCategory) }}</button>
20
- <button mat-raised-button color="secondary" [matMenuTriggerFor]="actionMenu">{{ localizedVal('Actions...', localeCategory) }}</button>
20
+ <button mat-raised-button color="secondary" [matMenuTriggerFor]="actionMenu" [disabled]="bActionsMenuDisabled$">
21
+ {{ localizedVal('Actions...', localeCategory) }}
22
+ </button>
21
23
  <mat-menu #actionMenu="matMenu" overlapTrigger="false">
22
24
  <ng-container *ngFor="let action of arAvailableActions$">
23
25
  <button mat-menu-item (click)="_menuActionClick(action)">
@@ -29,6 +31,16 @@
29
31
  {{ process.name }}
30
32
  </button>
31
33
  </ng-container>
34
+ <ng-container *ngFor="let action of arDataObjectActions$">
35
+ <button mat-menu-item (click)="_menuDataObjectActionClick(action)">
36
+ {{ localizedVal(action.name, '', localeKey) }}
37
+ </button>
38
+ </ng-container>
39
+ <ng-container *ngFor="let action of arCreateCaseActions$">
40
+ <button mat-menu-item (click)="_menuCreateCaseActionClick(action)">
41
+ {{ localizedVal(action.name, '', localeKey) }}
42
+ </button>
43
+ </ng-container>
32
44
  </mat-menu>
33
45
  </div>
34
46
 
@@ -1,24 +1,133 @@
1
- import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
1
+ import { ChangeDetectorRef } from '@angular/core';
2
2
 
3
3
  import { CaseViewComponent } from './case-view.component';
4
4
 
5
5
  describe('CaseViewComponent', () => {
6
6
  let component: CaseViewComponent;
7
- let fixture: ComponentFixture<CaseViewComponent>;
7
+ let actionsApi: { openDataObjectAction: jasmine.Spy; createWork: jasmine.Spy };
8
+ let dataInfoActions: any;
9
+ let dataRecord: any;
10
+ let caseInfo: any;
8
11
 
9
- beforeEach(waitForAsync(() => {
10
- TestBed.configureTestingModule({
11
- declarations: [CaseViewComponent]
12
- }).compileComponents();
13
- }));
12
+ const tabsChild = {
13
+ getPConnect: () => ({
14
+ getRawMetadata: () => ({ type: 'region', name: 'Tabs' }),
15
+ getChildren: () => []
16
+ })
17
+ };
14
18
 
15
19
  beforeEach(() => {
16
- fixture = TestBed.createComponent(CaseViewComponent);
17
- component = fixture.componentInstance;
18
- fixture.detectChanges();
20
+ (globalThis as any).PCore = {
21
+ getLocaleUtils: () => ({ getLocaleValue: (value: string) => value })
22
+ };
23
+
24
+ actionsApi = {
25
+ openDataObjectAction: jasmine.createSpy('openDataObjectAction'),
26
+ createWork: jasmine.createSpy('createWork')
27
+ };
28
+ dataInfoActions = undefined;
29
+ dataRecord = { PlanID: 'P-1', CustomerID: 'C-9' };
30
+ caseInfo = { ID: 'C-1', availableActions: [], availableProcesses: [] };
31
+
32
+ const cdRef = { detectChanges: () => {}, markForCheck: () => {} } as unknown as ChangeDetectorRef;
33
+ const utils = { getImageSrc: () => '', getSDKStaticContentUrl: () => '' } as any;
34
+
35
+ component = new CaseViewComponent(cdRef, {} as any, utils);
36
+ component.pConn$ = {
37
+ resolveConfigProps: (props: any) => props,
38
+ getConfigProps: () => ({ icon: 'case', header: 'Header', subheader: 'ID-1' }),
39
+ getCaseInfo: () => ({ getClassName: () => 'My-Class', getName: () => 'My Case' }),
40
+ getChildren: () => [tabsChild],
41
+ getLocalizationService: () => ({ getLocalizedText: (value: string) => value }),
42
+ getDataObject: () => ({ caseInfo }),
43
+ getValue: (prop: string, context?: string) => {
44
+ if (prop === '.actions' && context === 'dataInfo') return dataInfoActions;
45
+ if (prop === '.content' && context === 'dataInfo') return dataRecord;
46
+ if (prop === '.classID' && context === 'dataInfo.content') return 'My-Data-Class';
47
+ if (context === '') return dataRecord[prop.slice(1)];
48
+ return undefined;
49
+ },
50
+ getActionsApi: () => actionsApi
51
+ } as any;
52
+ component.localizedVal = (value: string) => value;
19
53
  });
20
54
 
21
55
  it('should create', () => {
22
56
  expect(component).toBeTruthy();
23
57
  });
58
+
59
+ it('lists data object actions published under dataInfo', () => {
60
+ dataInfoActions = {
61
+ availableActions: [{ ID: 'Edit', name: 'Edit' }],
62
+ availableCreateCaseActions: [{ ID: 'My-Work-Class', name: 'Request Plan Change' }]
63
+ };
64
+
65
+ component.fullUpdate();
66
+
67
+ expect(component.arDataObjectActions$.length).toBe(1);
68
+ expect(component.arCreateCaseActions$.length).toBe(1);
69
+ });
70
+
71
+ it('falls back to empty arrays when the dataInfo context is absent', () => {
72
+ dataInfoActions = undefined;
73
+
74
+ component.fullUpdate();
75
+
76
+ expect(component.arDataObjectActions$).toEqual([]);
77
+ expect(component.arCreateCaseActions$).toEqual([]);
78
+ });
79
+
80
+ it('disables the actions menu when no action of any kind is available', () => {
81
+ component.fullUpdate();
82
+
83
+ expect(component.bActionsMenuDisabled$).toBeTrue();
84
+ });
85
+
86
+ it('enables the actions menu when only data object actions are available', () => {
87
+ dataInfoActions = { availableActions: [{ ID: 'Edit', name: 'Edit' }] };
88
+
89
+ component.fullUpdate();
90
+
91
+ expect(component.bActionsMenuDisabled$).toBeFalse();
92
+ });
93
+
94
+ it('picks up case actions that arrive after the first update, without a case ID change', () => {
95
+ component.fullUpdate();
96
+ expect(component.bActionsMenuDisabled$).toBeTrue();
97
+ expect(component.editAction).toBeUndefined();
98
+
99
+ caseInfo.availableActions = [{ ID: 'pyUpdateCaseDetails', name: 'Edit details' }];
100
+ component.updateCaseActions();
101
+
102
+ expect(component.arAvailableActions$.length).toBe(1);
103
+ expect(component.editAction).toBeTruthy();
104
+ expect(component.bActionsMenuDisabled$).toBeFalse();
105
+ });
106
+
107
+ it('opens a data object action with the record class and content', () => {
108
+ component._menuDataObjectActionClick({ ID: 'updateCVV', name: 'Update CVV' });
109
+
110
+ expect(actionsApi.openDataObjectAction).toHaveBeenCalledWith('My-Data-Class', dataRecord, 'updateCVV', 'Update CVV');
111
+ });
112
+
113
+ it('starts a new case with starting fields nested under the target reference field', () => {
114
+ component._menuCreateCaseActionClick({
115
+ ID: 'My-Work-Class',
116
+ name: 'Request Plan Change',
117
+ targetDataReferenceField: {
118
+ field: 'PlanRef',
119
+ inputs: [{ linkedField: 'PlanID' }, { linkedField: 'CustomerID' }]
120
+ }
121
+ });
122
+
123
+ expect(actionsApi.createWork).toHaveBeenCalledWith('My-Work-Class', {
124
+ startingFields: { PlanRef: { PlanID: 'P-1', CustomerID: 'C-9' } }
125
+ });
126
+ });
127
+
128
+ it('starts a new case with no starting fields when the action declares no inputs', () => {
129
+ component._menuCreateCaseActionClick({ ID: 'My-Work-Class', name: 'Request Plan Change' });
130
+
131
+ expect(actionsApi.createWork).toHaveBeenCalledWith('My-Work-Class', { startingFields: {} });
132
+ });
24
133
  });
@@ -8,6 +8,7 @@ import { interval } from 'rxjs';
8
8
  import { AngularPConnectData, AngularPConnectService } from '@pega/angular-sdk-components';
9
9
  import { Utils } from '@pega/angular-sdk-components';
10
10
  import { ComponentMapperComponent } from '@pega/angular-sdk-components';
11
+ import { CreateCaseAction, DataObjectAction, DataObjectActions } from '@pega/angular-sdk-components';
11
12
 
12
13
  interface CaseViewProps {
13
14
  // If any, enter additional props that only exist on this component
@@ -45,6 +46,9 @@ export class CaseViewComponent implements OnInit, OnDestroy {
45
46
 
46
47
  arAvailableActions$: any[] = [];
47
48
  arAvailabeProcesses$: any[] = [];
49
+ arDataObjectActions$: DataObjectAction[] = [];
50
+ arCreateCaseActions$: CreateCaseAction[] = [];
51
+ bActionsMenuDisabled$ = true;
48
52
 
49
53
  caseSummaryPConn$: any;
50
54
  currentCaseID = '';
@@ -98,10 +102,31 @@ export class CaseViewComponent implements OnInit, OnDestroy {
98
102
  sessionStorage.setItem('okToInitFlowContainer', 'true');
99
103
  } else {
100
104
  this.updateHeaderAndSummary();
105
+ this.updateCaseActions();
101
106
  }
102
107
  }
103
108
  }
104
109
 
110
+ // React re-reads every action source on each render. Here fullUpdate is gated on a case ID
111
+ // change and so runs once, while actions (and a data object's `dataInfo.actions`) arrive
112
+ // later, so they are all refreshed on every store update instead.
113
+ updateCaseActions() {
114
+ const caseInfo = this.pConn$.getDataObject()?.caseInfo ?? {};
115
+ this.arAvailableActions$ = caseInfo.availableActions ?? [];
116
+ this.arAvailabeProcesses$ = caseInfo.availableProcesses ?? [];
117
+ this.editAction = this.arAvailableActions$.find(action => action.ID === 'pyUpdateCaseDetails');
118
+
119
+ const dataInfoActions: DataObjectActions = this.pConn$.getValue('.actions', 'dataInfo') ?? {};
120
+ this.arDataObjectActions$ = dataInfoActions.availableActions ?? [];
121
+ this.arCreateCaseActions$ = dataInfoActions.availableCreateCaseActions ?? [];
122
+
123
+ this.bActionsMenuDisabled$ =
124
+ this.arAvailableActions$.length === 0 &&
125
+ this.arAvailabeProcesses$.length === 0 &&
126
+ this.arDataObjectActions$.length === 0 &&
127
+ this.arCreateCaseActions$.length === 0;
128
+ }
129
+
105
130
  hasCaseIDChanged(): boolean {
106
131
  if (this.currentCaseID !== this.pConn$.getDataObject().caseInfo.ID) {
107
132
  this.currentCaseID = this.pConn$.getDataObject().caseInfo.ID;
@@ -144,9 +169,8 @@ export class CaseViewComponent implements OnInit, OnDestroy {
144
169
 
145
170
  const caseInfo = this.pConn$.getDataObject().caseInfo;
146
171
  this.currentCaseID = caseInfo.ID;
147
- this.arAvailableActions$ = caseInfo?.availableActions ? caseInfo.availableActions : [];
148
- this.editAction = this.arAvailableActions$.find(action => action.ID === 'pyUpdateCaseDetails');
149
- this.arAvailabeProcesses$ = caseInfo?.availableProcesses ? caseInfo.availableProcesses : [];
172
+
173
+ this.updateCaseActions();
150
174
 
151
175
  this.svgCase$ = this.utils.getImageSrc(this.configProps$.icon, this.utils.getSDKStaticContentUrl());
152
176
 
@@ -218,4 +242,29 @@ export class CaseViewComponent implements OnInit, OnDestroy {
218
242
 
219
243
  openProcessAction(data.ID, { ...data });
220
244
  }
245
+
246
+ _menuDataObjectActionClick(action: DataObjectAction) {
247
+ const actionsAPI = this.pConn$.getActionsApi();
248
+ const openDataObjectAction = actionsAPI.openDataObjectAction.bind(actionsAPI);
249
+ const classID = this.pConn$.getValue('.classID', 'dataInfo.content');
250
+ const dataRecord = this.pConn$.getValue('.content', 'dataInfo');
251
+
252
+ openDataObjectAction(classID, dataRecord, action.ID, this.localizedVal(action.name, '', this.localeKey));
253
+ }
254
+
255
+ _menuCreateCaseActionClick(action: CreateCaseAction) {
256
+ const actionsAPI = this.pConn$.getActionsApi();
257
+ const createWork = actionsAPI.createWork.bind(actionsAPI);
258
+ const { field, inputs } = action.targetDataReferenceField ?? {};
259
+ const startingFields: Record<string, any> = {};
260
+
261
+ // The engine expects the linked values nested under the target reference field.
262
+ if (field) {
263
+ inputs?.forEach(input => {
264
+ startingFields[field] = { ...startingFields[field], [input.linkedField]: this.pConn$.getValue(`.${input.linkedField}`, '') };
265
+ });
266
+ }
267
+
268
+ createWork(action.ID, { startingFields });
269
+ }
221
270
  }
@@ -58,6 +58,7 @@ export class DataReferenceComponent implements OnInit, OnDestroy {
58
58
  dataRelationshipContext: any;
59
59
  imagePosition: any;
60
60
  showImageDescription: any;
61
+ private isUpdatingFromDataCallback = false;
61
62
 
62
63
  constructor(
63
64
  private angularPConnect: AngularPConnectService,
@@ -69,7 +70,30 @@ export class DataReferenceComponent implements OnInit, OnDestroy {
69
70
  this.angularPConnectData = this.angularPConnect.registerAndSubscribeComponent(this, this.onStateChange);
70
71
  this.children = this.pConn$.getChildren();
71
72
  this.updateSelf();
73
+ }
74
+
75
+ ngOnDestroy(): void {
76
+ if (this.angularPConnectData.unsubscribeFn) {
77
+ this.angularPConnectData.unsubscribeFn();
78
+ }
79
+ }
80
+
81
+ // Callback passed when subscribing to store change
82
+ onStateChange() {
83
+ // Should always check the bridge to see if the component should
84
+ // update itself (re-render)
85
+ const bUpdateSelf = this.angularPConnect.shouldComponentUpdate(this);
86
+
87
+ // ONLY call updateSelf when the component should update
88
+ if (bUpdateSelf) {
89
+ this.updateSelf();
90
+ }
91
+ }
72
92
 
93
+ getData() {
94
+ if (this.isUpdatingFromDataCallback) {
95
+ return;
96
+ }
73
97
  if (
74
98
  this.rawViewMetadata.config?.parameters &&
75
99
  !this.isDDSourceDeferred &&
@@ -94,7 +118,9 @@ export class DataReferenceComponent implements OnInit, OnDestroy {
94
118
  }))
95
119
  .filter(item => item.key); // Filtering out undefined entries
96
120
  this.dropDownDataSource = ddDataSource;
121
+ this.isUpdatingFromDataCallback = true;
97
122
  this.updateSelf();
123
+ this.isUpdatingFromDataCallback = false;
98
124
  } else {
99
125
  const ddDataSource: any = [];
100
126
  this.dropDownDataSource = ddDataSource;
@@ -110,24 +136,6 @@ export class DataReferenceComponent implements OnInit, OnDestroy {
110
136
  }
111
137
  }
112
138
 
113
- ngOnDestroy(): void {
114
- if (this.angularPConnectData.unsubscribeFn) {
115
- this.angularPConnectData.unsubscribeFn();
116
- }
117
- }
118
-
119
- // Callback passed when subscribing to store change
120
- onStateChange() {
121
- // Should always check the bridge to see if the component should
122
- // update itself (re-render)
123
- const bUpdateSelf = this.angularPConnect.shouldComponentUpdate(this);
124
-
125
- // ONLY call updateSelf when the component should update
126
- if (bUpdateSelf) {
127
- this.updateSelf();
128
- }
129
- }
130
-
131
139
  updateSelf() {
132
140
  // Update properties based on configProps
133
141
  const theConfigProps = this.pConn$.getConfigProps();
@@ -186,6 +194,7 @@ export class DataReferenceComponent implements OnInit, OnDestroy {
186
194
 
187
195
  this.generateChildrenToRender();
188
196
  this.displayChild = !(this.displaySingleRef || this.displayMultiRef);
197
+ this.getData();
189
198
  }
190
199
 
191
200
  updatePropertiesFromProps(theConfigProps) {
@@ -1,5 +1,5 @@
1
1
  <div class="search-form" style="display: flex; flex-direction: column">
2
- <h5>{{ propsToUse.label }}</h5>
2
+ <h3>{{ label$ }}</h3>
3
3
  <div *ngIf="searchCategoriesComp === 'dropdown'">
4
4
  <mat-form-field class="psdk-full-width">
5
5
  <mat-select [value]="currentTabId" (selectionChange)="handleTabClick($event.value)">
@@ -48,6 +48,7 @@ export class SearchFormComponent implements OnInit, OnChanges {
48
48
  tabItems: any[] = [];
49
49
  searchCategoriesComp: any;
50
50
  propsToUse: any;
51
+ label$: string;
51
52
  tabData: any = [];
52
53
  tabCountSources: any;
53
54
  deferLoadedTabs: any;
@@ -59,6 +60,7 @@ export class SearchFormComponent implements OnInit, OnChanges {
59
60
  this.isInitialized = true;
60
61
  this.configProps$ = this.pConn$.resolveConfigProps(this.pConn$.getConfigProps());
61
62
  this.propsToUse = { ...this.pConn$.getInheritedProps() };
63
+ this.label$ = this.configProps$?.label || this.propsToUse.label;
62
64
  // When called from ObjectReference, pConn$ itself acts as deferLoadedTabs (no children[2])
63
65
  if (this.type === 'ObjectReference') {
64
66
  this.deferLoadedTabs = { getPConnect: () => this.pConn$ };
@@ -1,6 +1,22 @@
1
- <div *ngIf="instructions" class="psdk-default-form-instruction-text">
2
- <div key="instructions" id="instruction-text" [innerHTML]="instructions"></div>
3
- </div>
1
+ <ng-container *ngIf="instructions && bShowInstructions$">
2
+ <component-mapper
3
+ *ngIf="instructionBannerVariant$; else instructionText"
4
+ name="Alert"
5
+ [props]="{
6
+ message: instructions,
7
+ messageIsHtml: true,
8
+ severity: instructionBannerVariant$,
9
+ hideClose: !bDismissInstructions$
10
+ }"
11
+ [parent]="this"
12
+ [outputEvents]="{ onClose: dismissInstructions }"
13
+ ></component-mapper>
14
+ <ng-template #instructionText>
15
+ <div class="psdk-default-form-instruction-text">
16
+ <div key="instructions" id="instruction-text" [innerHTML]="instructions"></div>
17
+ </div>
18
+ </ng-template>
19
+ </ng-container>
4
20
  <div [className]="divClass$">
5
21
  <div *ngFor="let kid of arChildren$" [ngClass]="{ 'grid-column': kid.getPConnect().getComponentName() === 'reference' }">
6
22
  <component-mapper
@@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common';
3
3
  import { FormGroup } from '@angular/forms';
4
4
  import { AngularPConnectData, AngularPConnectService } from '@pega/angular-sdk-components';
5
5
  import { ComponentMapperComponent } from '@pega/angular-sdk-components';
6
- import { TemplateUtils } from '@pega/angular-sdk-components';
6
+ import { InstructionObject, TemplateUtils } from '@pega/angular-sdk-components';
7
7
  import { FormTemplateBase } from '@pega/angular-sdk-components';
8
8
 
9
9
  function areViewsChanged(oldViews: any[], newViews: any[]): boolean {
@@ -20,7 +20,7 @@ function areViewsChanged(oldViews: any[], newViews: any[]): boolean {
20
20
  interface DefaultFormProps {
21
21
  // If any, enter additional props that only exist on this component
22
22
  NumCols: string;
23
- instructions: string;
23
+ instructions?: string | InstructionObject;
24
24
  }
25
25
 
26
26
  @Component({
@@ -38,7 +38,10 @@ export class DefaultFormComponent extends FormTemplateBase implements OnInit, On
38
38
 
39
39
  arChildren$: any[];
40
40
  divClass$: string;
41
- instructions: string;
41
+ instructions: string | undefined;
42
+ instructionBannerVariant$: 'warning' | 'info' | 'success' | undefined;
43
+ bDismissInstructions$ = false;
44
+ bShowInstructions$ = true;
42
45
 
43
46
  constructor(
44
47
  private angularPConnect: AngularPConnectService,
@@ -70,6 +73,9 @@ export class DefaultFormComponent extends FormTemplateBase implements OnInit, On
70
73
  const configProps = this.pConn$.getConfigProps() as DefaultFormProps;
71
74
  const kids = this.pConn$.getChildren();
72
75
  this.instructions = this.templateUtils.getInstructions(this.pConn$, configProps?.instructions);
76
+ const instructionsType = this.templateUtils.getInstructionsType(configProps?.instructions);
77
+ this.instructionBannerVariant$ = instructionsType ? this.templateUtils.mapInstructionsTypeToBannerVariant(instructionsType) : undefined;
78
+ this.bDismissInstructions$ = this.templateUtils.getDismissBanner(configProps?.instructions);
73
79
 
74
80
  const numCols = configProps.NumCols ? configProps.NumCols : '1';
75
81
  switch (numCols) {
@@ -96,4 +102,8 @@ export class DefaultFormComponent extends FormTemplateBase implements OnInit, On
96
102
  this.arChildren$ = children;
97
103
  }
98
104
  }
105
+
106
+ dismissInstructions() {
107
+ this.bShowInstructions$ = false;
108
+ }
99
109
  }
@@ -1,3 +1,23 @@
1
- <div>
2
- <component-mapper name="MaterialDetails" [props]="{ arFields$, arHighlightedFields: highlightedDataArr, layout: 'one-column' }"></component-mapper>
3
- </div>
1
+ <component-mapper
2
+ name="FieldGroup"
3
+ [props]="{ name: propsToUse.showLabel ? propsToUse.label : '', childrenTemplate: detailsContent }"
4
+ ></component-mapper>
5
+
6
+ <ng-template #detailsContent>
7
+ <ng-container *ngIf="showHighlightedData && highlightedDataArr.length > 0">
8
+ <div class="psdk-grid-highlighted">
9
+ <component-mapper
10
+ *ngFor="let field of highlightedDataArr"
11
+ [name]="field.getComponentName()"
12
+ [props]="{ pConn$: field, formGroup$ }"
13
+ ></component-mapper>
14
+ </div>
15
+ </ng-container>
16
+ <div class="psdk-grid-filter-1">
17
+ <component-mapper
18
+ *ngFor="let child of children"
19
+ [name]="child.getPConnect().getComponentName()"
20
+ [props]="{ pConn$: child.getPConnect(), formGroup$ }"
21
+ ></component-mapper>
22
+ </div>
23
+ </ng-template>
@@ -1,7 +1,14 @@
1
- .psdk-grid-filter {
1
+ .psdk-grid-filter-1 {
2
2
  display: grid;
3
- grid-template-columns: repeat(2, minmax(0, 1fr));
3
+ grid-template-columns: repeat(1, minmax(0, 1fr));
4
4
  column-gap: calc(2 * 0.5rem);
5
5
  row-gap: calc(2 * 0.5rem);
6
6
  align-items: start;
7
7
  }
8
+
9
+ .psdk-grid-highlighted {
10
+ display: flex;
11
+ flex-wrap: wrap;
12
+ align-items: flex-start;
13
+ gap: calc(0.5rem) calc(5rem);
14
+ }