@pega/custom-dx-components 0.0.1

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 (71) hide show
  1. package/.eslintrc.json +18 -0
  2. package/.prettierignore +2 -0
  3. package/.prettierrc.json +1 -0
  4. package/.vscode/extensions.json +9 -0
  5. package/.vscode/settings.json +48 -0
  6. package/LICENSE +185 -0
  7. package/README.md +204 -0
  8. package/bin/index.js +5 -0
  9. package/cspell.json +26 -0
  10. package/package.json +67 -0
  11. package/src/cli.js +92 -0
  12. package/src/constants.js +92 -0
  13. package/src/tasks/authenticate/helper.js +60 -0
  14. package/src/tasks/authenticate/index.js +75 -0
  15. package/src/tasks/create/index.js +304 -0
  16. package/src/tasks/create/template/config.json.mustache +67 -0
  17. package/src/tasks/create/template/demo.stories.jsx.mustache +44 -0
  18. package/src/tasks/create/template/index.jsx.mustache +55 -0
  19. package/src/tasks/create/template/styles.js.mustache +7 -0
  20. package/src/tasks/delete/helper.js +105 -0
  21. package/src/tasks/delete/index.js +45 -0
  22. package/src/tasks/index.js +9 -0
  23. package/src/tasks/init/helper.js +63 -0
  24. package/src/tasks/init/index.js +165 -0
  25. package/src/tasks/init/template/.eslintrc.json +10 -0
  26. package/src/tasks/init/template/.prettierrc.json +1 -0
  27. package/src/tasks/init/template/.storybook/main.js +6 -0
  28. package/src/tasks/init/template/.storybook/pega-logo.svg +16 -0
  29. package/src/tasks/init/template/.storybook/preview.tsx +44 -0
  30. package/src/tasks/init/template/.storybook/theme.js +44 -0
  31. package/src/tasks/init/template/.stylelintrc.json +4 -0
  32. package/src/tasks/init/template/.vscode/extensions.json +9 -0
  33. package/src/tasks/init/template/.vscode/settings.json +48 -0
  34. package/src/tasks/init/template/README.md +204 -0
  35. package/src/tasks/init/template/build.config.json +1 -0
  36. package/src/tasks/init/template/cspell.json +14 -0
  37. package/src/tasks/init/template/package.json.mustache +69 -0
  38. package/src/tasks/init/template/src/components/MBTAList/MBTAList.stories.tsx +10 -0
  39. package/src/tasks/init/template/src/components/MBTAList/MBTAList.tsx +122 -0
  40. package/src/tasks/init/template/src/components/MBTAList/icons/accessible.icon.tsx +14 -0
  41. package/src/tasks/init/template/src/components/MBTAList/icons/bus.icon.tsx +13 -0
  42. package/src/tasks/init/template/src/components/MBTAList/icons/mbta.icon.tsx +10 -0
  43. package/src/tasks/init/template/src/components/MBTAList/icons/subway.icon.tsx +14 -0
  44. package/src/tasks/init/template/src/components/MBTAList/index.ts +2 -0
  45. package/src/tasks/init/template/src/components/Organization_Library_MediaURL/MediaURL.stories.tsx +19 -0
  46. package/src/tasks/init/template/src/components/Organization_Library_MediaURL/MediaURL.styles.ts +5 -0
  47. package/src/tasks/init/template/src/components/Organization_Library_MediaURL/MediaURL.types.ts +10 -0
  48. package/src/tasks/init/template/src/components/Organization_Library_MediaURL/MediaURLDisplay.tsx +25 -0
  49. package/src/tasks/init/template/src/components/Organization_Library_MediaURL/MediaURLInput.tsx +24 -0
  50. package/src/tasks/init/template/src/components/Organization_Library_MediaURL/config.json +13 -0
  51. package/src/tasks/init/template/src/components/Organization_Library_MediaURL/index.ts +2 -0
  52. package/src/tasks/init/template/src/components/Organization_Library_VideoPlayer/config.json +44 -0
  53. package/src/tasks/init/template/src/components/Organization_Library_VideoPlayer/demo.stories.jsx +40 -0
  54. package/src/tasks/init/template/src/components/Organization_Library_VideoPlayer/index.jsx +42 -0
  55. package/src/tasks/init/template/src/components/Organization_Library_VideoPlayer/styles.js +5 -0
  56. package/src/tasks/init/template/tasks.config.json +21 -0
  57. package/src/tasks/init/template/tsconfig.build.json +16 -0
  58. package/src/tasks/init/template/tsconfig.json +8 -0
  59. package/src/tasks/list/helper.js +118 -0
  60. package/src/tasks/list/index.js +51 -0
  61. package/src/tasks/publish/bundle/builder/index.js +174 -0
  62. package/src/tasks/publish/bundle/builder/modules.js +139 -0
  63. package/src/tasks/publish/bundle/builder/paths.js +50 -0
  64. package/src/tasks/publish/bundle/builder/webpack.config.js +334 -0
  65. package/src/tasks/publish/bundle/index.js +65 -0
  66. package/src/tasks/publish/helper.js +145 -0
  67. package/src/tasks/publish/index.js +55 -0
  68. package/src/tasks/update/index.js +5 -0
  69. package/src/tasks/upgrade/index.js +31 -0
  70. package/src/tasks/validator/index.js +102 -0
  71. package/src/util.js +277 -0
@@ -0,0 +1,92 @@
1
+ // cSpell:ignore words timeofday caseview casepreview objectview
2
+ export const TASKS_CONFIG_JSON_FILENAME = 'tasks.config.json';
3
+ export const BUILD_CONFIG_JSON_FILENAME = 'build.config.json';
4
+ export const COMPONENTS_DIRECTORY_PATH = 'components-directory-path';
5
+ export const TOKEN_PATH = 'node_modules/@pega/custom-dx-components/.access_token';
6
+ export const OOTB_COMPONENTS = 'node_modules/@pega/custom-dx-components/.ootb_components';
7
+
8
+ /* REST end points */
9
+ export const PUBLISH_COMPONENT_SERVICE_REST_ENDPOINT = 'PRRestService/dev/v1/publish_component';
10
+ export const LIST_COMPONENT_SERVICE_REST_ENDPOINT = 'PRRestService/dev/v1/list_component';
11
+ export const OOTB_COMPONENT_SERVICE_REST_ENDPOINT = 'PRRestService/dev/v1/ootb_component';
12
+ export const DELETE_COMPONENT_SERVICE_REST_ENDPOINT = 'PRRestService/dev/v1/delete_component';
13
+ export const TOKEN_URL_V1 = 'PRRestService/oauth2/v1/token';
14
+
15
+ export const COMPONENT_SCHEMA = {
16
+ type: [
17
+ {
18
+ name: 'Field',
19
+ value: 'Field'
20
+ },
21
+ {
22
+ name: 'Layout template',
23
+ value: 'Template'
24
+ },
25
+ {
26
+ name: 'Widget',
27
+ value: 'Widget'
28
+ }
29
+ ],
30
+ subtype: {
31
+ field: [
32
+ {
33
+ name: 'Text',
34
+ value: 'Text'
35
+ },
36
+ {
37
+ name: 'Paragraph',
38
+ value: 'Text-Paragraph'
39
+ },
40
+ {
41
+ name: 'Email',
42
+ value: 'Email'
43
+ },
44
+ {
45
+ name: 'Phone',
46
+ value: 'Phone'
47
+ },
48
+ {
49
+ name: 'URL',
50
+ value: 'URL'
51
+ },
52
+ {
53
+ name: 'Integer',
54
+ value: 'Integer'
55
+ },
56
+ {
57
+ name: 'Decimal',
58
+ value: 'Decimal'
59
+ },
60
+ {
61
+ name: 'Currency',
62
+ value: 'Decimal-Currency'
63
+ },
64
+ {
65
+ name: 'Percentage',
66
+ value: 'Decimal-Percentage'
67
+ },
68
+ {
69
+ name: 'Boolean',
70
+ value: 'Boolean'
71
+ },
72
+ {
73
+ name: 'Date',
74
+ value: 'Date'
75
+ },
76
+ {
77
+ name: 'DateTime',
78
+ value: 'DateTime'
79
+ },
80
+ {
81
+ name: 'TimeofDay',
82
+ value: 'TimeofDay'
83
+ },
84
+ {
85
+ name: 'Picklist',
86
+ value: 'Picklist'
87
+ }
88
+ ],
89
+ template: ['DETAILS', 'FORM', 'PAGE'],
90
+ widget: ['PAGE', 'CASE']
91
+ }
92
+ };
@@ -0,0 +1,60 @@
1
+ /* eslint-disable import/prefer-default-export */
2
+ import { getPegaServerConfig } from '../../util.js';
3
+
4
+ export const getAuthQuestions = async () => {
5
+ const defaultPegaConfig = await getPegaServerConfig();
6
+
7
+ return [
8
+ {
9
+ name: 'server',
10
+ message: 'Enter pega server url',
11
+ default: defaultPegaConfig.server
12
+ },
13
+ {
14
+ name: 'user',
15
+ message: 'Enter user id',
16
+ default: defaultPegaConfig.user,
17
+ validate: value => {
18
+ if (value.trim()) {
19
+ return true;
20
+ }
21
+ return 'Username cannot be empty';
22
+ }
23
+ },
24
+ {
25
+ name: 'password',
26
+ type: 'password',
27
+ message: 'Enter password',
28
+ default: defaultPegaConfig.password,
29
+ validate: value => {
30
+ if (value.trim()) {
31
+ return true;
32
+ }
33
+ return 'Password cannot be empty';
34
+ }
35
+ },
36
+ {
37
+ name: 'clientId',
38
+ message: 'Enter clientId',
39
+ default: defaultPegaConfig.clientId,
40
+ validate: value => {
41
+ if (value.trim()) {
42
+ return true;
43
+ }
44
+ return 'clientId cannot be empty';
45
+ }
46
+ },
47
+ {
48
+ name: 'clientSecret',
49
+ type: 'password',
50
+ message: 'Enter clientSecret',
51
+ default: defaultPegaConfig.clientSecret,
52
+ validate: value => {
53
+ if (value.trim()) {
54
+ return true;
55
+ }
56
+ return 'clientSecret cannot be empty';
57
+ }
58
+ }
59
+ ];
60
+ };
@@ -0,0 +1,75 @@
1
+ import path from 'path';
2
+ import fs from 'fs';
3
+ import https from 'https';
4
+
5
+ import chalk from 'chalk';
6
+ import inquirer from 'inquirer';
7
+ import fetch from 'node-fetch';
8
+
9
+ import { checkPathAccess, getTokenUrl } from '../../util.js';
10
+ import { TOKEN_PATH } from '../../constants.js';
11
+
12
+ import { getAuthQuestions } from './helper.js';
13
+
14
+ export const TASKS_CONFIG_JSON_FILENAME = 'tasks.config.json';
15
+
16
+ const pegaConfigJsonPath = path.join(path.resolve(), TASKS_CONFIG_JSON_FILENAME);
17
+
18
+ const generateAccessToken = async config => {
19
+ const formData = new URLSearchParams();
20
+ formData.append('client_id', config.clientId);
21
+ formData.append('client_secret', config.clientSecret);
22
+ formData.append('username', config.user);
23
+ formData.append('password', config.password);
24
+ formData.append('grant_type', 'password');
25
+
26
+ const accessTokenUri = await getTokenUrl(config.server);
27
+ fetch(accessTokenUri, {
28
+ agent: new https.Agent({ rejectUnauthorized: false }),
29
+ method: 'POST',
30
+ headers: { 'content-type': 'application/x-www-form-urlencoded' },
31
+ body: formData.toString()
32
+ })
33
+ .then(response => response.json())
34
+ .then(data => {
35
+ if (data && data.errors) {
36
+ const errMessages = data.errors;
37
+ errMessages.forEach(msgArr => {
38
+ throw new Error(`Failed with error - ${JSON.stringify(msgArr.message)}`);
39
+ });
40
+ }
41
+ if (data.token_type) {
42
+ fs.writeFile(TOKEN_PATH, JSON.stringify(data), err => {
43
+ if (err) {
44
+ console.error(err);
45
+ }
46
+ console.log(`${chalk.bold.green('Authenticated successfully !!')}`);
47
+ });
48
+ }
49
+ })
50
+ .catch(e => console.log(`${chalk.bold.red(e)}`));
51
+ };
52
+
53
+ export default async options => {
54
+ await checkPathAccess(pegaConfigJsonPath);
55
+
56
+ let data = fs.readFileSync(pegaConfigJsonPath, { encoding: 'utf8' });
57
+ data = data && JSON.parse(data);
58
+
59
+ const serverConfig = data['server-config'];
60
+ const params = options.params;
61
+
62
+ const questions = await getAuthQuestions(params);
63
+ const answers = await inquirer.prompt(questions);
64
+ serverConfig.server = answers.server;
65
+ serverConfig.user = answers.user;
66
+ serverConfig.password = answers.password;
67
+ serverConfig.clientId = answers.clientId;
68
+ serverConfig.clientSecret = answers.clientSecret;
69
+ await generateAccessToken(serverConfig);
70
+
71
+ data = JSON.stringify(data, null, 2);
72
+ fs.writeFile(pegaConfigJsonPath, data, err => {
73
+ if (err) throw err;
74
+ });
75
+ };
@@ -0,0 +1,304 @@
1
+ import path from 'path';
2
+ import { URL, fileURLToPath } from 'url';
3
+ import fs from 'fs';
4
+ import { promisify } from 'util';
5
+
6
+ import inquirer from 'inquirer';
7
+ import ncp from 'ncp';
8
+ import chalk from 'chalk';
9
+
10
+ import {
11
+ convertIntoPascalCase,
12
+ compileMustacheTemplate,
13
+ getComponentDirectoryPath,
14
+ getComponents,
15
+ getComponentDefaults,
16
+ sanitize,
17
+ validateSemver
18
+ } from '../../util.js';
19
+ import { COMPONENT_SCHEMA } from '../../constants.js';
20
+
21
+ const copy = promisify(ncp);
22
+
23
+ const copyComponentTemplate = async options => {
24
+ return copy(options.templateDirectory, options.targetDirectory, {
25
+ clobber: false
26
+ });
27
+ };
28
+
29
+ const compileMustacheTemplates = async (
30
+ componentDirectory,
31
+ {
32
+ componentKey,
33
+ componentName,
34
+ componentLabel,
35
+ library,
36
+ version,
37
+ type,
38
+ subtype,
39
+ description,
40
+ organization,
41
+ allowedApplications
42
+ }
43
+ ) => {
44
+ const files = fs.readdirSync(componentDirectory);
45
+ const componentClassName = convertIntoPascalCase(componentKey);
46
+
47
+ const isTemplate = type === 'Template';
48
+ const isPicklist = subtype === 'Picklist';
49
+ const isBool = subtype === 'Boolean';
50
+ const isPhone = subtype === 'Phone';
51
+
52
+ if (Array.isArray(subtype) && subtype.length === 1) {
53
+ subtype = subtype.toString();
54
+ }
55
+
56
+ if (allowedApplications.trim() === '') {
57
+ allowedApplications = [];
58
+ } else {
59
+ if (allowedApplications.indexOf(',') !== -1) {
60
+ allowedApplications = allowedApplications.split(',');
61
+ }
62
+ allowedApplications = [].concat(allowedApplications);
63
+ }
64
+
65
+ allowedApplications = allowedApplications.filter(el => !!el.trim()).map(el => el.trim());
66
+ files.forEach(file => {
67
+ const filePath = path.join(componentDirectory, file);
68
+
69
+ const output = compileMustacheTemplate(filePath, {
70
+ COMPONENT_KEY: componentKey,
71
+ COMPONENT_NAME: componentName,
72
+ COMPONENT_LABEL: componentLabel,
73
+ COMPONENT_CLASS_NAME: componentClassName,
74
+ ORGANIZATION: organization,
75
+ VERSION: version,
76
+ LIBRARY: library,
77
+ ALLOWEDAPPS: JSON.stringify(allowedApplications),
78
+ TYPE: type,
79
+ SUB_TYPE: JSON.stringify(subtype),
80
+ DESCRIPTION: description,
81
+ IS_TEMPLATE: isTemplate,
82
+ IS_PICKLIST: isPicklist,
83
+ IS_BOOLEAN: isBool,
84
+ IS_PHONE: isPhone
85
+ });
86
+ fs.writeFileSync(filePath, output);
87
+ fs.renameSync(filePath, filePath.replace('.mustache', ''));
88
+ });
89
+ };
90
+
91
+ const validateCompile = async (
92
+ {
93
+ componentName,
94
+ componentLabel,
95
+ library,
96
+ version,
97
+ type,
98
+ subtype,
99
+ description,
100
+ allowedApplications
101
+ },
102
+ options
103
+ ) => {
104
+ const templateDirectory = fileURLToPath(new URL('./template', import.meta.url));
105
+
106
+ const { organization } = JSON.parse(fs.readFileSync(path.resolve('package.json'), 'utf8'));
107
+
108
+ const componentKey = `${organization}_${library}_${componentName}`;
109
+ /*
110
+ if (!isPascalCase(componentKey)) {
111
+ console.log(
112
+ chalk.yellowBright(
113
+ `${componentKey} is not in Pascal Case. Attempting to create component in Pascal Case !!`
114
+ )
115
+ );
116
+ componentKey = convertIntoPascalCase(componentKey);
117
+ } */
118
+
119
+ const targetDirectory = await getComponentDirectoryPath(componentKey);
120
+
121
+ const components = await getComponents();
122
+ if (components.includes(componentKey)) {
123
+ console.log(chalk.red(`${componentKey} component already exists in ${targetDirectory}`));
124
+ return;
125
+ }
126
+
127
+ await copyComponentTemplate({
128
+ ...options,
129
+ targetDirectory,
130
+ templateDirectory
131
+ });
132
+ await compileMustacheTemplates(targetDirectory, {
133
+ componentKey,
134
+ componentName,
135
+ componentLabel,
136
+ library,
137
+ version,
138
+ type,
139
+ subtype,
140
+ description,
141
+ organization,
142
+ allowedApplications
143
+ });
144
+
145
+ console.log(chalk.green(`${componentKey} component is created in ${targetDirectory}`));
146
+ };
147
+
148
+ export default async options => {
149
+ if (options.params[9]) {
150
+ const componentName = options.params[3];
151
+ const componentLabel = options.params[4];
152
+ const library = options.params[5];
153
+ const version = options.params[6];
154
+ const type = options.params[7];
155
+ const subtype = options.params[8];
156
+ const description = options.params[9];
157
+ await validateCompile(
158
+ {
159
+ componentName,
160
+ componentLabel,
161
+ library,
162
+ version,
163
+ type,
164
+ subtype,
165
+ description
166
+ },
167
+ options
168
+ );
169
+ } else {
170
+ const componentDefaults = await getComponentDefaults();
171
+ const questions = [
172
+ {
173
+ name: 'componentName',
174
+ message: 'Enter component name (required)',
175
+ validate: value => {
176
+ /* value should not be empty
177
+ It should not have spaces
178
+ It should not start with a number
179
+ Only case-insensitive alphanumeric values are allowed
180
+ */
181
+ if (value && !/^\d/.test(value) && value === sanitize(value)) {
182
+ return true;
183
+ }
184
+ return 'Only alphanumeric values are allowed, starting with alphabets';
185
+ }
186
+ },
187
+ {
188
+ name: 'componentLabel',
189
+ message: 'Enter component label for display (required)',
190
+ validate: value => {
191
+ if (value) {
192
+ return true;
193
+ }
194
+ return 'Please provide value for label';
195
+ }
196
+ },
197
+ {
198
+ name: 'version',
199
+ message: 'Enter component version',
200
+ validate: value => {
201
+ if (validateSemver(value)) {
202
+ return true;
203
+ }
204
+ return 'Please provide semver compatible version e.g 0.0.1';
205
+ }
206
+ },
207
+ {
208
+ name: 'library',
209
+ message: 'Enter library name (required)',
210
+ default: componentDefaults.library,
211
+ validate: value => {
212
+ /* value should not be empty
213
+ It should not have spaces
214
+ It should not start with a number
215
+ Only case-insensitive alphanumeric values are allowed
216
+ */
217
+ if (value && !/^\d/.test(value) && value === sanitize(value)) {
218
+ return true;
219
+ }
220
+ return 'Only alphanumeric values are allowed, starting with alphabets';
221
+ }
222
+ },
223
+ {
224
+ name: 'allowedApplications',
225
+ message: 'Please enter the application names to be supported (comma-separated). ',
226
+ suffix: 'Keep empty for all applications'
227
+ },
228
+ {
229
+ name: 'type',
230
+ type: 'rawlist',
231
+ message: 'Enter type of component',
232
+ default: componentDefaults.type,
233
+ choices: COMPONENT_SCHEMA.type
234
+ },
235
+ {
236
+ name: 'subtype',
237
+ type: 'rawlist',
238
+ message: 'Enter subtype of component',
239
+ default: componentDefaults.subtype,
240
+ choices: COMPONENT_SCHEMA.subtype.field,
241
+ when(answers) {
242
+ return answers.type === 'Field';
243
+ }
244
+ },
245
+ {
246
+ name: 'subtype',
247
+ type: 'rawlist',
248
+ message: 'Enter subtype of component',
249
+ default: componentDefaults.subtype,
250
+ choices: COMPONENT_SCHEMA.subtype.template,
251
+ when(answers) {
252
+ return answers.type === 'Template';
253
+ }
254
+ },
255
+ {
256
+ name: 'subtype',
257
+ type: 'checkbox',
258
+ message: 'Enter subtype of component',
259
+ default: [componentDefaults.subtype || COMPONENT_SCHEMA.subtype.widget[0]],
260
+ choices: COMPONENT_SCHEMA.subtype.widget,
261
+ when(answers) {
262
+ return answers.type === 'Widget';
263
+ },
264
+ validate: value => {
265
+ /* value should not be empty */
266
+ if (Array.isArray(value) && value.length) {
267
+ return true;
268
+ }
269
+ return 'Please select subtype';
270
+ }
271
+ },
272
+ {
273
+ name: 'description',
274
+ message: 'Enter description for the component',
275
+ default: componentDefaults.description
276
+ }
277
+ ];
278
+ await inquirer.prompt(questions).then(async answers => {
279
+ const {
280
+ componentName,
281
+ componentLabel,
282
+ library,
283
+ version,
284
+ type,
285
+ subtype,
286
+ description,
287
+ allowedApplications
288
+ } = answers;
289
+ await validateCompile(
290
+ {
291
+ componentName,
292
+ componentLabel,
293
+ library,
294
+ version,
295
+ type,
296
+ subtype,
297
+ description,
298
+ allowedApplications
299
+ },
300
+ options
301
+ );
302
+ });
303
+ }
304
+ };
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "{{COMPONENT_KEY}}",
3
+ "label": "{{COMPONENT_LABEL}}",
4
+ "description": "{{DESCRIPTION}}",
5
+ "organization": "{{ORGANIZATION}}",
6
+ "version": "{{VERSION}}",
7
+ "library": "{{LIBRARY}}",
8
+ "allowedApplications": {{{ALLOWEDAPPS}}},
9
+ "componentKey": "{{COMPONENT_KEY}}",
10
+ "type": "{{TYPE}}",
11
+ "subtype": {{{SUB_TYPE}}},
12
+ {{#IS_TEMPLATE}}
13
+ "icon": "images/pz-text-input-active.svg",
14
+ {{/IS_TEMPLATE}}
15
+ "properties": [
16
+ {
17
+ "name": "value",
18
+ "label": "Property",
19
+ "format": "PROPERTY"
20
+ },
21
+ {
22
+ "name": "label",
23
+ "label": "Label value",
24
+ "format": "TEXT"
25
+ },
26
+ {
27
+ "name": "placeholder",
28
+ "label": "Placeholder",
29
+ "format": "TEXT"
30
+ },
31
+ {
32
+ "name": "visibility",
33
+ "label": "Visible",
34
+ "format": "VISIBILITY"
35
+ },
36
+ {
37
+ "name": "disabled",
38
+ "label": "Disabled",
39
+ "format": "DISABLED"
40
+ },
41
+ {
42
+ "name": "required",
43
+ "label": "Required",
44
+ "format": "REQUIRED"
45
+ }
46
+ ],
47
+ "defaultConfig": {
48
+ {{#IS_PICKLIST}}
49
+ "label": "@L $this.label",
50
+ "placeholder": "@L Select...",
51
+ "listType": "associated",
52
+ "datasource": "@ASSOCIATED .$this.name"
53
+ {{/IS_PICKLIST}}
54
+ {{#IS_BOOLEAN}}
55
+ "trueText": "True",
56
+ "falseText": "False"
57
+ {{/IS_BOOLEAN}}
58
+ {{#IS_PHONE}}
59
+ "datasource": {
60
+ "source": "@DATASOURCE D_pyCountryCallingCodeList.pxResults",
61
+ "fields": {
62
+ "value": "@P .pyCallingCode"
63
+ }
64
+ }
65
+ {{/IS_PHONE}}
66
+ }
67
+ }
@@ -0,0 +1,44 @@
1
+ import { useState } from 'react';
2
+ import { withKnobs, text, boolean } from '@storybook/addon-knobs';
3
+
4
+ import {{COMPONENT_CLASS_NAME}} from './index.jsx';
5
+
6
+ export default {
7
+ title: '{{COMPONENT_CLASS_NAME}}',
8
+ decorators: [withKnobs],
9
+ component: {{COMPONENT_CLASS_NAME}}
10
+ };
11
+
12
+ export const base{{COMPONENT_CLASS_NAME}} = () => {
13
+ const [value, setValue] = useState('test value');
14
+
15
+ const props = {
16
+ getPConnect: () => {
17
+ return {
18
+ getActionsApi: () => {
19
+ return {
20
+ updateFieldValue: (propName, value) => {
21
+ setValue(value);
22
+ }
23
+ };
24
+ },
25
+ getStateProps: () => {
26
+ return { value: '.name' };
27
+ }
28
+ };
29
+ },
30
+ value,
31
+ placeholder: text('Placeholder', 'Test placeholder'),
32
+ disabled: boolean('Disabled', false),
33
+ readOnly: boolean('Disabled', false),
34
+ required: boolean('Disabled', false),
35
+ label: text('Label', 'Sample Label'),
36
+ testId: text('Test id', 'text-12344566')
37
+ };
38
+
39
+ return (
40
+ <>
41
+ <{{COMPONENT_CLASS_NAME}} {...props} />
42
+ </>
43
+ );
44
+ };
@@ -0,0 +1,55 @@
1
+ import PropTypes from 'prop-types';
2
+ import { Input, Label } from '@pega/cosmos-react-core';
3
+
4
+ import Styled{{COMPONENT_CLASS_NAME}}Wrapper from './styles';
5
+
6
+ const {{COMPONENT_CLASS_NAME}} = props => {
7
+ const { getPConnect, value, placeholder, disabled, readOnly, required, label, testId } = props;
8
+
9
+ const pConn = getPConnect();
10
+ const actions = pConn.getActionsApi();
11
+ const propName = pConn?.getStateProps()?.value;
12
+
13
+ const handleOnChange = event => {
14
+ const { value: updatedValue } = event.target;
15
+ actions.updateFieldValue(propName, updatedValue);
16
+ };
17
+
18
+ return (
19
+ <Styled{{COMPONENT_CLASS_NAME}}Wrapper>
20
+ <Label>{label}</Label>
21
+ <Input
22
+ type='text'
23
+ value={value}
24
+ placeholder={placeholder}
25
+ disabled={disabled}
26
+ readOnly={readOnly}
27
+ required={required}
28
+ onChange={handleOnChange}
29
+ testId={testId}
30
+ />
31
+ </Styled{{COMPONENT_CLASS_NAME}}Wrapper>
32
+ );
33
+ };
34
+
35
+ {{COMPONENT_CLASS_NAME}}.defaultProps = {
36
+ value: '',
37
+ placeholder: '',
38
+ disabled: false,
39
+ readOnly: false,
40
+ required: false,
41
+ testId: null
42
+ };
43
+
44
+ {{COMPONENT_CLASS_NAME}}.propTypes = {
45
+ label: PropTypes.string,
46
+ value: PropTypes.string,
47
+ placeholder: PropTypes.string,
48
+ getPConnect: PropTypes.func.isRequired,
49
+ disabled: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
50
+ readOnly: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
51
+ required: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
52
+ testId: PropTypes.string
53
+ };
54
+
55
+ export default {{COMPONENT_CLASS_NAME}};
@@ -0,0 +1,7 @@
1
+ import styled, { css } from 'styled-components';
2
+
3
+ export default styled.div(() => {
4
+ return css`
5
+ margin: 8px 0;
6
+ `;
7
+ });