@pie-element/hotspot 4.10.10 → 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 +349 -0
  2. package/configure/CHANGELOG.md +311 -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
@@ -6,14 +6,14 @@ import MenuItem from '@material-ui/core/MenuItem';
6
6
  import { withStyles } from '@material-ui/core/styles';
7
7
 
8
8
  class Palette extends React.Component {
9
- onChange = (name) => event => {
9
+ onChange = (name) => (event) => {
10
10
  const { value } = event.target;
11
11
  const { onHotspotColorChange, onOutlineColorChange } = this.props;
12
12
 
13
13
  if (name === 'hotspot') {
14
14
  onHotspotColorChange(value);
15
15
  } else {
16
- onOutlineColorChange(value)
16
+ onOutlineColorChange(value);
17
17
  }
18
18
  };
19
19
 
@@ -23,18 +23,9 @@ class Palette extends React.Component {
23
23
  return (
24
24
  <div className={classes.base}>
25
25
  <InputContainer label="Hot Spot" className={classes.input}>
26
- <Select
27
- className={classes.select}
28
- onChange={this.onChange('hotspot')}
29
- value={hotspotColor}
30
- >
31
- {hotspotList.map(hotspot => (
32
- <MenuItem
33
- key={hotspot}
34
- value={hotspot}
35
- className={classes.item}
36
- style={{ backgroundColor: hotspot }}
37
- >
26
+ <Select className={classes.select} onChange={this.onChange('hotspot')} value={hotspotColor}>
27
+ {hotspotList.map((hotspot) => (
28
+ <MenuItem key={hotspot} value={hotspot} className={classes.item} style={{ backgroundColor: hotspot }}>
38
29
  {hotspot}
39
30
  </MenuItem>
40
31
  ))}
@@ -42,12 +33,8 @@ class Palette extends React.Component {
42
33
  </InputContainer>
43
34
 
44
35
  <InputContainer label="Response Outline" className={classes.input}>
45
- <Select
46
- className={classes.select}
47
- onChange={this.onChange('outline')}
48
- value={outlineColor}
49
- >
50
- {outlineList.map(outline => (
36
+ <Select className={classes.select} onChange={this.onChange('outline')} value={outlineColor}>
37
+ {outlineList.map((outline) => (
51
38
  <MenuItem
52
39
  key={outline}
53
40
  value={outline}
@@ -64,21 +51,21 @@ class Palette extends React.Component {
64
51
  }
65
52
  }
66
53
 
67
- const styles = theme => ({
54
+ const styles = (theme) => ({
68
55
  base: {
69
56
  marginTop: theme.spacing.unit * 2,
70
- display: 'flex'
57
+ display: 'flex',
71
58
  },
72
59
  input: {
73
60
  flex: 1,
74
- width: '90%'
61
+ width: '90%',
75
62
  },
76
63
  item: {
77
64
  borderRadius: '2px',
78
65
  height: '22px',
79
66
  marginLeft: theme.spacing.unit * 2,
80
67
  marginRight: theme.spacing.unit * 2,
81
- marginTop: theme.spacing.unit * 2
68
+ marginTop: theme.spacing.unit * 2,
82
69
  },
83
70
  });
84
71
 
@@ -89,7 +76,7 @@ Palette.propTypes = {
89
76
  onHotspotColorChange: PropTypes.func.isRequired,
90
77
  onOutlineColorChange: PropTypes.func.isRequired,
91
78
  outlineColor: PropTypes.string.isRequired,
92
- outlineList: PropTypes.array.isRequired
79
+ outlineList: PropTypes.array.isRequired,
93
80
  };
94
81
 
95
82
  export default withStyles(styles)(Palette);
@@ -6,8 +6,8 @@ import { withStyles } from '@material-ui/core/styles/index';
6
6
  class PolComponent extends React.Component {
7
7
  static getDerivedStateFromProps(nextProps) {
8
8
  const { points } = nextProps;
9
- const xList = points.map(p => (p.x));
10
- const yList = points.map(p => (p.y));
9
+ const xList = points.map((p) => p.x);
10
+ const yList = points.map((p) => p.y);
11
11
 
12
12
  const xOffset = Math.min(...xList);
13
13
  const yOffset = Math.max(...yList);
@@ -16,14 +16,16 @@ class PolComponent extends React.Component {
16
16
  return {
17
17
  x: xOffset,
18
18
  y: yOffset,
19
- points: points.reduce((acc, point) => { return acc.concat([point.x - xOffset, point.y - yOffset]) }, [])
19
+ points: points.reduce((acc, point) => {
20
+ return acc.concat([point.x - xOffset, point.y - yOffset]);
21
+ }, []),
20
22
  };
21
23
  }
22
24
 
23
25
  return {
24
26
  x: 0,
25
27
  y: 0,
26
- points: []
28
+ points: [],
27
29
  };
28
30
  }
29
31
 
@@ -31,8 +33,8 @@ class PolComponent extends React.Component {
31
33
 
32
34
  handleClick = (e) => {
33
35
  const { points } = this.props;
34
- const xList = points.map(p => (p.x));
35
- const yList = points.map(p => (p.y));
36
+ const xList = points.map((p) => p.x);
37
+ const yList = points.map((p) => p.y);
36
38
 
37
39
  const width = Math.max(...xList) - Math.min(...xList);
38
40
  const height = Math.max(...yList) - Math.min(...yList);
@@ -63,7 +65,7 @@ class PolComponent extends React.Component {
63
65
  if (index % 2 === 0 && index + 1 < points.length) {
64
66
  acc.push({
65
67
  x: currentPointCoordinate + e.target.x(),
66
- y: points[index + 1] + e.target.y()
68
+ y: points[index + 1] + e.target.y(),
67
69
  });
68
70
 
69
71
  return acc;
@@ -73,18 +75,12 @@ class PolComponent extends React.Component {
73
75
  }, []);
74
76
 
75
77
  onDragEnd(id, {
76
- points: newPoints
77
- })
78
+ points: newPoints,
79
+ });
78
80
  };
79
81
 
80
82
  render() {
81
- const {
82
- classes,
83
- correct,
84
- hotspotColor,
85
- outlineColor,
86
- strokeWidth = 5
87
- } = this.props;
83
+ const { classes, correct, hotspotColor, outlineColor, strokeWidth = 5 } = this.props;
88
84
  const { points, x, y } = this.state;
89
85
 
90
86
  return (
@@ -113,7 +109,7 @@ class PolComponent extends React.Component {
113
109
  const styles = () => ({
114
110
  base: {
115
111
  cursor: 'pointer',
116
- opacity: 0.5
112
+ opacity: 0.5,
117
113
  },
118
114
  });
119
115
 
@@ -126,11 +122,13 @@ PolComponent.propTypes = {
126
122
  onClick: PropTypes.func.isRequired,
127
123
  onDragEnd: PropTypes.func.isRequired,
128
124
  outlineColor: PropTypes.string.isRequired,
129
- points: PropTypes.arrayOf(PropTypes.shape({
130
- x: PropTypes.number,
131
- y: PropTypes.number
132
- })).isRequired,
133
- strokeWidth: PropTypes.number
125
+ points: PropTypes.arrayOf(
126
+ PropTypes.shape({
127
+ x: PropTypes.number,
128
+ y: PropTypes.number,
129
+ }),
130
+ ).isRequired,
131
+ strokeWidth: PropTypes.number,
134
132
  };
135
133
 
136
134
  PolComponent.defaultProps = {
@@ -26,22 +26,12 @@ class RectComponent extends React.Component {
26
26
 
27
27
  onDragEnd(id, {
28
28
  x: e.target.x(),
29
- y: e.target.y()
30
- })
29
+ y: e.target.y(),
30
+ });
31
31
  };
32
32
 
33
33
  render() {
34
- const {
35
- classes,
36
- correct,
37
- height,
38
- hotspotColor,
39
- outlineColor,
40
- width,
41
- x,
42
- y,
43
- strokeWidth = 5
44
- } = this.props;
34
+ const { classes, correct, height, hotspotColor, outlineColor, width, x, y, strokeWidth = 5 } = this.props;
45
35
 
46
36
  return (
47
37
  <Group>
@@ -69,7 +59,7 @@ class RectComponent extends React.Component {
69
59
  const styles = () => ({
70
60
  base: {
71
61
  cursor: 'pointer',
72
- opacity: 0.5
62
+ opacity: 0.5,
73
63
  },
74
64
  });
75
65
 
@@ -86,7 +76,7 @@ RectComponent.propTypes = {
86
76
  width: PropTypes.number.isRequired,
87
77
  x: PropTypes.number.isRequired,
88
78
  y: PropTypes.number.isRequired,
89
- strokeWidth: PropTypes.number
79
+ strokeWidth: PropTypes.number,
90
80
  };
91
81
 
92
82
  RectComponent.defaultProps = {
@@ -5,6 +5,8 @@ import {
5
5
  ModelUpdatedEvent,
6
6
  DeleteImageEvent,
7
7
  InsertImageEvent,
8
+ InsertSoundEvent,
9
+ DeleteSoundEvent,
8
10
  } from '@pie-framework/pie-configure-events';
9
11
 
10
12
  import Root from './root';
@@ -18,7 +20,7 @@ export default class HotspotConfigure extends HTMLElement {
18
20
  ...model,
19
21
  hotspotList: model.hotspotList || [model.hotspotColor] || sensibleDefaults.model.hotspotList,
20
22
  outlineList: model.outlineList || [model.outlineColor] || sensibleDefaults.model.outlineList,
21
- shapes: model.shapes || sensibleDefaults.model.shapes || {}
23
+ shapes: model.shapes || sensibleDefaults.model.shapes || {},
22
24
  });
23
25
 
24
26
  constructor() {
@@ -61,8 +63,8 @@ export default class HotspotConfigure extends HTMLElement {
61
63
  if (propertyType === 'multipleCorrect') {
62
64
  const { rectangles = [], polygons = [] } = _model.shapes || {};
63
65
 
64
- _model.shapes.rectangles = rectangles.map(shape => ({ ...shape, correct: false }));
65
- _model.shapes.polygons = polygons.map(shape => ({ ...shape, correct: false }));
66
+ _model.shapes.rectangles = rectangles.map((shape) => ({ ...shape, correct: false }));
67
+ _model.shapes.polygons = polygons.map((shape) => ({ ...shape, correct: false }));
66
68
  }
67
69
 
68
70
  this.onModelChanged(_model);
@@ -71,53 +73,53 @@ export default class HotspotConfigure extends HTMLElement {
71
73
  onColorChanged = (colorType, color) => {
72
74
  this.onModelChanged({
73
75
  ...this._model,
74
- [colorType]: color
76
+ [colorType]: color,
75
77
  });
76
78
  };
77
79
 
78
- onPromptChanged = prompt => {
80
+ onPromptChanged = (prompt) => {
79
81
  this.onModelChanged({
80
82
  ...this._model,
81
- prompt
83
+ prompt,
82
84
  });
83
85
  };
84
86
 
85
- onRationaleChanged = rationale => {
87
+ onRationaleChanged = (rationale) => {
86
88
  this.onModelChanged({
87
89
  ...this._model,
88
- rationale
90
+ rationale,
89
91
  });
90
92
  };
91
93
 
92
- onTeacherInstructionsChanged = teacherInstructions => {
94
+ onTeacherInstructionsChanged = (teacherInstructions) => {
93
95
  this.onModelChanged({
94
96
  ...this._model,
95
- teacherInstructions
97
+ teacherInstructions,
96
98
  });
97
99
  };
98
100
 
99
- onUpdateImageDimension = dimensions => {
101
+ onUpdateImageDimension = (dimensions) => {
100
102
  this.onModelChanged({
101
103
  ...this._model,
102
- dimensions
104
+ dimensions,
103
105
  });
104
106
  };
105
107
 
106
- onUpdateShapes = shapes => {
108
+ onUpdateShapes = (shapes) => {
107
109
  this.onModelChanged({
108
110
  ...this._model,
109
- shapes
111
+ shapes,
110
112
  });
111
113
  };
112
114
 
113
- onImageUpload = imageUrl => {
115
+ onImageUpload = (imageUrl) => {
114
116
  this.onModelChanged({
115
117
  ...this._model,
116
- imageUrl
118
+ imageUrl,
117
119
  });
118
120
  };
119
121
 
120
- insertImage = handler => {
122
+ insertImage = (handler) => {
121
123
  this.dispatchEvent(new InsertImageEvent(handler));
122
124
  };
123
125
 
@@ -125,6 +127,14 @@ export default class HotspotConfigure extends HTMLElement {
125
127
  this.dispatchEvent(new DeleteImageEvent(src, done));
126
128
  };
127
129
 
130
+ insertSound(handler) {
131
+ this.dispatchEvent(new InsertSoundEvent(handler));
132
+ }
133
+
134
+ onDeleteSound(src, done) {
135
+ this.dispatchEvent(new DeleteSoundEvent(src, done));
136
+ }
137
+
128
138
  _render() {
129
139
  log('_render');
130
140
  let element = React.createElement(Root, {
@@ -138,7 +148,11 @@ export default class HotspotConfigure extends HTMLElement {
138
148
  onUpdateImageDimension: this.onUpdateImageDimension,
139
149
  imageSupport: {
140
150
  add: this.insertImage,
141
- delete: this.onDeleteImage
151
+ delete: this.onDeleteImage,
152
+ },
153
+ uploadSoundSupport: {
154
+ add: this.insertSound.bind(this),
155
+ delete: this.onDeleteSound.bind(this),
142
156
  },
143
157
  onUpdateShapes: this.onUpdateShapes,
144
158
  onModelChangedByConfig: this.onModelChangedByConfig,
@@ -1,18 +1,15 @@
1
1
  import React from 'react';
2
- import {
3
- settings,
4
- layout,
5
- InputContainer,
6
- NumberTextField
7
- } from '@pie-lib/config-ui';
2
+ import { settings, layout, InputContainer, NumberTextField } from '@pie-lib/config-ui';
8
3
  import PropTypes from 'prop-types';
9
4
  import EditableHtml from '@pie-lib/editable-html';
10
- import Typography from '@material-ui/core/Typography';
11
5
  import { withStyles } from '@material-ui/core/styles';
12
-
6
+ import Typography from '@material-ui/core/Typography';
7
+ import Info from '@material-ui/icons/Info';
8
+ import Tooltip from '@material-ui/core/Tooltip';
13
9
  import HotspotPalette from './hotspot-palette';
14
10
  import HotspotContainer from './hotspot-container';
15
- import { updateImageDimensions, getUpdatedShapes, getAllShapes, groupShapes } from './utils';
11
+ import classNames from 'classnames';
12
+ import { updateImageDimensions, generateValidationMessage, getUpdatedShapes, getAllShapes, groupShapes } from './utils';
16
13
 
17
14
  const { Panel, toggle } = settings;
18
15
 
@@ -39,7 +36,7 @@ export class Root extends React.Component {
39
36
  dimensions,
40
37
  nextImageDimensions,
41
38
  preserveAspectRatio.enabled,
42
- resizeType
39
+ resizeType,
43
40
  );
44
41
  // transform shapes map into shapes array
45
42
  const shapesArray = getAllShapes(shapes);
@@ -57,6 +54,7 @@ export class Root extends React.Component {
57
54
  configuration,
58
55
  model,
59
56
  imageSupport,
57
+ uploadSoundSupport,
60
58
  onConfigurationChanged,
61
59
  onImageUpload,
62
60
  onModelChangedByConfig,
@@ -64,31 +62,55 @@ export class Root extends React.Component {
64
62
  onRationaleChanged,
65
63
  onUpdateImageDimension,
66
64
  onTeacherInstructionsChanged,
67
- onUpdateShapes
65
+ onUpdateShapes,
68
66
  } = this.props;
69
67
  const {
68
+ maxImageWidth = {},
69
+ maxImageHeight = {},
70
70
  multipleCorrect = {},
71
71
  partialScoring = {},
72
+ preserveAspectRatio = {},
72
73
  prompt = {},
73
- teacherInstructions = {},
74
74
  rationale = {},
75
- preserveAspectRatio = {}
75
+ settingsPanelDisabled,
76
+ spellCheck = {},
77
+ teacherInstructions = {},
78
+ withRubric = {},
76
79
  } = configuration || {};
77
- const { teacherInstructionsEnabled, promptEnabled, rationaleEnabled } = model || {};
78
- const toolbarOpts = {};
80
+ const {
81
+ errors,
82
+ promptEnabled,
83
+ rationaleEnabled,
84
+ spellCheckEnabled,
85
+ teacherInstructionsEnabled,
86
+ toolbarEditorPosition,
87
+ } = model || {};
88
+ const { shapesError, selectionsError } = errors || {};
89
+ const validationMessage = generateValidationMessage(configuration);
90
+
91
+ const defaultImageMaxWidth = maxImageWidth && maxImageWidth.prompt;
92
+ const defaultImageMaxHeight = maxImageHeight && maxImageHeight.prompt;
93
+
94
+ const toolbarOpts = {
95
+ position: toolbarEditorPosition === 'top' ? 'top' : 'bottom',
96
+ };
79
97
 
80
- switch (model.toolbarEditorPosition) {
81
- case 'top':
82
- toolbarOpts.position = 'top';
83
- break;
84
- default:
85
- toolbarOpts.position = 'bottom';
86
- break;
87
- }
98
+ const panelSettings = {
99
+ multipleCorrect: multipleCorrect.settings && toggle(multipleCorrect.label),
100
+ partialScoring: partialScoring.settings && toggle(partialScoring.label),
101
+ promptEnabled: prompt.settings && toggle(prompt.label),
102
+ };
103
+ const panelProperties = {
104
+ teacherInstructionsEnabled: teacherInstructions.settings && toggle(teacherInstructions.label),
105
+ rationaleEnabled: rationale.settings && toggle(rationale.label),
106
+ spellCheckEnabled: spellCheck.settings && toggle(spellCheck.label),
107
+ rubricEnabled: withRubric?.settings && toggle(withRubric?.label),
108
+ };
88
109
 
89
110
  return (
90
111
  <div className={classes.base}>
91
112
  <layout.ConfigLayout
113
+ hideSettings={settingsPanelDisabled}
92
114
  settings={
93
115
  <Panel
94
116
  model={model}
@@ -96,19 +118,8 @@ export class Root extends React.Component {
96
118
  configuration={configuration}
97
119
  onChangeConfiguration={onConfigurationChanged}
98
120
  groups={{
99
- 'Settings': {
100
- multipleCorrect:
101
- multipleCorrect.settings && toggle(multipleCorrect.label),
102
- partialScoring:
103
- partialScoring.settings && toggle(partialScoring.label),
104
- promptEnabled:
105
- prompt.settings && toggle(prompt.label),
106
- },
107
- Properties: {
108
- teacherInstructionsEnabled:
109
- teacherInstructions.settings && toggle(teacherInstructions.label),
110
- rationaleEnabled: rationale.settings && toggle(rationale.label)
111
- }
121
+ Settings: panelSettings,
122
+ Properties: panelProperties,
112
123
  }}
113
124
  />
114
125
  }
@@ -122,9 +133,15 @@ export class Root extends React.Component {
122
133
  imageSupport={imageSupport}
123
134
  nonEmpty={false}
124
135
  toolbarOpts={toolbarOpts}
136
+ spellCheck={spellCheckEnabled}
137
+ maxImageWidth={(maxImageWidth && maxImageWidth.teacherInstructions) || defaultImageMaxWidth}
138
+ maxImageHeight={(maxImageHeight && maxImageHeight.teacherInstructions) || defaultImageMaxHeight}
139
+ uploadSoundSupport={uploadSoundSupport}
140
+ languageCharactersProps={[{ language: 'spanish' }, { language: 'special' }]}
125
141
  />
126
142
  </InputContainer>
127
143
  )}
144
+
128
145
  {promptEnabled && (
129
146
  <InputContainer label={prompt.label} className={classes.prompt}>
130
147
  <EditableHtml
@@ -133,45 +150,60 @@ export class Root extends React.Component {
133
150
  imageSupport={imageSupport}
134
151
  nonEmpty={false}
135
152
  toolbarOpts={toolbarOpts}
153
+ spellCheck={spellCheckEnabled}
154
+ maxImageWidth={defaultImageMaxWidth}
155
+ maxImageHeight={defaultImageMaxHeight}
156
+ uploadSoundSupport={uploadSoundSupport}
157
+ languageCharactersProps={[{ language: 'spanish' }, { language: 'special' }]}
136
158
  />
137
159
  </InputContainer>
138
160
  )}
139
161
 
140
162
  {rationaleEnabled && (
141
- <InputContainer
142
- label={rationale.label}
143
- className={classes.prompt}
144
- >
163
+ <InputContainer label={rationale.label} className={classes.prompt}>
145
164
  <EditableHtml
146
165
  markup={model.rationale || ''}
147
166
  onChange={onRationaleChanged}
148
167
  imageSupport={imageSupport}
149
168
  toolbarOpts={toolbarOpts}
169
+ spellCheck={spellCheckEnabled}
170
+ maxImageWidth={(maxImageWidth && maxImageWidth.rationale) || defaultImageMaxWidth}
171
+ maxImageHeight={(maxImageHeight && maxImageHeight.rationale) || defaultImageMaxHeight}
172
+ uploadSoundSupport={uploadSoundSupport}
173
+ languageCharactersProps={[{ language: 'spanish' }, { language: 'special' }]}
150
174
  />
151
175
  </InputContainer>
152
176
  )}
153
177
 
154
- <Typography className={classes.label} variant="subheading">
155
- Define Hotspot
156
- </Typography>
178
+ <div className={classNames(classes.label, classes.flexContainer)}>
179
+ <Typography className={classes.subheading} variant="subheading">
180
+ Define Hotspot
181
+ </Typography>
182
+ <Tooltip
183
+ classes={{ tooltip: classes.tooltip }}
184
+ disableFocusListener
185
+ disableTouchListener
186
+ placement={'left'}
187
+ title={validationMessage}
188
+ >
189
+ <Info fontSize={'small'} color={'primary'} style={{ float: 'right' }} />
190
+ </Tooltip>
191
+ </div>
157
192
 
158
193
  <HotspotPalette
159
194
  hotspotColor={model.hotspotColor}
160
195
  hotspotList={model.hotspotList}
161
196
  outlineColor={model.outlineColor}
162
197
  outlineList={model.outlineList}
163
- onHotspotColorChange={color =>
164
- this.handleColorChange('hotspot', color)
165
- }
166
- onOutlineColorChange={color =>
167
- this.handleColorChange('outline', color)
168
- }
198
+ onHotspotColorChange={(color) => this.handleColorChange('hotspot', color)}
199
+ onOutlineColorChange={(color) => this.handleColorChange('outline', color)}
169
200
  />
170
201
 
171
202
  <HotspotContainer
172
203
  dimensions={model.dimensions}
173
204
  imageUrl={model.imageUrl}
174
205
  multipleCorrect={model.multipleCorrect}
206
+ hasErrors={!!shapesError || !!selectionsError}
175
207
  hotspotColor={model.hotspotColor}
176
208
  outlineColor={model.outlineColor}
177
209
  onUpdateImageDimension={onUpdateImageDimension}
@@ -181,6 +213,8 @@ export class Root extends React.Component {
181
213
  strokeWidth={model.strokeWidth}
182
214
  preserveAspectRatioEnabled={preserveAspectRatio.enabled}
183
215
  />
216
+ {shapesError && <div className={classes.errorText}>{shapesError}</div>}
217
+ {selectionsError && <div className={classes.errorText}>{selectionsError}</div>}
184
218
 
185
219
  {model.imageUrl && (
186
220
  <div>
@@ -198,6 +232,7 @@ export class Root extends React.Component {
198
232
  showErrorWhenOutsideRange
199
233
  className={classes.field}
200
234
  />
235
+
201
236
  <NumberTextField
202
237
  key="hotspot-manual-height"
203
238
  label="Height"
@@ -217,35 +252,52 @@ export class Root extends React.Component {
217
252
  }
218
253
  }
219
254
 
220
- const styles = theme => ({
255
+ const styles = (theme) => ({
221
256
  base: {
222
- marginTop: theme.spacing.unit * 3
257
+ marginTop: theme.spacing.unit * 3,
223
258
  },
224
259
  container: {
225
260
  display: 'flex',
226
- marginTop: theme.spacing.unit
261
+ marginTop: theme.spacing.unit,
227
262
  },
228
263
  dimensions: {
229
- display: 'flex'
264
+ display: 'flex',
230
265
  },
231
266
  field: {
232
267
  flex: 1,
233
- width: '90%'
268
+ width: '90%',
234
269
  },
235
270
  label: {
236
- marginTop: theme.spacing.unit * 4
271
+ marginTop: theme.spacing.unit * 4,
237
272
  },
238
273
  prompt: {
239
274
  paddingTop: theme.spacing.unit * 2,
240
- width: '100%'
275
+ width: '100%',
276
+ },
277
+ subheading: {
278
+ marginRight: theme.spacing.unit,
241
279
  },
242
280
  regular: {
243
- marginBottom: theme.spacing.unit * 3
281
+ marginBottom: theme.spacing.unit * 3,
244
282
  },
245
283
  switchElement: {
246
284
  justifyContent: 'space-between',
247
- margin: 0
248
- }
285
+ margin: 0,
286
+ },
287
+ flexContainer: {
288
+ display: 'flex',
289
+ alignItems: 'center',
290
+ },
291
+ tooltip: {
292
+ fontSize: '12px',
293
+ whiteSpace: 'pre',
294
+ maxWidth: '500px',
295
+ },
296
+ errorText: {
297
+ fontSize: '12px',
298
+ color: theme.palette.error.main,
299
+ paddingTop: theme.spacing.unit,
300
+ },
249
301
  });
250
302
 
251
303
  Root.propTypes = {
@@ -254,7 +306,11 @@ Root.propTypes = {
254
306
  model: PropTypes.object.isRequired,
255
307
  imageSupport: PropTypes.shape({
256
308
  add: PropTypes.func,
257
- delete: PropTypes.func
309
+ delete: PropTypes.func,
310
+ }),
311
+ uploadSoundSupport: PropTypes.shape({
312
+ add: PropTypes.func,
313
+ delete: PropTypes.func,
258
314
  }),
259
315
  onImageUpload: PropTypes.func.isRequired,
260
316
  onColorChanged: PropTypes.func.isRequired,