@pie-lib/charting 5.24.1 → 5.26.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/lib/actions-button.js +4 -3
  3. package/lib/actions-button.js.map +1 -1
  4. package/lib/axes.js +44 -10
  5. package/lib/axes.js.map +1 -1
  6. package/lib/bars/common/bars.js +63 -9
  7. package/lib/bars/common/bars.js.map +1 -1
  8. package/lib/bars/common/correct-check-icon.js +55 -0
  9. package/lib/bars/common/correct-check-icon.js.map +1 -0
  10. package/lib/chart.js +17 -10
  11. package/lib/chart.js.map +1 -1
  12. package/lib/common/correctness-indicators.js +99 -0
  13. package/lib/common/correctness-indicators.js.map +1 -0
  14. package/lib/common/drag-handle.js +36 -8
  15. package/lib/common/drag-handle.js.map +1 -1
  16. package/lib/line/common/drag-handle.js +32 -9
  17. package/lib/line/common/drag-handle.js.map +1 -1
  18. package/lib/line/common/line.js +5 -2
  19. package/lib/line/common/line.js.map +1 -1
  20. package/lib/line/line-cross.js +26 -5
  21. package/lib/line/line-cross.js.map +1 -1
  22. package/lib/line/line-dot.js +24 -4
  23. package/lib/line/line-dot.js.map +1 -1
  24. package/lib/mark-label.js +18 -7
  25. package/lib/mark-label.js.map +1 -1
  26. package/lib/plot/common/plot.js +119 -9
  27. package/lib/plot/common/plot.js.map +1 -1
  28. package/lib/plot/dot.js +17 -4
  29. package/lib/plot/dot.js.map +1 -1
  30. package/lib/plot/line.js +19 -6
  31. package/lib/plot/line.js.map +1 -1
  32. package/package.json +5 -5
  33. package/src/__tests__/__snapshots__/axes.test.jsx.snap +5 -1
  34. package/src/__tests__/__snapshots__/chart.test.jsx.snap +6 -3
  35. package/src/__tests__/__snapshots__/mark-label.test.jsx.snap +62 -56
  36. package/src/actions-button.jsx +3 -2
  37. package/src/axes.jsx +37 -3
  38. package/src/bars/common/bars.jsx +57 -4
  39. package/src/bars/common/correct-check-icon.jsx +20 -0
  40. package/src/chart.jsx +12 -3
  41. package/src/common/__tests__/__snapshots__/drag-handle.test.jsx.snap +3 -0
  42. package/src/common/correctness-indicators.jsx +55 -0
  43. package/src/common/drag-handle.jsx +28 -14
  44. package/src/line/common/__tests__/__snapshots__/drag-handle.test.jsx.snap +5 -0
  45. package/src/line/common/__tests__/__snapshots__/line.test.jsx.snap +2 -0
  46. package/src/line/common/drag-handle.jsx +31 -8
  47. package/src/line/common/line.jsx +3 -1
  48. package/src/line/line-cross.js +25 -3
  49. package/src/line/line-dot.js +40 -3
  50. package/src/mark-label.jsx +73 -58
  51. package/src/plot/common/__tests__/__snapshots__/plot.test.jsx.snap +1 -0
  52. package/src/plot/common/plot.jsx +114 -5
  53. package/src/plot/dot.js +19 -3
  54. package/src/plot/line.js +18 -4
@@ -3,6 +3,7 @@ import classNames from 'classnames';
3
3
  import { withStyles } from '@material-ui/core/styles';
4
4
  import AutosizeInput from 'react-input-autosize';
5
5
  import PropTypes from 'prop-types';
6
+
6
7
  import { types } from '@pie-lib/plot';
7
8
  import { correct, incorrect, disabled } from './common/styles';
8
9
  import { color } from '@pie-lib/render-ui';
@@ -42,6 +43,11 @@ const styles = (theme) => ({
42
43
  incorrect: {
43
44
  ...incorrect('color'),
44
45
  },
46
+ flexContainer: {
47
+ display: 'flex',
48
+ flexDirection: 'column',
49
+ alignItems: 'center',
50
+ },
45
51
  });
46
52
 
47
53
  function isFractionFormat(label) {
@@ -89,6 +95,7 @@ export const MarkLabel = (props) => {
89
95
  error,
90
96
  isHiddenLabel,
91
97
  limitCharacters,
98
+ correctnessIndicator,
92
99
  } = props;
93
100
 
94
101
  const [label, setLabel] = useState(mark.label);
@@ -131,65 +138,72 @@ export const MarkLabel = (props) => {
131
138
  renderMath(root);
132
139
  }, []);
133
140
 
134
- return isMathRendering() ? (
135
- <div
136
- ref={(r) => {
137
- root = r;
138
- externalInputRef(r);
139
- }}
140
- dangerouslySetInnerHTML={{ __html: getLabelMathFormat(label) }}
141
- className={classNames(classes.mathInput, {
142
- [classes.disabled]: disabled,
143
- [classes.error]: error,
144
- [classes.correct]: mark.editable && correctness?.label === 'correct',
145
- [classes.incorrect]: mark.editable && correctness?.label === 'incorrect',
146
- })}
147
- onClick={() => setIsEditing(true)}
148
- style={{
149
- minWidth: barWidth,
150
- position: 'fixed',
151
- transformOrigin: 'left',
152
- transform: `rotate(${rotate}deg)`,
153
- visibility: isHiddenLabel ? 'hidden' : 'unset',
154
- }}
155
- ></div>
156
- ) : (
157
- <AutosizeInput
158
- inputRef={(r) => {
159
- _ref(r);
160
- externalInputRef(r);
161
- }}
162
- autoFocus={isEditing || autoFocus}
163
- disabled={disabled}
164
- inputClassName={classNames(
165
- classes.input,
166
- correctness && mark.editable ? correctness.label : null,
167
- disabled && 'disabled',
168
- error && 'error',
141
+ return (
142
+ <div className={classes.flexContainer}>
143
+ {correctnessIndicator}
144
+ {isMathRendering() ? (
145
+ <div
146
+ ref={(r) => {
147
+ root = r;
148
+ externalInputRef(r);
149
+ }}
150
+ dangerouslySetInnerHTML={{ __html: getLabelMathFormat(label) }}
151
+ className={classNames(classes.mathInput, {
152
+ [classes.disabled]: disabled,
153
+ [classes.error]: error,
154
+ [classes.correct]: mark.editable && correctness?.label === 'correct',
155
+ [classes.incorrect]: mark.editable && correctness?.label === 'incorrect',
156
+ })}
157
+ onClick={() => setIsEditing(true)}
158
+ style={{
159
+ minWidth: barWidth,
160
+ position: 'fixed',
161
+ transformOrigin: 'left',
162
+ transform: `rotate(${rotate}deg)`,
163
+ visibility: isHiddenLabel ? 'hidden' : 'unset',
164
+ marginTop: correctnessIndicator ? '24px' : '0',
165
+ }}
166
+ ></div>
167
+ ) : (
168
+ <AutosizeInput
169
+ inputRef={(r) => {
170
+ _ref(r);
171
+ externalInputRef(r);
172
+ }}
173
+ autoFocus={isEditing || autoFocus}
174
+ disabled={disabled}
175
+ inputClassName={classNames(
176
+ classes.input,
177
+ correctness && mark.editable ? correctness.label : null,
178
+ disabled && 'disabled',
179
+ error && 'error',
180
+ )}
181
+ inputStyle={{
182
+ minWidth: barWidth,
183
+ textAlign: 'center',
184
+ background: 'transparent',
185
+ boxSizing: 'border-box',
186
+ paddingLeft: 0,
187
+ paddingRight: 0,
188
+ ...extraStyle,
189
+ }}
190
+ value={label}
191
+ style={{
192
+ position: 'fixed',
193
+ pointerEvents: 'auto',
194
+ top: 0,
195
+ left: 0,
196
+ minWidth: barWidth,
197
+ transformOrigin: 'left',
198
+ transform: `rotate(${rotate}deg)`,
199
+ visibility: isHiddenLabel ? 'hidden' : 'unset',
200
+ marginTop: correctnessIndicator ? '24px' : '0',
201
+ }}
202
+ onChange={onChange}
203
+ onBlur={onChangeProp}
204
+ />
169
205
  )}
170
- inputStyle={{
171
- minWidth: barWidth,
172
- textAlign: 'center',
173
- background: 'transparent',
174
- boxSizing: 'border-box',
175
- paddingLeft: 0,
176
- paddingRight: 0,
177
- ...extraStyle,
178
- }}
179
- value={label}
180
- style={{
181
- position: 'fixed',
182
- pointerEvents: 'auto',
183
- top: 0,
184
- left: 0,
185
- minWidth: barWidth,
186
- transformOrigin: 'left',
187
- transform: `rotate(${rotate}deg)`,
188
- visibility: isHiddenLabel ? 'hidden' : 'unset',
189
- }}
190
- onChange={onChange}
191
- onBlur={onChangeProp}
192
- />
206
+ </div>
193
207
  );
194
208
  };
195
209
 
@@ -210,6 +224,7 @@ MarkLabel.propTypes = {
210
224
  }),
211
225
  isHiddenLabel: PropTypes.bool,
212
226
  limitCharacters: PropTypes.bool,
227
+ correctnessIndicator: PropTypes.node,
213
228
  };
214
229
 
215
230
  export default withStyles(styles)(MarkLabel);
@@ -88,6 +88,7 @@ exports[`RawPlot snapshot renders 1`] = `
88
88
  }
89
89
  }
90
90
  isHovered={false}
91
+ isPlot={true}
91
92
  onDrag={[Function]}
92
93
  onDragStop={[Function]}
93
94
  />
@@ -1,15 +1,19 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { types } from '@pie-lib/plot';
4
- import { Group } from '@vx/group';
3
+ import classNames from 'classnames';
4
+ import Check from '@material-ui/icons/Check';
5
5
  import { withStyles } from '@material-ui/core/styles/index';
6
- import DraggableHandle, { DragHandle } from '../../common/drag-handle';
6
+ import { Group } from '@vx/group';
7
7
  import debug from 'debug';
8
+
9
+ import { types } from '@pie-lib/plot';
10
+ import DraggableHandle, { DragHandle } from '../../common/drag-handle';
8
11
  import { color } from '@pie-lib/render-ui';
9
12
  import { bandKey } from '../../utils';
10
13
  import { correct, incorrect } from '../../common/styles';
11
14
 
12
15
  const log = debug('pie-lib:chart:bars');
16
+ const ICON_SIZE = 16; // 10px icon + 2px padding on all sides + 1px border
13
17
 
14
18
  export class RawPlot extends React.Component {
15
19
  static propTypes = {
@@ -64,6 +68,20 @@ export class RawPlot extends React.Component {
64
68
  this.setDragValue(next);
65
69
  };
66
70
 
71
+ renderCorrectnessIcon = (barX, barWidth, correctVal, correctness, classes, scale) => (
72
+ <foreignObject
73
+ x={barX + barWidth / 2 - ICON_SIZE / 2}
74
+ y={scale.y(correctVal) + ICON_SIZE}
75
+ width={ICON_SIZE}
76
+ height={ICON_SIZE}
77
+ >
78
+ <Check
79
+ className={classNames(classes.correctnessIcon, classes.correctIcon, classes.smallIcon)}
80
+ title={correctness.label}
81
+ />
82
+ </foreignObject>
83
+ );
84
+
67
85
  render() {
68
86
  const {
69
87
  graphProps,
@@ -76,6 +94,7 @@ export class RawPlot extends React.Component {
76
94
  interactive,
77
95
  correctness,
78
96
  defineChart,
97
+ correctData,
79
98
  } = this.props;
80
99
 
81
100
  const { scale, range, size } = graphProps;
@@ -132,6 +151,67 @@ export class RawPlot extends React.Component {
132
151
  scale,
133
152
  }),
134
153
  )}
154
+ {correctness &&
155
+ correctness.value === 'incorrect' &&
156
+ (() => {
157
+ const correctVal = parseFloat(correctData[index] && correctData[index].value);
158
+ if (isNaN(correctVal)) return null;
159
+ const selectedVal = v;
160
+ if (selectedVal > correctVal) {
161
+ // selected is higher than correct: overlay the correct last segment
162
+ const overlayValues = [];
163
+ for (let i = 0; i < correctVal; i++) {
164
+ overlayValues.push(i);
165
+ }
166
+ const lastIndexOfOverlay = overlayValues.length - 1;
167
+ const lastOverlayValue = overlayValues[lastIndexOfOverlay];
168
+ const barX = xBand(bandKey({ label }, index));
169
+ const barWidth = xBand.bandwidth();
170
+ const pointHeight = size.height / max;
171
+ const pointDiameter = (pointHeight > barWidth ? barWidth : pointHeight) * 0.8;
172
+ return (
173
+ <>
174
+ <CustomBarElement
175
+ index={lastOverlayValue}
176
+ pointDiameter={pointDiameter + 10} // increase point diameter for dotted line
177
+ barX={barX}
178
+ barWidth={barWidth}
179
+ pointHeight={pointHeight}
180
+ label={label}
181
+ value={value}
182
+ classes={classes}
183
+ scale={scale}
184
+ dottedOverline={true}
185
+ />
186
+ {this.renderCorrectnessIcon(barX, barWidth, correctVal, correctness, classes, scale)}
187
+ </>
188
+ );
189
+ }
190
+ // selected is lower than correct, render missing segment below the correct bar
191
+ const valuesToRender = [];
192
+ for (let i = selectedVal; i < correctVal; i++) {
193
+ valuesToRender.push(i);
194
+ }
195
+ return (
196
+ <>
197
+ {valuesToRender.map((idx) =>
198
+ CustomBarElement({
199
+ index: idx,
200
+ pointDiameter,
201
+ barX,
202
+ barWidth,
203
+ pointHeight,
204
+ label,
205
+ value,
206
+ classes,
207
+ scale,
208
+ dottedOverline: true,
209
+ }),
210
+ )}
211
+ {this.renderCorrectnessIcon(barX, barWidth, correctVal, correctness, classes, scale)}
212
+ </>
213
+ );
214
+ })()}
135
215
  <Component
136
216
  x={barX}
137
217
  y={v}
@@ -144,6 +224,7 @@ export class RawPlot extends React.Component {
144
224
  isHovered={isHovered}
145
225
  defineChart={defineChart}
146
226
  color={color.primaryDark()}
227
+ isPlot
147
228
  />
148
229
  </g>
149
230
  </React.Fragment>
@@ -151,17 +232,44 @@ export class RawPlot extends React.Component {
151
232
  }
152
233
  }
153
234
 
154
- const Bar = withStyles(() => ({
235
+ const Bar = withStyles((theme) => ({
155
236
  dot: {
156
237
  fill: color.visualElementsColors.PLOT_FILL_COLOR,
157
238
  '&.correct': correct('stroke'),
158
239
  '&.incorrect': incorrect('stroke'),
159
240
  },
241
+ dotColor: {
242
+ fill: color.visualElementsColors.PLOT_FILL_COLOR,
243
+ '&.correct': correct('fill'),
244
+ '&.incorrect': incorrect('fill'),
245
+ },
160
246
  line: {
161
247
  stroke: color.visualElementsColors.PLOT_FILL_COLOR,
162
248
  '&.correct': correct('stroke'),
163
249
  '&.incorrect': incorrect('stroke'),
164
250
  },
251
+ correctIcon: {
252
+ backgroundColor: color.correct(),
253
+ },
254
+ incorrectIcon: {
255
+ backgroundColor: color.incorrectWithIcon(),
256
+ },
257
+ correctnessIcon: {
258
+ borderRadius: theme.spacing.unit * 2,
259
+ color: color.defaults.WHITE,
260
+ fontSize: '16px',
261
+ width: '16px',
262
+ height: '16px',
263
+ padding: '2px',
264
+ border: `1px solid ${color.defaults.WHITE}`,
265
+ stroke: 'initial',
266
+ boxSizing: 'unset', // to override the default border-box in IBX
267
+ },
268
+ smallIcon: {
269
+ fontSize: '10px',
270
+ width: '10px',
271
+ height: '10px',
272
+ },
165
273
  }))(RawPlot);
166
274
 
167
275
  export class Plot extends React.Component {
@@ -175,7 +283,7 @@ export class Plot extends React.Component {
175
283
  };
176
284
 
177
285
  render() {
178
- const { data, graphProps, xBand, CustomBarElement, onChangeCategory, defineChart } = this.props;
286
+ const { data, graphProps, xBand, CustomBarElement, onChangeCategory, defineChart, correctData } = this.props;
179
287
 
180
288
  return (
181
289
  <Group>
@@ -192,6 +300,7 @@ export class Plot extends React.Component {
192
300
  graphProps={graphProps}
193
301
  CustomBarElement={CustomBarElement}
194
302
  correctness={d.correctness}
303
+ correctData={correctData}
195
304
  />
196
305
  ))}
197
306
  </Group>
package/src/plot/dot.js CHANGED
@@ -1,18 +1,33 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
+ import { Circle } from '@vx/shape';
4
+
3
5
  import { types } from '@pie-lib/plot';
4
6
  import { dataToXBand } from '../utils';
5
7
  import Plot from './common/plot';
6
- import { Circle } from '@vx/shape';
8
+ import { color } from '@pie-lib/render-ui';
7
9
 
8
10
  const CustomBarElement = (props) => {
9
- const { index, pointDiameter, barX, barWidth, pointHeight, label, value, classes, scale } = props;
11
+ const { index, pointDiameter, barX, barWidth, pointHeight, label, value, classes, scale, dottedOverline } = props;
10
12
 
11
13
  const r = pointDiameter / 2;
12
14
  const cx = barX + (barWidth - pointDiameter) / 2 + r;
13
15
  const cy = scale.y(index) - (pointHeight - pointDiameter) / 2 - r;
16
+ const EXTRA_PADDING = 2;
14
17
 
15
- return <Circle key={`point-${label}-${value}-${index}`} className={classes.dot} cx={cx} cy={cy} r={r} />;
18
+ return dottedOverline ? (
19
+ <Circle
20
+ key={`point-${label}-${value}-${index}`}
21
+ cx={cx}
22
+ cy={cy}
23
+ r={r + EXTRA_PADDING}
24
+ strokeDasharray="4,4"
25
+ stroke={color.defaults.BORDER_GRAY}
26
+ fill="none"
27
+ />
28
+ ) : (
29
+ <Circle key={`point-${label}-${value}-${index}`} className={classes.dot} cx={cx} cy={cy} r={r} />
30
+ );
16
31
  };
17
32
 
18
33
  CustomBarElement.propTypes = {
@@ -25,6 +40,7 @@ CustomBarElement.propTypes = {
25
40
  label: PropTypes.string,
26
41
  classes: PropTypes.object,
27
42
  scale: PropTypes.object,
43
+ dottedOverline: PropTypes.bool,
28
44
  };
29
45
 
30
46
  export class DotPlot extends React.Component {
package/src/plot/line.js CHANGED
@@ -1,18 +1,32 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
+ import { LinePath } from '@vx/shape';
4
+ import { Group } from '@vx/group';
5
+
3
6
  import { types } from '@pie-lib/plot';
4
7
  import { dataToXBand } from '../utils';
8
+ import { color } from '@pie-lib/render-ui';
5
9
  import Plot from './common/plot';
6
- import { LinePath } from '@vx/shape';
7
- import { Group } from '@vx/group';
8
10
 
9
11
  const CustomBarElement = (props) => {
10
- const { index, pointDiameter, barX, barWidth, pointHeight, label, value, classes, scale } = props;
12
+ const { index, pointDiameter, barX, barWidth, pointHeight, label, value, classes, scale, dottedOverline } = props;
11
13
 
12
14
  const x = barX + (barWidth - pointDiameter) / 2;
13
15
  const y = scale.y(index) - (pointHeight - pointDiameter) / 2;
16
+ const EXTRA_PADDING = 2;
14
17
 
15
- return (
18
+ return dottedOverline ? (
19
+ <rect
20
+ key={`point-${label}-${value}-${index}`}
21
+ x={x - EXTRA_PADDING}
22
+ y={y - pointDiameter - EXTRA_PADDING}
23
+ width={pointDiameter + EXTRA_PADDING * 2}
24
+ height={pointDiameter + EXTRA_PADDING * 2}
25
+ strokeDasharray="4,4"
26
+ stroke={color.defaults.BORDER_GRAY}
27
+ fill="none"
28
+ />
29
+ ) : (
16
30
  <Group>
17
31
  <LinePath
18
32
  data={[