cozy-ui 62.9.0 → 62.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ # [62.11.0](https://github.com/cozy/cozy-ui/compare/v62.10.0...v62.11.0) (2022-04-22)
2
+
3
+
4
+ ### Features
5
+
6
+ * Add dropdown ([ee214f8](https://github.com/cozy/cozy-ui/commit/ee214f8))
7
+
8
+ # [62.10.0](https://github.com/cozy/cozy-ui/compare/v62.9.1...v62.10.0) (2022-04-21)
9
+
10
+
11
+ ### Features
12
+
13
+ * Add chart.js and react-chartjs-2 packages ([16df033](https://github.com/cozy/cozy-ui/commit/16df033))
14
+ * Add PieChart component ([b7ce49f](https://github.com/cozy/cozy-ui/commit/b7ce49f))
15
+
16
+ ## [62.9.1](https://github.com/cozy/cozy-ui/compare/v62.9.0...v62.9.1) (2022-04-21)
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+ * bump convict from 6.0.0 to 6.2.2 ([403c38e](https://github.com/cozy/cozy-ui/commit/403c38e))
22
+
1
23
  # [62.9.0](https://github.com/cozy/cozy-ui/compare/v62.8.0...v62.9.0) (2022-04-20)
2
24
 
3
25
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-ui",
3
- "version": "62.9.0",
3
+ "version": "62.11.0",
4
4
  "description": "Cozy apps UI SDK",
5
5
  "main": "./index.js",
6
6
  "bin": {
@@ -113,6 +113,7 @@
113
113
  "http-server": "0.13.0",
114
114
  "husky": "0.14.3",
115
115
  "identity-obj-proxy": "3.0.0",
116
+ "jest-canvas-mock": "2.3.1",
116
117
  "jest-cli": "^26.6.3",
117
118
  "kss": "3.0.1",
118
119
  "kss-webpack-plugin": "1.5.0",
@@ -153,6 +154,7 @@
153
154
  "dependencies": {
154
155
  "@babel/runtime": "^7.3.4",
155
156
  "@popperjs/core": "^2.4.4",
157
+ "chart.js": "3.7.1",
156
158
  "classnames": "^2.2.5",
157
159
  "cozy-interapp": "^0.5.4",
158
160
  "date-fns": "^1.28.5",
@@ -164,6 +166,7 @@
164
166
  "node-polyglot": "^2.2.2",
165
167
  "normalize.css": "^8.0.0",
166
168
  "piwik-react-router": "0.12.1",
169
+ "react-chartjs-2": "4.1.0",
167
170
  "react-markdown": "^4.0.8",
168
171
  "react-pdf": "^4.0.5",
169
172
  "react-popper": "^2.2.3",
@@ -200,7 +203,8 @@
200
203
  },
201
204
  "jest": {
202
205
  "setupFilesAfterEnv": [
203
- "./test/jestsetup.js"
206
+ "./test/jestsetup.js",
207
+ "jest-canvas-mock"
204
208
  ],
205
209
  "moduleFileExtensions": [
206
210
  "js",
@@ -0,0 +1,53 @@
1
+ This component can be used as a trigger to open menus, for example an ActionMenu component. There is also `DropdownButton` which offers the same possibilities, but with the added button wrapper.
2
+
3
+ ```jsx
4
+ import DropdownText from 'cozy-ui/transpiled/react/DropdownText'
5
+ import Typography from 'cozy-ui/transpiled/react/Typography'
6
+ import Grid from 'cozy-ui/transpiled/react/MuiCozyTheme/Grid'
7
+
8
+ const variants = [
9
+ 'h1',
10
+ 'h2',
11
+ 'h3',
12
+ 'h4',
13
+ 'h5',
14
+ 'subtitle1',
15
+ 'subtitle2',
16
+ 'body1',
17
+ 'body2',
18
+ 'caption'
19
+ ]
20
+
21
+ ;
22
+
23
+ <>
24
+ <Grid container>
25
+ <Grid item xs={6}>
26
+ Default
27
+ {variants.map((variant, index) => (
28
+ <div key={index} className='u-mb-1'>
29
+ <DropdownText variant={variant}>
30
+ {variant}
31
+ </DropdownText>
32
+ </div>
33
+ ))}
34
+ </Grid>
35
+ <Grid item xs={6}>
36
+ Disabled
37
+ {variants.map((variant, index) => (
38
+ <div key={index} className='u-mb-1'>
39
+ <DropdownText variant={variant} disabled>
40
+ {variant}
41
+ </DropdownText>
42
+ </div>
43
+ ))}
44
+ </Grid>
45
+ </Grid>
46
+ <DropdownText spaceBetween style={{ border: '1px solid var(--borderMainColor)', width: '100%', marginBottom: '1rem' }}>
47
+ Space between text and icon
48
+ </DropdownText>
49
+ <DropdownText style={{ border: '1px solid var(--borderMainColor)' }}>
50
+ Text with<br />breaking spaces<br />inside content
51
+ </DropdownText>
52
+ </>
53
+ ```
@@ -0,0 +1,79 @@
1
+ import React, { forwardRef } from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { makeStyles } from '@material-ui/core/styles'
4
+
5
+ import Typography from '../Typography'
6
+ import Icon from '../Icon'
7
+ import BottomIcon from '../Icons/Bottom'
8
+
9
+ const useStyles = makeStyles(theme => ({
10
+ endIcon: {
11
+ marginLeft: '5px'
12
+ },
13
+ typography: {
14
+ display: 'flex',
15
+ alignItems: 'center',
16
+ width: '100%',
17
+ justifyContent: ({ spaceBetween }) =>
18
+ spaceBetween ? 'space-between' : 'left',
19
+ color: ({ disabled }) =>
20
+ theme.palette.text[disabled ? 'disabled' : 'primary']
21
+ }
22
+ }))
23
+
24
+ const endIconSizeByVariant = {
25
+ h1: 24,
26
+ h2: 17,
27
+ h3: 15,
28
+ h4: 14,
29
+ h5: 13,
30
+ h6: 12,
31
+ body1: 12,
32
+ body2: 11,
33
+ caption: 10,
34
+ subtitle1: 11,
35
+ subtitle2: 10
36
+ }
37
+
38
+ const DropdownText = forwardRef(
39
+ (
40
+ {
41
+ spaceBetween = false,
42
+ variant = 'body1',
43
+ disabled = false,
44
+ children,
45
+ ...props
46
+ },
47
+ ref
48
+ ) => {
49
+ const styles = useStyles({ spaceBetween, disabled })
50
+
51
+ return (
52
+ <Typography
53
+ ref={ref}
54
+ classes={{ root: styles.typography }}
55
+ variant={variant}
56
+ {...props}
57
+ >
58
+ {children}
59
+ <Icon
60
+ className={styles.endIcon}
61
+ icon={BottomIcon}
62
+ size={endIconSizeByVariant[variant]}
63
+ />
64
+ </Typography>
65
+ )
66
+ }
67
+ )
68
+
69
+ DropdownText.propTypes = {
70
+ /** Whether there is a space between the label and the icon */
71
+ spaceBetween: PropTypes.bool,
72
+ /** Variant used by Typography component */
73
+ variant: PropTypes.string,
74
+ /** Whether the component is disabled */
75
+ disabled: PropTypes.bool,
76
+ children: PropTypes.node
77
+ }
78
+
79
+ export default DropdownText
@@ -0,0 +1,53 @@
1
+ #### PieChart
2
+
3
+ ```jsx
4
+ import Grid from 'cozy-ui/transpiled/react/MuiCozyTheme/Grid'
5
+ import PieChart from 'cozy-ui/transpiled/react/PieChart'
6
+
7
+ ;
8
+
9
+ <Grid container>
10
+ <Grid item xs={12} sm={6}>
11
+ <PieChart
12
+ single={true}
13
+ primaryText="60 %"
14
+ secondaryText="of the total"
15
+ data={{
16
+ labels: ['Health'],
17
+ datasets: [{ data: [60, 40] }]
18
+ }}
19
+ options={{
20
+ plugins: {
21
+ tooltip: {
22
+ callbacks: {
23
+ label: tooltipItems => `${tooltipItems.label}: ${tooltipItems.raw}%`
24
+ }
25
+ }
26
+ }
27
+ }}
28
+ />
29
+ </Grid>
30
+ <Grid item xs={12} sm={6}>
31
+ <PieChart
32
+ primaryText="105 €"
33
+ secondaryText="on the period"
34
+ data={{
35
+ labels: ['Earnings', 'Children', 'Sports', 'Housing', 'Health'],
36
+ datasets: [{
37
+ data: [40, 30, 20, 10, 5],
38
+ backgroundColor: ['#8978FF', '#FF7B5E', '#F85AA8', '#F1B61E', '#15CACD']
39
+ }]
40
+ }}
41
+ options={{
42
+ plugins: {
43
+ tooltip: {
44
+ callbacks: {
45
+ label: tooltipItems => `${tooltipItems.label}: ${tooltipItems.raw}€`
46
+ }
47
+ }
48
+ }
49
+ }}
50
+ />
51
+ </Grid>
52
+ </Grid>
53
+ ```
@@ -0,0 +1,141 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import cx from 'classnames'
4
+ import { Chart as ChartJS, ArcElement, Tooltip } from 'chart.js'
5
+ import { Doughnut } from 'react-chartjs-2'
6
+ import { useTheme } from '@material-ui/core'
7
+ import { makeStyles } from '@material-ui/core/styles'
8
+ import set from 'lodash/set'
9
+ import cloneDeep from 'lodash/cloneDeep'
10
+
11
+ import Typography from '../Typography'
12
+ import isTesting from '../helpers/isTesting'
13
+
14
+ ChartJS.register(ArcElement, Tooltip)
15
+
16
+ const useStyles = makeStyles(theme => ({
17
+ container: {
18
+ position: 'relative',
19
+ zIndex: '1',
20
+ width: '192px',
21
+ height: '192px'
22
+ },
23
+ background: {
24
+ position: 'absolute',
25
+ boxSizing: 'border-box',
26
+ top: 0,
27
+ bottom: 0,
28
+ left: 0,
29
+ right: 0,
30
+ border: '24px solid var(--actionColorGhost)',
31
+ borderRadius: '100%',
32
+ zIndex: -1,
33
+ backgroundColor: ({ single }) =>
34
+ !single && theme.palette.type === 'dark'
35
+ ? theme.palette.primary.main
36
+ : 'none',
37
+ boxShadow: ({ single }) =>
38
+ !single && theme.palette.type === 'dark'
39
+ ? `0 0 0 2px ${theme.palette.primary.main}`
40
+ : 'none'
41
+ },
42
+ centerText: {
43
+ display: 'flex',
44
+ flexDirection: 'column',
45
+ position: 'absolute',
46
+ alignItems: 'center',
47
+ justifyContent: 'center',
48
+ textAlign: 'center',
49
+ top: '25px',
50
+ left: '25px',
51
+ right: '25px',
52
+ bottom: '25px',
53
+ borderRadius: '50%',
54
+ zIndex: '-1'
55
+ },
56
+ typography: {
57
+ color: ({ single }) =>
58
+ !single && theme.palette.type === 'dark'
59
+ ? theme.palette.primary.contrastText
60
+ : theme.palette.text.primary
61
+ }
62
+ }))
63
+
64
+ export const makeOptions = ({ options, single }) => {
65
+ const madeOptions = {
66
+ cutout: '75%',
67
+ elements: {
68
+ arc: {
69
+ borderWidth: 0
70
+ }
71
+ },
72
+ animation: isTesting() ? false : { duration: 1000 },
73
+ ...options
74
+ }
75
+ if (single) set(madeOptions, 'plugins.tooltip.filter', item => item.label)
76
+
77
+ return madeOptions
78
+ }
79
+
80
+ export const makeData = ({ data, single, theme }) => {
81
+ const madeData = cloneDeep(data)
82
+ if (single)
83
+ set(madeData, 'datasets[0].backgroundColor', [
84
+ theme.palette.primary.main,
85
+ 'transparent'
86
+ ])
87
+
88
+ return madeData
89
+ }
90
+
91
+ const PieChart = ({
92
+ data,
93
+ options = {},
94
+ primaryText,
95
+ secondaryText,
96
+ single = false,
97
+ className,
98
+ ...props
99
+ }) => {
100
+ const theme = useTheme()
101
+ const classes = useStyles({ single })
102
+ const madeOptions = makeOptions({ options, single })
103
+ const madeData = makeData({ data, single, theme })
104
+
105
+ return (
106
+ <div className={cx(classes.container, className)} {...props}>
107
+ <div className={classes.background} />
108
+ <Doughnut data={madeData} options={madeOptions} />
109
+ {(primaryText || secondaryText) && (
110
+ <div className={classes.centerText}>
111
+ {primaryText && (
112
+ <Typography classes={{ root: classes.typography }} variant="h3">
113
+ {primaryText}
114
+ </Typography>
115
+ )}
116
+ {secondaryText && (
117
+ <Typography classes={{ root: classes.typography }} variant="body2">
118
+ {secondaryText}
119
+ </Typography>
120
+ )}
121
+ </div>
122
+ )}
123
+ </div>
124
+ )
125
+ }
126
+
127
+ PieChart.propTypes = {
128
+ /** Data to be passed to chart.js graph */
129
+ data: PropTypes.object.isRequired,
130
+ /** Options to be passed to chart.js graph */
131
+ options: PropTypes.object,
132
+ /** Text to show as primary */
133
+ primaryText: PropTypes.string,
134
+ /** Text to show as secondary */
135
+ secondaryText: PropTypes.string,
136
+ /** Whether to use a single data */
137
+ single: PropTypes.bool,
138
+ className: PropTypes.string
139
+ }
140
+
141
+ export default PieChart
@@ -0,0 +1,123 @@
1
+ import { makeOptions, makeData } from './index'
2
+
3
+ describe('makeOptions', () => {
4
+ it('should return default options', () => {
5
+ const options = makeOptions({ options: {}, single: false })
6
+
7
+ expect(options).toStrictEqual({
8
+ cutout: '75%',
9
+ elements: {
10
+ arc: {
11
+ borderWidth: 0
12
+ }
13
+ },
14
+ animation: { duration: 1000 }
15
+ })
16
+ })
17
+
18
+ it('should return default options for a single pie chart', () => {
19
+ const options = makeOptions({ options: {}, single: true })
20
+
21
+ expect(options).toStrictEqual({
22
+ cutout: '75%',
23
+ elements: {
24
+ arc: {
25
+ borderWidth: 0
26
+ }
27
+ },
28
+ animation: { duration: 1000 },
29
+ plugins: { tooltip: { filter: expect.any(Function) } }
30
+ })
31
+ })
32
+
33
+ it('should return default and customized options', () => {
34
+ const options = makeOptions({ options: { foo: 'bar' }, single: false })
35
+
36
+ expect(options).toStrictEqual({
37
+ cutout: '75%',
38
+ elements: {
39
+ arc: {
40
+ borderWidth: 0
41
+ }
42
+ },
43
+ animation: { duration: 1000 },
44
+ foo: 'bar'
45
+ })
46
+ })
47
+
48
+ it('should return default and customized options for a single pie chart', () => {
49
+ const options = makeOptions({ options: { foo: 'bar' }, single: true })
50
+
51
+ expect(options).toStrictEqual({
52
+ cutout: '75%',
53
+ elements: {
54
+ arc: {
55
+ borderWidth: 0
56
+ }
57
+ },
58
+ animation: { duration: 1000 },
59
+ plugins: { tooltip: { filter: expect.any(Function) } },
60
+ foo: 'bar'
61
+ })
62
+ })
63
+
64
+ it('should return default and customized options for a single pie chart even for customized default option', () => {
65
+ const options = makeOptions({
66
+ options: { plugins: { tooltip: { foo: 'bar' } } },
67
+ single: true
68
+ })
69
+
70
+ expect(options).toStrictEqual({
71
+ cutout: '75%',
72
+ elements: {
73
+ arc: {
74
+ borderWidth: 0
75
+ }
76
+ },
77
+ animation: { duration: 1000 },
78
+ plugins: {
79
+ tooltip: {
80
+ foo: 'bar',
81
+ filter: expect.any(Function)
82
+ }
83
+ }
84
+ })
85
+ })
86
+ })
87
+
88
+ describe('makeData', () => {
89
+ it('should return previous data is not a single pie chart', () => {
90
+ const prevData = { labels: ['Health'] }
91
+ const data = makeData({
92
+ data: prevData,
93
+ single: false,
94
+ theme: { palette: { primary: { main: '#fff' } } }
95
+ })
96
+
97
+ expect(data).toStrictEqual(prevData)
98
+ })
99
+
100
+ it('should add backgroud color for a single pie chart', () => {
101
+ const data = makeData({
102
+ data: { labels: ['Health'] },
103
+ single: true,
104
+ theme: { palette: { primary: { main: '#fff' } } }
105
+ })
106
+
107
+ expect(data).toStrictEqual({
108
+ labels: ['Health'],
109
+ datasets: [{ backgroundColor: ['#fff', 'transparent'] }]
110
+ })
111
+ })
112
+
113
+ it('should not mutate previous data for a single pie chart', () => {
114
+ const prevData = { labels: ['Health'] }
115
+ makeData({
116
+ data: prevData,
117
+ single: true,
118
+ theme: { palette: { primary: { main: '#fff' } } }
119
+ })
120
+
121
+ expect(prevData).toStrictEqual({ labels: ['Health'] })
122
+ })
123
+ })