@pie-element/hotspot 4.10.9 → 4.10.11-beta.1443

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 (91) hide show
  1. package/CHANGELOG.md +360 -0
  2. package/configure/CHANGELOG.md +319 -0
  3. package/configure/lib/button.js.map +1 -1
  4. package/configure/lib/defaults.js +23 -2
  5. package/configure/lib/defaults.js.map +1 -1
  6. package/configure/lib/hotspot-container.js +28 -19
  7. package/configure/lib/hotspot-container.js.map +1 -1
  8. package/configure/lib/hotspot-drawable.js +3 -3
  9. package/configure/lib/hotspot-drawable.js.map +1 -1
  10. package/configure/lib/hotspot-palette.js.map +1 -1
  11. package/configure/lib/hotspot-polygon.js.map +1 -1
  12. package/configure/lib/hotspot-rectangle.js.map +1 -1
  13. package/configure/lib/index.js +16 -2
  14. package/configure/lib/index.js.map +1 -1
  15. package/configure/lib/root.js +129 -38
  16. package/configure/lib/root.js.map +1 -1
  17. package/configure/lib/upload-control.js.map +1 -1
  18. package/configure/lib/utils.js +15 -3
  19. package/configure/lib/utils.js.map +1 -1
  20. package/configure/package.json +4 -4
  21. package/configure/src/__tests__/__snapshots__/hotspot-container.test.js.snap +1 -1
  22. package/configure/src/__tests__/__snapshots__/root.test.js.snap +56 -4
  23. package/configure/src/__tests__/hotspot-container.test.js +98 -71
  24. package/configure/src/__tests__/hotspot-drawable.test.js +48 -49
  25. package/configure/src/__tests__/index.test.js +28 -51
  26. package/configure/src/__tests__/root.test.js +162 -82
  27. package/configure/src/__tests__/utils.test.js +112 -98
  28. package/configure/src/button.jsx +4 -4
  29. package/configure/src/defaults.js +35 -18
  30. package/configure/src/hotspot-container.jsx +99 -98
  31. package/configure/src/hotspot-drawable.jsx +56 -56
  32. package/configure/src/hotspot-palette.jsx +12 -25
  33. package/configure/src/hotspot-polygon.jsx +20 -22
  34. package/configure/src/hotspot-rectangle.jsx +5 -15
  35. package/configure/src/index.js +32 -18
  36. package/configure/src/root.jsx +116 -60
  37. package/configure/src/upload-control.jsx +9 -20
  38. package/configure/src/utils.js +51 -22
  39. package/controller/CHANGELOG.md +23 -0
  40. package/controller/lib/defaults.js +2 -2
  41. package/controller/lib/defaults.js.map +1 -1
  42. package/controller/lib/index.js +39 -3
  43. package/controller/lib/index.js.map +1 -1
  44. package/controller/lib/utils.js.map +1 -1
  45. package/controller/package.json +2 -2
  46. package/controller/src/__tests__/index.test.js +29 -50
  47. package/controller/src/defaults.js +5 -9
  48. package/controller/src/index.js +47 -33
  49. package/controller/src/utils.js +9 -6
  50. package/docs/config-schema.json +217 -0
  51. package/docs/config-schema.json.md +169 -0
  52. package/docs/demo/config.js +2 -2
  53. package/docs/demo/generate.js +53 -35
  54. package/docs/demo/session.js +2 -2
  55. package/docs/pie-schema.json +49 -0
  56. package/docs/pie-schema.json.md +36 -0
  57. package/lib/hotspot/container.js +24 -6
  58. package/lib/hotspot/container.js.map +1 -1
  59. package/lib/hotspot/icons.js.map +1 -1
  60. package/lib/hotspot/image.js.map +1 -1
  61. package/lib/hotspot/index.js +65 -5
  62. package/lib/hotspot/index.js.map +1 -1
  63. package/lib/hotspot/polygon.js +45 -19
  64. package/lib/hotspot/polygon.js.map +1 -1
  65. package/lib/hotspot/rectangle.js +45 -19
  66. package/lib/hotspot/rectangle.js.map +1 -1
  67. package/lib/index.js +3 -0
  68. package/lib/index.js.map +1 -1
  69. package/lib/session-updater.js.map +1 -1
  70. package/package.json +6 -4
  71. package/src/__tests__/__snapshots__/{container-test.jsx.snap → container.test.jsx.snap} +8 -0
  72. package/src/__tests__/__snapshots__/image.test.jsx.snap +43 -0
  73. package/src/__tests__/__snapshots__/{polygon-test.jsx.snap → polygon.test.jsx.snap} +11 -0
  74. package/src/__tests__/__snapshots__/{rectangle-test.jsx.snap → rectangle.test.jsx.snap} +11 -0
  75. package/src/__tests__/container.test.jsx +214 -0
  76. package/src/__tests__/{image-test.jsx → image.test.jsx} +2 -2
  77. package/src/__tests__/polygon.test.jsx +235 -0
  78. package/src/__tests__/rectangle.test.jsx +237 -0
  79. package/src/__tests__/{session-updater-test.jsx → session-updater.test.jsx} +22 -31
  80. package/src/hotspot/container.jsx +40 -23
  81. package/src/hotspot/icons.js +4 -2
  82. package/src/hotspot/image.jsx +4 -8
  83. package/src/hotspot/index.jsx +73 -33
  84. package/src/hotspot/polygon.jsx +48 -30
  85. package/src/hotspot/rectangle.jsx +48 -30
  86. package/src/index.js +10 -18
  87. package/src/session-updater.js +1 -1
  88. package/src/__tests__/__snapshots__/image-test.jsx.snap +0 -21
  89. package/src/__tests__/container-test.jsx +0 -143
  90. package/src/__tests__/polygon-test.jsx +0 -77
  91. package/src/__tests__/rectangle-test.jsx +0 -78
@@ -0,0 +1,237 @@
1
+ import React from 'react';
2
+ import _ from 'lodash';
3
+ import { shallow } from 'enzyme';
4
+ import toJson from 'enzyme-to-json';
5
+ import Konva from 'konva';
6
+ import { Rect } from 'react-konva';
7
+
8
+ import { shallowChild } from '@pie-lib/test-utils';
9
+
10
+ import Rectangle from '../hotspot/rectangle';
11
+ import Image from '../hotspot/image';
12
+ import { faCorrect, faWrong } from '../hotspot/icons';
13
+
14
+ global.MutationObserver = class {
15
+ constructor(callback) {}
16
+ disconnect() {}
17
+ observe(element, initObject) {}
18
+ };
19
+
20
+ Konva.isBrowser = false;
21
+
22
+ describe('Rectangle', () => {
23
+ let onClick, wrapper;
24
+
25
+ const mkWrapper = (opts = {}) => {
26
+ opts = _.extend(
27
+ {
28
+ classes: {
29
+ base: 'base',
30
+ },
31
+ height: 200,
32
+ hotspotColor: 'rgba(137, 183, 244, 0.65)',
33
+ id: '1',
34
+ isCorrect: false,
35
+ isEvaluateMode: false,
36
+ evaluateText: null,
37
+ disabled: false,
38
+ outlineColor: 'blue',
39
+ selected: false,
40
+ width: 300,
41
+ x: 5,
42
+ y: 5,
43
+ },
44
+ opts,
45
+ );
46
+
47
+ return shallow(<Rectangle {...opts} onClick={onClick} />);
48
+ };
49
+
50
+ beforeEach(() => {
51
+ onClick = jest.fn();
52
+ wrapper = mkWrapper();
53
+ });
54
+
55
+ describe('snapshots', () => {
56
+ describe('outline color', () => {
57
+ it('renders', () => {
58
+ const wrapper = mkWrapper({ outlineColor: 'red' });
59
+ expect(toJson(wrapper)).toMatchSnapshot();
60
+ });
61
+ });
62
+
63
+ describe('outline width', () => {
64
+ it('renders with default border width', () => {
65
+ const wrapper = mkWrapper();
66
+ expect(toJson(wrapper)).toMatchSnapshot();
67
+ });
68
+
69
+ it('renders with given border width', () => {
70
+ const wrapper = mkWrapper({ strokeWidth: 10 });
71
+ expect(toJson(wrapper)).toMatchSnapshot();
72
+ });
73
+ });
74
+
75
+ describe('hotspot color', () => {
76
+ it('renders', () => {
77
+ const wrapper = mkWrapper({ hotspotColor: 'rgba(217, 30, 24, 0.65)' });
78
+ expect(toJson(wrapper)).toMatchSnapshot();
79
+ });
80
+ });
81
+
82
+ describe('evaluate with correct answer', () => {
83
+ it('renders', () => {
84
+ const wrapper = mkWrapper({ isEvaluateMode: true, isCorrect: true, evaluateText: 'Correctly\nselected' });
85
+ expect(toJson(wrapper)).toMatchSnapshot();
86
+ });
87
+ });
88
+ });
89
+
90
+ describe('in evaluate mode', () => {
91
+ const defaultModel = {
92
+ onClick,
93
+ id: '1',
94
+ height: 200,
95
+ hotspotColor: 'rgba(137, 183, 244, 0.65)',
96
+ disabled: true,
97
+ width: 300,
98
+ x: 5,
99
+ y: 5,
100
+ selected: false,
101
+ isCorrect: false,
102
+ isEvaluateMode: true,
103
+ evaluateText: null,
104
+ strokeWidth: 5,
105
+ outlineColor: 'blue',
106
+ markAsCorrect: false,
107
+ showCorrectEnabled: false,
108
+ };
109
+
110
+ const getComponents = (model) => {
111
+ const testWrapper = shallowChild(
112
+ Rectangle,
113
+ {
114
+ ...defaultModel,
115
+ ...model,
116
+ },
117
+ 1,
118
+ );
119
+ const rectangleComponent = testWrapper();
120
+ const rectComponent = rectangleComponent.find(Rect);
121
+ return {
122
+ rectangleComponent,
123
+ rectComponent,
124
+ };
125
+ };
126
+
127
+ describe('when correctly selected', () => {
128
+ const { rectangleComponent, rectComponent } = getComponents({
129
+ selected: true,
130
+ isCorrect: true,
131
+ });
132
+
133
+ it('should have a blue outline', () => {
134
+ expect(rectComponent.prop('stroke')).toEqual('blue');
135
+ expect(rectComponent.prop('strokeWidth')).toEqual(defaultModel.strokeWidth);
136
+ });
137
+
138
+ it('should have a green checkmark icon', () => {
139
+ const imgComponent = rectangleComponent.find(Image);
140
+ expect(imgComponent.length).toEqual(1);
141
+ expect(imgComponent.prop('src')).toEqual(faCorrect);
142
+ });
143
+ });
144
+
145
+ describe('when correctly not selected', () => {
146
+ const { rectangleComponent, rectComponent } = getComponents({
147
+ selected: false,
148
+ isCorrect: true,
149
+ });
150
+
151
+ it('should have no outline', () => {
152
+ expect(rectComponent.prop('strokeWidth')).toEqual(0);
153
+ });
154
+
155
+ it('should have no icon', () => {
156
+ const imgComponent = rectangleComponent.find(Image);
157
+ expect(imgComponent.length).toEqual(0);
158
+ });
159
+ });
160
+
161
+ describe('when incorrectly selected', () => {
162
+ const { rectangleComponent, rectComponent } = getComponents({
163
+ selected: true,
164
+ isCorrect: false,
165
+ });
166
+
167
+ it('should have a red outline', () => {
168
+ expect(rectComponent.prop('stroke')).toEqual('red');
169
+ expect(rectComponent.prop('strokeWidth')).toEqual(defaultModel.strokeWidth);
170
+ });
171
+
172
+ it('should have a red x icon', () => {
173
+ const imgComponent = rectangleComponent.find(Image);
174
+ expect(imgComponent.length).toEqual(1);
175
+ expect(imgComponent.prop('src')).toEqual(faWrong);
176
+ });
177
+ });
178
+
179
+ describe('when incorrectly not selected', () => {
180
+ const { rectangleComponent, rectComponent } = getComponents({
181
+ selected: false,
182
+ isCorrect: false,
183
+ });
184
+
185
+ it('should have no outline', () => {
186
+ expect(rectComponent.prop('strokeWidth')).toEqual(0);
187
+ });
188
+
189
+ it('should have a red x icon', () => {
190
+ const imgComponent = rectangleComponent.find(Image);
191
+ expect(imgComponent.length).toEqual(1);
192
+ expect(imgComponent.prop('src')).toEqual(faWrong);
193
+ });
194
+ });
195
+
196
+ describe('when showing correct answer (showCorrectEnabled = true)', () => {
197
+ it('should have a green outline', () => {
198
+ const { rectComponent } = getComponents({
199
+ showCorrectEnabled: true,
200
+ markAsCorrect: true,
201
+ });
202
+ expect(rectComponent.prop('stroke')).toEqual('green');
203
+ expect(rectComponent.prop('strokeWidth')).toEqual(defaultModel.strokeWidth);
204
+ });
205
+
206
+ it('should have a green checkmark icon', () => {
207
+ const { rectangleComponent } = getComponents({
208
+ showCorrectEnabled: true,
209
+ markAsCorrect: true,
210
+ });
211
+ const imgComponent = rectangleComponent.find(Image);
212
+ expect(imgComponent.prop('src')).toEqual(faCorrect);
213
+ });
214
+
215
+ it('should not be selected if the answer is incorrect', () => {
216
+ const { rectComponent } = getComponents({
217
+ showCorrectEnabled: true,
218
+ markAsCorrect: false,
219
+ selected: true,
220
+ isCorrect: false,
221
+ });
222
+ expect(rectComponent.prop('strokeWidth')).toEqual(0);
223
+ });
224
+
225
+ it('should not render an icon if the answer is incorrect', () => {
226
+ const { rectangleComponent } = getComponents({
227
+ showCorrectEnabled: true,
228
+ markAsCorrect: false,
229
+ selected: true,
230
+ isCorrect: false,
231
+ });
232
+ const imgComponent = rectangleComponent.find(Image);
233
+ expect(imgComponent.length).toEqual(0);
234
+ });
235
+ });
236
+ });
237
+ });
@@ -5,38 +5,38 @@ const modelCopy = {
5
5
  rectangles: [
6
6
  {
7
7
  id: '1',
8
- correct: true
8
+ correct: true,
9
9
  },
10
10
  {
11
- id: '2'
11
+ id: '2',
12
12
  },
13
13
  {
14
- id: '3'
15
- }
14
+ id: '3',
15
+ },
16
16
  ],
17
17
  polygons: [
18
18
  {
19
19
  id: '4',
20
- correct: true
20
+ correct: true,
21
21
  },
22
22
  {
23
- id: '5'
24
- }
25
- ]
23
+ id: '5',
24
+ },
25
+ ],
26
26
  },
27
27
  configure: {},
28
- multipleCorrect: true
28
+ multipleCorrect: true,
29
29
  };
30
30
 
31
31
  const sessionCopy = {
32
32
  answers: [
33
33
  {
34
- id: '1'
34
+ id: '1',
35
35
  },
36
36
  {
37
- id: '2'
38
- }
39
- ]
37
+ id: '2',
38
+ },
39
+ ],
40
40
  };
41
41
 
42
42
  describe('session-updater', () => {
@@ -51,28 +51,19 @@ describe('session-updater', () => {
51
51
  };
52
52
 
53
53
  describe('deselect', () => {
54
- it('deselects shape', assert(
55
- sessionCopy,
56
- modelCopy,
57
- { id: '1', selected: false },
58
- [{ id: '2' }]
59
- ));
54
+ it('deselects shape', assert(sessionCopy, modelCopy, { id: '1', selected: false }, [{ id: '2' }]));
60
55
  });
61
56
 
62
57
  describe('select hotspot', () => {
63
- it('selects shape in multiple choice mode', assert(
64
- sessionCopy,
65
- modelCopy,
66
- { id: '1', selected: true },
67
- [{ id: '2' }, { id: '1' }]
68
- ));
58
+ it(
59
+ 'selects shape in multiple choice mode',
60
+ assert(sessionCopy, modelCopy, { id: '1', selected: true }, [{ id: '2' }, { id: '1' }]),
61
+ );
69
62
 
70
- it('selects shape in single choice mode', assert(
71
- sessionCopy,
72
- { ...modelCopy, multipleCorrect: false },
73
- { id: '1', selected: true },
74
- [{ id: '1' }]
75
- ));
63
+ it(
64
+ 'selects shape in single choice mode',
65
+ assert(sessionCopy, { ...modelCopy, multipleCorrect: false }, { id: '1', selected: true }, [{ id: '1' }]),
66
+ );
76
67
  });
77
68
  });
78
69
  });
@@ -9,11 +9,11 @@ import Polygon from './polygon';
9
9
 
10
10
  export class Container extends React.Component {
11
11
  isSelected(shape) {
12
- const selectedShape = this.props.session.answers.filter(answer => answer.id === shape.id)[0];
12
+ const selectedShape = this.props.session.answers.filter((answer) => answer.id === shape.id)[0];
13
13
  return !!selectedShape;
14
14
  }
15
15
 
16
- correctness = (isCorrect, isChecked) => isCorrect ? isChecked : !isChecked;
16
+ correctness = (isCorrect, isChecked) => (isCorrect ? isChecked : !isChecked);
17
17
 
18
18
  getEvaluateText = (isCorrect, selected) => {
19
19
  if (selected && isCorrect) {
@@ -34,7 +34,7 @@ export class Container extends React.Component {
34
34
  render() {
35
35
  const {
36
36
  classes,
37
- dimensions: { width, height },
37
+ dimensions: { width: withProp, height: heightProp },
38
38
  disabled,
39
39
  hotspotColor,
40
40
  imageUrl,
@@ -42,17 +42,20 @@ export class Container extends React.Component {
42
42
  outlineColor,
43
43
  onSelectChoice,
44
44
  shapes: { rectangles, polygons },
45
- strokeWidth
45
+ scale: SCALE,
46
+ strokeWidth,
47
+ showCorrect,
46
48
  } = this.props;
47
49
 
50
+ const width = withProp * SCALE;
51
+ const height = heightProp * SCALE;
52
+
48
53
  return (
49
- <div
50
- className={classes.base}
51
- style={{ padding: strokeWidth / 2 }}
52
- >
54
+ <div className={classes.base} style={{ padding: strokeWidth / 2 }}>
53
55
  {imageUrl ? (
54
56
  <div className={classes.imageContainer}>
55
57
  <img
58
+ alt="hotspot-image"
56
59
  className={classes.image}
57
60
  height="auto"
58
61
  src={imageUrl}
@@ -73,9 +76,11 @@ export class Container extends React.Component {
73
76
  const selected = this.isSelected(shape);
74
77
  const isCorrect = isEvaluateMode ? this.correctness(shape.correct, selected) : undefined;
75
78
  const evaluateText = isEvaluateMode ? this.getEvaluateText(shape.correct, selected) : null;
79
+ const markAsCorrect = !!(isEvaluateMode && showCorrect && shape.correct);
76
80
 
77
81
  return (
78
82
  <Rectangle
83
+ scale={SCALE}
79
84
  isEvaluateMode={isEvaluateMode}
80
85
  isCorrect={isCorrect}
81
86
  evaluateText={evaluateText}
@@ -91,16 +96,20 @@ export class Container extends React.Component {
91
96
  x={shape.x}
92
97
  y={shape.y}
93
98
  strokeWidth={strokeWidth}
99
+ markAsCorrect={markAsCorrect}
100
+ showCorrectEnabled={showCorrect}
94
101
  />
95
- )
102
+ );
96
103
  })}
97
104
  {polygons.map((polygon) => {
98
105
  const selected = this.isSelected(polygon);
99
106
  const isCorrect = isEvaluateMode ? this.correctness(polygon.correct, selected) : undefined;
100
107
  const evaluateText = isEvaluateMode ? this.getEvaluateText(polygon.correct, selected) : null;
108
+ const markAsCorrect = !!(isEvaluateMode && showCorrect && polygon.correct);
101
109
 
102
110
  return (
103
111
  <Polygon
112
+ scale={SCALE}
104
113
  isEvaluateMode={isEvaluateMode}
105
114
  isCorrect={!!isCorrect}
106
115
  evaluateText={evaluateText}
@@ -113,8 +122,10 @@ export class Container extends React.Component {
113
122
  outlineColor={outlineColor}
114
123
  points={polygon.points}
115
124
  strokeWidth={strokeWidth}
125
+ markAsCorrect={markAsCorrect}
126
+ showCorrectEnabled={showCorrect}
116
127
  />
117
- )
128
+ );
118
129
  })}
119
130
  </Layer>
120
131
  </Stage>
@@ -123,10 +134,10 @@ export class Container extends React.Component {
123
134
  }
124
135
  }
125
136
 
126
- const styles = theme => ({
137
+ const styles = (theme) => ({
127
138
  base: {
128
139
  marginTop: theme.spacing.unit * 3,
129
- position: 'relative'
140
+ position: 'relative',
130
141
  },
131
142
  image: {
132
143
  alignItems: 'center',
@@ -135,12 +146,12 @@ const styles = theme => ({
135
146
  },
136
147
  imageContainer: {
137
148
  position: 'relative',
138
- width: 'fit-content'
149
+ width: 'fit-content',
139
150
  },
140
151
  stage: {
141
152
  left: 0,
142
153
  top: 0,
143
- position: 'absolute'
154
+ position: 'absolute',
144
155
  },
145
156
  resize: {
146
157
  borderBottom: `1px solid ${color.disabled()}`,
@@ -151,21 +162,27 @@ const styles = theme => ({
151
162
  position: 'absolute',
152
163
  right: '-10px',
153
164
  width: '10px',
154
- }
165
+ },
155
166
  });
156
167
 
157
168
  Container.propTypes = {
158
169
  classes: PropTypes.object.isRequired,
159
170
  dimensions: PropTypes.object.isRequired,
160
171
  disabled: PropTypes.bool.isRequired,
161
- hotspotColor:PropTypes.string.isRequired,
162
- imageUrl:PropTypes.string.isRequired,
163
- isEvaluateMode:PropTypes.bool.isRequired,
164
- onSelectChoice:PropTypes.func.isRequired,
165
- outlineColor:PropTypes.string.isRequired,
166
- session:PropTypes.object.isRequired,
167
- shapes:PropTypes.object.isRequired,
168
- strokeWidth: PropTypes.number
172
+ hotspotColor: PropTypes.string.isRequired,
173
+ imageUrl: PropTypes.string.isRequired,
174
+ isEvaluateMode: PropTypes.bool.isRequired,
175
+ onSelectChoice: PropTypes.func.isRequired,
176
+ outlineColor: PropTypes.string.isRequired,
177
+ session: PropTypes.object.isRequired,
178
+ shapes: PropTypes.object.isRequired,
179
+ strokeWidth: PropTypes.number,
180
+ scale: PropTypes.number,
181
+ showCorrect: PropTypes.bool,
182
+ };
183
+
184
+ Container.defaultProps = {
185
+ scale: 1,
169
186
  };
170
187
 
171
188
  export default withStyles(styles)(Container);