Skip to content

Commit

Permalink
Merge pull request #1116 from cozy/figure-component
Browse files Browse the repository at this point in the history
Figure component
  • Loading branch information
ptbrowne authored Jul 24, 2019
2 parents f09b78d + dde7e1f commit ceeffe8
Show file tree
Hide file tree
Showing 10 changed files with 705 additions and 1 deletion.
4 changes: 3 additions & 1 deletion docs/styleguide.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ module.exports = {
'../react/Infos/index.jsx',
'../react/ContextHeader/index.jsx',
'../react/Filename/index.jsx',
'../react/AppTitle/index.jsx'
'../react/AppTitle/index.jsx',
'../react/Figure/Figure.jsx',
'../react/Figure/FigureBlock.jsx'
]
},
{
Expand Down
113 changes: 113 additions & 0 deletions react/Figure/Figure.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import React from 'react'
import Types from 'prop-types'
import cx from 'classnames'
import styles from './Figure.styl'

/**
* Shows a number, typically a balance or an important financial
* number in a bold way.
*/
const stylePositive = styles['Figure-content--positive']
const styleNegative = styles['Figure-content--negative']
const styleWarning = styles['Figure-content--warning']
const styleBig = styles['Figure--big']
const styleClickable = styles['Figure--clickable']

const Figure = props => {
const {
symbol,
withCurrencySpacing = true,
coloredPositive,
coloredNegative,
coloredWarning,
warningLimit,
signed,
className,
theme = 'default',
total,
totalClassName,
currencyClassName,
size,
onClick,
inline,
blurred
} = props

let { decimalNumbers } = props
decimalNumbers = isNaN(decimalNumbers) ? 2 : decimalNumbers

const totalLocalized =
typeof total === 'number'
? total.toLocaleString('fr-FR', {
minimumFractionDigits: decimalNumbers,
maximumFractionDigits: decimalNumbers
})
: total
const isTotalPositive = total > 0
const isTotalInLimit = total > warningLimit
const isWarning = !isTotalPositive && !isTotalInLimit && coloredWarning

return (
<div
className={cx(
styles[theme],
{
[stylePositive]: isTotalPositive && coloredPositive,
[styleNegative]:
total !== 0 && !isTotalPositive && !isWarning && coloredNegative,
[styleWarning]: isWarning,
[styleBig]: size == 'big',
[styleClickable]: onClick,
[styles.Figure_blur]: blurred,
[styles['Figure--inline']]: inline
},
className
)}
onClick={onClick}
>
<span className={cx(styles['Figure-total'], totalClassName)}>
{isTotalPositive && signed && '+'}
{totalLocalized}
</span>
{symbol && (
<span
className={cx(
styles['Figure-currency'],
{
[styles['Figure__currency--withSpacing']]: withCurrencySpacing
},
currencyClassName
)}
>
{symbol}
</span>
)}
</div>
)
}

Figure.propTypes = {
/** Number to display */
total: Types.oneOfType([Types.number, Types.string]).isRequired,
/** Class name applied to the element wrapping the number */
totalClassName: Types.string,
/** Currency to show */
symbol: Types.string,
currencyClassName: Types.string,
/** Colors positive numbers in green */
coloredPositive: Types.bool,
/** Colors negative numbers in red */
coloredNegative: Types.bool,
/** Displays the sign */
signed: Types.bool,
/** Numbers of decimals to show (default=2) */
decimalNumbers: Types.number,
/** Whether to add a specific class to show warning */
warningLimit: Types.number,
/** Whether to add some spacing between the figure and the currency or not */
withCurrencySpacing: Types.bool,
/** Blur the amount, useful for personal content (banking for example) */
blurred: Types.bool
}

export default Figure
18 changes: 18 additions & 0 deletions react/Figure/Figure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
```jsx
<div>
<Figure
total={1000}
symbol=''
coloredPositive coloredNegative signed />

<Figure
total={-1000}
symbol=''
coloredPositive coloredNegative signed />

<Figure
total={-1000}
symbol=''
signed />
</div>
```
48 changes: 48 additions & 0 deletions react/Figure/Figure.spec.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react'
import { shallow } from 'enzyme'
import { flatten, merge } from 'lodash'
import Figure from './Figure'

const combine = (...iterables) => {
if (iterables.length === 1) {
return iterables[0].map(x => [x])
} else {
const combinationsNMinus1 = combine.apply(null, iterables.slice(1))
return flatten(
combinationsNMinus1.map(c => iterables[0].map(item => [item, ...c]))
)
}
}

const formatAttrs = attrs => {
return Object.keys(attrs)
.map(x => `${x}: ${attrs[x]}`)
.join(', ')
}

describe('Figure', () => {
const amounts = [-100, 100, 500, 4]

const coloredAttributes = [
'coloredPositive',
'coloredNegative',
'coloredWarning'
]

const combinations = combine
.apply(null, coloredAttributes.map(x => [{ [x]: true }, { [x]: false }]))
.map(attrs => {
return merge.apply(null, [{}, ...attrs])
})

for (let amount of amounts) {
for (let attrs of combinations) {
it(`should render correctly ${amount} ${formatAttrs(attrs)}`, () => {
const el = shallow(
<Figure warningLimit={110} total={amount} {...attrs} />
).getElement()
expect(el).toMatchSnapshot()
})
}
}
})
50 changes: 50 additions & 0 deletions react/Figure/Figure.styl
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
@require 'settings/breakpoints'

.default
&.Figure-currency
color var(--coolGrey)

&.Figure-content--positive
color var(--emerald)

.Figure-currency
color var(--emerald)

&.Figure-content--negative
color var(--pomegranate)

.Figure-currency
color var(--pomegranate)

&.Figure-content--warning
color var(--texasRose)

.Figure-currency
color var(--texasRose)

.primary
color var(--white)

.Figure-total
font-weight 900

.Figure__currency--withSpacing
margin-left .2em

.Figure--big
font-size 2rem
line-height 2.625rem

+small-screen()
.Figure--big
font-size 1.5rem
line-height 1.75rem

.Figure--clickable
cursor pointer

.Figure_blur
filter blur(8px)

.Figure--inline
display inline
51 changes: 51 additions & 0 deletions react/Figure/FigureBlock.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react'
import Types from 'prop-types'
import classNames from 'classnames'
import Figure from './Figure'
import styles from './FigureBlock.styl'
import labelStyles from '../Label/styles.styl'

const labelStyle = labelStyles['c-label']

/**
* Shows a `Figure` with a label, useful for important numbers.
*
* A part from `className` and `label`, takes same properties
* as `Figure`.
*/
const FigureBlock = ({
className,
label,
total,
symbol,
coloredPositive,
coloredNegative,
signed,
decimalNumbers = 0,
figureClassName,
withCurrencySpacing
}) => (
<div className={classNames(styles['FigureBlock'], className)}>
<div className={labelStyle}>{label}</div>
<Figure
size="big"
className={classNames(styles['FigureBlock-figure'], figureClassName)}
total={total}
symbol={symbol}
coloredPositive={coloredPositive}
coloredNegative={coloredNegative}
signed={signed}
decimalNumbers={decimalNumbers}
withCurrencySpacing={withCurrencySpacing}
/>
</div>
)

FigureBlock.propTypes = {
/** Label of the figure */
label: Types.string.isRequired,
/** Extra classname */
className: Types.string
}

export default FigureBlock
23 changes: 23 additions & 0 deletions react/Figure/FigureBlock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Pour montrer une KPI importante.

```jsx
<div>
<FigureBlock
label='Balance totale'
total={1000}
symbol=''
coloredPositive coloredNegative signed />

<FigureBlock
label='Balance totale (negative number)'
total={-1000}
symbol=''
coloredPositive coloredNegative signed />

<FigureBlock
label='Balance totale (no color)'
total={-1000}
symbol=''
signed />
</div>
```
12 changes: 12 additions & 0 deletions react/Figure/FigureBlock.styl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@require 'settings/breakpoints'

.FigureBlock
color var(--charcoalGrey)

.FigureBlock-figure
font-size 2rem
line-height 2.625rem

+small-screen()
.FigureBlock
font-size .7em
Loading

0 comments on commit ceeffe8

Please sign in to comment.