@pega/custom-dx-components 0.24.9 → 0.24.11
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.
- package/package.json +1 -1
- package/src/tasks/create/templates/Constellation/Field/Icon-Button-URL/index.tsx.mustache +70 -8
- package/src/tasks/create/templates/Constellation/Template/DETAILS/index.tsx.mustache +7 -3
- package/src/tasks/create/templates/Constellation/Template/DETAILS-region/index.tsx.mustache +6 -3
- package/src/tasks/create/templates/Constellation/Template/FORM/DetailsRender.tsx +2 -1
- package/src/tasks/create/templates/Constellation/Template/FORM/index.tsx.mustache +1 -1
- package/src/tasks/create/templates/Constellation/Template/FORM-region/DetailsRender.tsx +2 -1
- package/src/tasks/init/template/package.json.mustache +1 -1
- package/tests/assets/components/createAll/Constellation/Pega_DXIL_MyDetails/index.tsx +7 -3
- package/tests/assets/components/createAll/Constellation/Pega_DXIL_MyForm/DetailsRender.tsx +2 -1
- package/tests/assets/components/createAll/Constellation/Pega_DXIL_MyForm/index.tsx +1 -1
- package/tests/assets/components/createAll/Constellation/Pega_DXIL_MyIconButtonURL/index.tsx +70 -8
- package/tests/assets/components/createAll/Constellation/Pega_DXIL_MyTwoColumnDetails/index.tsx +6 -3
- package/tests/assets/components/createAll/Constellation/Pega_DXIL_MyTwoColumnForm/DetailsRender.tsx +2 -1
- package/tests/assets/components/createAll/Launchpad/Pega_DXIL_MyDetails/index.tsx +7 -3
- package/tests/assets/components/createAll/Launchpad/Pega_DXIL_MyForm/DetailsRender.tsx +2 -1
- package/tests/assets/components/createAll/Launchpad/Pega_DXIL_MyForm/index.tsx +1 -1
- package/tests/assets/components/createAll/Launchpad/Pega_DXIL_MyIconButtonURL/index.tsx +70 -8
- package/tests/assets/components/createAll/Launchpad/Pega_DXIL_MyTwoColumnDetails/index.tsx +6 -3
- package/tests/assets/components/createAll/Launchpad/Pega_DXIL_MyTwoColumnForm/DetailsRender.tsx +2 -1
package/package.json
CHANGED
|
@@ -5,7 +5,9 @@ import {
|
|
|
5
5
|
withConfiguration,
|
|
6
6
|
registerIcon,
|
|
7
7
|
Table,
|
|
8
|
-
Label
|
|
8
|
+
Label,
|
|
9
|
+
Link,
|
|
10
|
+
URLDisplay
|
|
9
11
|
} from '@pega/cosmos-react-core';
|
|
10
12
|
import type { PConnFieldProps } from './PConnProps';
|
|
11
13
|
|
|
@@ -15,6 +17,7 @@ import type { PConnFieldProps } from './PConnProps';
|
|
|
15
17
|
// Icons need to be registered (camel case), and then used (kabab case)
|
|
16
18
|
import * as listNumber from '@pega/cosmos-react-core/lib/components/Icon/icons/list-number.icon';
|
|
17
19
|
import * as arrowRight from '@pega/cosmos-react-core/lib/components/Icon/icons/arrow-right.icon';
|
|
20
|
+
import * as globe from '@pega/cosmos-react-core/lib/components/Icon/icons/globe.icon';
|
|
18
21
|
|
|
19
22
|
import Styled{{COMPONENT_CLASS_NAME}}Wrapper from './styles';
|
|
20
23
|
|
|
@@ -24,16 +27,32 @@ interface {{COMPONENT_CLASS_NAME}}Props extends PConnFieldProps {
|
|
|
24
27
|
countryCode: string;
|
|
25
28
|
}
|
|
26
29
|
|
|
30
|
+
export const textFormatter = (value: string) => {
|
|
31
|
+
const displayComponent = (
|
|
32
|
+
<URLDisplay target='_blank' value={value} displayText={value} variant='link' />
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
return displayComponent;
|
|
36
|
+
};
|
|
37
|
+
|
|
27
38
|
// *************
|
|
28
|
-
// This component is meant to be an example of usage (Icon, Button, etc.) and NOT meant to be
|
|
39
|
+
// This component is meant to be an example of usage (Icon, Button, Link, etc.) and NOT meant to be
|
|
29
40
|
// used "as is"
|
|
30
41
|
//
|
|
31
42
|
// In this example, Icon is shown on its own and inside a Button
|
|
32
43
|
// Though an Icon can have an "onClick" event is it recommended if you want to click on it,
|
|
33
|
-
// to put it in a Button (shown below), so you can have a tooltip and hover style
|
|
44
|
+
// to put it in a Button (shown below), so you can have a tooltip and hover style or put it
|
|
45
|
+
// in a Link (shown below) as a URL.
|
|
46
|
+
//
|
|
47
|
+
// For the button, the click will get a list of states for a given country and display in a
|
|
48
|
+
// table below. The table display will toggle with the click of the button icon. Example shows
|
|
49
|
+
// how to handle click, retrieve data from PCore and display it.
|
|
34
50
|
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
51
|
+
// Globe icon is an icon placed in a Link element. The Link element to use an icon internally. (NOTE: In general
|
|
52
|
+
// the Link element shows the open icon to the left of the link text, but here we are using
|
|
53
|
+
// another icon, so with "icon" specified in the Link element, the open icon doesn't show)
|
|
54
|
+
// Link elements do not handle bad urls well, so we have a simple validator to prevent the Link element
|
|
55
|
+
// from getting most bad urls.
|
|
37
56
|
//
|
|
38
57
|
// NOTE: because icons have to be imported and registered, you can NOT put the icon name
|
|
39
58
|
// inside the config.json and let the user pick it, if it is not already part of your
|
|
@@ -43,13 +62,14 @@ interface {{COMPONENT_CLASS_NAME}}Props extends PConnFieldProps {
|
|
|
43
62
|
// props passed in combination of props from property panel (config.json) and run time props from Constellation
|
|
44
63
|
// any default values in config.pros should be set in defaultProps at bottom of this file
|
|
45
64
|
function {{COMPONENT_CLASS_NAME}}(props: {{COMPONENT_CLASS_NAME}}Props) {
|
|
46
|
-
const { countryCode = 'USA', getPConnect } = props;
|
|
65
|
+
const { value, displayMode, countryCode = 'USA', getPConnect, testId } = props;
|
|
47
66
|
const [statelist, setStatelist] = useState([]);
|
|
48
67
|
const [showTable, setShowTable] = useState(false);
|
|
68
|
+
const [validUrl, setValidUrl] = useState('about:blank');
|
|
49
69
|
const PConnect = getPConnect();
|
|
50
70
|
|
|
51
71
|
// IMPORTANT: need to register icons used in camel case
|
|
52
|
-
registerIcon(listNumber, arrowRight);
|
|
72
|
+
registerIcon(listNumber, arrowRight, globe);
|
|
53
73
|
|
|
54
74
|
|
|
55
75
|
// setting up columns for the work list table
|
|
@@ -63,6 +83,29 @@ function {{COMPONENT_CLASS_NAME}}(props: {{COMPONENT_CLASS_NAME}}Props) {
|
|
|
63
83
|
setShowTable(prevShowTable => !prevShowTable);
|
|
64
84
|
}
|
|
65
85
|
|
|
86
|
+
// check if url is valid (simple)
|
|
87
|
+
function validateUrl(href: string) {
|
|
88
|
+
return /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(
|
|
89
|
+
href
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Testing to see if value entered is valid url, if not can't use it.
|
|
94
|
+
// Reason for this code is that you can NOT pass to Link component
|
|
95
|
+
// a bad url, or it will error out.
|
|
96
|
+
// For example: Since the value is the property field,
|
|
97
|
+
// it could be entered from another component and thus as it is
|
|
98
|
+
// entered (typed in), it won't be valid until it is all entered.
|
|
99
|
+
// NOTE: can't have a blank value for the url as the Link element will error out
|
|
100
|
+
// in App studio preview
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (validateUrl(value)) {
|
|
103
|
+
setValidUrl(value);
|
|
104
|
+
} else {
|
|
105
|
+
setValidUrl('about:blank');
|
|
106
|
+
}
|
|
107
|
+
}, [value]);
|
|
108
|
+
|
|
66
109
|
// going to get data for worklist to put into a table
|
|
67
110
|
// data comes from calling a PCore function to get
|
|
68
111
|
// get data from a data page and a PConnect function
|
|
@@ -98,10 +141,19 @@ function {{COMPONENT_CLASS_NAME}}(props: {{COMPONENT_CLASS_NAME}}Props) {
|
|
|
98
141
|
}
|
|
99
142
|
}, [PConnect, countryCode, showTable]);
|
|
100
143
|
|
|
144
|
+
// when DISPLAY_ONLY, in this example we will just show the URL.
|
|
145
|
+
if (displayMode === 'DISPLAY_ONLY') {
|
|
146
|
+
const displayComp = textFormatter(validUrl);
|
|
147
|
+
return (
|
|
148
|
+
<Styled{{COMPONENT_CLASS_NAME}}Wrapper> {displayComp} </Styled{{COMPONENT_CLASS_NAME}}Wrapper>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
101
152
|
return (
|
|
102
153
|
<Styled{{COMPONENT_CLASS_NAME}}Wrapper>
|
|
103
154
|
{{! pick new delimiters for mustache }}
|
|
104
155
|
{{=<% %>=}}
|
|
156
|
+
<div style={{ display: 'flex' }} data-testid={testId}>
|
|
105
157
|
<Icon
|
|
106
158
|
style={{ height: '1.5rem', width: '1.5rem' }}
|
|
107
159
|
// icon name is kabab case
|
|
@@ -109,7 +161,7 @@ function {{COMPONENT_CLASS_NAME}}(props: {{COMPONENT_CLASS_NAME}}Props) {
|
|
|
109
161
|
role='img'
|
|
110
162
|
aria-label='arrow right'
|
|
111
163
|
/>
|
|
112
|
-
<Label>
|
|
164
|
+
<Label style={{ margin: '0.1rem' }}>
|
|
113
165
|
{' '}
|
|
114
166
|
{PConnect.getLocalizedValue('State list for', '', '')}{' '}
|
|
115
167
|
{PConnect.getLocalizedValue(`${countryCode}`, '', '')}{' '}
|
|
@@ -129,6 +181,16 @@ function {{COMPONENT_CLASS_NAME}}(props: {{COMPONENT_CLASS_NAME}}Props) {
|
|
|
129
181
|
aria-label='numbered list'
|
|
130
182
|
/>
|
|
131
183
|
</Button>
|
|
184
|
+
<Link href={validUrl} variant='link' label={validUrl} icon>
|
|
185
|
+
<Icon
|
|
186
|
+
style={{ height: '1.5rem', width: '1.5rem' }}
|
|
187
|
+
// icon name is kabab case
|
|
188
|
+
name='globe'
|
|
189
|
+
role='img'
|
|
190
|
+
aria-label='open link'
|
|
191
|
+
/>
|
|
192
|
+
</Link>
|
|
193
|
+
</div>
|
|
132
194
|
{showTable && (
|
|
133
195
|
<Table
|
|
134
196
|
title={PConnect.getLocalizedValue('State list', '', '')}
|
|
@@ -34,7 +34,7 @@ function {{COMPONENT_CLASS_NAME}}(props: {{COMPONENT_CLASS_NAME}}Props) {
|
|
|
34
34
|
|
|
35
35
|
const numRegions = getAllFields(getPConnect)?.length;
|
|
36
36
|
const gridRepeat = "repeat(".concat(numRegions).concat(", 1fr)");
|
|
37
|
-
const gridContainer = {
|
|
37
|
+
const gridContainer = { colGap: 0, 'margin-line-start': 0 };
|
|
38
38
|
// @ts-ignore
|
|
39
39
|
gridContainer.cols = gridRepeat;
|
|
40
40
|
// @ts-ignore
|
|
@@ -88,8 +88,12 @@ function {{COMPONENT_CLASS_NAME}}(props: {{COMPONENT_CLASS_NAME}}Props) {
|
|
|
88
88
|
)}
|
|
89
89
|
<Grid container={gridContainer} data-testid={`column-count-${numRegions}`} >
|
|
90
90
|
{children.map((child: any, i: number) => (
|
|
91
|
-
|
|
92
|
-
<Flex
|
|
91
|
+
|
|
92
|
+
<Flex
|
|
93
|
+
// @ts-ignore
|
|
94
|
+
container={{ direction: 'column', alignItems: 'normal', colGap: 0, rowGap: 1.5 }}
|
|
95
|
+
key={`r-${i + 1}`}
|
|
96
|
+
>
|
|
93
97
|
<DetailsRender child={child} />
|
|
94
98
|
</Flex>
|
|
95
99
|
))}
|
|
@@ -34,7 +34,7 @@ function {{COMPONENT_CLASS_NAME}}(props: {{COMPONENT_CLASS_NAME}}Props) {
|
|
|
34
34
|
|
|
35
35
|
const numRegions = getAllFields(getPConnect)?.length;
|
|
36
36
|
const gridRepeat = "repeat(".concat(numRegions).concat(", 1fr)");
|
|
37
|
-
const gridContainer = {
|
|
37
|
+
const gridContainer = { colGap: 0, 'margin-line-start': 0 };
|
|
38
38
|
// @ts-ignore
|
|
39
39
|
gridContainer.cols = gridRepeat;
|
|
40
40
|
// @ts-ignore
|
|
@@ -95,8 +95,11 @@ function {{COMPONENT_CLASS_NAME}}(props: {{COMPONENT_CLASS_NAME}}Props) {
|
|
|
95
95
|
)}
|
|
96
96
|
<Grid container={gridContainer} data-testid={`column-count-${numRegions}`} >
|
|
97
97
|
{children.map((child: any, i: number) => (
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
<Flex
|
|
99
|
+
// @ts-ignore
|
|
100
|
+
container={{ direction: 'column', alignItems: 'normal', colGap: 0, rowGap: 1.5 }}
|
|
101
|
+
key={`r-${i + 1}`}
|
|
102
|
+
>
|
|
100
103
|
<DetailsRender child={child} />
|
|
101
104
|
</Flex>
|
|
102
105
|
))}
|
|
@@ -27,7 +27,7 @@ function {{COMPONENT_CLASS_NAME}}(props: {{COMPONENT_CLASS_NAME}}Props) {
|
|
|
27
27
|
if (readOnly && readOnly === true && displayMode && displayMode === 'DISPLAY_ONLY') {
|
|
28
28
|
const numRegions = '1';
|
|
29
29
|
const gridRepeat = 'repeat('.concat(numRegions).concat(', 1fr)');
|
|
30
|
-
const gridContainer = { colGap: 0 };
|
|
30
|
+
const gridContainer = { colGap: 0, 'margin-line-start': 0 };
|
|
31
31
|
// @ts-ignore
|
|
32
32
|
gridContainer.cols = gridRepeat;
|
|
33
33
|
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@pega/configs": "^0.6.0",
|
|
72
|
-
"@pega/custom-dx-components": "0.24.
|
|
72
|
+
"@pega/custom-dx-components": "0.24.11",
|
|
73
73
|
"@pega/pcore-pconnect-typedefs": "~3.0.0",
|
|
74
74
|
"@storybook/addon-actions": "^7.6.7",
|
|
75
75
|
"@storybook/addon-essentials": "^7.6.7",
|
|
@@ -34,7 +34,7 @@ function PegaDxilMyDetails(props: PegaDxilMyDetailsProps) {
|
|
|
34
34
|
|
|
35
35
|
const numRegions = getAllFields(getPConnect)?.length;
|
|
36
36
|
const gridRepeat = "repeat(".concat(numRegions).concat(", 1fr)");
|
|
37
|
-
const gridContainer = {
|
|
37
|
+
const gridContainer = { colGap: 0, 'margin-line-start': 0 };
|
|
38
38
|
// @ts-ignore
|
|
39
39
|
gridContainer.cols = gridRepeat;
|
|
40
40
|
// @ts-ignore
|
|
@@ -86,8 +86,12 @@ function PegaDxilMyDetails(props: PegaDxilMyDetailsProps) {
|
|
|
86
86
|
)}
|
|
87
87
|
<Grid container={gridContainer} data-testid={`column-count-${numRegions}`} >
|
|
88
88
|
{children.map((child: any, i: number) => (
|
|
89
|
-
|
|
90
|
-
<Flex
|
|
89
|
+
|
|
90
|
+
<Flex
|
|
91
|
+
// @ts-ignore
|
|
92
|
+
container={{ direction: 'column', alignItems: 'normal', colGap: 0, rowGap: 1.5 }}
|
|
93
|
+
key={`r-${i + 1}`}
|
|
94
|
+
>
|
|
91
95
|
<DetailsRender child={child} />
|
|
92
96
|
</Flex>
|
|
93
97
|
))}
|
|
@@ -27,7 +27,7 @@ function PegaDxilMyForm(props: PegaDxilMyFormProps) {
|
|
|
27
27
|
if (readOnly && readOnly === true && displayMode && displayMode === 'DISPLAY_ONLY') {
|
|
28
28
|
const numRegions = '1';
|
|
29
29
|
const gridRepeat = 'repeat('.concat(numRegions).concat(', 1fr)');
|
|
30
|
-
const gridContainer = { colGap: 0 };
|
|
30
|
+
const gridContainer = { colGap: 0, 'margin-line-start': 0 };
|
|
31
31
|
// @ts-ignore
|
|
32
32
|
gridContainer.cols = gridRepeat;
|
|
33
33
|
|
|
@@ -5,7 +5,9 @@ import {
|
|
|
5
5
|
withConfiguration,
|
|
6
6
|
registerIcon,
|
|
7
7
|
Table,
|
|
8
|
-
Label
|
|
8
|
+
Label,
|
|
9
|
+
Link,
|
|
10
|
+
URLDisplay
|
|
9
11
|
} from '@pega/cosmos-react-core';
|
|
10
12
|
import type { PConnFieldProps } from './PConnProps';
|
|
11
13
|
|
|
@@ -15,6 +17,7 @@ import type { PConnFieldProps } from './PConnProps';
|
|
|
15
17
|
// Icons need to be registered (camel case), and then used (kabab case)
|
|
16
18
|
import * as listNumber from '@pega/cosmos-react-core/lib/components/Icon/icons/list-number.icon';
|
|
17
19
|
import * as arrowRight from '@pega/cosmos-react-core/lib/components/Icon/icons/arrow-right.icon';
|
|
20
|
+
import * as globe from '@pega/cosmos-react-core/lib/components/Icon/icons/globe.icon';
|
|
18
21
|
|
|
19
22
|
import StyledPegaDxilMyIconButtonUrlWrapper from './styles';
|
|
20
23
|
|
|
@@ -24,16 +27,32 @@ interface PegaDxilMyIconButtonUrlProps extends PConnFieldProps {
|
|
|
24
27
|
countryCode: string;
|
|
25
28
|
}
|
|
26
29
|
|
|
30
|
+
export const textFormatter = (value: string) => {
|
|
31
|
+
const displayComponent = (
|
|
32
|
+
<URLDisplay target='_blank' value={value} displayText={value} variant='link' />
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
return displayComponent;
|
|
36
|
+
};
|
|
37
|
+
|
|
27
38
|
// *************
|
|
28
|
-
// This component is meant to be an example of usage (Icon, Button, etc.) and NOT meant to be
|
|
39
|
+
// This component is meant to be an example of usage (Icon, Button, Link, etc.) and NOT meant to be
|
|
29
40
|
// used "as is"
|
|
30
41
|
//
|
|
31
42
|
// In this example, Icon is shown on its own and inside a Button
|
|
32
43
|
// Though an Icon can have an "onClick" event is it recommended if you want to click on it,
|
|
33
|
-
// to put it in a Button (shown below), so you can have a tooltip and hover style
|
|
44
|
+
// to put it in a Button (shown below), so you can have a tooltip and hover style or put it
|
|
45
|
+
// in a Link (shown below) as a URL.
|
|
46
|
+
//
|
|
47
|
+
// For the button, the click will get a list of states for a given country and display in a
|
|
48
|
+
// table below. The table display will toggle with the click of the button icon. Example shows
|
|
49
|
+
// how to handle click, retrieve data from PCore and display it.
|
|
34
50
|
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
51
|
+
// Globe icon is an icon placed in a Link element. The Link element to use an icon internally. (NOTE: In general
|
|
52
|
+
// the Link element shows the open icon to the left of the link text, but here we are using
|
|
53
|
+
// another icon, so with "icon" specified in the Link element, the open icon doesn't show)
|
|
54
|
+
// Link elements do not handle bad urls well, so we have a simple validator to prevent the Link element
|
|
55
|
+
// from getting most bad urls.
|
|
37
56
|
//
|
|
38
57
|
// NOTE: because icons have to be imported and registered, you can NOT put the icon name
|
|
39
58
|
// inside the config.json and let the user pick it, if it is not already part of your
|
|
@@ -43,13 +62,14 @@ interface PegaDxilMyIconButtonUrlProps extends PConnFieldProps {
|
|
|
43
62
|
// props passed in combination of props from property panel (config.json) and run time props from Constellation
|
|
44
63
|
// any default values in config.pros should be set in defaultProps at bottom of this file
|
|
45
64
|
function PegaDxilMyIconButtonUrl(props: PegaDxilMyIconButtonUrlProps) {
|
|
46
|
-
const { countryCode = 'USA', getPConnect } = props;
|
|
65
|
+
const { value, displayMode, countryCode = 'USA', getPConnect, testId } = props;
|
|
47
66
|
const [statelist, setStatelist] = useState([]);
|
|
48
67
|
const [showTable, setShowTable] = useState(false);
|
|
68
|
+
const [validUrl, setValidUrl] = useState('about:blank');
|
|
49
69
|
const PConnect = getPConnect();
|
|
50
70
|
|
|
51
71
|
// IMPORTANT: need to register icons used in camel case
|
|
52
|
-
registerIcon(listNumber, arrowRight);
|
|
72
|
+
registerIcon(listNumber, arrowRight, globe);
|
|
53
73
|
|
|
54
74
|
|
|
55
75
|
// setting up columns for the work list table
|
|
@@ -63,6 +83,29 @@ function PegaDxilMyIconButtonUrl(props: PegaDxilMyIconButtonUrlProps) {
|
|
|
63
83
|
setShowTable(prevShowTable => !prevShowTable);
|
|
64
84
|
}
|
|
65
85
|
|
|
86
|
+
// check if url is valid (simple)
|
|
87
|
+
function validateUrl(href: string) {
|
|
88
|
+
return /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(
|
|
89
|
+
href
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Testing to see if value entered is valid url, if not can't use it.
|
|
94
|
+
// Reason for this code is that you can NOT pass to Link component
|
|
95
|
+
// a bad url, or it will error out.
|
|
96
|
+
// For example: Since the value is the property field,
|
|
97
|
+
// it could be entered from another component and thus as it is
|
|
98
|
+
// entered (typed in), it won't be valid until it is all entered.
|
|
99
|
+
// NOTE: can't have a blank value for the url as the Link element will error out
|
|
100
|
+
// in App studio preview
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (validateUrl(value)) {
|
|
103
|
+
setValidUrl(value);
|
|
104
|
+
} else {
|
|
105
|
+
setValidUrl('about:blank');
|
|
106
|
+
}
|
|
107
|
+
}, [value]);
|
|
108
|
+
|
|
66
109
|
// going to get data for worklist to put into a table
|
|
67
110
|
// data comes from calling a PCore function to get
|
|
68
111
|
// get data from a data page and a PConnect function
|
|
@@ -98,8 +141,17 @@ function PegaDxilMyIconButtonUrl(props: PegaDxilMyIconButtonUrlProps) {
|
|
|
98
141
|
}
|
|
99
142
|
}, [PConnect, countryCode, showTable]);
|
|
100
143
|
|
|
144
|
+
// when DISPLAY_ONLY, in this example we will just show the URL.
|
|
145
|
+
if (displayMode === 'DISPLAY_ONLY') {
|
|
146
|
+
const displayComp = textFormatter(validUrl);
|
|
147
|
+
return (
|
|
148
|
+
<StyledPegaDxilMyIconButtonUrlWrapper> {displayComp} </StyledPegaDxilMyIconButtonUrlWrapper>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
101
152
|
return (
|
|
102
153
|
<StyledPegaDxilMyIconButtonUrlWrapper>
|
|
154
|
+
<div style={{ display: 'flex' }} data-testid={testId}>
|
|
103
155
|
<Icon
|
|
104
156
|
style={{ height: '1.5rem', width: '1.5rem' }}
|
|
105
157
|
// icon name is kabab case
|
|
@@ -107,7 +159,7 @@ function PegaDxilMyIconButtonUrl(props: PegaDxilMyIconButtonUrlProps) {
|
|
|
107
159
|
role='img'
|
|
108
160
|
aria-label='arrow right'
|
|
109
161
|
/>
|
|
110
|
-
<Label>
|
|
162
|
+
<Label style={{ margin: '0.1rem' }}>
|
|
111
163
|
{' '}
|
|
112
164
|
{PConnect.getLocalizedValue('State list for', '', '')}{' '}
|
|
113
165
|
{PConnect.getLocalizedValue(`${countryCode}`, '', '')}{' '}
|
|
@@ -127,6 +179,16 @@ function PegaDxilMyIconButtonUrl(props: PegaDxilMyIconButtonUrlProps) {
|
|
|
127
179
|
aria-label='numbered list'
|
|
128
180
|
/>
|
|
129
181
|
</Button>
|
|
182
|
+
<Link href={validUrl} variant='link' label={validUrl} icon>
|
|
183
|
+
<Icon
|
|
184
|
+
style={{ height: '1.5rem', width: '1.5rem' }}
|
|
185
|
+
// icon name is kabab case
|
|
186
|
+
name='globe'
|
|
187
|
+
role='img'
|
|
188
|
+
aria-label='open link'
|
|
189
|
+
/>
|
|
190
|
+
</Link>
|
|
191
|
+
</div>
|
|
130
192
|
{showTable && (
|
|
131
193
|
<Table
|
|
132
194
|
title={PConnect.getLocalizedValue('State list', '', '')}
|
package/tests/assets/components/createAll/Constellation/Pega_DXIL_MyTwoColumnDetails/index.tsx
CHANGED
|
@@ -34,7 +34,7 @@ function PegaDxilMyTwoColumnDetails(props: PegaDxilMyTwoColumnDetailsProps) {
|
|
|
34
34
|
|
|
35
35
|
const numRegions = getAllFields(getPConnect)?.length;
|
|
36
36
|
const gridRepeat = "repeat(".concat(numRegions).concat(", 1fr)");
|
|
37
|
-
const gridContainer = {
|
|
37
|
+
const gridContainer = { colGap: 0, 'margin-line-start': 0 };
|
|
38
38
|
// @ts-ignore
|
|
39
39
|
gridContainer.cols = gridRepeat;
|
|
40
40
|
// @ts-ignore
|
|
@@ -93,8 +93,11 @@ function PegaDxilMyTwoColumnDetails(props: PegaDxilMyTwoColumnDetailsProps) {
|
|
|
93
93
|
)}
|
|
94
94
|
<Grid container={gridContainer} data-testid={`column-count-${numRegions}`} >
|
|
95
95
|
{children.map((child: any, i: number) => (
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
<Flex
|
|
97
|
+
// @ts-ignore
|
|
98
|
+
container={{ direction: 'column', alignItems: 'normal', colGap: 0, rowGap: 1.5 }}
|
|
99
|
+
key={`r-${i + 1}`}
|
|
100
|
+
>
|
|
98
101
|
<DetailsRender child={child} />
|
|
99
102
|
</Flex>
|
|
100
103
|
))}
|
|
@@ -34,7 +34,7 @@ function PegaDxilMyDetails(props: PegaDxilMyDetailsProps) {
|
|
|
34
34
|
|
|
35
35
|
const numRegions = getAllFields(getPConnect)?.length;
|
|
36
36
|
const gridRepeat = "repeat(".concat(numRegions).concat(", 1fr)");
|
|
37
|
-
const gridContainer = {
|
|
37
|
+
const gridContainer = { colGap: 0, 'margin-line-start': 0 };
|
|
38
38
|
// @ts-ignore
|
|
39
39
|
gridContainer.cols = gridRepeat;
|
|
40
40
|
// @ts-ignore
|
|
@@ -86,8 +86,12 @@ function PegaDxilMyDetails(props: PegaDxilMyDetailsProps) {
|
|
|
86
86
|
)}
|
|
87
87
|
<Grid container={gridContainer} data-testid={`column-count-${numRegions}`} >
|
|
88
88
|
{children.map((child: any, i: number) => (
|
|
89
|
-
|
|
90
|
-
<Flex
|
|
89
|
+
|
|
90
|
+
<Flex
|
|
91
|
+
// @ts-ignore
|
|
92
|
+
container={{ direction: 'column', alignItems: 'normal', colGap: 0, rowGap: 1.5 }}
|
|
93
|
+
key={`r-${i + 1}`}
|
|
94
|
+
>
|
|
91
95
|
<DetailsRender child={child} />
|
|
92
96
|
</Flex>
|
|
93
97
|
))}
|
|
@@ -27,7 +27,7 @@ function PegaDxilMyForm(props: PegaDxilMyFormProps) {
|
|
|
27
27
|
if (readOnly && readOnly === true && displayMode && displayMode === 'DISPLAY_ONLY') {
|
|
28
28
|
const numRegions = '1';
|
|
29
29
|
const gridRepeat = 'repeat('.concat(numRegions).concat(', 1fr)');
|
|
30
|
-
const gridContainer = { colGap: 0 };
|
|
30
|
+
const gridContainer = { colGap: 0, 'margin-line-start': 0 };
|
|
31
31
|
// @ts-ignore
|
|
32
32
|
gridContainer.cols = gridRepeat;
|
|
33
33
|
|
|
@@ -5,7 +5,9 @@ import {
|
|
|
5
5
|
withConfiguration,
|
|
6
6
|
registerIcon,
|
|
7
7
|
Table,
|
|
8
|
-
Label
|
|
8
|
+
Label,
|
|
9
|
+
Link,
|
|
10
|
+
URLDisplay
|
|
9
11
|
} from '@pega/cosmos-react-core';
|
|
10
12
|
import type { PConnFieldProps } from './PConnProps';
|
|
11
13
|
|
|
@@ -15,6 +17,7 @@ import type { PConnFieldProps } from './PConnProps';
|
|
|
15
17
|
// Icons need to be registered (camel case), and then used (kabab case)
|
|
16
18
|
import * as listNumber from '@pega/cosmos-react-core/lib/components/Icon/icons/list-number.icon';
|
|
17
19
|
import * as arrowRight from '@pega/cosmos-react-core/lib/components/Icon/icons/arrow-right.icon';
|
|
20
|
+
import * as globe from '@pega/cosmos-react-core/lib/components/Icon/icons/globe.icon';
|
|
18
21
|
|
|
19
22
|
import StyledPegaDxilMyIconButtonUrlWrapper from './styles';
|
|
20
23
|
|
|
@@ -24,16 +27,32 @@ interface PegaDxilMyIconButtonUrlProps extends PConnFieldProps {
|
|
|
24
27
|
countryCode: string;
|
|
25
28
|
}
|
|
26
29
|
|
|
30
|
+
export const textFormatter = (value: string) => {
|
|
31
|
+
const displayComponent = (
|
|
32
|
+
<URLDisplay target='_blank' value={value} displayText={value} variant='link' />
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
return displayComponent;
|
|
36
|
+
};
|
|
37
|
+
|
|
27
38
|
// *************
|
|
28
|
-
// This component is meant to be an example of usage (Icon, Button, etc.) and NOT meant to be
|
|
39
|
+
// This component is meant to be an example of usage (Icon, Button, Link, etc.) and NOT meant to be
|
|
29
40
|
// used "as is"
|
|
30
41
|
//
|
|
31
42
|
// In this example, Icon is shown on its own and inside a Button
|
|
32
43
|
// Though an Icon can have an "onClick" event is it recommended if you want to click on it,
|
|
33
|
-
// to put it in a Button (shown below), so you can have a tooltip and hover style
|
|
44
|
+
// to put it in a Button (shown below), so you can have a tooltip and hover style or put it
|
|
45
|
+
// in a Link (shown below) as a URL.
|
|
46
|
+
//
|
|
47
|
+
// For the button, the click will get a list of states for a given country and display in a
|
|
48
|
+
// table below. The table display will toggle with the click of the button icon. Example shows
|
|
49
|
+
// how to handle click, retrieve data from PCore and display it.
|
|
34
50
|
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
51
|
+
// Globe icon is an icon placed in a Link element. The Link element to use an icon internally. (NOTE: In general
|
|
52
|
+
// the Link element shows the open icon to the left of the link text, but here we are using
|
|
53
|
+
// another icon, so with "icon" specified in the Link element, the open icon doesn't show)
|
|
54
|
+
// Link elements do not handle bad urls well, so we have a simple validator to prevent the Link element
|
|
55
|
+
// from getting most bad urls.
|
|
37
56
|
//
|
|
38
57
|
// NOTE: because icons have to be imported and registered, you can NOT put the icon name
|
|
39
58
|
// inside the config.json and let the user pick it, if it is not already part of your
|
|
@@ -43,13 +62,14 @@ interface PegaDxilMyIconButtonUrlProps extends PConnFieldProps {
|
|
|
43
62
|
// props passed in combination of props from property panel (config.json) and run time props from Constellation
|
|
44
63
|
// any default values in config.pros should be set in defaultProps at bottom of this file
|
|
45
64
|
function PegaDxilMyIconButtonUrl(props: PegaDxilMyIconButtonUrlProps) {
|
|
46
|
-
const { countryCode = 'USA', getPConnect } = props;
|
|
65
|
+
const { value, displayMode, countryCode = 'USA', getPConnect, testId } = props;
|
|
47
66
|
const [statelist, setStatelist] = useState([]);
|
|
48
67
|
const [showTable, setShowTable] = useState(false);
|
|
68
|
+
const [validUrl, setValidUrl] = useState('about:blank');
|
|
49
69
|
const PConnect = getPConnect();
|
|
50
70
|
|
|
51
71
|
// IMPORTANT: need to register icons used in camel case
|
|
52
|
-
registerIcon(listNumber, arrowRight);
|
|
72
|
+
registerIcon(listNumber, arrowRight, globe);
|
|
53
73
|
|
|
54
74
|
|
|
55
75
|
// setting up columns for the work list table
|
|
@@ -63,6 +83,29 @@ function PegaDxilMyIconButtonUrl(props: PegaDxilMyIconButtonUrlProps) {
|
|
|
63
83
|
setShowTable(prevShowTable => !prevShowTable);
|
|
64
84
|
}
|
|
65
85
|
|
|
86
|
+
// check if url is valid (simple)
|
|
87
|
+
function validateUrl(href: string) {
|
|
88
|
+
return /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(
|
|
89
|
+
href
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Testing to see if value entered is valid url, if not can't use it.
|
|
94
|
+
// Reason for this code is that you can NOT pass to Link component
|
|
95
|
+
// a bad url, or it will error out.
|
|
96
|
+
// For example: Since the value is the property field,
|
|
97
|
+
// it could be entered from another component and thus as it is
|
|
98
|
+
// entered (typed in), it won't be valid until it is all entered.
|
|
99
|
+
// NOTE: can't have a blank value for the url as the Link element will error out
|
|
100
|
+
// in App studio preview
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (validateUrl(value)) {
|
|
103
|
+
setValidUrl(value);
|
|
104
|
+
} else {
|
|
105
|
+
setValidUrl('about:blank');
|
|
106
|
+
}
|
|
107
|
+
}, [value]);
|
|
108
|
+
|
|
66
109
|
// going to get data for worklist to put into a table
|
|
67
110
|
// data comes from calling a PCore function to get
|
|
68
111
|
// get data from a data page and a PConnect function
|
|
@@ -98,8 +141,17 @@ function PegaDxilMyIconButtonUrl(props: PegaDxilMyIconButtonUrlProps) {
|
|
|
98
141
|
}
|
|
99
142
|
}, [PConnect, countryCode, showTable]);
|
|
100
143
|
|
|
144
|
+
// when DISPLAY_ONLY, in this example we will just show the URL.
|
|
145
|
+
if (displayMode === 'DISPLAY_ONLY') {
|
|
146
|
+
const displayComp = textFormatter(validUrl);
|
|
147
|
+
return (
|
|
148
|
+
<StyledPegaDxilMyIconButtonUrlWrapper> {displayComp} </StyledPegaDxilMyIconButtonUrlWrapper>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
101
152
|
return (
|
|
102
153
|
<StyledPegaDxilMyIconButtonUrlWrapper>
|
|
154
|
+
<div style={{ display: 'flex' }} data-testid={testId}>
|
|
103
155
|
<Icon
|
|
104
156
|
style={{ height: '1.5rem', width: '1.5rem' }}
|
|
105
157
|
// icon name is kabab case
|
|
@@ -107,7 +159,7 @@ function PegaDxilMyIconButtonUrl(props: PegaDxilMyIconButtonUrlProps) {
|
|
|
107
159
|
role='img'
|
|
108
160
|
aria-label='arrow right'
|
|
109
161
|
/>
|
|
110
|
-
<Label>
|
|
162
|
+
<Label style={{ margin: '0.1rem' }}>
|
|
111
163
|
{' '}
|
|
112
164
|
{PConnect.getLocalizedValue('State list for', '', '')}{' '}
|
|
113
165
|
{PConnect.getLocalizedValue(`${countryCode}`, '', '')}{' '}
|
|
@@ -127,6 +179,16 @@ function PegaDxilMyIconButtonUrl(props: PegaDxilMyIconButtonUrlProps) {
|
|
|
127
179
|
aria-label='numbered list'
|
|
128
180
|
/>
|
|
129
181
|
</Button>
|
|
182
|
+
<Link href={validUrl} variant='link' label={validUrl} icon>
|
|
183
|
+
<Icon
|
|
184
|
+
style={{ height: '1.5rem', width: '1.5rem' }}
|
|
185
|
+
// icon name is kabab case
|
|
186
|
+
name='globe'
|
|
187
|
+
role='img'
|
|
188
|
+
aria-label='open link'
|
|
189
|
+
/>
|
|
190
|
+
</Link>
|
|
191
|
+
</div>
|
|
130
192
|
{showTable && (
|
|
131
193
|
<Table
|
|
132
194
|
title={PConnect.getLocalizedValue('State list', '', '')}
|
|
@@ -34,7 +34,7 @@ function PegaDxilMyTwoColumnDetails(props: PegaDxilMyTwoColumnDetailsProps) {
|
|
|
34
34
|
|
|
35
35
|
const numRegions = getAllFields(getPConnect)?.length;
|
|
36
36
|
const gridRepeat = "repeat(".concat(numRegions).concat(", 1fr)");
|
|
37
|
-
const gridContainer = {
|
|
37
|
+
const gridContainer = { colGap: 0, 'margin-line-start': 0 };
|
|
38
38
|
// @ts-ignore
|
|
39
39
|
gridContainer.cols = gridRepeat;
|
|
40
40
|
// @ts-ignore
|
|
@@ -93,8 +93,11 @@ function PegaDxilMyTwoColumnDetails(props: PegaDxilMyTwoColumnDetailsProps) {
|
|
|
93
93
|
)}
|
|
94
94
|
<Grid container={gridContainer} data-testid={`column-count-${numRegions}`} >
|
|
95
95
|
{children.map((child: any, i: number) => (
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
<Flex
|
|
97
|
+
// @ts-ignore
|
|
98
|
+
container={{ direction: 'column', alignItems: 'normal', colGap: 0, rowGap: 1.5 }}
|
|
99
|
+
key={`r-${i + 1}`}
|
|
100
|
+
>
|
|
98
101
|
<DetailsRender child={child} />
|
|
99
102
|
</Flex>
|
|
100
103
|
))}
|