@pie-element/hotspot 4.7.18-next.85 → 4.8.1-beta.587

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 (95) hide show
  1. package/CHANGELOG.md +485 -0
  2. package/configure/CHANGELOG.md +425 -0
  3. package/configure/lib/button.js.map +1 -1
  4. package/configure/lib/defaults.js +24 -2
  5. package/configure/lib/defaults.js.map +1 -1
  6. package/configure/lib/hotspot-container.js +44 -23
  7. package/configure/lib/hotspot-container.js.map +1 -1
  8. package/configure/lib/hotspot-drawable.js +11 -5
  9. package/configure/lib/hotspot-drawable.js.map +1 -1
  10. package/configure/lib/hotspot-palette.js +1 -1
  11. package/configure/lib/hotspot-palette.js.map +1 -1
  12. package/configure/lib/hotspot-polygon.js +1 -1
  13. package/configure/lib/hotspot-polygon.js.map +1 -1
  14. package/configure/lib/hotspot-rectangle.js +1 -1
  15. package/configure/lib/hotspot-rectangle.js.map +1 -1
  16. package/configure/lib/index.js +17 -3
  17. package/configure/lib/index.js.map +1 -1
  18. package/configure/lib/root.js +133 -27
  19. package/configure/lib/root.js.map +1 -1
  20. package/configure/lib/upload-control.js.map +1 -1
  21. package/configure/lib/utils.js +18 -4
  22. package/configure/lib/utils.js.map +1 -1
  23. package/configure/package.json +4 -4
  24. package/configure/src/__tests__/__snapshots__/hotspot-container.test.js.snap +2 -1
  25. package/configure/src/__tests__/__snapshots__/root.test.js.snap +56 -4
  26. package/configure/src/__tests__/hotspot-container.test.js +98 -71
  27. package/configure/src/__tests__/hotspot-drawable.test.js +48 -49
  28. package/configure/src/__tests__/index.test.js +28 -51
  29. package/configure/src/__tests__/root.test.js +162 -82
  30. package/configure/src/__tests__/utils.test.js +112 -98
  31. package/configure/src/button.jsx +4 -4
  32. package/configure/src/defaults.js +36 -18
  33. package/configure/src/hotspot-container.jsx +107 -97
  34. package/configure/src/hotspot-drawable.jsx +59 -53
  35. package/configure/src/hotspot-palette.jsx +12 -25
  36. package/configure/src/hotspot-polygon.jsx +20 -22
  37. package/configure/src/hotspot-rectangle.jsx +5 -15
  38. package/configure/src/index.js +32 -18
  39. package/configure/src/root.jsx +120 -51
  40. package/configure/src/upload-control.jsx +9 -20
  41. package/configure/src/utils.js +51 -22
  42. package/controller/CHANGELOG.md +72 -0
  43. package/controller/lib/defaults.js +2 -2
  44. package/controller/lib/defaults.js.map +1 -1
  45. package/controller/lib/index.js +57 -9
  46. package/controller/lib/index.js.map +1 -1
  47. package/controller/lib/utils.js.map +1 -1
  48. package/controller/package.json +2 -2
  49. package/controller/src/__tests__/index.test.js +99 -55
  50. package/controller/src/defaults.js +5 -9
  51. package/controller/src/index.js +58 -34
  52. package/controller/src/utils.js +9 -6
  53. package/docs/config-schema.json +217 -0
  54. package/docs/config-schema.json.md +169 -0
  55. package/docs/demo/config.js +2 -2
  56. package/docs/demo/generate.js +54 -35
  57. package/docs/demo/session.js +2 -2
  58. package/docs/pie-schema.json +59 -0
  59. package/docs/pie-schema.json.md +47 -0
  60. package/lib/hotspot/container.js +44 -7
  61. package/lib/hotspot/container.js.map +1 -1
  62. package/lib/hotspot/icons.js.map +1 -1
  63. package/lib/hotspot/image.js +35 -8
  64. package/lib/hotspot/image.js.map +1 -1
  65. package/lib/hotspot/index.js +72 -18
  66. package/lib/hotspot/index.js.map +1 -1
  67. package/lib/hotspot/polygon.js +51 -32
  68. package/lib/hotspot/polygon.js.map +1 -1
  69. package/lib/hotspot/rectangle.js +51 -32
  70. package/lib/hotspot/rectangle.js.map +1 -1
  71. package/lib/index.js +4 -1
  72. package/lib/index.js.map +1 -1
  73. package/lib/session-updater.js.map +1 -1
  74. package/package.json +7 -4
  75. package/src/__tests__/__snapshots__/{container-test.jsx.snap → container.test.jsx.snap} +8 -0
  76. package/src/__tests__/__snapshots__/image.test.jsx.snap +43 -0
  77. package/src/__tests__/__snapshots__/{polygon-test.jsx.snap → polygon.test.jsx.snap} +11 -0
  78. package/src/__tests__/__snapshots__/{rectangle-test.jsx.snap → rectangle.test.jsx.snap} +11 -0
  79. package/src/__tests__/container.test.jsx +214 -0
  80. package/src/__tests__/{image-test.jsx → image.test.jsx} +10 -2
  81. package/src/__tests__/polygon.test.jsx +235 -0
  82. package/src/__tests__/rectangle.test.jsx +237 -0
  83. package/src/__tests__/{session-updater-test.jsx → session-updater.test.jsx} +22 -31
  84. package/src/hotspot/container.jsx +60 -23
  85. package/src/hotspot/icons.js +4 -2
  86. package/src/hotspot/image.jsx +26 -13
  87. package/src/hotspot/index.jsx +73 -33
  88. package/src/hotspot/polygon.jsx +51 -40
  89. package/src/hotspot/rectangle.jsx +51 -40
  90. package/src/index.js +10 -18
  91. package/src/session-updater.js +1 -1
  92. package/src/__tests__/__snapshots__/image-test.jsx.snap +0 -21
  93. package/src/__tests__/container-test.jsx +0 -143
  94. package/src/__tests__/polygon-test.jsx +0 -76
  95. package/src/__tests__/rectangle-test.jsx +0 -77
@@ -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,21 +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 || {};
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
+ };
97
+
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
+ };
78
109
 
79
110
  return (
80
111
  <div className={classes.base}>
81
112
  <layout.ConfigLayout
113
+ hideSettings={settingsPanelDisabled}
82
114
  settings={
83
115
  <Panel
84
116
  model={model}
@@ -86,19 +118,8 @@ export class Root extends React.Component {
86
118
  configuration={configuration}
87
119
  onChangeConfiguration={onConfigurationChanged}
88
120
  groups={{
89
- 'Settings': {
90
- multipleCorrect:
91
- multipleCorrect.settings && toggle(multipleCorrect.label),
92
- partialScoring:
93
- partialScoring.settings && toggle(partialScoring.label),
94
- promptEnabled:
95
- prompt.settings && toggle(prompt.label),
96
- },
97
- Properties: {
98
- teacherInstructionsEnabled:
99
- teacherInstructions.settings && toggle(teacherInstructions.label),
100
- rationaleEnabled: rationale.settings && toggle(rationale.label)
101
- }
121
+ Settings: panelSettings,
122
+ Properties: panelProperties,
102
123
  }}
103
124
  />
104
125
  }
@@ -111,9 +132,16 @@ export class Root extends React.Component {
111
132
  onChange={onTeacherInstructionsChanged}
112
133
  imageSupport={imageSupport}
113
134
  nonEmpty={false}
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' }]}
114
141
  />
115
142
  </InputContainer>
116
143
  )}
144
+
117
145
  {promptEnabled && (
118
146
  <InputContainer label={prompt.label} className={classes.prompt}>
119
147
  <EditableHtml
@@ -121,44 +149,61 @@ export class Root extends React.Component {
121
149
  onChange={onPromptChanged}
122
150
  imageSupport={imageSupport}
123
151
  nonEmpty={false}
152
+ toolbarOpts={toolbarOpts}
153
+ spellCheck={spellCheckEnabled}
154
+ maxImageWidth={defaultImageMaxWidth}
155
+ maxImageHeight={defaultImageMaxHeight}
156
+ uploadSoundSupport={uploadSoundSupport}
157
+ languageCharactersProps={[{ language: 'spanish' }, { language: 'special' }]}
124
158
  />
125
159
  </InputContainer>
126
160
  )}
127
161
 
128
162
  {rationaleEnabled && (
129
- <InputContainer
130
- label={rationale.label}
131
- className={classes.prompt}
132
- >
163
+ <InputContainer label={rationale.label} className={classes.prompt}>
133
164
  <EditableHtml
134
165
  markup={model.rationale || ''}
135
166
  onChange={onRationaleChanged}
136
167
  imageSupport={imageSupport}
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' }]}
137
174
  />
138
175
  </InputContainer>
139
176
  )}
140
177
 
141
- <Typography className={classes.label} variant="subheading">
142
- Define Hotspot
143
- </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>
144
192
 
145
193
  <HotspotPalette
146
194
  hotspotColor={model.hotspotColor}
147
195
  hotspotList={model.hotspotList}
148
196
  outlineColor={model.outlineColor}
149
197
  outlineList={model.outlineList}
150
- onHotspotColorChange={color =>
151
- this.handleColorChange('hotspot', color)
152
- }
153
- onOutlineColorChange={color =>
154
- this.handleColorChange('outline', color)
155
- }
198
+ onHotspotColorChange={(color) => this.handleColorChange('hotspot', color)}
199
+ onOutlineColorChange={(color) => this.handleColorChange('outline', color)}
156
200
  />
157
201
 
158
202
  <HotspotContainer
159
203
  dimensions={model.dimensions}
160
204
  imageUrl={model.imageUrl}
161
205
  multipleCorrect={model.multipleCorrect}
206
+ hasErrors={!!shapesError || !!selectionsError}
162
207
  hotspotColor={model.hotspotColor}
163
208
  outlineColor={model.outlineColor}
164
209
  onUpdateImageDimension={onUpdateImageDimension}
@@ -168,6 +213,8 @@ export class Root extends React.Component {
168
213
  strokeWidth={model.strokeWidth}
169
214
  preserveAspectRatioEnabled={preserveAspectRatio.enabled}
170
215
  />
216
+ {shapesError && <div className={classes.errorText}>{shapesError}</div>}
217
+ {selectionsError && <div className={classes.errorText}>{selectionsError}</div>}
171
218
 
172
219
  {model.imageUrl && (
173
220
  <div>
@@ -185,6 +232,7 @@ export class Root extends React.Component {
185
232
  showErrorWhenOutsideRange
186
233
  className={classes.field}
187
234
  />
235
+
188
236
  <NumberTextField
189
237
  key="hotspot-manual-height"
190
238
  label="Height"
@@ -204,35 +252,52 @@ export class Root extends React.Component {
204
252
  }
205
253
  }
206
254
 
207
- const styles = theme => ({
255
+ const styles = (theme) => ({
208
256
  base: {
209
- marginTop: theme.spacing.unit * 3
257
+ marginTop: theme.spacing.unit * 3,
210
258
  },
211
259
  container: {
212
260
  display: 'flex',
213
- marginTop: theme.spacing.unit
261
+ marginTop: theme.spacing.unit,
214
262
  },
215
263
  dimensions: {
216
- display: 'flex'
264
+ display: 'flex',
217
265
  },
218
266
  field: {
219
267
  flex: 1,
220
- width: '90%'
268
+ width: '90%',
221
269
  },
222
270
  label: {
223
- marginTop: theme.spacing.unit * 4
271
+ marginTop: theme.spacing.unit * 4,
224
272
  },
225
273
  prompt: {
226
274
  paddingTop: theme.spacing.unit * 2,
227
- width: '100%'
275
+ width: '100%',
276
+ },
277
+ subheading: {
278
+ marginRight: theme.spacing.unit,
228
279
  },
229
280
  regular: {
230
- marginBottom: theme.spacing.unit * 3
281
+ marginBottom: theme.spacing.unit * 3,
231
282
  },
232
283
  switchElement: {
233
284
  justifyContent: 'space-between',
234
- margin: 0
235
- }
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
+ },
236
301
  });
237
302
 
238
303
  Root.propTypes = {
@@ -241,7 +306,11 @@ Root.propTypes = {
241
306
  model: PropTypes.object.isRequired,
242
307
  imageSupport: PropTypes.shape({
243
308
  add: PropTypes.func,
244
- delete: PropTypes.func
309
+ delete: PropTypes.func,
310
+ }),
311
+ uploadSoundSupport: PropTypes.shape({
312
+ add: PropTypes.func,
313
+ delete: PropTypes.func,
245
314
  }),
246
315
  onImageUpload: PropTypes.func.isRequired,
247
316
  onColorChanged: PropTypes.func.isRequired,