-
Notifications
You must be signed in to change notification settings - Fork 89
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(meter): add new meter component
- Loading branch information
1 parent
6d4b6af
commit 2c2975b
Showing
19 changed files
with
155,294 additions
and
0 deletions.
There are no files selected for viewing
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,213 @@ | ||
import * as React from 'react'; | ||
import { Step, StepColor, StepType } from './Step'; | ||
import { BaseProps } from '@/utils/types'; | ||
import { Text } from '@/index'; | ||
import styles from '@css/components/meter.module.css'; | ||
import classNames from 'classnames'; | ||
import { useMeterValues } from './useMeterValues'; | ||
|
||
export type MeterLabelSize = 'small' | 'regular' | 'large'; | ||
export type MeterSize = 'small' | 'regular' | 'large'; | ||
|
||
export type RenderLabelProps = { | ||
filledSteps: number; | ||
value: number; | ||
min: number; | ||
max: number; | ||
stepCount: number; | ||
percentage: number; | ||
}; | ||
|
||
export type FillStepProps = { | ||
value: number; | ||
stepCount: number; | ||
min: number; | ||
max: number; | ||
}; | ||
|
||
export type MeterValueProps = { | ||
value: number; | ||
min: number; | ||
max: number; | ||
stepCount: number; | ||
getFilledSteps?: (props: FillStepProps) => number; | ||
}; | ||
|
||
export interface MeterProps extends BaseProps, React.HTMLAttributes<HTMLDivElement> { | ||
/** | ||
* Value of the `Meter` | ||
*/ | ||
value: number; | ||
/** | ||
* Minimum range of the `Meter` | ||
*/ | ||
min: number; | ||
/** | ||
* Maximum range of the `Meter` | ||
*/ | ||
max: number; | ||
/** | ||
* Total number of steps in the `Meter` | ||
*/ | ||
stepCount: number; | ||
/** | ||
* Color of the empty `Step` | ||
*/ | ||
emptyColor?: string; | ||
/** | ||
* Color of the filled `Step` | ||
*/ | ||
fillColor?: StepColor | StepColor[]; | ||
/** | ||
* Size of the Meter | ||
*/ | ||
meterSize: MeterSize; | ||
/** | ||
* Size of the Meter `Label` | ||
*/ | ||
labelSize?: MeterLabelSize; | ||
/** | ||
* Render prop to display Meter Label | ||
* | ||
* <pre className="DocPage-codeBlock"> | ||
* RenderLabelProps: { | ||
* filledSteps: number; | ||
* value: number; | ||
* min: number; | ||
* max: number; | ||
* stepCount: number; | ||
* percentage: number; | ||
* } | ||
* </pre> | ||
* | ||
* It receives an object with the following properties: | ||
* `filledSteps`: Number of filled steps in the Meter <br /> | ||
* `value`: Value of the Meter <br /> | ||
* `min`: Minimum range of the Meter <br /> | ||
* `max`: Maximum range of the Meter <br /> | ||
* `stepCount`: Total number of steps in the Meter <br /> | ||
* `percentage`: Percentage of the Meter filled <br /> | ||
* | ||
*/ | ||
renderLabel?: (props: RenderLabelProps) => React.ReactText; | ||
/** | ||
* Determines whether to show default value label in percentage | ||
*/ | ||
showLabel?: boolean; | ||
/** | ||
* Custom function to calculate the number of filled steps | ||
* @param FillStepProps | ||
* @returns number | ||
* @default calculateFilledSteps | ||
* | ||
* <pre className="DocPage-codeBlock"> | ||
* FillStepProps: { | ||
* value: number; | ||
* stepCount: number; | ||
* min: number; | ||
* max: number; | ||
* } | ||
* </pre> | ||
*/ | ||
getFilledSteps?: (props: FillStepProps) => number; | ||
/** | ||
* Aria label for the Meter | ||
*/ | ||
ariaLabel?: string; | ||
} | ||
|
||
/** | ||
* **Note:** | ||
* - Meter component is using `> half step range` logic to calculate filled steps | ||
* - Use [custom hook `useMeterValues`](https://mds.innovaccer.com/?path=/docs/components-meter-custom-label--custom-label) to get number of filled steps and percentage internally calculated by the component | ||
* - To use a [custom logic](https://mds.innovaccer.com/?path=/docs/components-meter-custom-step-logic--custom-step-logic) to calculate filled steps, you can pass it as `getFilledSteps` prop | ||
* - To render [custom label](https://mds.innovaccer.com/?path=/docs/components-meter-all--all), you can pass a render prop `renderLabel` | ||
*/ | ||
|
||
export const Meter = (props: MeterProps) => { | ||
const { | ||
value, | ||
min, | ||
max, | ||
stepCount, | ||
emptyColor, | ||
fillColor, | ||
getFilledSteps, | ||
meterSize, | ||
className, | ||
renderLabel, | ||
labelSize, | ||
ariaLabel, | ||
showLabel, | ||
...rest | ||
} = props; | ||
|
||
const { filledSteps, percentage } = useMeterValues({ value, min, max, stepCount, getFilledSteps }); | ||
|
||
const steps = Array.from({ length: stepCount }, (_, index) => { | ||
const type = index < filledSteps ? 'filled' : 'empty'; | ||
const stepColor = Array.isArray(fillColor) ? fillColor[index % fillColor.length] : fillColor; | ||
|
||
const stepClassName = classNames({ | ||
['mr-2']: index < stepCount - 1, | ||
}); | ||
|
||
const stepProps = { | ||
stepSize: meterSize, | ||
emptyColor, | ||
type: type as StepType, | ||
fillColor: stepColor, | ||
className: stepClassName, | ||
}; | ||
|
||
return <Step key={index} {...stepProps} />; | ||
}); | ||
|
||
const renderLabelProps = { | ||
filledSteps, | ||
value, | ||
min, | ||
max, | ||
stepCount, | ||
percentage, | ||
}; | ||
|
||
const label = renderLabel ? renderLabel(renderLabelProps) : `${percentage}%`; | ||
|
||
return ( | ||
<div | ||
data-test="DesignSystem-Meter" | ||
className={classNames(styles.Meter, className)} | ||
role="meter" | ||
aria-valuemin={min} | ||
aria-valuemax={max} | ||
aria-valuenow={value} | ||
aria-label={ariaLabel} | ||
{...rest} | ||
> | ||
{steps} | ||
{(showLabel || renderLabel) && ( | ||
<Text className="ml-4" size={labelSize || meterSize} data-test="DesignSystem-Meter-Label"> | ||
{label} | ||
</Text> | ||
)} | ||
</div> | ||
); | ||
}; | ||
|
||
Meter.displayName = 'Meter'; | ||
Meter.defaultProps = { | ||
value: 0, | ||
min: 0, | ||
max: 100, | ||
stepCount: 5, | ||
fillColor: 'info', | ||
meterSize: 'regular', | ||
type: 'empty', | ||
showLabel: true, | ||
emptyColor: 'var(--secondary-light)', | ||
}; | ||
|
||
Meter.useMeterValues = useMeterValues; | ||
|
||
export default Meter; |
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,65 @@ | ||
import * as React from 'react'; | ||
import classNames from 'classnames'; | ||
import { BaseProps, extractBaseProps } from '@/utils/types'; | ||
import styles from '@css/components/meter.module.css'; | ||
|
||
export type StepSize = 'small' | 'regular' | 'large'; | ||
export type StepType = 'filled' | 'empty'; | ||
export type StepColor = 'info' | 'success' | 'warning' | 'alert'; | ||
|
||
export interface StepProps extends BaseProps { | ||
/** | ||
* Size of the `Step` | ||
*/ | ||
stepSize: StepSize; | ||
/** | ||
* Type of the `Step` | ||
*/ | ||
type: StepType; | ||
/** | ||
* Color of the empty `Step` | ||
*/ | ||
emptyColor?: string; | ||
/** | ||
* Color of the filled `Step` | ||
*/ | ||
fillColor?: StepColor; | ||
} | ||
|
||
export const Step = (props: StepProps) => { | ||
const { stepSize, type, fillColor, className, emptyColor } = props; | ||
|
||
const baseProps = extractBaseProps(props); | ||
|
||
const classes = classNames( | ||
{ | ||
[styles['Meter-Step']]: true, | ||
[styles[`Meter-Step--${type}`]]: type, | ||
[styles[`Meter-Step--${stepSize}`]]: stepSize, | ||
[styles[`Meter-Step--${fillColor}`]]: type === 'filled' && fillColor, | ||
}, | ||
className | ||
); | ||
|
||
const emptyStyle = type === 'empty' ? { background: emptyColor } : {}; | ||
|
||
return ( | ||
<span | ||
data-test="DesignSystem-Meter-Step" | ||
{...baseProps} | ||
style={emptyStyle} | ||
className={classes} | ||
role="presentation" | ||
aria-hidden="true" | ||
/> | ||
); | ||
}; | ||
|
||
Step.displayName = 'Step'; | ||
Step.defaultProps = { | ||
stepSize: 'regular', | ||
type: 'empty', | ||
emptyColor: 'var(--secondary-light)', | ||
}; | ||
|
||
export default Step; |
31 changes: 31 additions & 0 deletions
31
core/components/atoms/meter/__stories__/appearance.story.jsx
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,31 @@ | ||
import * as React from 'react'; | ||
import { Meter, Text } from '@/index'; | ||
|
||
// CSF format story | ||
export const appearance = () => { | ||
const appearance = ['alert', 'warning', 'success', 'info']; | ||
return ( | ||
<div className="d-flex flex-column justify-content-around"> | ||
{appearance.map((color, index) => ( | ||
<div key={index} className="d-flex align-items-center mb-5"> | ||
<Text weight="medium" className="mr-5"> | ||
{color.charAt(0).toUpperCase() + color.slice(1)}: | ||
</Text> | ||
<Meter value={40} fillColor={color} /> | ||
</div> | ||
))} | ||
</div> | ||
); | ||
}; | ||
|
||
export default { | ||
title: 'Components/Meter/Variants/Appearance', | ||
component: Meter, | ||
parameters: { | ||
docs: { | ||
docPage: { | ||
title: 'Meter', | ||
}, | ||
}, | ||
}, | ||
}; |
52 changes: 52 additions & 0 deletions
52
core/components/atoms/meter/__stories__/customHook.story.jsx
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,52 @@ | ||
import * as React from 'react'; | ||
import { Meter, Text } from '@/index'; | ||
|
||
// CSF format story | ||
export const customLabel = () => { | ||
const value = 100; | ||
const min = 50; | ||
const max = 150; | ||
const stepCount = 5; | ||
|
||
const { filledSteps, percentage } = Meter.useMeterValues({ value, min, max, stepCount }); | ||
|
||
return ( | ||
<div className="d-flex flex-column"> | ||
<Meter value={value} stepCount={stepCount} min={min} max={max} showLabel={false} /> | ||
<Text size="small" appearance="subtle" className="mt-6"> | ||
{filledSteps} batches completed ({percentage}%) | ||
</Text> | ||
</div> | ||
); | ||
}; | ||
|
||
const customCode = `() => { | ||
const value = 100; | ||
const min = 50; | ||
const max = 150; | ||
const stepCount = 5; | ||
const { filledSteps, percentage } = Meter.useMeterValues({ value, min, max, stepCount }); | ||
return ( | ||
<div className="d-flex flex-column"> | ||
<Meter value={value} stepCount={stepCount} min={min} max={max} showLabel={false} /> | ||
<Text size="small" appearance="subtle" className="mt-6"> | ||
{filledSteps} batches completed ({percentage}%) | ||
</Text> | ||
</div> | ||
); | ||
}`; | ||
|
||
export default { | ||
title: 'Components/Meter/Custom Label', | ||
component: Meter, | ||
parameters: { | ||
docs: { | ||
docPage: { | ||
title: 'Meter', | ||
customCode, | ||
}, | ||
}, | ||
}, | ||
}; |
19 changes: 19 additions & 0 deletions
19
core/components/atoms/meter/__stories__/customLabelSize.story.jsx
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,19 @@ | ||
import * as React from 'react'; | ||
import { Meter } from '@/index'; | ||
|
||
// CSF format story | ||
export const customLabelSize = () => { | ||
return <Meter value={40} meterSize="small" labelSize="large" />; | ||
}; | ||
|
||
export default { | ||
title: 'Components/Meter/Variants/Custom Label Size', | ||
component: Meter, | ||
parameters: { | ||
docs: { | ||
docPage: { | ||
title: 'Meter', | ||
}, | ||
}, | ||
}, | ||
}; |
Oops, something went wrong.