Skip to content

Commit

Permalink
Merge pull request #29 from pegasystems/feature/RatingInput
Browse files Browse the repository at this point in the history
Add new RatingInput component
  • Loading branch information
ricmars authored Feb 23, 2024
2 parents 4ff9d73 + 2441c90 commit 9fd8d3d
Show file tree
Hide file tree
Showing 14 changed files with 1,556 additions and 1,054 deletions.
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.
2,157 changes: 1,103 additions & 1,054 deletions package-lock.json

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions src/components/Pega_Extensions_RatingLayout/Docs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Meta, Primary, Controls, Story } from '@storybook/blocks';
import * as DemoStories from './demo.stories';

<Meta of={DemoStories} />

# Overview

The RatingLayout component is a template component that let you enter a set of numeric values between 1 and 5 - Each set of fields is grouped by tabs representing different categories.

This template can be used inside an form view by adding the values of the embedded page that will contain all the ratings

<Primary />

## Props

<Controls />

## Example

Assuming that you have an embedded object called 'Ratings'. This object contains 3 fields: Category, Label and Value. To use this component, create a new subview inside your step, select the RatingLayout template,
add the following 3 fields in the picker in this order: [Ratings.Category, Ratings.Label, Ratings.Value] - These 3 values must be passed in this exact order to be correctly used at runtime.

![View configuration](RatingInput_Example_Config_View.png)

![Fields selection](RatingInput_Example_Config_Fields.png)

![Fields configuration](RatingInput_Example_Config_Fields1.png)

Here is how the component looks at runtime:
![Runtime example](RatingInput_Example_Runtime.png)
60 changes: 60 additions & 0 deletions src/components/Pega_Extensions_RatingLayout/RatingElem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { useState, useMemo } from 'react';
import { Slider, type SliderProps } from '@pega/cosmos-react-core';

type RatingElemProps = {
value: number;
label: string;
propIndex: number;
path: string;
getPConnect?: any;
};

const tickValues = ['N/A', 'Low', 'Medium', 'High', 'Severe'];
const RatingElem = (props: RatingElemProps) => {
const { getPConnect, label, value, path, propIndex } = props;
const [inputValue, setInputValue] = useState(value);
const ticksObject: SliderProps['ticks'] = {};
const numTicks = 5;
Array(numTicks)
.fill(0)
.map((_, index) => index)
.forEach(tick => {
ticksObject![tick + 1] = tickValues[tick];
});

/* When calling updateFieldValue, we need to be in the context of the object in the array
Using useMemo to cache the actionsApi object to only create it once and not when changing tabs
path should be set to the embedded object name like '.Ratings' */
const actionsApi: any = useMemo(() => {
const messageConfig = {
meta: props,
options: {
context: getPConnect().getContextName(),
pageReference: `caseInfo.content${path}[${propIndex}]`,
target: getPConnect().getTarget()
}
};
const c11nEnv = (window as any).PCore.createPConnect(messageConfig);
return c11nEnv.getPConnect().getActionsApi();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [getPConnect]);

const onChangeValue = (changeValue: number) => {
setInputValue(changeValue);
actionsApi?.updateFieldValue('.Value', changeValue);
};

return (
<Slider
step={1}
min={1}
max={5}
ticks={ticksObject}
showInput={false}
label={label}
value={inputValue}
onChange={onChangeValue}
/>
);
};
export default RatingElem;
29 changes: 29 additions & 0 deletions src/components/Pega_Extensions_RatingLayout/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "Pega_Extensions_RatingLayout",
"label": "Rating Layout",
"description": "Rating Layout",
"organization": "Pega",
"version": "1.0.0",
"library": "Extensions",
"allowedApplications": [],
"componentKey": "Pega_Extensions_RatingLayout",
"type": "Template",
"subtype": "DETAILS",
"properties": [
{
"name": "minWidth",
"label": "Slider min width",
"format": "TEXT",
"defaultValue": "40ch"
},
{
"name": "A",
"label": "Region A",
"format": "CONTENTPICKER",
"addTypeList": ["Fields"]
}
],
"defaultConfig": {
"minWidth": "40ch"
}
}
169 changes: 169 additions & 0 deletions src/components/Pega_Extensions_RatingLayout/demo.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import type { StoryObj } from '@storybook/react';
import PegaExtensionsRatingLayout from './index';

type configInfo = {
values?: Array<any>;
value?: string;
componentType?: string;
label?: string;
heading?: string;
};

type info = {
config: configInfo;
type: string;
children?: Array<info>;
};

export default {
title: 'Templates/Rating Layout',
argTypes: {
getPConnect: {
table: {
disable: true
}
}
},
component: PegaExtensionsRatingLayout
};

const genComponent = (config: any) => {
return config.config.text;
};

const setPCore = () => {
(window as any).PCore = {
createPConnect: () => ({
getPConnect: () => ({
getActionsApi: () => ({ updateFieldValue: () => {} })
})
})
};
};

const genResponse = (numCategories?: number, numRatings?: number) => {
const demoView = {
name: 'demoView',
type: 'View',
config: {
template: 'Pega_Extensions_RatingLayout',
ruleClass: 'Work-',
inheritedProps: []
},
children: [
{
name: 'A',
type: 'Region',
children: [] as Array<info>,
getPConnect: () => {}
}
],
classID: 'Work-MyComponents'
};
const objects_categories = [];
const objects_labels = [];
const objects_values = [];
if (numCategories && numRatings) {
for (let i = 1; i <= numCategories; i += 1) {
for (let j = 1; j <= numRatings; j += 1) {
objects_categories.push(`Category #${i}`);
objects_labels.push(`Category #${i} - label #${j}`);
objects_values.push(Math.floor(Math.random() * 5) + 1);
}
}
}
demoView.children[0].children = [
{
config: {
values: objects_categories,
value: '@FILTERED_LIST .Ratings[].Category'
},
type: 'ScalarList'
},
{
config: {
values: objects_labels,
value: '@FILTERED_LIST .Ratings[].Label'
},
type: 'ScalarList'
},
{
config: {
values: objects_values,
value: '@FILTERED_LIST .Ratings[].Value'
},
type: 'ScalarList'
}
];

demoView.children[0].getPConnect = () => {
return {
getRawMetadata: () => {
return demoView.children[0];
}
};
};
return demoView;
};

type Story = StoryObj<typeof PegaExtensionsRatingLayout>;
export const Default: Story = {
render: args => {
const response = genResponse(args.numCategories, args.numRatings);
setPCore();
const props = {
template: 'RatingLayout',
...args,
getPConnect: () => {
return {
getListActions: () => {
return {
update: () => {}
};
},
getActionsApi: () => {
return {
updateFieldValue: (prop: string, value: string) => {
// eslint-disable-next-line no-console
//console.log(`Updating property ${prop} with value: ${value}`);
}
};
},
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 { value: f.values };
}
};
}
};
return <PegaExtensionsRatingLayout {...props}></PegaExtensionsRatingLayout>;
},
args: {
minWidth: '40ch',
numCategories: 3,
numRatings: 3
}
};
13 changes: 13 additions & 0 deletions src/components/Pega_Extensions_RatingLayout/demo.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { render, screen } from '@testing-library/react';
import { composeStories } from '@storybook/react';
import * as DemoStories from './demo.stories';

const { Default } = composeStories(DemoStories);

test('renders RatingLayout component with default args', async () => {
render(<Default />);
expect(await screen.findByText('Category #1')).toBeVisible();
expect(await screen.findByText('Category #2')).toBeVisible();
expect(await screen.findByText('Category #3')).toBeVisible();
expect(await screen.findByText('Category #1 - label #1')).toBeVisible();
});
Loading

0 comments on commit 9fd8d3d

Please sign in to comment.