@pie-lib/config-ui 11.9.25-next.0 → 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.
- package/CHANGELOG.json +8 -1653
- package/CHANGELOG.md +131 -39
- package/NEXT.CHANGELOG.json +1 -0
- package/lib/alert-dialog.js +38 -7
- package/lib/alert-dialog.js.map +1 -1
- package/lib/checkbox.js +6 -1
- package/lib/checkbox.js.map +1 -1
- package/lib/choice-configuration/index.js +19 -8
- package/lib/choice-configuration/index.js.map +1 -1
- package/lib/feedback-config/feedback-selector.js +0 -0
- package/lib/inputs.js +8 -2
- package/lib/inputs.js.map +1 -1
- package/lib/layout/config-layout.js +27 -10
- package/lib/layout/config-layout.js.map +1 -1
- package/lib/number-text-field-custom.js +134 -43
- package/lib/number-text-field-custom.js.map +1 -1
- package/lib/number-text-field.js +17 -18
- package/lib/number-text-field.js.map +1 -1
- package/lib/radio-with-label.js +9 -1
- package/lib/radio-with-label.js.map +1 -1
- package/lib/settings/index.js +3 -1
- package/lib/settings/index.js.map +1 -1
- package/lib/settings/panel.js +7 -4
- package/lib/settings/panel.js.map +1 -1
- package/lib/settings/settings-radio-label.js +9 -1
- package/lib/settings/settings-radio-label.js.map +1 -1
- package/lib/settings/toggle.js +18 -0
- package/lib/settings/toggle.js.map +1 -1
- package/package.json +8 -5
- package/src/__tests__/__snapshots__/langs.test.jsx.snap +32 -0
- package/src/__tests__/__snapshots__/settings-panel.test.js.snap +115 -0
- package/src/__tests__/__snapshots__/two-choice.test.js.snap +171 -0
- package/src/__tests__/choice-utils.test.js +12 -0
- package/src/__tests__/langs.test.jsx +37 -0
- package/src/__tests__/number-text-field.test.jsx +148 -0
- package/src/__tests__/settings-panel.test.js +204 -0
- package/src/__tests__/two-choice.test.js +24 -0
- package/src/alert-dialog.jsx +27 -7
- package/src/checkbox.jsx +8 -1
- package/src/choice-configuration/__tests__/__snapshots__/feedback-menu.test.jsx.snap +51 -0
- package/src/choice-configuration/__tests__/__snapshots__/index.test.jsx.snap +519 -0
- package/src/choice-configuration/__tests__/feedback-menu.test.jsx +10 -0
- package/src/choice-configuration/__tests__/index.test.jsx +92 -0
- package/src/choice-configuration/index.jsx +14 -3
- package/src/feedback-config/__tests__/__snapshots__/feedback-config.test.jsx.snap +27 -0
- package/src/feedback-config/__tests__/__snapshots__/feedback-selector.test.jsx.snap +38 -0
- package/src/feedback-config/__tests__/feedback-config.test.jsx +71 -0
- package/src/feedback-config/__tests__/feedback-selector.test.jsx +60 -0
- package/src/feedback-config/feedback-selector.jsx +0 -0
- package/src/inputs.jsx +9 -2
- package/src/layout/__tests__/__snapshots__/config.layout.test.jsx.snap +59 -0
- package/src/layout/__tests__/config.layout.test.jsx +42 -0
- package/src/layout/__tests__/layout-content.test.jsx +3 -0
- package/src/layout/config-layout.jsx +16 -8
- package/src/number-text-field-custom.jsx +86 -28
- package/src/number-text-field.jsx +6 -5
- package/src/radio-with-label.jsx +6 -2
- package/src/settings/index.js +2 -1
- package/src/settings/panel.jsx +5 -2
- package/src/settings/settings-radio-label.jsx +6 -2
- package/src/settings/toggle.jsx +20 -2
- package/src/tags-input/__tests__/__snapshots__/index.test.jsx.snap +170 -0
- package/src/tags-input/__tests__/index.test.jsx +62 -0
- 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
|
+
});
|
|
@@ -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
|
-
|
|
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:
|
|
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
|
|
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=
|
|
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.
|
|
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 {
|
|
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
|
|
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 =
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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=
|
|
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(
|
|
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
|
|