@pega/react-sdk-overrides 23.1.11 → 24.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 (41) hide show
  1. package/lib/designSystemExtension/FieldValueList/FieldValueList.tsx +10 -4
  2. package/lib/field/CancelAlert/CancelAlert.tsx +2 -0
  3. package/lib/field/Checkbox/Checkbox.tsx +101 -23
  4. package/lib/field/Currency/Currency.tsx +36 -26
  5. package/lib/field/Decimal/Decimal.tsx +44 -21
  6. package/lib/field/Multiselect/Multiselect.tsx +244 -0
  7. package/lib/field/Multiselect/index.tsx +1 -0
  8. package/lib/field/Multiselect/utils.ts +230 -0
  9. package/lib/field/Percentage/Percentage.tsx +33 -18
  10. package/lib/field/Phone/Phone.tsx +16 -7
  11. package/lib/field/RichText/RichText.tsx +2 -2
  12. package/lib/field/ScalarList/ScalarList.tsx +2 -0
  13. package/lib/field/UserReference/UserReference.tsx +11 -1
  14. package/lib/helpers/common-utils.ts +1 -5
  15. package/lib/helpers/instructions-utils.ts +38 -0
  16. package/lib/infra/Assignment/Assignment.tsx +28 -1
  17. package/lib/infra/Containers/FlowContainer/FlowContainer.tsx +5 -3
  18. package/lib/infra/DashboardFilter/DashboardFilter.tsx +1 -1
  19. package/lib/infra/DeferLoad/DeferLoad.tsx +5 -1
  20. package/lib/infra/MultiStep/MultiStep.tsx +3 -1
  21. package/lib/infra/NavBar/NavBar.tsx +1 -1
  22. package/lib/infra/Reference/Reference.tsx +1 -1
  23. package/lib/template/CaseView/CaseView.tsx +2 -1
  24. package/lib/template/CaseViewActionsMenu/CaseViewActionsMenu.tsx +1 -1
  25. package/lib/template/DataReference/DataReference.tsx +1 -1
  26. package/lib/template/DefaultForm/utils/index.ts +2 -7
  27. package/lib/template/Details/Details/Details.tsx +1 -1
  28. package/lib/template/Details/DetailsThreeColumn/DetailsThreeColumn.tsx +1 -1
  29. package/lib/template/Details/DetailsTwoColumn/DetailsTwoColumn.tsx +1 -1
  30. package/lib/template/FieldGroupTemplate/FieldGroupTemplate.tsx +2 -0
  31. package/lib/template/ListView/ListView.tsx +22 -33
  32. package/lib/template/MultiReferenceReadOnly/MultiReferenceReadOnly.tsx +1 -1
  33. package/lib/template/NarrowWide/NarrowWideDetails/NarrowWideDetails.tsx +1 -1
  34. package/lib/template/PromotedFilters/PromotedFilters.tsx +1 -0
  35. package/lib/template/SimpleTable/SimpleTableManual/SimpleTableManual.tsx +8 -4
  36. package/lib/template/SingleReferenceReadOnly/SingleReferenceReadOnly.tsx +1 -1
  37. package/lib/template/WideNarrow/WideNarrowDetails/WideNarrowDetails.tsx +1 -1
  38. package/lib/widget/Attachment/Attachment.tsx +3 -2
  39. package/lib/widget/FileUtility/FileUtility/FileUtility.tsx +1 -1
  40. package/lib/widget/QuickCreate/QuickCreate.tsx +1 -0
  41. package/package.json +1 -1
@@ -0,0 +1,244 @@
1
+ import { Checkbox, TextField } from '@material-ui/core';
2
+ import Autocomplete from '@material-ui/lab/Autocomplete';
3
+ import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
4
+ import CheckBoxIcon from '@material-ui/icons/CheckBox';
5
+ import { useEffect, useMemo, useRef, useState } from 'react';
6
+ import { doSearch, getDisplayFieldsMetaData, useDeepMemo, preProcessColumns, getGroupDataForItemsTree } from './utils';
7
+ import { insertInstruction, deleteInstruction } from '@pega/react-sdk-components/lib/components/helpers/instructions-utils';
8
+ import { debounce } from 'throttle-debounce';
9
+
10
+ const icon = <CheckBoxOutlineBlankIcon fontSize='small' />;
11
+ const checkedIcon = <CheckBoxIcon fontSize='small' />;
12
+
13
+ export default function Multiselect(props) {
14
+ const {
15
+ getPConnect,
16
+ label,
17
+ placeholder,
18
+ referenceList,
19
+ selectionKey,
20
+ primaryField,
21
+ initialCaseClass,
22
+ showSecondaryInSearchOnly = false,
23
+ listType = '',
24
+ isGroupData = false,
25
+ referenceType,
26
+ secondaryFields,
27
+ groupDataSource = [],
28
+ parameters = {},
29
+ matchPosition = 'contains',
30
+ maxResultsDisplay,
31
+ groupColumnsConfig = [{}],
32
+ selectionList,
33
+ value
34
+ } = props;
35
+ let { datasource = [], columns = [{}] } = props;
36
+
37
+ if (referenceList.length > 0) {
38
+ datasource = referenceList;
39
+ columns = [
40
+ {
41
+ value: primaryField,
42
+ display: 'true',
43
+ useForSearch: true,
44
+ primary: 'true'
45
+ },
46
+ {
47
+ value: selectionKey,
48
+ setProperty: selectionKey,
49
+ key: 'true'
50
+ }
51
+ ];
52
+ let secondaryColumns: any = [];
53
+ if (secondaryFields) {
54
+ secondaryColumns = secondaryFields.map(secondaryField => ({
55
+ value: secondaryField,
56
+ display: 'true',
57
+ secondary: 'true',
58
+ useForSearch: 'true'
59
+ }));
60
+ } else {
61
+ secondaryColumns = [
62
+ {
63
+ value: selectionKey,
64
+ display: 'true',
65
+ secondary: 'true',
66
+ useForSearch: 'true'
67
+ }
68
+ ];
69
+ }
70
+ if (referenceType === 'Case') {
71
+ columns = [...columns, ...secondaryColumns];
72
+ }
73
+ }
74
+ const [inputValue, setInputValue] = useState(value);
75
+ const [selectedItems, setSelectedItems] = useState([]);
76
+
77
+ const dataConfig = useDeepMemo(() => {
78
+ return {
79
+ dataSource: datasource,
80
+ groupDataSource,
81
+ isGroupData,
82
+ showSecondaryInSearchOnly,
83
+ parameters,
84
+ matchPosition,
85
+ listType,
86
+ maxResultsDisplay: maxResultsDisplay || '100',
87
+ columns: preProcessColumns(columns),
88
+ groupColumnsConfig: preProcessColumns(groupColumnsConfig)
89
+ };
90
+ }, [
91
+ datasource,
92
+ groupDataSource,
93
+ isGroupData,
94
+ showSecondaryInSearchOnly,
95
+ parameters,
96
+ matchPosition,
97
+ listType,
98
+ maxResultsDisplay,
99
+ columns,
100
+ groupColumnsConfig
101
+ ]);
102
+ const groupsDisplayFieldMeta = useMemo(
103
+ () => (listType !== 'associated' ? getDisplayFieldsMetaData(dataConfig.groupColumnsConfig) : null),
104
+ [dataConfig.groupColumnsConfig]
105
+ );
106
+
107
+ const itemsTreeBaseData = getGroupDataForItemsTree(groupDataSource, groupsDisplayFieldMeta, showSecondaryInSearchOnly) || [];
108
+
109
+ const [itemsTree, setItemsTree] = useState(
110
+ isGroupData ? getGroupDataForItemsTree(groupDataSource, groupsDisplayFieldMeta, showSecondaryInSearchOnly) : []
111
+ );
112
+
113
+ const displayFieldMeta = listType !== 'associated' ? getDisplayFieldsMetaData(dataConfig.columns) : null;
114
+ const getCaseListBasedOnParamsDebounced: any = useRef();
115
+ const pConn = getPConnect();
116
+ const contextName = pConn.getContextName();
117
+ const listActions = pConn.getListActions();
118
+ const dataApiObj: any = useRef();
119
+
120
+ // main search function trigger
121
+ const getCaseListBasedOnParams = async (searchText, group, selectedRows, currentItemsTree, isTriggeredFromSearch = false) => {
122
+ if (referenceList && referenceList.length > 0) {
123
+ selectedRows = await listActions.getSelectedRows(true);
124
+
125
+ selectedRows =
126
+ selectedRows &&
127
+ selectedRows.map(item => {
128
+ return {
129
+ id: item[selectionKey.startsWith('.') ? selectionKey.substring(1) : selectionKey],
130
+ primary: item[primaryField.startsWith('.') ? primaryField.substring(1) : primaryField]
131
+ };
132
+ });
133
+ setSelectedItems(selectedRows);
134
+ }
135
+
136
+ // if items tree is null or text search is triggered then always should use fresh data object, we use the original object
137
+ const initalItemsTree = isTriggeredFromSearch || !currentItemsTree ? [...itemsTreeBaseData] : [...currentItemsTree];
138
+ const res = await doSearch(
139
+ searchText,
140
+ group,
141
+ initialCaseClass,
142
+ displayFieldMeta,
143
+ dataApiObj.current,
144
+ initalItemsTree,
145
+ isGroupData,
146
+ showSecondaryInSearchOnly,
147
+ selectedRows || []
148
+ );
149
+ setItemsTree(res);
150
+ };
151
+
152
+ useEffect(() => {
153
+ if (referenceList && referenceList.length > 0) {
154
+ pConn.setReferenceList(selectionList);
155
+ }
156
+ }, [pConn]);
157
+
158
+ useEffect(() => {
159
+ getCaseListBasedOnParamsDebounced.current = debounce(500, getCaseListBasedOnParams);
160
+ }, []);
161
+
162
+ useEffect(() => {
163
+ if (listType !== 'associated') {
164
+ PCore.getDataApi()
165
+ ?.init(dataConfig, contextName)
166
+ .then(dataObj => {
167
+ dataApiObj.current = dataObj;
168
+ if (!isGroupData) {
169
+ getCaseListBasedOnParamsDebounced.current(inputValue ?? '', '', [...selectedItems], [...itemsTree]);
170
+ }
171
+ });
172
+ }
173
+ }, [dataConfig, listType, dataConfig.columns, inputValue, dataConfig.groupColumnsConfig, showSecondaryInSearchOnly]);
174
+
175
+ const onSearchHandler = ev => {
176
+ const searchText = ev.target.value;
177
+ setInputValue(searchText);
178
+ getCaseListBasedOnParamsDebounced.current(searchText, '', [...selectedItems], [...itemsTree], true);
179
+ };
180
+
181
+ const setSelectedItemsForReferenceList = item => {
182
+ // Clear error messages if any
183
+ const propName = pConn.getStateProps().selectionList;
184
+ pConn.clearErrorMessages({
185
+ property: propName
186
+ });
187
+ const { selected } = item;
188
+ if (selected) {
189
+ insertInstruction(pConn, selectionList, selectionKey, primaryField, item);
190
+ } else {
191
+ deleteInstruction(pConn, selectionList, selectionKey, item);
192
+ }
193
+ };
194
+
195
+ const handleChange = (event, newSelectedValues) => {
196
+ let clickedItem;
197
+ let updatedItems: any = [];
198
+ if (newSelectedValues && newSelectedValues.length > 0) {
199
+ updatedItems = newSelectedValues.map(ele => {
200
+ ele.selected = true;
201
+ return ele;
202
+ });
203
+ }
204
+ if (newSelectedValues.length > selectedItems.length) {
205
+ clickedItem = newSelectedValues.filter(item => !selectedItems.some((ele: any) => ele.id === item.id));
206
+ } else {
207
+ clickedItem = selectedItems.filter((item: any) => !newSelectedValues.some((ele: any) => ele.id === item.id));
208
+ clickedItem[0].selected = false;
209
+ }
210
+ itemsTree.forEach(ele => {
211
+ ele.selected = !!updatedItems.find(item => item.id === ele.id);
212
+ });
213
+
214
+ setSelectedItems(updatedItems);
215
+ setItemsTree(itemsTree);
216
+
217
+ setInputValue('');
218
+
219
+ // if this is a referenceList case
220
+ if (referenceList) setSelectedItemsForReferenceList(clickedItem[0]);
221
+ };
222
+
223
+ return (
224
+ <Autocomplete
225
+ multiple
226
+ fullWidth
227
+ options={itemsTree}
228
+ disableCloseOnSelect
229
+ getOptionSelected={(option: any, val: any) => option?.primary === val?.primary}
230
+ getOptionLabel={(option: any) => option?.primary}
231
+ onChange={handleChange}
232
+ value={selectedItems}
233
+ renderOption={(option: any, { selected }) => (
234
+ <>
235
+ <Checkbox icon={icon} checkedIcon={checkedIcon} style={{ marginRight: 8 }} checked={selected} />
236
+ {option.primary}
237
+ </>
238
+ )}
239
+ renderInput={params => (
240
+ <TextField {...params} variant='outlined' fullWidth label={label} placeholder={placeholder} size='small' onChange={onSearchHandler} />
241
+ )}
242
+ />
243
+ );
244
+ }
@@ -0,0 +1 @@
1
+ export { default } from './Multiselect';
@@ -0,0 +1,230 @@
1
+ import { useRef } from 'react';
2
+ import equal from 'fast-deep-equal';
3
+ import cloneDeep from 'lodash/cloneDeep';
4
+ import { updateNewInstuctions, insertInstruction, deleteInstruction } from '@pega/react-sdk-components/lib/components/helpers/instructions-utils';
5
+
6
+ export const setVisibilityForList = (c11nEnv, visibility) => {
7
+ const { selectionMode, selectionList, renderMode, referenceList } = c11nEnv.getComponentConfig();
8
+ // usecase:multiselect, fieldgroup, editable table
9
+ if ((selectionMode === PCore.getConstants().LIST_SELECTION_MODE.MULTI && selectionList) || (renderMode === 'Editable' && referenceList)) {
10
+ c11nEnv.getListActions().setVisibility(visibility);
11
+ }
12
+ };
13
+
14
+ const useDeepMemo = (memoFn, key) => {
15
+ const ref: any = useRef();
16
+ if (!ref.current || !equal(key, ref.current.key)) {
17
+ ref.current = { key, value: memoFn() };
18
+ }
19
+ return ref.current.value;
20
+ };
21
+
22
+ const preProcessColumns = columns => {
23
+ return columns?.map(col => {
24
+ const tempColObj = { ...col };
25
+ tempColObj.value = col.value && col.value.startsWith('.') ? col.value.substring(1) : col.value;
26
+ if (tempColObj.setProperty) {
27
+ tempColObj.setProperty = col.setProperty && col.setProperty.startsWith('.') ? col.setProperty.substring(1) : col.setProperty;
28
+ }
29
+ return tempColObj;
30
+ });
31
+ };
32
+
33
+ const getDisplayFieldsMetaData = columns => {
34
+ const displayColumns = columns?.filter(col => col.display === 'true');
35
+ const metaDataObj: any = {
36
+ key: '',
37
+ primary: '',
38
+ secondary: []
39
+ };
40
+ const keyCol = columns?.filter(col => col.key === 'true');
41
+ metaDataObj.key = keyCol?.length > 0 ? keyCol[0].value : 'auto';
42
+ const itemsRecordsColumn = columns?.filter(col => col.itemsRecordsColumn === 'true');
43
+ if (itemsRecordsColumn?.length > 0) {
44
+ metaDataObj.itemsRecordsColumn = itemsRecordsColumn[0].value;
45
+ }
46
+ const itemsGroupKeyColumn = columns?.filter(col => col.itemsGroupKeyColumn === 'true');
47
+ if (itemsGroupKeyColumn?.length > 0) {
48
+ metaDataObj.itemsGroupKeyColumn = itemsGroupKeyColumn[0].value;
49
+ }
50
+ for (let index = 0; index < displayColumns?.length; index += 1) {
51
+ if (displayColumns[index].secondary === 'true') {
52
+ metaDataObj.secondary.push(displayColumns[index].value);
53
+ } else if (displayColumns[index].primary === 'true') {
54
+ metaDataObj.primary = displayColumns[index].value;
55
+ }
56
+ }
57
+ return metaDataObj;
58
+ };
59
+
60
+ const createSingleTreeObejct = (entry, displayFieldMeta, showSecondaryData, selected) => {
61
+ const secondaryArr: any = [];
62
+ displayFieldMeta.secondary.forEach(col => {
63
+ secondaryArr.push(entry[col]);
64
+ });
65
+ const isSelected = selected.some(item => item.id === entry[displayFieldMeta.key]);
66
+
67
+ return {
68
+ id: entry[displayFieldMeta.key],
69
+ primary: entry[displayFieldMeta.primary],
70
+ secondary: showSecondaryData ? secondaryArr : [],
71
+ selected: isSelected
72
+ };
73
+ };
74
+
75
+ const putItemsDataInItemsTree = (listObjData, displayFieldMeta, itemsTree, showSecondaryInSearchOnly, selected) => {
76
+ let newTreeItems = itemsTree.slice();
77
+ const showSecondaryData = !showSecondaryInSearchOnly;
78
+ for (const obj of listObjData) {
79
+ const items = obj[displayFieldMeta.itemsRecordsColumn].map(entry => createSingleTreeObejct(entry, displayFieldMeta, showSecondaryData, selected));
80
+
81
+ newTreeItems = newTreeItems.map(caseObject => {
82
+ if (caseObject.id === obj[displayFieldMeta.itemsGroupKeyColumn]) {
83
+ caseObject.items = [...items];
84
+ }
85
+ return caseObject;
86
+ });
87
+ }
88
+ return newTreeItems;
89
+ };
90
+
91
+ const prepareSearchResults = (listObjData, displayFieldMeta) => {
92
+ const searchResults: any = [];
93
+ for (const obj of listObjData) {
94
+ searchResults.push(...obj[displayFieldMeta.itemsRecordsColumn]);
95
+ }
96
+ return searchResults;
97
+ };
98
+
99
+ const doSearch = async (
100
+ searchText,
101
+ clickedGroup,
102
+ initialCaseClass,
103
+ displayFieldMeta,
104
+ dataApiObj, // deep clone of the dataApiObj
105
+ itemsTree,
106
+ isGroupData,
107
+ showSecondaryInSearchOnly,
108
+ selected
109
+ ) => {
110
+ let searchTextForUngroupedData = '';
111
+ if (dataApiObj) {
112
+ // creating dataApiObject in grouped data cases
113
+ if (isGroupData) {
114
+ dataApiObj = cloneDeep(dataApiObj);
115
+ dataApiObj.fetchedNQData = false;
116
+ dataApiObj.cache = {};
117
+
118
+ // if we have no search text and no group selected, return the original tree
119
+ if (searchText === '' && clickedGroup === '') {
120
+ return itemsTree;
121
+ }
122
+
123
+ // setting the inital search text & search classes in ApiObject
124
+ dataApiObj.parameters[Object.keys(dataApiObj.parameters)[1]] = searchText;
125
+ dataApiObj.parameters[Object.keys(dataApiObj.parameters)[0]] = initialCaseClass;
126
+
127
+ // if we have a selected group
128
+ if (clickedGroup) {
129
+ // check if the data for this group is already present and no search text
130
+ if (searchText === '') {
131
+ const containsData = itemsTree.find(item => item.id === clickedGroup);
132
+ // do not make API call when items of respective group are already fetched
133
+ if (containsData?.items?.length) return itemsTree;
134
+ }
135
+
136
+ dataApiObj.parameters[Object.keys(dataApiObj.parameters)[0]] = JSON.stringify([clickedGroup]);
137
+ }
138
+ } else {
139
+ searchTextForUngroupedData = searchText;
140
+ }
141
+
142
+ // search API call
143
+ const response = await dataApiObj.fetchData(searchTextForUngroupedData).catch(() => {
144
+ return itemsTree;
145
+ });
146
+
147
+ let listObjData = response.data;
148
+ let newItemsTree = [];
149
+ if (isGroupData) {
150
+ if (searchText) {
151
+ listObjData = prepareSearchResults(listObjData, displayFieldMeta);
152
+ } else {
153
+ newItemsTree = putItemsDataInItemsTree(listObjData, displayFieldMeta, itemsTree, showSecondaryInSearchOnly, selected);
154
+ return newItemsTree;
155
+ }
156
+ }
157
+ const showSecondaryData = showSecondaryInSearchOnly ? !!searchText : true;
158
+ if (listObjData !== undefined && listObjData.length > 0) {
159
+ newItemsTree = listObjData.map(entry => createSingleTreeObejct(entry, displayFieldMeta, showSecondaryData, selected));
160
+ }
161
+ return newItemsTree;
162
+ }
163
+
164
+ return itemsTree;
165
+ };
166
+
167
+ const setValuesToPropertyList = (searchText, assocProp, items, columns, actions, updatePropertyInRedux = true) => {
168
+ const setPropertyList = columns
169
+ ?.filter(col => col.setProperty)
170
+ .map(col => {
171
+ return {
172
+ source: col.value,
173
+ target: col.setProperty,
174
+ key: col.key,
175
+ primary: col.primary
176
+ };
177
+ });
178
+ const valueToSet: any = [];
179
+ if (setPropertyList.length > 0) {
180
+ setPropertyList.forEach(prop => {
181
+ items.forEach(item => {
182
+ if (prop.key === 'true' && item) {
183
+ valueToSet.push(item.id);
184
+ } else if (prop.primary === 'true' || !item) {
185
+ valueToSet.push(searchText);
186
+ }
187
+ });
188
+
189
+ if (updatePropertyInRedux) {
190
+ // BUG-666851 setting options so that the store values are replaced and not merged
191
+ const options = {
192
+ isArrayDeepMerge: false
193
+ };
194
+ if (prop.target === 'Associated property') {
195
+ actions.updateFieldValue(assocProp, valueToSet, options);
196
+ } else {
197
+ actions.updateFieldValue(`.${prop.target}`, valueToSet, options);
198
+ }
199
+ }
200
+ });
201
+ }
202
+ return valueToSet;
203
+ };
204
+
205
+ const getGroupDataForItemsTree = (groupDataSource, groupsDisplayFieldMeta, showSecondaryInSearchOnly) => {
206
+ return groupDataSource?.map(group => {
207
+ const secondaryArr: any = [];
208
+ groupsDisplayFieldMeta.secondary.forEach(col => {
209
+ secondaryArr.push(group[col]);
210
+ });
211
+ return {
212
+ id: group[groupsDisplayFieldMeta.key],
213
+ primary: group[groupsDisplayFieldMeta.primary],
214
+ secondary: showSecondaryInSearchOnly ? [] : secondaryArr,
215
+ items: []
216
+ };
217
+ });
218
+ };
219
+
220
+ export {
221
+ useDeepMemo,
222
+ preProcessColumns,
223
+ getDisplayFieldsMetaData,
224
+ doSearch,
225
+ setValuesToPropertyList,
226
+ getGroupDataForItemsTree,
227
+ updateNewInstuctions,
228
+ insertInstruction,
229
+ deleteInstruction
230
+ };
@@ -1,17 +1,20 @@
1
- import CurrencyTextField from '@unicef/material-ui-currency-textfield';
2
-
1
+ import { TextField } from '@material-ui/core';
2
+ import { NumericFormat } from 'react-number-format';
3
+ import { useState } from 'react';
3
4
  import { getComponentFromMap } from '@pega/react-sdk-components/lib/bridge/helpers/sdk_component_map';
4
5
  import { PConnFieldProps } from '@pega/react-sdk-components/lib/types/PConnProps';
5
6
  import { getCurrencyCharacters, getCurrencyOptions } from '@pega/react-sdk-components/lib/components/field/Currency/currency-utils';
6
7
  import handleEvent from '@pega/react-sdk-components/lib/components/helpers/event-utils';
7
8
  import { format } from '@pega/react-sdk-components/lib/components/helpers/formatters';
8
9
 
9
- /* Using @unicef/material-ui-currency-textfield component here, since it allows formatting decimal values,
10
+ /* Using react-number-format component here, since it allows formatting decimal values,
10
11
  as per the locale.
11
12
  */
12
13
  interface PercentageProps extends PConnFieldProps {
13
14
  // If any, enter additional props that only exist on Percentage here
14
15
  currencyISOCode?: string;
16
+ showGroupSeparators?: string;
17
+ decimalPrecision?: number;
15
18
  }
16
19
 
17
20
  export default function Percentage(props: PercentageProps) {
@@ -34,9 +37,13 @@ export default function Percentage(props: PercentageProps) {
34
37
  helperText,
35
38
  displayMode,
36
39
  hideLabel,
37
- placeholder
40
+ placeholder,
41
+ showGroupSeparators,
42
+ decimalPrecision
38
43
  } = props;
39
44
 
45
+ const [values, setValues] = useState(value.toString());
46
+
40
47
  const pConn = getPConnect();
41
48
  const actions = pConn.getActionsApi();
42
49
  const propName = (pConn.getStateProps() as any).value;
@@ -45,7 +52,11 @@ export default function Percentage(props: PercentageProps) {
45
52
  const theCurrencyOptions = getCurrencyOptions(currencyISOCode);
46
53
  const formattedValue = format(value, pConn.getComponentName().toLowerCase(), theCurrencyOptions);
47
54
 
48
- // console.log(`Percentage: label: ${label} value: ${value}`);
55
+ let readOnlyProp = {}; // Note: empty if NOT ReadOnly
56
+
57
+ if (readOnly) {
58
+ readOnlyProp = { readOnly: true };
59
+ }
49
60
 
50
61
  if (displayMode === 'LABELS_LEFT') {
51
62
  return <FieldValueList name={hideLabel ? '' : label} value={formattedValue} />;
@@ -65,12 +76,17 @@ export default function Percentage(props: PercentageProps) {
65
76
  const theCurrDec = theSymbols.theDecimalIndicator;
66
77
  const theCurrSep = theSymbols.theDigitGroupSeparator;
67
78
 
68
- function PercentageOnBlur(event, inValue) {
69
- handleEvent(actions, 'changeNblur', propName, inValue !== '' ? Number(inValue) : inValue);
79
+ function PercentageOnBlur() {
80
+ handleEvent(actions, 'changeNblur', propName, values);
70
81
  }
71
82
 
83
+ const handleChange = val => {
84
+ setValues(val.value);
85
+ };
86
+
72
87
  return (
73
- <CurrencyTextField
88
+ <NumericFormat
89
+ valueIsNumericString
74
90
  fullWidth
75
91
  variant={readOnly ? 'standard' : 'outlined'}
76
92
  helperText={helperTextToDisplay}
@@ -78,20 +94,19 @@ export default function Percentage(props: PercentageProps) {
78
94
  size='small'
79
95
  required={required}
80
96
  disabled={disabled}
81
- readOnly={!!readOnly}
82
97
  error={status === 'error'}
83
98
  label={label}
84
- value={value}
85
- type='text'
86
- outputFormat='number'
87
- textAlign='left'
88
- InputProps={{
89
- inputProps: { ...testProp }
99
+ value={values}
100
+ onValueChange={val => {
101
+ handleChange(val);
90
102
  }}
91
- currencySymbol=''
92
- decimalCharacter={theCurrDec}
93
- digitGroupSeparator={theCurrSep}
94
103
  onBlur={!readOnly ? PercentageOnBlur : undefined}
104
+ decimalSeparator={theCurrDec}
105
+ thousandSeparator={showGroupSeparators ? theCurrSep : ''}
106
+ decimalScale={decimalPrecision}
107
+ suffix='%'
108
+ InputProps={{ ...readOnlyProp, inputProps: { ...testProp } }}
109
+ customInput={TextField}
95
110
  />
96
111
  );
97
112
  }
@@ -1,7 +1,9 @@
1
1
  import MuiPhoneNumber from 'material-ui-phone-number';
2
+ import { useEffect, useState } from 'react';
2
3
 
3
4
  import { getComponentFromMap } from '@pega/react-sdk-components/lib/bridge/helpers/sdk_component_map';
4
5
  import { PConnFieldProps } from '@pega/react-sdk-components/lib/types/PConnProps';
6
+ import handleEvent from '@pega/react-sdk-components/lib/components/helpers/event-utils';
5
7
 
6
8
  interface PhoneProps extends PConnFieldProps {
7
9
  // If any, enter additional props that only exist on Phone here
@@ -12,6 +14,7 @@ export default function Phone(props: PhoneProps) {
12
14
  const FieldValueList = getComponentFromMap('FieldValueList');
13
15
 
14
16
  const {
17
+ getPConnect,
15
18
  label,
16
19
  required,
17
20
  disabled,
@@ -19,7 +22,6 @@ export default function Phone(props: PhoneProps) {
19
22
  validatemessage,
20
23
  status,
21
24
  onChange,
22
- onBlur,
23
25
  readOnly,
24
26
  testId,
25
27
  helperText,
@@ -27,6 +29,14 @@ export default function Phone(props: PhoneProps) {
27
29
  hideLabel,
28
30
  placeholder
29
31
  } = props;
32
+
33
+ const pConn = getPConnect();
34
+ const actions = pConn.getActionsApi();
35
+ const propName = (pConn.getStateProps() as any).value;
36
+
37
+ const [inputValue, setInputValue] = useState(value);
38
+ useEffect(() => setInputValue(value), [value]);
39
+
30
40
  const helperTextToDisplay = validatemessage || helperText;
31
41
 
32
42
  let testProp = {};
@@ -69,15 +79,14 @@ export default function Phone(props: PhoneProps) {
69
79
  }
70
80
 
71
81
  const handleChange = inputVal => {
72
- let phoneValue = inputVal && inputVal.replace(/\D+/g, '');
73
- phoneValue = `+${phoneValue}`;
74
- onChange({ value: phoneValue });
82
+ setInputValue(inputVal);
75
83
  };
76
84
 
77
85
  const handleBlur = event => {
78
86
  const phoneValue = event?.target?.value;
79
- event.target.value = `+${phoneValue && phoneValue.replace(/\D+/g, '')}`;
80
- onBlur(event);
87
+ let phoneNumber = phoneValue.split(' ').slice(1).join();
88
+ phoneNumber = phoneNumber ? `+${phoneValue && phoneValue.replace(/\D+/g, '')}` : '';
89
+ handleEvent(actions, 'changeNblur', propName, phoneNumber);
81
90
  };
82
91
 
83
92
  return (
@@ -94,7 +103,7 @@ export default function Phone(props: PhoneProps) {
94
103
  onBlur={!readOnly ? handleBlur : undefined}
95
104
  error={status === 'error'}
96
105
  label={label}
97
- value={value}
106
+ value={inputValue}
98
107
  InputProps={{ ...testProp }}
99
108
  />
100
109
  );
@@ -24,11 +24,11 @@ export default function RichText(props: RichTextProps) {
24
24
  const helperTextToDisplay = validatemessage || helperText;
25
25
 
26
26
  if (displayMode === 'LABELS_LEFT') {
27
- return <FieldValueList name={hideLabel ? '' : label} value={value} />;
27
+ return <FieldValueList name={hideLabel ? '' : label} value={value} isHtml />;
28
28
  }
29
29
 
30
30
  if (displayMode === 'STACKED_LARGE_VAL') {
31
- return <FieldValueList name={hideLabel ? '' : label} value={value} variant='stacked' />;
31
+ return <FieldValueList name={hideLabel ? '' : label} value={value} isHtml variant='stacked' />;
32
32
  }
33
33
 
34
34
  let richTextComponent;
@@ -36,10 +36,12 @@ export default function ScalarList(props: ScalarListProps) {
36
36
  {
37
37
  type: componentType,
38
38
  config: {
39
+ // @ts-ignore - Type '{ readOnly: true; displayInModal: boolean; value: any; displayMode: string; label: string; }' is not assignable to type 'ComponentMetadataConfig'.
39
40
  value: scalarValue,
40
41
  displayMode: 'LABELS_LEFT',
41
42
  label,
42
43
  ...restProps,
44
+ // @ts-ignore - Type 'string' is not assignable to type 'boolean | undefined'.
43
45
  readOnly: 'true'
44
46
  }
45
47
  },