-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #46 from pegasystems/feature/dynamicform
Add support for the new Dynamic Form component
- Loading branch information
Showing
13 changed files
with
1,087 additions
and
270 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Large diffs are not rendered by default.
Oops, something went wrong.
45 changes: 45 additions & 0 deletions
45
src/components/Pega_Extensions_DynamicHierarchicalForm/Docs.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { Meta, Primary, Controls, Story } from '@storybook/blocks'; | ||
import * as DemoStories from './demo.stories'; | ||
|
||
<Meta of={DemoStories} /> | ||
|
||
# Overview | ||
|
||
The DynamicHierarchicalForm template is designed for utilization within a form template. Its core function is to allow users to choose from a multitude of items available in a multi-select combobox. Each selected item correlates to a data object embedded within the case, exposing a series of input fields. | ||
|
||
This component displays the form of every embedded object within a unique tab component. The tabs dynamically adjust their displays based on the items selected from the combobox. | ||
|
||
The component is divided into two distinct regions: | ||
|
||
- The 'Selection' region: This area should house a singular embedded data list of available items. This field serves to hold your chosen items list. Every selected item will be recognizable through a unique pyGUID, pyLabel, RuleClass and an IsSelected property. | ||
- The 'Tabs' region: This is where all the embedded data fields should be placed, leading to the view that will be rendered for object. The RuleClass field from the item should match the class of the embedded data object. | ||
|
||
The component also exposes an action that will trigger a refresh on the assignment - This action can be used to update parts of the assignment, for example provide a detailed quotation. It is also possible to hide this refresh action as well as the multi-select picklist. | ||
|
||
<Primary /> | ||
|
||
## Props | ||
|
||
<Controls /> | ||
|
||
## example | ||
|
||
Assuming that you have a set of data objects that represent different types of insurance products and you want users to pick and configure them. Create a Products data object that exposes a pyLabel, IsSelected and RuleClass fields | ||
|
||
![Product list](DynamicHierarchicalForm_Configuration_1.png) | ||
|
||
Include in your case type each product as an embedded data object as well as create a embedded list object of products. | ||
|
||
![Case type model](DynamicHierarchicalForm_Configuration_2.png) | ||
|
||
Before rendering the widget, make sure to populate the products field with the pyLabel, pyGUID, RuleClass and IsSelected | ||
|
||
![Products initialization](DynamicHierarchicalForm_Configuration_3.png) | ||
|
||
In your assignment, use the component as a template and configure the different regions | ||
|
||
![Template usage](DynamicHierarchicalForm_Configuration_4.png) | ||
|
||
Here is how the component will render at runtime. | ||
|
||
![Demo](DynamicHierarchicalForm_Configuration_Demo.png) |
46 changes: 46 additions & 0 deletions
46
src/components/Pega_Extensions_DynamicHierarchicalForm/config.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
{ | ||
"name": "Pega_Extensions_DynamicHierarchicalForm", | ||
"label": "Dynamic Hierarchical Form", | ||
"description": "Dynamic Hierarchical Form", | ||
"organization": "Pega", | ||
"version": "1.0.0", | ||
"library": "Extensions", | ||
"allowedApplications": [], | ||
"componentKey": "Pega_Extensions_DynamicHierarchicalForm", | ||
"type": "Template", | ||
"subtype": "DETAILS", | ||
"properties": [ | ||
{ | ||
"name": "refreshActionLabel", | ||
"label": "Label of the Refresh Action button", | ||
"format": "TEXT" | ||
}, | ||
{ | ||
"name": "showRefreshAction", | ||
"label": "Show refresh action", | ||
"format": "BOOLEAN", | ||
"defaultValue": true | ||
}, | ||
{ | ||
"name": "enableItemSelection", | ||
"label": "Show multi-select picker", | ||
"format": "BOOLEAN", | ||
"defaultValue": true | ||
}, | ||
{ | ||
"name": "Selection", | ||
"label": "Selection", | ||
"format": "CONTENTPICKER", | ||
"addTypeList": ["Fields"] | ||
}, | ||
{ | ||
"name": "Tabs", | ||
"label": "Tabs", | ||
"format": "CONTENTPICKER", | ||
"addTypeList": ["Fields"] | ||
} | ||
], | ||
"defaultConfig": { | ||
"refreshActionLabel": "Refresh details" | ||
} | ||
} |
302 changes: 302 additions & 0 deletions
302
src/components/Pega_Extensions_DynamicHierarchicalForm/demo.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,302 @@ | ||
import type { StoryObj } from '@storybook/react'; | ||
import PegaExtensionsDynamicHierarchicalForm from './index'; | ||
import { type DynamicHierarchicalFormProps } from './index'; | ||
import { | ||
Checkbox, | ||
CheckboxGroup, | ||
Grid, | ||
Input, | ||
RadioButton, | ||
RadioButtonGroup, | ||
TextArea | ||
} from '@pega/cosmos-react-core'; | ||
import type { ChangeEvent } from 'react'; | ||
|
||
type configInfo = { | ||
values?: Array<any>; | ||
value?: string; | ||
componentType?: string; | ||
pyGUID?: string; | ||
authorContext?: string; | ||
pyLabel?: string; | ||
label?: string; | ||
IsSelected?: boolean; | ||
heading?: string; | ||
inheritedProps?: Array<any>; | ||
name?: string; | ||
ruleClass?: string; | ||
}; | ||
|
||
type info = { | ||
config: configInfo; | ||
type: string; | ||
children?: Array<info>; | ||
}; | ||
|
||
export default { | ||
title: 'Templates/Dynamic Hierarchical Form', | ||
argTypes: { | ||
getPConnect: { | ||
table: { | ||
disable: true | ||
} | ||
} | ||
}, | ||
parameters: { | ||
a11y: { | ||
element: '#storybook-root', | ||
config: { | ||
rules: [ | ||
{ | ||
id: 'nested-interactive', | ||
enabled: false | ||
} | ||
] | ||
} | ||
} | ||
}, | ||
component: PegaExtensionsDynamicHierarchicalForm | ||
}; | ||
|
||
const genComponent = (config: any) => { | ||
return config.config.text; | ||
}; | ||
|
||
const setPCore = (numProducts: number) => { | ||
const productsConfig: any[] = []; | ||
for (let i = 1; i <= numProducts; i += 1) { | ||
productsConfig.push({ | ||
pyLabel: `Product #${i}`, | ||
pyGUID: `product${i}`, | ||
IsSelected: true, | ||
RuleClass: `Class-Product-${i}` /* The classID must match the ruleClass from the embedded obj */ | ||
}); | ||
} | ||
|
||
(window as any).PCore = { | ||
createPConnect: () => ({ | ||
getPConnect: () => ({ | ||
createComponent: (meta: any) => { | ||
return ( | ||
<Grid | ||
container={{ gap: 1, cols: `repeat(1, minmax(0, 1fr))` }} | ||
style={{ maxWidth: '80ch' }} | ||
> | ||
{Math.random() < 0.8 ? ( | ||
<Input name={`${meta.config.name}field1`} label='Field1' /> | ||
) : null} | ||
{Math.random() < 0.8 ? ( | ||
<Input name={`${meta.config.name}field2`} label='Field2' /> | ||
) : null} | ||
{Math.random() < 0.8 ? ( | ||
<TextArea name={`${meta.config.name}field3`} label='Field3' /> | ||
) : null} | ||
{Math.random() < 0.8 ? ( | ||
<Input name={`${meta.config.name}field4`} label='Field4' /> | ||
) : null} | ||
{Math.random() < 0.8 ? ( | ||
<Input name={`${meta.config.name}field5`} label='Field5' /> | ||
) : null} | ||
{Math.random() < 0.8 ? ( | ||
<CheckboxGroup name={`${meta.config.name}options`} label='Options'> | ||
{['Option1', 'Option2', 'Option3'].map(option => ( | ||
<Checkbox key={option} label={option} value={option} /> | ||
))} | ||
</CheckboxGroup> | ||
) : null} | ||
{Math.random() < 0.8 ? ( | ||
<RadioButtonGroup name={`${meta.config.name}choice`} label='Choice'> | ||
{['Yes', 'No'].map(option => ( | ||
<RadioButton key={option} label={option} value={option} /> | ||
))} | ||
</RadioButtonGroup> | ||
) : null} | ||
</Grid> | ||
); | ||
}, | ||
getActionsApi: () => { | ||
return { | ||
updateFieldValue: (prop: string, value: string) => {} | ||
}; | ||
} | ||
}) | ||
}), | ||
getComponentsRegistry: () => { | ||
return { | ||
getLazyComponent: (f: string) => f | ||
}; | ||
}, | ||
getViewResources: () => { | ||
return { | ||
fetchViewResources: (name: string) => { | ||
return { | ||
config: { | ||
name | ||
} | ||
}; | ||
}, | ||
updateViewResources: () => {} | ||
}; | ||
}, | ||
getStore: () => { | ||
return { | ||
getState: () => { | ||
return { | ||
data: { | ||
primary: { | ||
caseInfo: { | ||
content: { | ||
Products: productsConfig | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
}, | ||
dispatch: () => {} | ||
}; | ||
} | ||
}; | ||
}; | ||
|
||
const genResponse = (numProducts: number) => { | ||
const demoView = { | ||
name: 'demoView', | ||
type: 'View', | ||
config: { | ||
template: 'Pega_Extensions_DynamicHierarchicalForm', | ||
ruleClass: 'Work-', | ||
inheritedProps: [] | ||
}, | ||
children: [ | ||
{ | ||
name: 'Selection', | ||
type: 'Region', | ||
children: [] as Array<info>, | ||
getPConnect: () => {} | ||
}, | ||
{ | ||
name: 'Tabs', | ||
type: 'Region', | ||
children: [] as Array<info>, | ||
getPConnect: () => {} | ||
} | ||
], | ||
classID: 'Work-MyComponents' | ||
}; | ||
|
||
demoView.children[0].children = [ | ||
{ | ||
config: { | ||
authorContext: '.Products', | ||
inheritedProps: [{ prop: 'label', value: 'Products' }] | ||
}, | ||
type: 'reference' | ||
} | ||
]; | ||
|
||
const productsConfig = []; | ||
for (let i = 1; i <= numProducts; i += 1) { | ||
productsConfig.push({ | ||
config: { | ||
name: `Product #${i}`, | ||
ruleClass: `Class-Product-${i}`, | ||
inheritedProps: [{ value: `Product #${i}` }] | ||
}, | ||
type: 'reference' | ||
}); | ||
} | ||
|
||
demoView.children[1].children = productsConfig; | ||
demoView.children[0].getPConnect = () => { | ||
return { | ||
getRawMetadata: () => { | ||
return demoView.children[0]; | ||
} | ||
}; | ||
}; | ||
demoView.children[1].getPConnect = () => { | ||
return { | ||
getRawMetadata: () => { | ||
return demoView.children[1]; | ||
} | ||
}; | ||
}; | ||
return demoView; | ||
}; | ||
|
||
interface DynamicHierarchicalFormPropsExt extends DynamicHierarchicalFormProps { | ||
numProducts: number; | ||
} | ||
type Story = StoryObj<DynamicHierarchicalFormPropsExt>; | ||
export const Default: Story = { | ||
render: (args: any) => { | ||
const response = genResponse(args.numProducts); | ||
setPCore(args.numProducts); | ||
const props = { | ||
template: 'DynamicTabs', | ||
...args, | ||
getPConnect: () => { | ||
return { | ||
getListActions: () => { | ||
return { | ||
update: () => {} | ||
}; | ||
}, | ||
getCaseInfo: () => { | ||
return { | ||
getKey: () => 'S-123', | ||
getCurrentAssignmentViewName: () => 'Enter info' | ||
}; | ||
}, | ||
getActionsApi: () => { | ||
return { | ||
updateFieldValue: (prop: string, value: string) => {}, | ||
refreshCaseView: () => { | ||
alert('Refresh UI'); | ||
} | ||
}; | ||
}, | ||
getChildren: () => { | ||
return response.children; | ||
}, | ||
getRawMetadata: () => { | ||
return response; | ||
}, | ||
getInheritedProps: () => { | ||
return response.config.inheritedProps; | ||
}, | ||
getContextName: () => { | ||
return 'primary'; | ||
}, | ||
getTarget: () => { | ||
return 'caseInfo'; | ||
}, | ||
createComponent: (config: any) => { | ||
return genComponent(config); | ||
}, | ||
setInheritedProp: () => { | ||
/* nothing */ | ||
}, | ||
setValue: () => { | ||
/* nothing */ | ||
}, | ||
resolveConfigProps: (f: any) => { | ||
return f; | ||
} | ||
}; | ||
} | ||
}; | ||
return ( | ||
<PegaExtensionsDynamicHierarchicalForm {...props}></PegaExtensionsDynamicHierarchicalForm> | ||
); | ||
}, | ||
args: { | ||
label: 'Select your products', | ||
showLabel: true, | ||
refreshActionLabel: 'Refresh product', | ||
showRefreshAction: true, | ||
enableItemSelection: true, | ||
numProducts: 3 | ||
} | ||
}; |
Oops, something went wrong.