-
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.
- Loading branch information
Showing
10 changed files
with
674 additions
and
0 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.
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 |
---|---|---|
|
@@ -28,8 +28,14 @@ When adding new components, make sure that: | |
- The component follow [best practices](https://pegasystems.github.io/constellation-ui-gallery/?path=/docs/getting-started--docs#implementing-a-constellation-dx-component). | ||
- The component props are exposed as an interface type object with description and default values so that the table of properties is documented in storybook. | ||
- Make sure that all files are linted correctly (npm run lint and npm run fix). | ||
- Make sure that all styles follow best practices (no use for px, no hardcoded colors...) | ||
- Make sure that you have a unit test. | ||
- Make sure that end to end and accessibility test is passing (see [README.md](https://github.com/pegasystems/constellation-ui-gallery/blob/master/README.md#end-to-end-testing) for instructions). | ||
- If using a 3rd party library, make sure that the library is open source and compatible with the React version and the Constellation component library - see [Library](https://pegasystems.github.io/constellation-ui-gallery/?path=/docs/libraries--docs). | ||
- You are not using a 3rd party presentational component library like Material UI - All the components should use the Constellation presentational components. | ||
- Do not include 3rd party icon library - there should be enough OOB icons in the Constellation presentational component library. | ||
- Test your component with different themes using the Storybook toolbar - the theme branding color and other tokens should be correctly handled by your components | ||
- The for right to left (rtl) using the Storybook toolbar. | ||
|
||
Once your code is ready, create a PR to the main branch using the following [procedure](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork). | ||
Send an email to [email protected] with a link to the github issue. |
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,32 @@ | ||
import { Meta, Primary, Controls, Story } from '@storybook/blocks'; | ||
import * as DemoStories from './demo.stories'; | ||
|
||
<Meta of={DemoStories} /> | ||
|
||
# Overview | ||
|
||
Meters are visual representations of a quantity or an achievement. Their progress is determined by user actions, rather than system actions. This component will use a percentage field type to render the value between 0 and 100. If the value that you want to render is an integer, create a declare expression to generate a percentage value. | ||
|
||
The meter should not be used to indicate progress, such as loading or percent completion of a task. To communicate progress, use the Loading component in the presentational library. | ||
|
||
<Primary /> | ||
|
||
## Props | ||
|
||
<Controls /> | ||
|
||
## Samples | ||
|
||
Example of using a multiple meter to display test coverage | ||
|
||
<Story of={DemoStories.Grouped} /> | ||
|
||
Example of using a multiple meter to display the storage capacity | ||
|
||
<Story of={DemoStories.Grouped1} /> | ||
|
||
## Configuration | ||
|
||
Here is an example of using the meter in different views (Case Summary, Details and form view) | ||
|
||
![Configuration](Meter_Configuration.jpg) |
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,66 @@ | ||
import styled, { css, useTheme } from 'styled-components'; | ||
import { type Event } from './index'; | ||
import { readableColor, themeDefinition } from '@pega/cosmos-react-core'; | ||
import { rgba } from 'polished'; | ||
|
||
type ElemDisplayProps = { | ||
type?: string; | ||
event: Event; | ||
totalValue?: number; | ||
}; | ||
|
||
export const StyledElementDisplay = styled.div( | ||
({ | ||
textColor, | ||
valueColor, | ||
theme | ||
}: { | ||
textColor: string; | ||
valueColor: string; | ||
theme: typeof themeDefinition; | ||
}) => { | ||
const { | ||
base: { spacing } | ||
} = theme; | ||
return css` | ||
display: inline-flex; | ||
align-items: center; | ||
gap: ${spacing}; | ||
& > i { | ||
background-color: ${valueColor}; | ||
width: ${spacing}; | ||
height: ${spacing}; | ||
border-radius: 50%; | ||
} | ||
& > span { | ||
color: ${textColor}; | ||
font-size: 0.75rem; | ||
} | ||
`; | ||
} | ||
); | ||
|
||
const ElemDisplay = (props: ElemDisplayProps) => { | ||
const { type, event, totalValue } = props; | ||
const theme = useTheme(); | ||
const Element = type === 'li' ? 'li' : 'div'; | ||
|
||
const textColor = rgba( | ||
readableColor(theme.base.palette['primary-background']), | ||
theme.base.transparency['transparent-3'] | ||
); | ||
|
||
const labelMsg = `${event.label} (${ | ||
totalValue && totalValue !== 100 | ||
? `${Math.floor((event.value * totalValue) / 100.0)} of ${totalValue}` | ||
: `${Math.floor(event.value)}%` | ||
})`; | ||
|
||
return ( | ||
<StyledElementDisplay as={Element} textColor={textColor} valueColor={event.color} aria-hidden> | ||
<i /> | ||
<span>{labelMsg}</span> | ||
</StyledElementDisplay> | ||
); | ||
}; | ||
export default ElemDisplay; |
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,41 @@ | ||
import { Tooltip, useElement } from '@pega/cosmos-react-core'; | ||
import { type Event } from './index'; | ||
import styled, { css } from 'styled-components'; | ||
|
||
type ElemMeterProps = { | ||
id?: string; | ||
event: Event; | ||
totalValue?: number; | ||
}; | ||
|
||
export const StyledElementMeter = styled.div(({ event }: { event: Event }) => { | ||
return css` | ||
background-color: ${event.color}; | ||
width: ${event.value}%; | ||
`; | ||
}); | ||
|
||
const ElemMeter = (props: ElemMeterProps) => { | ||
const { event, totalValue } = props; | ||
const [el, setEl] = useElement(); | ||
const labelMsg = | ||
totalValue && totalValue !== 100 | ||
? `${Math.floor((event.value * totalValue) / 100.0)} of ${totalValue}` | ||
: `${Math.floor(event.value)}%`; | ||
return ( | ||
<> | ||
<StyledElementMeter | ||
id={props.id} | ||
event={event} | ||
ref={setEl} | ||
role='meter' | ||
aria-valuenow={event.value} | ||
aria-valuemin={0} | ||
aria-valuemax={100} | ||
aria-valuetext={labelMsg} | ||
/> | ||
{el && <Tooltip target={el}>{`${event.label} (${labelMsg})`}</Tooltip>} | ||
</> | ||
); | ||
}; | ||
export default ElemMeter; |
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,60 @@ | ||
{ | ||
"name": "Pega_Extensions_Meter", | ||
"label": "Meter", | ||
"description": "Meter", | ||
"organization": "Pega", | ||
"version": "1.0.0", | ||
"library": "Extensions", | ||
"allowedApplications": [], | ||
"componentKey": "Pega_Extensions_Meter", | ||
"type": "Field", | ||
"subtype": "Decimal-Percentage", | ||
"properties": [ | ||
{ | ||
"name": "label", | ||
"label": "Field label", | ||
"format": "TEXT", | ||
"required": true | ||
}, | ||
{ | ||
"name": "additionalInfo", | ||
"label": "Additional information", | ||
"format": "TEXT" | ||
}, | ||
{ | ||
"name": "dataPage", | ||
"label": "Data Page name used to source the data", | ||
"format": "TEXT" | ||
}, | ||
{ | ||
"name": "showMetaData", | ||
"label": "Show the meta data on the screen", | ||
"format": "BOOLEAN" | ||
}, | ||
{ | ||
"name": "totalTasks", | ||
"label": "Total tasks", | ||
"format": "NUMBER" | ||
}, | ||
{ | ||
"name": "color", | ||
"label": "Color or JSON string", | ||
"format": "TEXT" | ||
}, | ||
{ | ||
"label": "Conditions", | ||
"format": "GROUP", | ||
"properties": [ | ||
{ | ||
"name": "visibility", | ||
"label": "Visibility", | ||
"format": "VISIBILITY" | ||
} | ||
] | ||
} | ||
], | ||
"defaultConfig": { | ||
"label": "@L $this.label", | ||
"detailFVLItem": true | ||
} | ||
} |
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,165 @@ | ||
import type { StoryObj } from '@storybook/react'; | ||
import { PegaExtensionsMeter, type MeterProps } from './index'; | ||
|
||
export default { | ||
title: 'Fields/Meter', | ||
argTypes: { | ||
value: { | ||
control: { type: 'number', min: 0, max: 100, step: 1 } | ||
}, | ||
fieldMetadata: { | ||
table: { | ||
disable: true | ||
} | ||
}, | ||
displayMode: { | ||
options: ['EDITABLE', 'LABELS_LEFT', 'DISPLAY_ONLY'], | ||
control: { type: 'radio' } | ||
}, | ||
variant: { | ||
table: { | ||
disable: true | ||
} | ||
}, | ||
getPConnect: { | ||
table: { | ||
disable: true | ||
} | ||
} | ||
}, | ||
component: PegaExtensionsMeter | ||
}; | ||
|
||
const setPCore = () => { | ||
(window as any).PCore = { | ||
getConstants: () => { | ||
return { | ||
CASE_INFO: { | ||
CASE_INFO_ID: 'ID' | ||
} | ||
}; | ||
}, | ||
getComponentsRegistry: () => { | ||
return { | ||
getLazyComponent: (f: string) => f | ||
}; | ||
}, | ||
getEnvironmentInfo: () => { | ||
return { | ||
getTimeZone: () => 'local' | ||
}; | ||
}, | ||
getDataApiUtils: () => { | ||
return { | ||
getData: (dataPage: string) => { | ||
if (dataPage === 'test') { | ||
return Promise.resolve({ | ||
data: { | ||
data: [ | ||
{ label: 'Pass', color: '#34a35d', value: 16 }, | ||
{ label: 'Failed', color: '#fb243d', value: 3 }, | ||
{ label: 'Blocked', color: '#f5fa60', value: 6 }, | ||
{ label: 'Not Executed', color: '#c084fc', value: 10 } | ||
] | ||
} | ||
}); | ||
} | ||
return Promise.resolve({ | ||
data: { | ||
data: [ | ||
{ label: 'Apps', color: '#34d399', value: 16 }, | ||
{ label: 'Messages', color: '#fbbf24', value: 8 }, | ||
{ label: 'Media', color: '#60a5fa', value: 24 }, | ||
{ label: 'System', color: '#c084fc', value: 10 } | ||
] | ||
} | ||
}); | ||
} | ||
}; | ||
} | ||
}; | ||
}; | ||
|
||
type Story = StoryObj<typeof PegaExtensionsMeter>; | ||
|
||
const MeterDemo = (inputs: MeterProps) => { | ||
return { | ||
render: (args: MeterProps) => { | ||
setPCore(); | ||
const props = { | ||
...args, | ||
getPConnect: () => { | ||
return { | ||
getValue: () => 'C-123', | ||
getContextName: () => '', | ||
getStateProps: () => { | ||
return { | ||
value: 'C-123' | ||
}; | ||
}, | ||
getActionsApi: () => { | ||
return { | ||
openWorkByHandle: () => { | ||
/* nothing */ | ||
}, | ||
createWork: () => { | ||
/* nothing */ | ||
}, | ||
updateFieldValue: () => { | ||
/* nothing */ | ||
}, | ||
triggerFieldChange: () => { | ||
/* nothing */ | ||
}, | ||
showCasePreview: () => { | ||
/* nothing */ | ||
} | ||
}; | ||
}, | ||
ignoreSuggestion: () => { | ||
/* nothing */ | ||
}, | ||
acceptSuggestion: () => { | ||
/* nothing */ | ||
}, | ||
setInheritedProps: () => { | ||
/* nothing */ | ||
}, | ||
resolveConfigProps: () => { | ||
/* nothing */ | ||
} | ||
}; | ||
} | ||
}; | ||
return <PegaExtensionsMeter {...props} />; | ||
}, | ||
args: inputs | ||
}; | ||
}; | ||
|
||
export const Default: Story = MeterDemo({ | ||
displayMode: 'EDITABLE', | ||
value: 50, | ||
dataPage: '', | ||
label: 'Tasks completed', | ||
hideLabel: false, | ||
color: '#34d399', | ||
additionalInfo: 'Number of tasks completed out of total tasks', | ||
showMetaData: false, | ||
totalTasks: 10 | ||
}); | ||
|
||
export const Grouped: Story = MeterDemo({ | ||
dataPage: 'test', | ||
label: 'Test coverage', | ||
showMetaData: true, | ||
displayMode: 'EDITABLE' | ||
}); | ||
|
||
export const Grouped1: Story = MeterDemo({ | ||
dataPage: 'storage', | ||
label: 'System storage', | ||
showMetaData: true, | ||
totalTasks: 100, | ||
displayMode: 'EDITABLE' | ||
}); |
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,10 @@ | ||
import { render, screen } from '@testing-library/react'; | ||
import { composeStories } from '@storybook/react'; | ||
import * as DemoStories from './demo.stories'; | ||
|
||
const { Default } = composeStories(DemoStories); | ||
|
||
test('renders meter component with default args', async () => { | ||
render(<Default />); | ||
expect(await screen.findByText('Tasks completed')).toBeVisible(); | ||
}); |
Oops, something went wrong.