Skip to content

Commit cefe257

Browse files
committed
Fixes #39056 - Update CounterInput component to PF5
1 parent b1f397d commit cefe257

File tree

8 files changed

+500
-118
lines changed

8 files changed

+500
-118
lines changed

test/integration/compute_profile_js_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class ComputeProfileJSTest < IntegrationTestWithJavascript
5959
assert click_button("Submit")
6060
visit compute_profile_path(selected_profile)
6161
assert click_link(compute_resources(:mycompute).to_s)
62-
assert_equal "2048 MB", find_field('compute_attribute_vm_attrs_memory').value
62+
assert_equal "2048", find_field('compute_attribute_vm_attrs_memory').value
6363
assert_equal "1", find_field('compute_attribute_vm_attrs_cpus').value
6464
end
6565

webpack/assets/javascripts/react_app/components/MemoryAllocationInput/MemoryAllocationInput.js

Lines changed: 21 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
import RCInputNumber from 'rc-input-number';
2-
import React, { useEffect, useState } from 'react';
1+
import React, { useState } from 'react';
32
import PropTypes from 'prop-types';
4-
import { sprintf, translate as __ } from '../../common/I18n';
53
import { MB_FORMAT, MEGABYTES } from './constants';
6-
import '../common/forms/NumericInput.scss';
4+
import CounterInput from '../common/forms/CounterInput/CounterInput';
75
import { noop } from '../../common/helpers';
86

97
const MemoryAllocationInput = ({
@@ -20,53 +18,36 @@ const MemoryAllocationInput = ({
2018
}) => {
2119
const [valueMB, setValueMB] = useState(value / MEGABYTES);
2220

23-
useEffect(() => {
24-
const valueBytes = valueMB * MEGABYTES;
25-
if (maxValue && valueBytes > maxValue) {
26-
setWarning(null);
27-
setError(
28-
sprintf(
29-
__('Specified value is higher than maximum value %s'),
30-
`${maxValue / MEGABYTES} ${MB_FORMAT}`
31-
)
32-
);
33-
} else if (recommendedMaxValue && valueBytes > recommendedMaxValue) {
34-
setError(null);
35-
setWarning(
36-
sprintf(
37-
__('Specified value is higher than recommended maximum %s'),
38-
`${recommendedMaxValue / MEGABYTES} ${MB_FORMAT}`
39-
)
40-
);
41-
} else {
42-
setWarning(null);
43-
}
44-
}, [valueMB, recommendedMaxValue, maxValue, setError, setWarning]);
45-
4621
const handleChange = v => {
47-
if (v === valueMB + 1) {
48-
v = valueMB * 2;
49-
} else if (v === valueMB - 1) {
50-
v = Math.floor(valueMB / 2);
51-
}
5222
setValueMB(v);
5323
onChange(v * MEGABYTES);
5424
};
55-
25+
const handlePlus = v => {
26+
v = valueMB * 2;
27+
handleChange(v);
28+
};
29+
const handleMinus = v => {
30+
v = Math.floor(valueMB / 2);
31+
handleChange(v);
32+
};
5633
return (
5734
<>
58-
<RCInputNumber
35+
<CounterInput
36+
unit={MB_FORMAT}
5937
value={valueMB}
6038
id={id}
61-
formatter={v => `${v} ${MB_FORMAT}`}
62-
parser={str => str.replace(/\D/g, '')}
6339
onChange={handleChange}
40+
handlePlus={handlePlus}
41+
handleMinus={handleMinus}
6442
disabled={disabled}
65-
min={minValue && minValue / MEGABYTES}
66-
step={1}
67-
precision={0}
43+
min={minValue || undefined}
44+
max={maxValue || undefined}
6845
name=""
69-
prefixCls="foreman-numeric-input"
46+
setError={setError}
47+
setWarning={setWarning}
48+
recommendedMaxValue={
49+
recommendedMaxValue ? recommendedMaxValue / MEGABYTES : undefined
50+
}
7051
/>
7152
<input type="hidden" name={name} value={valueMB * MEGABYTES} />
7253
</>
Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,47 @@
11
import React from 'react';
2-
import { mount } from 'enzyme';
3-
import { Provider } from 'react-redux';
2+
import { render, screen, waitFor } from '@testing-library/react';
3+
import '@testing-library/jest-dom';
44
import { MEGABYTES } from '../constants';
55
import MemoryAllocationInput from '../';
66

7-
87
describe('MemoryAllocationInput', () => {
8+
beforeEach(() => {
9+
jest.clearAllMocks();
10+
});
11+
12+
it('calls setWarning when value exceeds recommendedMaxValue', async () => {
13+
const setWarning = jest.fn();
14+
render(
15+
<MemoryAllocationInput
16+
value={11264 * MEGABYTES}
17+
recommendedMaxValue={10240}
18+
setWarning={setWarning}
19+
/>
20+
);
921

10-
it('warning alert', async () => {
11-
const setWarning = jest.fn();
12-
const component = mount(
13-
<MemoryAllocationInput
14-
value={11264*MEGABYTES}
15-
recommendedMaxValue={10240}
16-
setWarning={setWarning}
17-
/>
18-
);
19-
expect(component.find('.foreman-numeric-input-input').prop('value')).toEqual('11264 MB');
20-
expect(setWarning.mock.calls.length).toBe(1);
22+
const input = screen.getByRole('spinbutton');
23+
expect(input).toHaveValue(11264);
24+
25+
await waitFor(() => {
26+
expect(setWarning).toHaveBeenCalledTimes(1);
27+
});
2128
});
2229

23-
it('error alert', async () => {
24-
const setError = jest.fn();
25-
const component = mount(
26-
<MemoryAllocationInput value={21504*MEGABYTES} maxValue={20480*MEGABYTES} setError={setError} />
27-
);
28-
expect(component.find('.foreman-numeric-input-input').prop('value')).toEqual('21504 MB');
29-
expect(setError.mock.calls.length).toBe(1);
30+
it('calls setError when value exceeds maxValue', async () => {
31+
const setError = jest.fn();
32+
render(
33+
<MemoryAllocationInput
34+
value={21504 * MEGABYTES}
35+
maxValue={20480 * MEGABYTES}
36+
setError={setError}
37+
/>
38+
);
39+
40+
const input = screen.getByRole('spinbutton');
41+
expect(input).toHaveValue(21504);
42+
43+
await waitFor(() => {
44+
expect(setError).toHaveBeenCalledTimes(1);
45+
});
3046
});
3147
});
Lines changed: 81 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { useEffect, useState } from 'react';
2-
import RCInputNumber from 'rc-input-number';
2+
import { NumberInput } from '@patternfly/react-core';
33
import PropTypes from 'prop-types';
4-
import { translate as __ } from '../../../../common/I18n';
4+
import { translate as __, sprintf } from '../../../../common/I18n';
55
import { noop } from '../../../../common/helpers';
66

77
const CounterInput = ({
@@ -16,37 +16,87 @@ const CounterInput = ({
1616
onChange,
1717
setError,
1818
setWarning,
19+
widthChars,
20+
unit,
21+
handlePlus,
22+
handleMinus,
1923
}) => {
2024
const [innerValue, setInnerValue] = useState(value);
25+
const [validated, setValidated] = useState('default');
26+
27+
useEffect(() => {
28+
setInnerValue(value);
29+
}, [value]);
30+
2131
useEffect(() => {
2232
if (max && innerValue > max) {
2333
setWarning(null);
24-
setError(__('Specified value is higher than maximum value'));
34+
setError(
35+
sprintf(
36+
__('Specified value is higher than maximum value %s %s'),
37+
max,
38+
unit
39+
)
40+
);
41+
setValidated('error');
2542
} else if (recommendedMaxValue && innerValue > recommendedMaxValue) {
2643
setError(null);
27-
setWarning(__('Specified value is higher than recommended maximum'));
44+
setWarning(
45+
sprintf(
46+
__('Specified value is higher than recommended maximum %s %s'),
47+
recommendedMaxValue,
48+
unit
49+
)
50+
);
51+
setValidated('warning');
2852
} else {
2953
setError(null);
3054
setWarning(null);
55+
setValidated('default');
3156
}
32-
// eslint-disable-next-line react-hooks/exhaustive-deps
33-
}, [recommendedMaxValue, max, innerValue]);
57+
}, [recommendedMaxValue, max, innerValue, setError, setWarning, unit]);
58+
const setValue = newValue => {
59+
setInnerValue(newValue);
60+
onChange(newValue);
61+
};
62+
const handleChange = event => {
63+
const inputValue = event.target.value;
64+
const numValue = inputValue === '' ? '' : parseInt(inputValue, 10) || min;
65+
setValue(numValue);
66+
};
3467

35-
const handleChange = v => {
36-
setInnerValue(v);
37-
onChange(v);
68+
const defaultHandlePlus = () => {
69+
if (handlePlus) {
70+
handlePlus(innerValue);
71+
} else {
72+
const newValue = (innerValue || 0) + (step || 1);
73+
setValue(newValue);
74+
}
75+
};
76+
77+
const defaultHandleMinus = () => {
78+
if (handleMinus) {
79+
handleMinus(innerValue);
80+
} else {
81+
const newValue = (innerValue || 0) - (step || 1);
82+
setValue(newValue);
83+
}
3884
};
3985

4086
return (
41-
<RCInputNumber
42-
value={innerValue}
43-
name={name}
44-
id={id}
87+
<NumberInput
88+
value={innerValue === null || innerValue === undefined ? 0 : innerValue}
89+
inputName={name}
90+
inputProps={{ id, name }}
4591
min={min}
46-
disabled={disabled}
92+
max={max}
93+
isDisabled={disabled}
4794
onChange={handleChange}
48-
step={step}
49-
prefixCls="foreman-numeric-input"
95+
onPlus={defaultHandlePlus}
96+
onMinus={defaultHandleMinus}
97+
validated={validated}
98+
widthChars={widthChars}
99+
unit={unit}
50100
/>
51101
);
52102
};
@@ -60,20 +110,28 @@ CounterInput.propTypes = {
60110
recommendedMaxValue: PropTypes.number,
61111
/** Set the max value of the numeric input */
62112
max: PropTypes.number,
63-
/** Set the min value of the numeric input */
113+
/** Set the min value of the numeric input, undefined will be defaulted to 0 */
64114
min: PropTypes.number,
65115
/** Set whether the numeric input will be disabled or not */
66116
disabled: PropTypes.bool,
67117
/** Set the onChange function of the numeric input */
68118
onChange: PropTypes.func,
69119
/** Set the default value of the numeric input */
70-
value: PropTypes.number,
120+
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
71121
/** Set the step, the counter will increase and decrease by */
72122
step: PropTypes.number,
73123
/** Component passes the validation error to this function */
74124
setError: PropTypes.func,
75125
/** Component passes the validation warning to this function */
76126
setWarning: PropTypes.func,
127+
/** Set the width of the numeric input in characters */
128+
widthChars: PropTypes.number,
129+
/** Set the unit of the numeric input */
130+
unit: PropTypes.string,
131+
/** Override the default handlePlus function */
132+
handlePlus: PropTypes.func,
133+
/** Override the default handleMinus function */
134+
handleMinus: PropTypes.func,
77135
};
78136

79137
CounterInput.defaultProps = {
@@ -83,11 +141,15 @@ CounterInput.defaultProps = {
83141
value: 1,
84142
step: 1,
85143
min: 1,
86-
max: null,
144+
max: undefined,
87145
recommendedMaxValue: null,
88146
onChange: noop,
89147
setError: noop,
90148
setWarning: noop,
149+
widthChars: 10,
150+
unit: '',
151+
handlePlus: null,
152+
handleMinus: null,
91153
};
92154

93155
export default CounterInput;

0 commit comments

Comments
 (0)