@pie-lib/config-ui 11.9.24 → 11.9.25-next.1595

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 (64) hide show
  1. package/CHANGELOG.json +8 -1653
  2. package/CHANGELOG.md +131 -39
  3. package/NEXT.CHANGELOG.json +1 -0
  4. package/lib/alert-dialog.js +38 -7
  5. package/lib/alert-dialog.js.map +1 -1
  6. package/lib/checkbox.js +6 -1
  7. package/lib/checkbox.js.map +1 -1
  8. package/lib/choice-configuration/index.js +19 -8
  9. package/lib/choice-configuration/index.js.map +1 -1
  10. package/lib/feedback-config/feedback-selector.js +0 -0
  11. package/lib/inputs.js +8 -2
  12. package/lib/inputs.js.map +1 -1
  13. package/lib/layout/config-layout.js +27 -10
  14. package/lib/layout/config-layout.js.map +1 -1
  15. package/lib/number-text-field-custom.js +134 -43
  16. package/lib/number-text-field-custom.js.map +1 -1
  17. package/lib/number-text-field.js +17 -18
  18. package/lib/number-text-field.js.map +1 -1
  19. package/lib/radio-with-label.js +9 -1
  20. package/lib/radio-with-label.js.map +1 -1
  21. package/lib/settings/index.js +3 -1
  22. package/lib/settings/index.js.map +1 -1
  23. package/lib/settings/panel.js +7 -4
  24. package/lib/settings/panel.js.map +1 -1
  25. package/lib/settings/settings-radio-label.js +9 -1
  26. package/lib/settings/settings-radio-label.js.map +1 -1
  27. package/lib/settings/toggle.js +18 -0
  28. package/lib/settings/toggle.js.map +1 -1
  29. package/package.json +8 -5
  30. package/src/__tests__/__snapshots__/langs.test.jsx.snap +32 -0
  31. package/src/__tests__/__snapshots__/settings-panel.test.js.snap +115 -0
  32. package/src/__tests__/__snapshots__/two-choice.test.js.snap +171 -0
  33. package/src/__tests__/choice-utils.test.js +12 -0
  34. package/src/__tests__/langs.test.jsx +37 -0
  35. package/src/__tests__/number-text-field.test.jsx +148 -0
  36. package/src/__tests__/settings-panel.test.js +204 -0
  37. package/src/__tests__/two-choice.test.js +24 -0
  38. package/src/alert-dialog.jsx +27 -7
  39. package/src/checkbox.jsx +8 -1
  40. package/src/choice-configuration/__tests__/__snapshots__/feedback-menu.test.jsx.snap +51 -0
  41. package/src/choice-configuration/__tests__/__snapshots__/index.test.jsx.snap +519 -0
  42. package/src/choice-configuration/__tests__/feedback-menu.test.jsx +10 -0
  43. package/src/choice-configuration/__tests__/index.test.jsx +92 -0
  44. package/src/choice-configuration/index.jsx +14 -3
  45. package/src/feedback-config/__tests__/__snapshots__/feedback-config.test.jsx.snap +27 -0
  46. package/src/feedback-config/__tests__/__snapshots__/feedback-selector.test.jsx.snap +38 -0
  47. package/src/feedback-config/__tests__/feedback-config.test.jsx +71 -0
  48. package/src/feedback-config/__tests__/feedback-selector.test.jsx +60 -0
  49. package/src/feedback-config/feedback-selector.jsx +0 -0
  50. package/src/inputs.jsx +9 -2
  51. package/src/layout/__tests__/__snapshots__/config.layout.test.jsx.snap +59 -0
  52. package/src/layout/__tests__/config.layout.test.jsx +42 -0
  53. package/src/layout/__tests__/layout-content.test.jsx +3 -0
  54. package/src/layout/config-layout.jsx +16 -8
  55. package/src/number-text-field-custom.jsx +86 -28
  56. package/src/number-text-field.jsx +6 -5
  57. package/src/radio-with-label.jsx +6 -2
  58. package/src/settings/index.js +2 -1
  59. package/src/settings/panel.jsx +5 -2
  60. package/src/settings/settings-radio-label.jsx +6 -2
  61. package/src/settings/toggle.jsx +20 -2
  62. package/src/tags-input/__tests__/__snapshots__/index.test.jsx.snap +170 -0
  63. package/src/tags-input/__tests__/index.test.jsx +62 -0
  64. package/README.md +0 -12
@@ -0,0 +1,27 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`FeedbackConfig render Feedback Config Component snapshot matches the snapshot 1`] = `
4
+ <div>
5
+ <WithStyles(ExpansionPanel)>
6
+ <WithStyles(ExpansionPanelSummary)
7
+ expandIcon={<pure(ExpandMoreIcon) />}
8
+ >
9
+ <WithStyles(Typography)>
10
+ Feedback
11
+ </WithStyles(Typography)>
12
+ </WithStyles(ExpansionPanelSummary)>
13
+ <WithStyles(ExpansionPanelDetails)>
14
+ <div>
15
+ <WithStyles(FeedbackSelector)
16
+ label="If correct, show"
17
+ onChange={[Function]}
18
+ />
19
+ <WithStyles(FeedbackSelector)
20
+ label="If incorrect, show"
21
+ onChange={[Function]}
22
+ />
23
+ </div>
24
+ </WithStyles(ExpansionPanelDetails)>
25
+ </WithStyles(ExpansionPanel)>
26
+ </div>
27
+ `;
@@ -0,0 +1,38 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`feedback-selector snapshot renders 1`] = `
4
+ <div>
5
+ <WithStyles(RawInputContainer)
6
+ extraClasses={
7
+ Object {
8
+ "label": undefined,
9
+ }
10
+ }
11
+ label="foo"
12
+ >
13
+ <WithStyles(Group)
14
+ feedbackLabels={
15
+ Object {
16
+ "custom": "Customized Feedback",
17
+ "default": "Simple Feedback",
18
+ "none": "No Feedback",
19
+ }
20
+ }
21
+ keys={
22
+ Array [
23
+ "default",
24
+ "none",
25
+ "custom",
26
+ ]
27
+ }
28
+ label="foo"
29
+ onChange={[Function]}
30
+ value="default"
31
+ />
32
+ </WithStyles(RawInputContainer)>
33
+ <div>
34
+
35
+ hi
36
+ </div>
37
+ </div>
38
+ `;
@@ -0,0 +1,71 @@
1
+ import React, { PropTypes } from 'react';
2
+ import Enzyme, { shallow, mount } from 'enzyme';
3
+ import { FeedbackConfig } from '../index';
4
+ import FeedbackSelector from '../feedback-selector';
5
+ import Adapter from 'enzyme-adapter-react-16';
6
+
7
+ Enzyme.configure({ adapter: new Adapter() });
8
+
9
+ describe('FeedbackConfig', () => {
10
+ describe('render', () => {
11
+ let component, selectors;
12
+ let feedback = {
13
+ correctFeedback: undefined,
14
+ correctFeedbackType: 'default',
15
+ incorrectFeedback: undefined,
16
+ incorrectFeedbackType: 'default',
17
+ };
18
+
19
+ let defaults = {
20
+ correct: 'Correct',
21
+ incorrect: 'Incorrect',
22
+ partial: 'Nearly',
23
+ };
24
+
25
+ describe('Feedback Config Component', () => {
26
+ it('should exist', () => {
27
+ component = shallow(
28
+ <FeedbackConfig feedback={feedback} defaults={defaults} onChange={jest.fn()} classes={{}} />,
29
+ );
30
+
31
+ selectors = component.find(FeedbackSelector);
32
+
33
+ expect(selectors.length).toEqual(3);
34
+ });
35
+
36
+ describe('props', () => {
37
+ it('should not render optionally correct if optional is not needed', () => {
38
+ component = shallow(
39
+ <FeedbackConfig
40
+ allowPartial={false}
41
+ feedback={feedback}
42
+ defaults={defaults}
43
+ onChange={jest.fn()}
44
+ classes={{}}
45
+ />,
46
+ );
47
+
48
+ selectors = component.find(FeedbackSelector);
49
+
50
+ expect(selectors.length).toEqual(2);
51
+ });
52
+ });
53
+
54
+ describe('snapshot', () => {
55
+ it('matches the snapshot', () => {
56
+ component = shallow(
57
+ <FeedbackConfig
58
+ allowPartial={false}
59
+ feedback={feedback}
60
+ defaults={defaults}
61
+ onChange={jest.fn()}
62
+ classes={{}}
63
+ />,
64
+ );
65
+
66
+ expect(component).toMatchSnapshot();
67
+ });
68
+ });
69
+ });
70
+ });
71
+ });
@@ -0,0 +1,60 @@
1
+ import { shallow } from 'enzyme';
2
+ import React from 'react';
3
+ import { FeedbackSelector } from '../feedback-selector';
4
+
5
+ describe('feedback-selector', () => {
6
+ let w, onChange;
7
+
8
+ const getWrapper = () => {
9
+ return shallow(
10
+ <FeedbackSelector
11
+ classes={{}}
12
+ label={'foo'}
13
+ onChange={onChange}
14
+ feedback={{
15
+ type: 'default',
16
+ default: 'hi',
17
+ }}
18
+ />,
19
+ );
20
+ };
21
+
22
+ beforeEach(() => {
23
+ onChange = jest.fn();
24
+ w = getWrapper();
25
+ });
26
+
27
+ describe('snapshot', () => {
28
+ it('renders', () => {
29
+ expect(w).toMatchSnapshot();
30
+ });
31
+ });
32
+
33
+ describe('logic', () => {
34
+ describe('changeCustom', () => {
35
+ it('calls onChange with text', () => {
36
+ w.instance().changeCustom('bar');
37
+ expect(onChange).toBeCalledWith({
38
+ type: 'custom',
39
+ custom: 'bar',
40
+ default: 'hi',
41
+ });
42
+ });
43
+ });
44
+
45
+ describe('changeType', () => {
46
+ it('calls onChange with default', () => {
47
+ w.instance().changeType('default');
48
+ expect(onChange).toBeCalledWith({ type: 'default', default: 'hi' });
49
+ });
50
+ it('calls onChange with custom', () => {
51
+ w.instance().changeType('custom');
52
+ expect(onChange).toBeCalledWith({ type: 'custom', default: 'hi' });
53
+ });
54
+ it('calls onChange with none', () => {
55
+ w.instance().changeType('none');
56
+ expect(onChange).toBeCalledWith({ type: 'none', default: 'hi' });
57
+ });
58
+ });
59
+ });
60
+ });
File without changes
package/src/inputs.jsx CHANGED
@@ -6,6 +6,7 @@ import React from 'react';
6
6
  import Switch from '@material-ui/core/Switch';
7
7
  import { withStyles } from '@material-ui/core/styles';
8
8
  import classNames from 'classnames';
9
+ import { color } from '@pie-lib/render-ui';
9
10
 
10
11
  const InputTypes = {
11
12
  classes: PropTypes.object.isRequired,
@@ -40,7 +41,7 @@ const RawInputCheckbox = (props) => {
40
41
  return (
41
42
  <InputContainer className={className} label={label}>
42
43
  <Checkbox
43
- className={classNames(classes.checkboxRoot, error && classes.error)}
44
+ className={classNames(classes.checkboxRoot, classes.customColor, error && classes.error)}
44
45
  disabled={disabled}
45
46
  checked={checked}
46
47
  onChange={onChange}
@@ -58,7 +59,7 @@ const RawInputRadio = (props) => {
58
59
  return (
59
60
  <InputContainer className={className} label={label}>
60
61
  <Radio
61
- className={classNames(classes.radioRoot, error && classes.error)}
62
+ className={classNames(classes.radioRoot, classes.customColor, error && classes.error)}
62
63
  disabled={disabled}
63
64
  checked={checked}
64
65
  onChange={onChange}
@@ -77,6 +78,9 @@ const InputCheckbox = withStyles((theme) => ({
77
78
  error: {
78
79
  color: theme.palette.error.main,
79
80
  },
81
+ customColor: {
82
+ color: `${color.tertiary()} !important`,
83
+ },
80
84
  }))(RawInputCheckbox);
81
85
 
82
86
  const InputRadio = withStyles((theme) => ({
@@ -86,6 +90,9 @@ const InputRadio = withStyles((theme) => ({
86
90
  error: {
87
91
  color: theme.palette.error.main,
88
92
  },
93
+ customColor: {
94
+ color: `${color.tertiary()} !important`,
95
+ },
89
96
  }))(RawInputRadio);
90
97
 
91
98
  export { InputSwitch, InputCheckbox, InputRadio };
@@ -0,0 +1,59 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`layout - snapshot renders correctly with a side panel 1`] = `
4
+ <WithContentRect
5
+ classes={
6
+ Object {
7
+ "extraCSSRules": "WithContentRect-extraCSSRules-1",
8
+ }
9
+ }
10
+ settings={
11
+ <div>
12
+ <div>
13
+ Foo
14
+ </div>
15
+ <div>
16
+ Bar
17
+ </div>
18
+ </div>
19
+ }
20
+ >
21
+ <div>
22
+ <div>
23
+ Foo
24
+ </div>
25
+ <div>
26
+ Bar
27
+ </div>
28
+ </div>
29
+ </WithContentRect>
30
+ `;
31
+
32
+ exports[`layout - snapshot renders correctly without a side panel 1`] = `
33
+ <WithContentRect
34
+ classes={
35
+ Object {
36
+ "extraCSSRules": "WithContentRect-extraCSSRules-1",
37
+ }
38
+ }
39
+ settings={
40
+ <div>
41
+ <div>
42
+ Foo
43
+ </div>
44
+ <div>
45
+ Bar
46
+ </div>
47
+ </div>
48
+ }
49
+ >
50
+ <div>
51
+ <div>
52
+ Foo
53
+ </div>
54
+ <div>
55
+ Bar
56
+ </div>
57
+ </div>
58
+ </WithContentRect>
59
+ `;
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+ import ConfigLayout from '../config-layout';
3
+ import { shallow } from 'enzyme';
4
+
5
+ describe('layout - snapshot', () => {
6
+ it('renders correctly with a side panel', () => {
7
+ const tree = shallow(
8
+ <ConfigLayout
9
+ settings={
10
+ <div>
11
+ <div key={0}>Foo</div>
12
+ <div key={1}>Bar</div>
13
+ </div>
14
+ }
15
+ >
16
+ <div>
17
+ <div>Foo</div>
18
+ <div>Bar</div>
19
+ </div>
20
+ </ConfigLayout>,
21
+ );
22
+ expect(tree).toMatchSnapshot();
23
+ });
24
+ it('renders correctly without a side panel', () => {
25
+ const tree = shallow(
26
+ <ConfigLayout
27
+ settings={
28
+ <div>
29
+ <div key={0}>Foo</div>
30
+ <div key={1}>Bar</div>
31
+ </div>
32
+ }
33
+ >
34
+ <div>
35
+ <div>Foo</div>
36
+ <div>Bar</div>
37
+ </div>
38
+ </ConfigLayout>,
39
+ );
40
+ expect(tree).toMatchSnapshot();
41
+ });
42
+ });
@@ -0,0 +1,3 @@
1
+ describe('layout-content', () => {
2
+ it.todo('add tests!');
3
+ });
@@ -1,11 +1,18 @@
1
1
  import React from 'react';
2
2
  import Measure from 'react-measure';
3
3
  import { withContentRect } from 'react-measure';
4
+ import { withStyles } from '@material-ui/core/styles';
4
5
  import PropTypes from 'prop-types';
6
+ import classNames from 'classnames';
5
7
  import LayoutContents from './layout-contents';
6
8
  import SettingsBox from './settings-box';
9
+ import { AppendCSSRules } from '@pie-lib/render-ui';
7
10
 
8
- class MeasuredConfigLayout extends React.Component {
11
+ const styles = {
12
+ extraCSSRules: {},
13
+ };
14
+
15
+ class MeasuredConfigLayout extends AppendCSSRules {
9
16
  static propTypes = {
10
17
  children: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.element), PropTypes.element]),
11
18
  className: PropTypes.string,
@@ -17,13 +24,13 @@ class MeasuredConfigLayout extends React.Component {
17
24
  };
18
25
 
19
26
  static defaultProps = {
20
- sidePanelMinWidth: 950,
27
+ sidePanelMinWidth: 1135,
21
28
  hideSettings: false,
22
29
  dimensions: {},
23
30
  };
24
31
 
25
- constructor(props) {
26
- super(props);
32
+ constructor(...props) {
33
+ super(...props);
27
34
  this.state = { layoutMode: undefined };
28
35
  }
29
36
 
@@ -33,7 +40,7 @@ class MeasuredConfigLayout extends React.Component {
33
40
  const { maxWidth } = dimensions || {};
34
41
 
35
42
  const layoutMode =
36
- bounds.width >= sidePanelMinWidth && (maxWidth ? maxWidth >= sidePanelMinWidth : true) ? 'inline' : 'tabbed';
43
+ bounds.width > sidePanelMinWidth && (maxWidth ? maxWidth > sidePanelMinWidth : true) ? 'inline' : 'tabbed';
37
44
 
38
45
  this.setState({ layoutMode });
39
46
  };
@@ -42,15 +49,16 @@ class MeasuredConfigLayout extends React.Component {
42
49
  return (
43
50
  <Measure bounds onResize={this.onResize}>
44
51
  {({ measureRef }) => {
45
- const { children, settings, hideSettings, dimensions } = this.props;
52
+ const { children, settings, hideSettings, dimensions, classes } = this.props;
46
53
  const { layoutMode } = this.state;
47
54
 
48
55
  const settingsPanel =
49
56
  layoutMode === 'inline' ? <SettingsBox className="settings-box">{settings}</SettingsBox> : settings;
50
57
  const secondaryContent = hideSettings ? null : settingsPanel;
58
+ const finalClass = classNames('main-container', classes.extraCSSRules);
51
59
 
52
60
  return (
53
- <div ref={measureRef} className="main-container">
61
+ <div ref={measureRef} className={finalClass}>
54
62
  <LayoutContents mode={layoutMode} secondary={secondaryContent} dimensions={dimensions}>
55
63
  {children}
56
64
  </LayoutContents>
@@ -62,6 +70,6 @@ class MeasuredConfigLayout extends React.Component {
62
70
  }
63
71
  }
64
72
 
65
- const ConfigLayout = withContentRect('bounds')(MeasuredConfigLayout);
73
+ const ConfigLayout = withStyles(styles)(withContentRect('bounds')(MeasuredConfigLayout));
66
74
 
67
75
  export default ConfigLayout;
@@ -8,6 +8,7 @@ import IconButton from '@material-ui/core/IconButton';
8
8
  import InputAdornment from '@material-ui/core/InputAdornment';
9
9
  import Remove from '@material-ui/icons/Remove';
10
10
  import Add from '@material-ui/icons/Add';
11
+ import * as math from 'mathjs';
11
12
 
12
13
  const styles = () => ({
13
14
  input: {
@@ -53,7 +54,7 @@ export class NumberTextFieldCustom extends React.Component {
53
54
  helperText: PropTypes.string,
54
55
  onChange: PropTypes.func.isRequired,
55
56
  onlyIntegersAllowed: PropTypes.bool,
56
- value: PropTypes.number,
57
+ value: PropTypes.any,
57
58
  min: PropTypes.number,
58
59
  max: PropTypes.number,
59
60
  step: PropTypes.number,
@@ -61,6 +62,7 @@ export class NumberTextFieldCustom extends React.Component {
61
62
  disableUnderline: PropTypes.bool,
62
63
  textAlign: PropTypes.string,
63
64
  variant: PropTypes.string,
65
+ type: PropTypes.string,
64
66
  };
65
67
 
66
68
  static defaultProps = {
@@ -89,13 +91,13 @@ export class NumberTextFieldCustom extends React.Component {
89
91
  }
90
92
 
91
93
  UNSAFE_componentWillReceiveProps(props) {
92
- const { value, currentIndex } = this.normalizeValueAndIndex(props.customValues, props.value);
94
+ const { value, currentIndex } = this.normalizeValueAndIndex(props.customValues, props.value, props.min, props.max);
93
95
 
94
96
  this.setState({ value, currentIndex });
95
97
  }
96
98
 
97
- clamp(value) {
98
- const { min, max, customValues } = this.props;
99
+ clamp(value, min = this.props.min, max = this.props.max) {
100
+ const { customValues } = this.props;
99
101
 
100
102
  if ((customValues || []).length > 0) {
101
103
  return value;
@@ -116,12 +118,14 @@ export class NumberTextFieldCustom extends React.Component {
116
118
  return value;
117
119
  }
118
120
 
119
- normalizeValueAndIndex = (customValues, number) => {
120
- const value = this.clamp(number);
121
+ normalizeValueAndIndex = (customValues, number, min, max) => {
122
+ const { type } = this.props;
123
+ const value = this.clamp(number, min, max);
121
124
  const currentIndex = (customValues || []).findIndex((val) => val === value);
122
125
 
123
126
  if ((customValues || []).length > 0 && currentIndex === -1) {
124
- const closestValue = this.getClosestValue(customValues, value);
127
+ const closestValue =
128
+ type === 'text' ? this.getClosestFractionValue(customValues, value) : this.getClosestValue(customValues, value);
125
129
 
126
130
  return { value: closestValue.value, currentIndex: closestValue.index };
127
131
  }
@@ -136,50 +140,92 @@ export class NumberTextFieldCustom extends React.Component {
136
140
  { value: customValues[0], index: 0 },
137
141
  );
138
142
 
139
- onBlur = (event) => {
140
- const { customValues, onlyIntegersAllowed } = this.props;
141
- const { value } = event.target;
142
- const rawNumber = onlyIntegersAllowed ? parseInt(value) : parseFloat(value);
143
+ getClosestFractionValue = (customValues, number) =>
144
+ customValues.reduce(
145
+ (closest, value, index) =>
146
+ Math.abs(math.number(math.fraction(value)) - math.number(math.fraction(number))) <
147
+ Math.abs(math.number(math.fraction(closest.value)) - math.number(math.fraction(number)))
148
+ ? { value, index }
149
+ : closest,
150
+ { value: customValues[0], index: 0 },
151
+ );
143
152
 
144
- const { value: number, currentIndex } = this.normalizeValueAndIndex(customValues, rawNumber);
153
+ getValidFraction = (value) => {
154
+ if (this.isPositiveInteger(value.trim())) {
155
+ return value.trim();
156
+ }
157
+ if (value.trim() === '' || value.trim().split('/').length !== 2) {
158
+ return false;
159
+ }
160
+ let [numerator, denominator] = value.trim().split('/');
161
+ if (isNaN(numerator) || isNaN(denominator)) {
162
+ return false;
163
+ }
164
+ numerator = parseFloat(numerator);
165
+ denominator = parseFloat(denominator);
166
+ if (!Number.isInteger(numerator) || !Number.isInteger(denominator)) {
167
+ return false;
168
+ }
169
+ if (numerator < 0 || denominator < 1) {
170
+ return false;
171
+ }
172
+ return numerator + '/' + denominator;
173
+ };
174
+
175
+ isPositiveInteger = (n) => {
176
+ return n >>> 0 === parseFloat(n);
177
+ };
145
178
 
146
- if (number !== this.state.value) {
147
- this.setState(
148
- {
149
- value: number.toString(),
150
- currentIndex,
151
- },
152
- () => this.props.onChange(event, number),
153
- );
179
+ onBlur = (event) => {
180
+ const { customValues, onlyIntegersAllowed, type } = this.props;
181
+ let { value } = event.target;
182
+ if (type === 'text') {
183
+ let tempValue = this.getValidFraction(value);
184
+ if (tempValue) {
185
+ value = tempValue;
186
+ } else {
187
+ value = this.props.value;
188
+ }
189
+ }
190
+ let rawNumber = onlyIntegersAllowed ? Math.round(parseFloat(value)) : parseFloat(value);
191
+ if (type === 'text') {
192
+ rawNumber = value.trim();
154
193
  }
194
+ const { value: number, currentIndex } = this.normalizeValueAndIndex(customValues, rawNumber);
195
+ this.setState(
196
+ {
197
+ value: number.toString(),
198
+ currentIndex,
199
+ },
200
+ () => this.props.onChange(event, number),
201
+ );
155
202
  };
156
203
 
157
204
  onChange(event) {
205
+ const { type } = this.props;
158
206
  const { value } = event.target;
159
-
207
+ if (type !== 'text' && typeof value === 'string' && value.trim() === '') {
208
+ return;
209
+ }
160
210
  this.setState({ value });
161
211
  }
162
212
 
163
213
  changeValue(event, sign = 1, shouldUpdate = false) {
164
214
  event.preventDefault();
165
-
166
215
  const { customValues, step, onlyIntegersAllowed, onChange } = this.props;
167
216
  const { currentIndex, value } = this.state;
168
217
  const updatedIndex = currentIndex + sign * 1;
169
218
  let number;
170
-
171
219
  if (customValues.length > 0) {
172
220
  if (updatedIndex < 0 || updatedIndex >= customValues.length) {
173
221
  return;
174
222
  }
175
-
176
223
  number = customValues[updatedIndex];
177
224
  } else {
178
225
  const rawNumber = onlyIntegersAllowed ? parseInt(value) : parseFloat(value);
179
226
  const updatedValue = (rawNumber * 10000 + step * sign * 10000) / 10000;
180
227
  number = this.clamp(updatedValue);
181
228
  }
182
-
183
229
  this.setState(
184
230
  {
185
231
  value: number.toString(),
@@ -202,14 +248,26 @@ export class NumberTextFieldCustom extends React.Component {
202
248
  error,
203
249
  min,
204
250
  max,
251
+ customValues,
205
252
  inputClassName,
206
253
  disableUnderline,
207
254
  helperText,
208
255
  variant,
209
256
  textAlign,
257
+ type = 'number',
210
258
  } = this.props;
211
259
  const { value } = this.state;
212
260
  const names = classNames(className, classes.input);
261
+ //Logic to disable the increment and decrement buttons
262
+ let disabledStart = false;
263
+ let disabledEnd = false;
264
+ if (customValues.length > 0) {
265
+ disabledStart = value === customValues[0];
266
+ disabledEnd = value === customValues[customValues.length - 1];
267
+ } else if (isFinite(min) && isFinite(max)) {
268
+ disabledStart = value === min;
269
+ disabledEnd = value === max;
270
+ }
213
271
 
214
272
  return (
215
273
  <TextField
@@ -238,7 +296,7 @@ export class NumberTextFieldCustom extends React.Component {
238
296
  }
239
297
  }}
240
298
  title={''}
241
- type="number"
299
+ type={type}
242
300
  className={names}
243
301
  InputProps={{
244
302
  className: inputClassName,
@@ -247,7 +305,7 @@ export class NumberTextFieldCustom extends React.Component {
247
305
  <InputAdornment position="start">
248
306
  <IconButton
249
307
  className={classes.iconButton}
250
- disabled={disabled}
308
+ disabled={disabled ? disabled : disabledStart}
251
309
  onClick={(e) => this.changeValue(e, -1, true)}
252
310
  >
253
311
  <Remove fontSize="small" />
@@ -258,7 +316,7 @@ export class NumberTextFieldCustom extends React.Component {
258
316
  <InputAdornment position="end">
259
317
  <IconButton
260
318
  className={classes.iconButton}
261
- disabled={disabled}
319
+ disabled={disabled ? disabled : disabledEnd}
262
320
  onClick={(e) => this.changeValue(e, 1, true)}
263
321
  >
264
322
  <Add fontSize="small" />
@@ -68,23 +68,24 @@ export class NumberTextField extends React.Component {
68
68
  }
69
69
 
70
70
  UNSAFE_componentWillReceiveProps(props) {
71
- const value = this.clamp(props.value);
71
+ const value = this.clamp(props.value, props.min, props.max);
72
+
72
73
  this.setState({ value });
73
74
  }
74
75
 
75
- clamp(value) {
76
+ clamp(value, min = this.props.min, max = this.props.max) {
76
77
  if (!isFinite(value)) {
77
- return fallbackNumber(this.props.min, this.props.max);
78
+ return fallbackNumber(min, max);
78
79
  }
79
80
 
80
- const { min, max } = this.props;
81
-
82
81
  if (isFinite(max)) {
83
82
  value = Math.min(value, max);
84
83
  }
84
+
85
85
  if (isFinite(min)) {
86
86
  value = Math.max(value, min);
87
87
  }
88
+
88
89
  return value;
89
90
  }
90
91