@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,297 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { withStyles } from '@material-ui/core/styles';
4
+ import get from 'lodash/get';
5
+ import set from 'lodash/set';
6
+ import Select from '@material-ui/core/Select';
7
+ import Input from '@material-ui/core/Input';
8
+ import MenuItem from '@material-ui/core/MenuItem';
9
+ import debug from 'debug';
10
+
11
+ import Toggle from './toggle';
12
+ import TwoChoice from '../two-choice';
13
+ import SettingsRadioLabel from './settings-radio-label';
14
+ import { NumberTextField } from '../index';
15
+ import Checkbox from '../checkbox';
16
+
17
+ const log = debug('pie-lib:config-ui:settings:panel');
18
+
19
+ const labelValue = {
20
+ label: PropTypes.string,
21
+ value: PropTypes.string
22
+ };
23
+
24
+ const baseTypes = {
25
+ label: PropTypes.string,
26
+ value: PropTypes.string,
27
+ onChange: PropTypes.func
28
+ };
29
+
30
+ const CheckboxChoice = ({ label, value, onChange }) => {
31
+ return (
32
+ <Checkbox
33
+ checked={value}
34
+ label={label}
35
+ onChange={event => {
36
+ onChange(event.target.checked);
37
+ }}
38
+ />
39
+ );
40
+ };
41
+
42
+ CheckboxChoice.propTypes = {
43
+ label: PropTypes.string,
44
+ value: PropTypes.bool,
45
+ onChange: PropTypes.func
46
+ };
47
+
48
+ const Radio = ({ classes, label, value, onChange, choices }) => {
49
+ return (
50
+ <TwoChoice
51
+ className={classes.radioSettings}
52
+ direction="vertical"
53
+ customLabel={SettingsRadioLabel}
54
+ value={value}
55
+ header={label}
56
+ one={choices[0]}
57
+ two={choices[1]}
58
+ onChange={onChange}
59
+ />
60
+ );
61
+ };
62
+
63
+ Radio.propTypes = { ...baseTypes, choices: PropTypes.arrayOf(PropTypes.shape(labelValue)) };
64
+
65
+ const StyledRadio = withStyles({
66
+ radioSettings: {
67
+ marginTop: '4px',
68
+ paddingBottom: '4px',
69
+ width: '100%',
70
+ '& > label': {
71
+ color: 'rgba(0, 0, 0, 0.89)',
72
+ transform: 'translate(0, 10px) scale(1)',
73
+ fontSize: '14px'
74
+ },
75
+ '& > div': {
76
+ marginTop: '20px'
77
+ }
78
+ },
79
+ label: {
80
+ display: 'none'
81
+ }
82
+ })(Radio);
83
+
84
+ const Dropdown = withStyles({
85
+ label: {
86
+ margin: 0,
87
+ fontSize: '14px'
88
+ },
89
+ wrapper: {
90
+ marginTop: '4px',
91
+ border: '2px solid lightgrey',
92
+ borderRadius: '4px',
93
+ padding: '0 8px'
94
+ }
95
+ })(({ classes, label, value, onChange, choices = [] }) => {
96
+ return (
97
+ <div>
98
+ {label && <p className={classes.label}>{label}</p>}
99
+ <Select
100
+ className={classes.wrapper}
101
+ value={value || (choices && choices[0])}
102
+ onChange={({ target }) => onChange(target.value)}
103
+ input={<Input id={`dropdown-${label}`} />}
104
+ disableUnderline
105
+ >
106
+ {choices.map((l, index) => (
107
+ <MenuItem key={index} value={l}>
108
+ {l}
109
+ </MenuItem>
110
+ ))}
111
+ </Select>
112
+ </div>
113
+ );
114
+ });
115
+
116
+ Dropdown.propTypes = { ...baseTypes, choices: PropTypes.arrayOf(PropTypes.string) };
117
+
118
+ const NumberField = withStyles({
119
+ field: {
120
+ width: '35%',
121
+ marginRight: '24px',
122
+ marginTop: '8px'
123
+ },
124
+ wrapper: {
125
+ marginTop: '4px',
126
+ border: '2px solid lightgrey',
127
+ borderRadius: '4px',
128
+ padding: '0 8px'
129
+ }
130
+ })(({ classes, label, value, onChange = () => {}, suffix, min, max }) => {
131
+ return (
132
+ <NumberTextField
133
+ label={label || 'Label'}
134
+ value={value}
135
+ max={max}
136
+ min={min}
137
+ onChange={(ev, value) => onChange(value)}
138
+ suffix={suffix}
139
+ className={classes.field}
140
+ showErrorWhenOutsideRange
141
+ inputClassName={classes.wrapper}
142
+ disableUnderline
143
+ />
144
+ );
145
+ });
146
+
147
+ NumberField.propTypes = {
148
+ ...baseTypes,
149
+ classes: PropTypes.object,
150
+ suffix: PropTypes.string,
151
+ min: PropTypes.number,
152
+ max: PropTypes.number,
153
+ value: PropTypes.number
154
+ };
155
+
156
+ const ToggleWrapper = ({ label, value, onChange }) => (
157
+ <Toggle label={label} checked={!!value} toggle={onChange} />
158
+ );
159
+
160
+ ToggleWrapper.propTypes = { ...baseTypes, value: PropTypes.bool };
161
+
162
+ const tagMap = {
163
+ toggle: ToggleWrapper,
164
+ radio: StyledRadio,
165
+ dropdown: Dropdown,
166
+ numberField: NumberField,
167
+ checkbox: CheckboxChoice
168
+ };
169
+
170
+ const Group = withStyles(() => ({
171
+ group: {
172
+ margin: '0 0 25px 0'
173
+ },
174
+ groupHeader: {
175
+ color: '#495B8F',
176
+ fontSize: '16px',
177
+ fontWeight: 600,
178
+ marginBottom: '8px'
179
+ },
180
+ numberFields: {
181
+ fontSize: '0.85rem',
182
+ marginBottom: 0
183
+ }
184
+ }))(props => {
185
+ const { classes, model, label, group, configuration, onChange } = props;
186
+
187
+ /**
188
+ * @param group - the group of settings
189
+ * @param key - the key(or path) to be used to set or get from model or configuration
190
+ * @param innerKey - the key(or path) to be used to get from the group (used only for numberField type)
191
+ * @returns tag that corresponds to element type */
192
+ const getTag = (group, key, innerKey) => {
193
+ const { isConfigProperty, ...properties } = get(group, innerKey || key);
194
+ const value = isConfigProperty ? get(configuration, key) : get(model, key);
195
+ const tagProps = { ...properties, key, value };
196
+ const Tag = tagMap[tagProps.type];
197
+
198
+ return <Tag key={key} {...tagProps} onChange={v => onChange(key, v, isConfigProperty)} />;
199
+ };
200
+
201
+ const content = (group, key) => {
202
+ const currentGroup = group[key];
203
+
204
+ if (!currentGroup) {
205
+ return null;
206
+ }
207
+
208
+ const { type, label, fields, choices } = currentGroup;
209
+
210
+ if (type === 'numberFields') {
211
+ return (
212
+ <div key={`numberField-${label}`}>
213
+ <p className={classes.numberFields}>{label}</p>
214
+ {Object.keys(fields).map(fieldKey => {
215
+ return getTag(group, `${key}.${fieldKey}`, `${key}.fields.${fieldKey}`);
216
+ })}
217
+ </div>
218
+ );
219
+ }
220
+
221
+ if (type === 'checkboxes') {
222
+ return (
223
+ <div key={`checkbox-${label}`}>
224
+ <p>{label}</p>
225
+ {Object.keys(choices).map(choiceKey => {
226
+ return getTag(group, `${key}.${choiceKey}`, `${key}.choices.${choiceKey}`);
227
+ })}
228
+ </div>
229
+ );
230
+ }
231
+
232
+ // if type is toggle, radio, dropdown or numberField
233
+ return getTag(group, key);
234
+ };
235
+
236
+ return (
237
+ <div className={classes.group}>
238
+ <div className={classes.groupHeader}>{label}</div>
239
+
240
+ {Object.keys(group).map(key => {
241
+ return content(group, key);
242
+ })}
243
+ </div>
244
+ );
245
+ });
246
+
247
+ export class Panel extends React.Component {
248
+ static propTypes = {
249
+ model: PropTypes.object,
250
+ configuration: PropTypes.object,
251
+ groups: PropTypes.object,
252
+ onChangeModel: PropTypes.func,
253
+ onChangeConfiguration: PropTypes.func
254
+ };
255
+
256
+ static defaultProps = {
257
+ onChangeModel: () => {},
258
+ onChangeConfiguration: () => {}
259
+ };
260
+
261
+ change = (key, value, isConfigProperty = false) => {
262
+ log('[changeModel]', key, value);
263
+ const { onChangeModel, onChangeConfiguration } = this.props;
264
+ const model = { ...this.props.model };
265
+ const configuration = { ...this.props.configuration };
266
+
267
+ if (isConfigProperty) {
268
+ set(configuration, key, value);
269
+ onChangeConfiguration(configuration, key);
270
+ } else {
271
+ set(model, key, value);
272
+ onChangeModel(model, key);
273
+ }
274
+ };
275
+
276
+ render() {
277
+ const { groups, model, configuration } = this.props;
278
+ log('render:', model);
279
+
280
+ return (
281
+ <div>
282
+ {Object.keys(groups).map(g => (
283
+ <Group
284
+ label={g}
285
+ key={g}
286
+ model={model}
287
+ configuration={configuration}
288
+ group={groups[g]}
289
+ onChange={this.change}
290
+ />
291
+ ))}
292
+ </div>
293
+ );
294
+ }
295
+ }
296
+
297
+ export default Panel;
@@ -0,0 +1,20 @@
1
+ import FormControlLabel from '@material-ui/core/FormControlLabel';
2
+ import Radio from '@material-ui/core/Radio';
3
+ import React from 'react';
4
+ import { withStyles } from '@material-ui/core/styles';
5
+
6
+ export default withStyles({
7
+ label: {
8
+ color: 'rgba(0, 0, 0, 0.89)',
9
+ fontSize: '12px',
10
+ left: '-5px',
11
+ position: 'relative'
12
+ }
13
+ })(({ label, value, checked, onChange, classes }) => (
14
+ <FormControlLabel
15
+ value={value}
16
+ classes={classes}
17
+ control={<Radio checked={checked} onChange={onChange} />}
18
+ label={label}
19
+ />
20
+ ));
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import InputLabel from '@material-ui/core/InputLabel';
4
+ import { withStyles } from '@material-ui/core/styles';
5
+ import Switch from '@material-ui/core/Switch';
6
+
7
+ const Toggle = withStyles(theme => ({
8
+ toggle: {
9
+ display: 'flex',
10
+ width: '100%',
11
+ justifyContent: 'space-between'
12
+ },
13
+ label: {
14
+ color: 'rgba(0, 0, 0, 0.89)',
15
+ fontSize: '14px',
16
+ paddingTop: theme.spacing.unit * 2
17
+ }
18
+ }))(({ checked, label, toggle, classes }) => (
19
+ <div className={classes.toggle}>
20
+ <InputLabel className={classes.label}>{label}</InputLabel>
21
+ <Switch checked={checked} onChange={e => toggle(e.target.checked)} />
22
+ </div>
23
+ ));
24
+
25
+ Toggle.propTypes = {
26
+ checked: PropTypes.bool,
27
+ label: PropTypes.string.isRequired,
28
+ toggle: PropTypes.func.isRequired
29
+ };
30
+
31
+ export default Toggle;
@@ -0,0 +1,51 @@
1
+ import React from 'react';
2
+
3
+ import MuiTabs from '@material-ui/core/Tabs';
4
+ import MuiTab from '@material-ui/core/Tab';
5
+ import PropTypes from 'prop-types';
6
+ import { withStyles } from '@material-ui/core';
7
+
8
+ export class Tabs extends React.Component {
9
+ static propTypes = {
10
+ classes: PropTypes.object,
11
+ className: PropTypes.string,
12
+ contentClassName: PropTypes.string,
13
+ children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired
14
+ };
15
+
16
+ constructor(props) {
17
+ super(props);
18
+ this.state = {
19
+ value: 0
20
+ };
21
+ }
22
+
23
+ handleChange = (event, value) => {
24
+ this.setState({ value });
25
+ };
26
+
27
+ render() {
28
+ const { value } = this.state;
29
+ const { children, className, contentClassName, classes } = this.props;
30
+
31
+ const tabClasses = {
32
+ root: classes.tabRoot
33
+ };
34
+ return (
35
+ <div className={className}>
36
+ <MuiTabs indicatorColor="primary" value={value} onChange={this.handleChange}>
37
+ {React.Children.map(children, (c, index) =>
38
+ c && c.props.title ? (
39
+ <MuiTab classes={tabClasses} key={index} label={c.props.title} />
40
+ ) : null
41
+ )}
42
+ </MuiTabs>
43
+ <div className={contentClassName}>{children[value]}</div>
44
+ </div>
45
+ );
46
+ }
47
+ }
48
+
49
+ export default withStyles(() => ({
50
+ tabRoot: {}
51
+ }))(Tabs);
@@ -0,0 +1,121 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { withStyles } from '@material-ui/core/styles';
4
+ import uniq from 'lodash/uniq';
5
+ import Chip from '@material-ui/core/Chip';
6
+ import MuiBox from '../mui-box';
7
+
8
+ const ENTER = 13;
9
+
10
+ const Tag = withStyles(() => ({
11
+ tag: {
12
+ padding: '0px',
13
+ margin: '1px'
14
+ }
15
+ }))(({ classes, label, onDelete }) => (
16
+ <Chip className={classes.tag} label={label} onDelete={onDelete} />
17
+ ));
18
+
19
+ Tag.propTypes = {
20
+ label: PropTypes.string.isRequired,
21
+ onDelete: PropTypes.func.isRequired
22
+ };
23
+
24
+ export class TagsInput extends React.Component {
25
+ static propTypes = {
26
+ classes: PropTypes.object.isRequired,
27
+ tags: PropTypes.arrayOf(PropTypes.string).isRequired,
28
+ onChange: PropTypes.func.isRequired
29
+ };
30
+
31
+ constructor(props) {
32
+ super(props);
33
+ this.state = {
34
+ value: '',
35
+ focused: false
36
+ };
37
+
38
+ this.onKeyDown = event => {
39
+ if (event.keyCode === ENTER && this.state.value !== '') {
40
+ const tag = this.state.value.trim();
41
+ const newTags = uniq(this.props.tags.concat([tag]));
42
+
43
+ if (newTags.length !== this.props.tags.length) {
44
+ this.props.onChange(newTags);
45
+ this.setState({ value: '' });
46
+ }
47
+ }
48
+ };
49
+
50
+ this.onChange = event => {
51
+ this.setState({ value: event.target.value });
52
+ };
53
+
54
+ this.deleteTag = tag => {
55
+ const { tags } = this.props;
56
+
57
+ const tagIndex = tags.indexOf(tag);
58
+ if (tagIndex !== -1) {
59
+ tags.splice(tagIndex, 1);
60
+ this.props.onChange(tags);
61
+ this.input.focus();
62
+ }
63
+ };
64
+ }
65
+
66
+ onFocus = () => {
67
+ this.setState({ focused: true });
68
+ };
69
+
70
+ onBlur = () => {
71
+ this.setState({ focused: false });
72
+ };
73
+
74
+ render() {
75
+ const { classes, tags } = this.props;
76
+ return (
77
+ <MuiBox focused={this.state.focused}>
78
+ <div className={classes.tagsInput}>
79
+ {(tags || []).map((t, index) => (
80
+ <Tag key={index} label={t} onDelete={() => this.deleteTag(t)} />
81
+ ))}
82
+ <input
83
+ ref={r => (this.input = r)}
84
+ onKeyDown={this.onKeyDown}
85
+ onChange={this.onChange}
86
+ className={classes.input}
87
+ value={this.state.value}
88
+ onFocus={this.onFocus}
89
+ onBlur={this.onBlur}
90
+ type="text"
91
+ />
92
+ </div>
93
+ </MuiBox>
94
+ );
95
+ }
96
+ }
97
+
98
+ const styles = theme => ({
99
+ tagsInput: {
100
+ border: 'solid 0px white',
101
+ display: 'flex',
102
+ flexWrap: 'wrap'
103
+ },
104
+ input: {
105
+ padding: '2px',
106
+ margin: '1px',
107
+ minWidth: '30px',
108
+ width: '100%',
109
+ flex: '1',
110
+ border: 'solid 0px white',
111
+ height: '28px',
112
+ fontSize: theme.typography.fontSize,
113
+ fontFamily: theme.typography.fontFamily,
114
+ outline: 'none',
115
+ '&:focus': {
116
+ outline: 'none'
117
+ }
118
+ }
119
+ });
120
+
121
+ export default withStyles(styles)(TagsInput);
@@ -0,0 +1,94 @@
1
+ import { InputContainer } from '@pie-lib/render-ui';
2
+ import PropTypes from 'prop-types';
3
+ import RadioWithLabel from './radio-with-label';
4
+ import React from 'react';
5
+ import classNames from 'classnames';
6
+ import { withStyles } from '@material-ui/core/styles';
7
+
8
+ const styles = theme => ({
9
+ group: {
10
+ display: 'flex',
11
+ flexWrap: 'wrap',
12
+ paddingLeft: 0,
13
+ marginTop: theme.spacing.unit
14
+ },
15
+ vertical: {
16
+ flexDirection: 'column'
17
+ }
18
+ });
19
+
20
+ class RawNChoice extends React.Component {
21
+ static propTypes = {
22
+ header: PropTypes.string.isRequired,
23
+ className: PropTypes.string,
24
+ customLabel: PropTypes.func,
25
+ opts: PropTypes.array.isRequired,
26
+ value: PropTypes.string,
27
+ onChange: PropTypes.func.isRequired,
28
+ direction: PropTypes.oneOf(['horizontal', 'vertical']),
29
+ classes: PropTypes.object.isRequired
30
+ };
31
+
32
+ handleChange = event => {
33
+ this.props.onChange(event.currentTarget.value);
34
+ };
35
+
36
+ render() {
37
+ const { header, className, classes, customLabel, opts, value, direction } = this.props;
38
+
39
+ const preppedOpts = opts.map(o => {
40
+ return typeof o === 'string' ? { label: o, value: o } : o;
41
+ });
42
+ const LabelComponent = customLabel || RadioWithLabel;
43
+
44
+ return (
45
+ <InputContainer label={header} className={className}>
46
+ <div className={classNames(classes.group, classes[direction])}>
47
+ {preppedOpts.map((o, index) => (
48
+ <LabelComponent
49
+ value={o.value}
50
+ key={index}
51
+ checked={o.value === value}
52
+ onChange={this.handleChange}
53
+ label={o.label}
54
+ />
55
+ ))}
56
+ </div>
57
+ </InputContainer>
58
+ );
59
+ }
60
+ }
61
+
62
+ export const NChoice = withStyles(styles)(RawNChoice);
63
+
64
+ const labelValue = PropTypes.shape({ label: PropTypes.string, value: PropTypes.string });
65
+
66
+ class TwoChoice extends React.Component {
67
+ static propTypes = {
68
+ header: PropTypes.string.isRequired,
69
+ value: PropTypes.string.isRequired,
70
+ onChange: PropTypes.func.isRequired,
71
+ one: PropTypes.oneOfType([labelValue, PropTypes.string]),
72
+ two: PropTypes.oneOfType([labelValue, PropTypes.string]),
73
+ className: PropTypes.string,
74
+ customLabel: PropTypes.func
75
+ };
76
+
77
+ render() {
78
+ const { one, two, header, className, customLabel, value, onChange } = this.props;
79
+ const opts = [one, two];
80
+
81
+ return (
82
+ <NChoice
83
+ customLabel={customLabel}
84
+ header={header}
85
+ className={className}
86
+ opts={opts}
87
+ value={value}
88
+ onChange={onChange}
89
+ />
90
+ );
91
+ }
92
+ }
93
+
94
+ export default withStyles(styles)(TwoChoice);
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ const withStatefulModel = Component => {
5
+ class Stateful extends React.Component {
6
+ static propTypes = {
7
+ model: PropTypes.object.isRequired,
8
+ onChange: PropTypes.func.isRequired
9
+ };
10
+
11
+ constructor(props) {
12
+ super(props);
13
+ this.state = {
14
+ model: props.model
15
+ };
16
+ }
17
+
18
+ componentWillReceiveProps(props) {
19
+ this.setState({ model: props.model });
20
+ }
21
+
22
+ onChange = model => {
23
+ this.setState({ model }, () => {
24
+ this.props.onChange(this.state.model);
25
+ });
26
+ };
27
+
28
+ render() {
29
+ return <Component model={this.state.model} onChange={this.onChange} />;
30
+ }
31
+ }
32
+
33
+ return Stateful;
34
+ };
35
+
36
+ export default withStatefulModel;
@@ -1 +0,0 @@
1
- []
@@ -1,3 +0,0 @@
1
- import React from 'react';
2
-
3
- export default () => <div>EditableHtml</div>;