@pie-lib/config-ui 9.0.3-next.2 → 10.0.0-beta.1

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 (100) hide show
  1. package/CHANGELOG.json +605 -0
  2. package/CHANGELOG.md +976 -328
  3. package/lib/alert-dialog.js +44 -0
  4. package/lib/alert-dialog.js.map +1 -0
  5. package/lib/checkbox.js +5 -5
  6. package/lib/checkbox.js.map +1 -1
  7. package/lib/choice-configuration/feedback-menu.js +42 -54
  8. package/lib/choice-configuration/feedback-menu.js.map +1 -1
  9. package/lib/choice-configuration/index.js +102 -86
  10. package/lib/choice-configuration/index.js.map +1 -1
  11. package/lib/choice-utils.js +9 -7
  12. package/lib/choice-utils.js.map +1 -1
  13. package/lib/feedback-config/feedback-selector.js +45 -49
  14. package/lib/feedback-config/feedback-selector.js.map +1 -1
  15. package/lib/feedback-config/group.js +13 -13
  16. package/lib/feedback-config/group.js.map +1 -1
  17. package/lib/feedback-config/index.js +57 -56
  18. package/lib/feedback-config/index.js.map +1 -1
  19. package/lib/form-section.js +19 -15
  20. package/lib/form-section.js.map +1 -1
  21. package/lib/help.js +31 -38
  22. package/lib/help.js.map +1 -1
  23. package/lib/index.js +73 -52
  24. package/lib/index.js.map +1 -1
  25. package/lib/input.js +36 -41
  26. package/lib/input.js.map +1 -1
  27. package/lib/inputs.js +42 -29
  28. package/lib/inputs.js.map +1 -1
  29. package/lib/langs.js +38 -46
  30. package/lib/langs.js.map +1 -1
  31. package/lib/layout/config-layout.js +36 -42
  32. package/lib/layout/config-layout.js.map +1 -1
  33. package/lib/layout/index.js +3 -3
  34. package/lib/layout/index.js.map +1 -1
  35. package/lib/layout/layout-contents.js +35 -35
  36. package/lib/layout/layout-contents.js.map +1 -1
  37. package/lib/layout/settings-box.js +29 -40
  38. package/lib/layout/settings-box.js.map +1 -1
  39. package/lib/mui-box/index.js +4 -4
  40. package/lib/mui-box/index.js.map +1 -1
  41. package/lib/number-text-field-custom.js +364 -0
  42. package/lib/number-text-field-custom.js.map +1 -0
  43. package/lib/number-text-field.js +46 -46
  44. package/lib/number-text-field.js.map +1 -1
  45. package/lib/radio-with-label.js +5 -5
  46. package/lib/radio-with-label.js.map +1 -1
  47. package/lib/settings/display-size.js +12 -10
  48. package/lib/settings/display-size.js.map +1 -1
  49. package/lib/settings/index.js +81 -14
  50. package/lib/settings/index.js.map +1 -1
  51. package/lib/settings/panel.js +297 -79
  52. package/lib/settings/panel.js.map +1 -1
  53. package/lib/settings/settings-radio-label.js +43 -0
  54. package/lib/settings/settings-radio-label.js.map +1 -0
  55. package/lib/settings/toggle.js +9 -7
  56. package/lib/settings/toggle.js.map +1 -1
  57. package/lib/tabs/index.js +29 -36
  58. package/lib/tabs/index.js.map +1 -1
  59. package/lib/tags-input/index.js +33 -41
  60. package/lib/tags-input/index.js.map +1 -1
  61. package/lib/two-choice.js +46 -50
  62. package/lib/two-choice.js.map +1 -1
  63. package/lib/with-stateful-model.js +24 -31
  64. package/lib/with-stateful-model.js.map +1 -1
  65. package/package.json +9 -7
  66. package/src/alert-dialog.jsx +43 -0
  67. package/src/checkbox.jsx +63 -0
  68. package/src/choice-configuration/feedback-menu.jsx +103 -0
  69. package/src/choice-configuration/index.jsx +319 -0
  70. package/src/choice-utils.js +30 -0
  71. package/src/feedback-config/feedback-selector.jsx +116 -0
  72. package/src/feedback-config/group.jsx +54 -0
  73. package/src/feedback-config/index.jsx +98 -0
  74. package/src/form-section.jsx +23 -0
  75. package/src/help.jsx +88 -0
  76. package/src/index.js +58 -0
  77. package/src/input.jsx +72 -0
  78. package/src/inputs.jsx +96 -0
  79. package/src/langs.jsx +122 -0
  80. package/src/layout/config-layout.jsx +64 -0
  81. package/src/layout/index.js +4 -0
  82. package/src/layout/layout-contents.jsx +60 -0
  83. package/src/layout/settings-box.jsx +31 -0
  84. package/src/mui-box/index.jsx +64 -0
  85. package/src/number-text-field-custom.jsx +276 -0
  86. package/src/number-text-field.jsx +196 -0
  87. package/src/radio-with-label.jsx +18 -0
  88. package/src/settings/display-size.jsx +52 -0
  89. package/src/settings/index.js +75 -0
  90. package/src/settings/panel.jsx +297 -0
  91. package/src/settings/settings-radio-label.jsx +20 -0
  92. package/src/settings/toggle.jsx +31 -0
  93. package/src/tabs/index.jsx +51 -0
  94. package/src/tags-input/index.jsx +121 -0
  95. package/src/two-choice.jsx +94 -0
  96. package/src/with-stateful-model.jsx +36 -0
  97. package/NEXT.CHANGELOG.json +0 -1
  98. package/__mocks__/@pie-lib/editable-html.jsx +0 -3
  99. package/lib/input-container.js +0 -59
  100. package/lib/input-container.js.map +0 -1
@@ -0,0 +1,103 @@
1
+ import Menu from '@material-ui/core/Menu';
2
+ import MenuItem from '@material-ui/core/MenuItem';
3
+ import ActionFeedback from '@material-ui/icons/Feedback';
4
+ import IconButton from '@material-ui/core/IconButton';
5
+ import PropTypes from 'prop-types';
6
+ import React from 'react';
7
+
8
+ export class IconMenu extends React.Component {
9
+ static propTypes = {
10
+ opts: PropTypes.object,
11
+ onClick: PropTypes.func.isRequired,
12
+ iconButtonElement: PropTypes.any
13
+ };
14
+
15
+ constructor(props) {
16
+ super(props);
17
+ this.state = {
18
+ anchorEl: undefined,
19
+ open: false
20
+ };
21
+ }
22
+
23
+ handleClick = event => {
24
+ this.setState({ open: true, anchorEl: event.currentTarget });
25
+ };
26
+
27
+ handleRequestClose = () => {
28
+ this.setState({ open: false });
29
+ };
30
+
31
+ render() {
32
+ const { opts, onClick } = this.props;
33
+
34
+ const keys = Object.keys(opts);
35
+
36
+ const handleMenuClick = key => () => {
37
+ onClick(key);
38
+ this.handleRequestClose();
39
+ };
40
+
41
+ return (
42
+ <div>
43
+ <div onClick={this.handleClick}>{this.props.iconButtonElement}</div>
44
+ <Menu
45
+ id="simple-menu"
46
+ anchorEl={this.state.anchorEl}
47
+ open={this.state.open}
48
+ onClose={this.handleRequestClose}
49
+ >
50
+ {keys.map((k, index) => (
51
+ <MenuItem key={index} onClick={handleMenuClick(k)}>
52
+ {opts[k]}
53
+ </MenuItem>
54
+ ))}
55
+ </Menu>
56
+ </div>
57
+ );
58
+ }
59
+ }
60
+
61
+ export default class FeedbackMenu extends React.Component {
62
+ static propTypes = {
63
+ value: PropTypes.object,
64
+ onChange: PropTypes.func.isRequired,
65
+ classes: PropTypes.object.isRequired
66
+ };
67
+
68
+ static defaultProps = {
69
+ classes: {}
70
+ };
71
+
72
+ render() {
73
+ const { value, onChange, classes } = this.props;
74
+
75
+ const t = value && value.type;
76
+ const iconColor = t === 'custom' || t === 'default' ? 'primary' : 'disabled';
77
+
78
+ const tooltip =
79
+ t === 'custom'
80
+ ? 'Custom Feedback'
81
+ : t === 'default'
82
+ ? 'Default Feedback'
83
+ : 'Feedback disabled';
84
+
85
+ const icon = (
86
+ <IconButton className={classes.icon} aria-label={tooltip}>
87
+ <ActionFeedback color={iconColor} />
88
+ </IconButton>
89
+ );
90
+
91
+ return (
92
+ <IconMenu
93
+ iconButtonElement={icon}
94
+ onClick={key => onChange(key)}
95
+ opts={{
96
+ none: 'No Feedback',
97
+ default: 'Default',
98
+ custom: 'Custom'
99
+ }}
100
+ />
101
+ );
102
+ }
103
+ }
@@ -0,0 +1,319 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { withStyles } from '@material-ui/core/styles';
4
+ import TextField from '@material-ui/core/TextField';
5
+ import classNames from 'classnames';
6
+ import { InputContainer } from '@pie-lib/render-ui';
7
+ import EditableHtml from '@pie-lib/editable-html';
8
+ import { InputCheckbox, InputRadio } from '../inputs';
9
+ import FeedbackMenu from './feedback-menu';
10
+ import ActionDelete from '@material-ui/icons/Delete';
11
+ import ArrowRight from '@material-ui/icons/SubdirectoryArrowRight';
12
+ import IconButton from '@material-ui/core/IconButton';
13
+
14
+ const EditableHtmlContainer = withStyles(theme => ({
15
+ labelContainer: {},
16
+ editorHolder: {
17
+ marginTop: theme.spacing.unit * 2
18
+ }
19
+ }))(
20
+ ({
21
+ label,
22
+ classes,
23
+ onChange,
24
+ value,
25
+ className,
26
+ imageSupport,
27
+ disabled,
28
+ spellCheck,
29
+ nonEmpty,
30
+ toolbarOpts,
31
+ error,
32
+ maxImageWidth,
33
+ maxImageHeight
34
+ }) => {
35
+ const names = classNames(classes.labelContainer, className);
36
+
37
+ return (
38
+ <InputContainer label={label} className={names}>
39
+ <div className={classes.editorHolder}>
40
+ <EditableHtml
41
+ markup={value || ''}
42
+ disabled={disabled}
43
+ spellCheck={spellCheck}
44
+ nonEmpty={nonEmpty}
45
+ onChange={onChange}
46
+ imageSupport={imageSupport}
47
+ className={classes.editor}
48
+ toolbarOpts={toolbarOpts}
49
+ error={error}
50
+ maxImageWidth={maxImageWidth}
51
+ maxImageHeight={maxImageHeight}
52
+ />
53
+ </div>
54
+ </InputContainer>
55
+ );
56
+ }
57
+ );
58
+
59
+ const Feedback = withStyles(() => ({
60
+ text: {
61
+ width: '100%'
62
+ },
63
+ feedbackContainer: {
64
+ position: 'relative'
65
+ },
66
+ arrowIcon: {
67
+ fill: '#ccc',
68
+ left: -56,
69
+ position: 'absolute',
70
+ top: 20
71
+ }
72
+ }))(({ value, onChange, type, correct, classes, defaults, toolbarOpts }) => {
73
+ if (!type || type === 'none') {
74
+ return null;
75
+ } else if (type === 'default') {
76
+ return (
77
+ <div className={classes.feedbackContainer}>
78
+ <ArrowRight className={classes.arrowIcon} />
79
+ <TextField
80
+ className={classes.text}
81
+ label="Feedback Text"
82
+ value={correct ? defaults.correct : defaults.incorrect}
83
+ />
84
+ </div>
85
+ );
86
+ } else {
87
+ return (
88
+ <div className={classes.feedbackContainer}>
89
+ <ArrowRight className={classes.arrowIcon} />
90
+ <EditableHtmlContainer
91
+ className={classes.text}
92
+ label="Feedback Text"
93
+ value={value}
94
+ onChange={onChange}
95
+ toolbarOpts={toolbarOpts}
96
+ />
97
+ </div>
98
+ );
99
+ }
100
+ });
101
+
102
+ export class ChoiceConfiguration extends React.Component {
103
+ static propTypes = {
104
+ classes: PropTypes.object.isRequired,
105
+ noLabels: PropTypes.bool,
106
+ useLetterOrdering: PropTypes.bool,
107
+ className: PropTypes.string,
108
+ mode: PropTypes.oneOf(['checkbox', 'radio']),
109
+ defaultFeedback: PropTypes.object.isRequired,
110
+ disabled: PropTypes.bool,
111
+ nonEmpty: PropTypes.bool,
112
+ data: PropTypes.shape({
113
+ label: PropTypes.string.isRequired,
114
+ value: PropTypes.string.isRequired,
115
+ correct: PropTypes.bool,
116
+ feedback: PropTypes.shape({
117
+ type: PropTypes.string,
118
+ value: PropTypes.string
119
+ })
120
+ }),
121
+ onDelete: PropTypes.func,
122
+ onChange: PropTypes.func,
123
+ index: PropTypes.number,
124
+ imageSupport: PropTypes.shape({
125
+ add: PropTypes.func.isRequired,
126
+ delete: PropTypes.func.isRequired
127
+ }),
128
+ allowFeedBack: PropTypes.bool,
129
+ allowDelete: PropTypes.bool,
130
+ toolbarOpts: PropTypes.object
131
+ };
132
+
133
+ static defaultProps = {
134
+ index: -1,
135
+ noLabels: false,
136
+ useLetterOrdering: false,
137
+ allowFeedBack: true,
138
+ allowDelete: true
139
+ };
140
+
141
+ _changeFn = key => update => {
142
+ const { data, onChange } = this.props;
143
+ if (onChange) {
144
+ onChange({ ...data, [key]: update });
145
+ }
146
+ };
147
+
148
+ onLabelChange = this._changeFn('label');
149
+
150
+ onCheckedChange = event => {
151
+ const correct = event.target.checked;
152
+ const { data, onChange } = this.props;
153
+
154
+ if (onChange) {
155
+ onChange({ ...data, correct });
156
+ }
157
+ };
158
+
159
+ onFeedbackValueChange = v => {
160
+ const { data, onChange } = this.props;
161
+
162
+ if (data.feedback.type !== 'custom') {
163
+ return;
164
+ }
165
+
166
+ const fb = { ...data.feedback, value: v };
167
+
168
+ if (onChange) onChange({ ...data, feedback: fb });
169
+ };
170
+
171
+ onFeedbackTypeChange = t => {
172
+ const { data, onChange } = this.props;
173
+ const fb = { ...data.feedback, type: t };
174
+ if (fb.type !== 'custom') {
175
+ fb.value = undefined;
176
+ }
177
+
178
+ if (onChange) onChange({ ...data, feedback: fb });
179
+ };
180
+
181
+ render() {
182
+ const {
183
+ data,
184
+ classes,
185
+ mode,
186
+ onDelete,
187
+ defaultFeedback,
188
+ index,
189
+ className,
190
+ noLabels,
191
+ useLetterOrdering,
192
+ imageSupport,
193
+ disabled,
194
+ spellCheck,
195
+ nonEmpty,
196
+ allowFeedBack,
197
+ allowDelete,
198
+ toolbarOpts,
199
+ error,
200
+ noCorrectAnswerError
201
+ } = this.props;
202
+
203
+ const InputToggle = mode === 'checkbox' ? InputCheckbox : InputRadio;
204
+ const names = classNames(classes.choiceConfiguration, className);
205
+ return (
206
+ <div className={names}>
207
+ <div className={classes.topRow}>
208
+ {index > 0 && (
209
+ <span className={classes.index} type="title">
210
+ {useLetterOrdering ? String.fromCharCode(96 + index).toUpperCase() : index}
211
+ </span>
212
+ )}
213
+ <InputToggle
214
+ className={classes.toggle}
215
+ onChange={this.onCheckedChange}
216
+ label={!noLabels ? 'Correct' : ''}
217
+ checked={!!data.correct}
218
+ error={noCorrectAnswerError}
219
+ />
220
+ <div className={classes.middleColumn}>
221
+ <EditableHtmlContainer
222
+ label={!noLabels ? 'Label' : ''}
223
+ value={data.label}
224
+ onChange={this.onLabelChange}
225
+ imageSupport={imageSupport}
226
+ disabled={disabled}
227
+ spellCheck={spellCheck}
228
+ nonEmpty={nonEmpty}
229
+ toolbarOpts={toolbarOpts}
230
+ error={error}
231
+ />
232
+ {error && <div className={classes.errorText}>{error}</div>}
233
+
234
+ {allowFeedBack && (
235
+ <Feedback
236
+ {...data.feedback}
237
+ correct={data.correct}
238
+ defaults={defaultFeedback}
239
+ onChange={this.onFeedbackValueChange}
240
+ toolbarOpts={toolbarOpts}
241
+ />
242
+ )}
243
+ </div>
244
+ {allowFeedBack && (
245
+ <InputContainer className={classes.feedback} label={!noLabels ? 'Feedback' : ''}>
246
+ <FeedbackMenu
247
+ onChange={this.onFeedbackTypeChange}
248
+ value={data.feedback}
249
+ classes={{
250
+ icon: classes.feedbackIcon
251
+ }}
252
+ />
253
+ </InputContainer>
254
+ )}
255
+ {allowDelete && (
256
+ <InputContainer className={classes.delete} label={!noLabels ? 'Delete' : ''}>
257
+ <IconButton aria-label="delete" className={classes.deleteIcon} onClick={onDelete}>
258
+ <ActionDelete />
259
+ </IconButton>
260
+ </InputContainer>
261
+ )}
262
+ </div>
263
+ </div>
264
+ );
265
+ }
266
+ }
267
+
268
+ const styles = theme => ({
269
+ index: {
270
+ padding: '24px 10px 0 0'
271
+ },
272
+ choiceConfiguration: {},
273
+ topRow: {
274
+ display: 'flex'
275
+ },
276
+ value: {
277
+ flex: '0.5',
278
+ paddingRight: theme.spacing.unit
279
+ },
280
+ editorHolder: {
281
+ marginTop: theme.spacing.unit * 2
282
+ },
283
+ toggle: {
284
+ flex: '0 1 auto'
285
+ },
286
+ feedback: {
287
+ flex: '0 1 auto',
288
+ paddingTop: theme.spacing.unit,
289
+ paddingLeft: 0,
290
+ marginLeft: 0,
291
+ paddingRight: theme.spacing.unit * 3
292
+ },
293
+ feedbackIcon: {
294
+ margin: 0,
295
+ paddingLeft: 0,
296
+ width: 'inherit'
297
+ },
298
+ deleteIcon: {
299
+ margin: 0,
300
+ width: 'inherit'
301
+ },
302
+ delete: {
303
+ flex: '0 1 auto',
304
+ paddingTop: theme.spacing.unit,
305
+ paddingLeft: 0,
306
+ marginLeft: 0
307
+ },
308
+ middleColumn: {
309
+ display: 'flex',
310
+ flex: 1,
311
+ flexDirection: 'column'
312
+ },
313
+ errorText: {
314
+ fontSize: '12px',
315
+ color: 'red'
316
+ }
317
+ });
318
+
319
+ export default withStyles(styles)(ChoiceConfiguration);
@@ -0,0 +1,30 @@
1
+ import includes from 'lodash/includes';
2
+
3
+ /**
4
+ * Add value to every model.choices.
5
+ * @param {Object} model the model to normalize
6
+ * @return {Object} the updated model
7
+ */
8
+ export const normalizeChoices = model => {
9
+ const choices = model.choices.map((c, index) => {
10
+ if (!c.value) {
11
+ c.value = `${index}`;
12
+ }
13
+ return c;
14
+ });
15
+ return { ...model, choices };
16
+ };
17
+
18
+ /**
19
+ * Find the first available index.
20
+ * @param {string[]} values
21
+ * @param {number} index
22
+ * @return {string}
23
+ */
24
+ export const firstAvailableIndex = (values, index) => {
25
+ if (includes(values, `${index}`)) {
26
+ return firstAvailableIndex(values, index + 1);
27
+ } else {
28
+ return `${index}`;
29
+ }
30
+ };
@@ -0,0 +1,116 @@
1
+ import EditableHTML from '@pie-lib/editable-html';
2
+ import { InputContainer } from '@pie-lib/render-ui';
3
+ import PropTypes from 'prop-types';
4
+ import React from 'react';
5
+ import { withStyles } from '@material-ui/core/styles';
6
+ import Group from './group';
7
+
8
+ const feedbackLabels = {
9
+ default: 'Simple Feedback',
10
+ none: 'No Feedback',
11
+ custom: 'Customized Feedback'
12
+ };
13
+
14
+ const holder = (theme, extras) => ({
15
+ marginTop: '0px',
16
+ background: '#e0dee0',
17
+ padding: theme.spacing.unit * 0.9,
18
+ marginBottom: theme.spacing.unit * 2,
19
+ ...extras
20
+ });
21
+
22
+ const style = theme => ({
23
+ feedbackSelector: {
24
+ marginBottom: theme.spacing.unit
25
+ },
26
+ label: {
27
+ cursor: 'pointer'
28
+ },
29
+ inputContainerLabel: {
30
+ transform: 'translateY(-20%)'
31
+ },
32
+ feedbackInputContainer: {
33
+ paddingBottom: 0
34
+ },
35
+ customHolder: holder(theme, {
36
+ background: '#e0dee0',
37
+ padding: 0
38
+ }),
39
+ defaultHolder: holder(theme, {
40
+ fontFamily: theme.typography.fontFamily,
41
+ cursor: 'default'
42
+ }),
43
+ editor: {
44
+ fontFamily: theme.typography.fontFamily
45
+ },
46
+ group: {
47
+ paddingTop: theme.spacing.unit
48
+ }
49
+ });
50
+
51
+ export const FeedbackType = {
52
+ type: PropTypes.oneOf(['default', 'custom', 'none']),
53
+ default: PropTypes.string,
54
+ custom: PropTypes.string
55
+ };
56
+
57
+ export class FeedbackSelector extends React.Component {
58
+ static propTypes = {
59
+ keys: PropTypes.arrayOf(PropTypes.string),
60
+ classes: PropTypes.object.isRequired,
61
+ label: PropTypes.string.isRequired,
62
+ feedback: PropTypes.shape(FeedbackType).isRequired,
63
+ onChange: PropTypes.func.isRequired,
64
+ toolbarOpts: PropTypes.object
65
+ };
66
+
67
+ changeType = type => {
68
+ const { onChange, feedback } = this.props;
69
+ onChange({ ...feedback, type });
70
+ };
71
+
72
+ changeCustom = custom => {
73
+ const { onChange, feedback } = this.props;
74
+ onChange({ ...feedback, type: 'custom', custom });
75
+ };
76
+
77
+ render() {
78
+ const { keys, classes, label, feedback, toolbarOpts } = this.props;
79
+
80
+ const feedbackKeys = keys || Object.keys(feedbackLabels);
81
+
82
+ return (
83
+ <div className={classes.feedbackSelector}>
84
+ <InputContainer
85
+ label={label}
86
+ className={classes.feedbackInputContainer}
87
+ extraClasses={{ label: classes.inputContainerLabel }}
88
+ >
89
+ <Group
90
+ className={classes.group}
91
+ keys={feedbackKeys}
92
+ label={label}
93
+ value={feedback.type}
94
+ onChange={this.changeType}
95
+ feedbackLabels={feedbackLabels}
96
+ />
97
+ </InputContainer>
98
+ {feedback.type === 'custom' && (
99
+ <div className={classes.customHolder}>
100
+ <EditableHTML
101
+ className={classes.editor}
102
+ onChange={this.changeCustom}
103
+ markup={feedback.custom || ''}
104
+ toolbarOpts={toolbarOpts}
105
+ />
106
+ </div>
107
+ )}
108
+ {feedback.type === 'default' && (
109
+ <div className={classes.defaultHolder}> {feedback.default}</div>
110
+ )}
111
+ </div>
112
+ );
113
+ }
114
+ }
115
+
116
+ export default withStyles(style)(FeedbackSelector);
@@ -0,0 +1,54 @@
1
+ import PropTypes from 'prop-types';
2
+ import RadioWithLabel from '../radio-with-label';
3
+ import React from 'react';
4
+ import { withStyles } from '@material-ui/core/styles';
5
+ import classNames from 'classnames';
6
+
7
+ const styles = () => ({
8
+ radioLabel: {
9
+ fontSize: '12px'
10
+ },
11
+ choice: {
12
+ display: 'flex',
13
+ alignItems: 'center'
14
+ },
15
+ choiceHolder: {
16
+ display: 'flex',
17
+ alignItems: 'center'
18
+ }
19
+ });
20
+
21
+ const Group = props => {
22
+ const { feedbackLabels, value, classes, className, onChange, keys } = props;
23
+
24
+ return (
25
+ <div className={classNames(classes.choiceHolder, className)}>
26
+ {keys.map(key => {
27
+ return (
28
+ <div className={classes.choice} key={key}>
29
+ <RadioWithLabel
30
+ value={key}
31
+ checked={value === key}
32
+ classes={{
33
+ label: classes.radioLabel
34
+ }}
35
+ onChange={e => onChange(e.currentTarget.value)}
36
+ label={feedbackLabels[key]}
37
+ />
38
+ </div>
39
+ );
40
+ })}
41
+ </div>
42
+ );
43
+ };
44
+
45
+ Group.propTypes = {
46
+ className: PropTypes.string,
47
+ feedbackLabels: PropTypes.object.isRequired,
48
+ value: PropTypes.string.isRequired,
49
+ classes: PropTypes.object.isRequired,
50
+ keys: PropTypes.arrayOf(PropTypes.string),
51
+ onChange: PropTypes.func
52
+ };
53
+
54
+ export default withStyles(styles)(Group);