Skip to content

Commit 6a01e76

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

File tree

6 files changed

+493
-116
lines changed

6 files changed

+493
-116
lines changed

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: 76 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,82 @@ 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(__('Specified value is higher than maximum value %s'), max)
36+
);
37+
setValidated('error');
2538
} else if (recommendedMaxValue && innerValue > recommendedMaxValue) {
2639
setError(null);
27-
setWarning(__('Specified value is higher than recommended maximum'));
40+
setWarning(
41+
sprintf(
42+
__('Specified value is higher than recommended maximum %s'),
43+
recommendedMaxValue
44+
)
45+
);
46+
setValidated('warning');
2847
} else {
2948
setError(null);
3049
setWarning(null);
50+
setValidated('default');
3151
}
32-
// eslint-disable-next-line react-hooks/exhaustive-deps
33-
}, [recommendedMaxValue, max, innerValue]);
52+
}, [recommendedMaxValue, max, innerValue, setError, setWarning]);
53+
const setValue = newValue => {
54+
setInnerValue(newValue);
55+
onChange(newValue);
56+
};
57+
const handleChange = event => {
58+
const inputValue = event.target.value;
59+
const numValue = inputValue === '' ? '' : parseInt(inputValue, 10) || min;
60+
setValue(numValue);
61+
};
3462

35-
const handleChange = v => {
36-
setInnerValue(v);
37-
onChange(v);
63+
const defaultHandlePlus = () => {
64+
if (handlePlus) {
65+
handlePlus(innerValue);
66+
} else {
67+
const newValue = (innerValue || 0) + (step || 1);
68+
setValue(newValue);
69+
}
70+
};
71+
72+
const defaultHandleMinus = () => {
73+
if (handleMinus) {
74+
handleMinus(innerValue);
75+
} else {
76+
const newValue = (innerValue || 0) - (step || 1);
77+
setValue(newValue);
78+
}
3879
};
3980

4081
return (
41-
<RCInputNumber
42-
value={innerValue}
43-
name={name}
44-
id={id}
82+
<NumberInput
83+
value={innerValue === null || innerValue === undefined ? 0 : innerValue}
84+
inputName={name}
85+
inputProps={{ id, name }}
4586
min={min}
46-
disabled={disabled}
87+
max={max}
88+
isDisabled={disabled}
4789
onChange={handleChange}
48-
step={step}
49-
prefixCls="foreman-numeric-input"
90+
onPlus={defaultHandlePlus}
91+
onMinus={defaultHandleMinus}
92+
validated={validated}
93+
widthChars={widthChars}
94+
unit={unit}
5095
/>
5196
);
5297
};
@@ -60,20 +105,28 @@ CounterInput.propTypes = {
60105
recommendedMaxValue: PropTypes.number,
61106
/** Set the max value of the numeric input */
62107
max: PropTypes.number,
63-
/** Set the min value of the numeric input */
108+
/** Set the min value of the numeric input, undefined will be defaulted to 0 */
64109
min: PropTypes.number,
65110
/** Set whether the numeric input will be disabled or not */
66111
disabled: PropTypes.bool,
67112
/** Set the onChange function of the numeric input */
68113
onChange: PropTypes.func,
69114
/** Set the default value of the numeric input */
70-
value: PropTypes.number,
115+
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
71116
/** Set the step, the counter will increase and decrease by */
72117
step: PropTypes.number,
73118
/** Component passes the validation error to this function */
74119
setError: PropTypes.func,
75120
/** Component passes the validation warning to this function */
76121
setWarning: PropTypes.func,
122+
/** Set the width of the numeric input in characters */
123+
widthChars: PropTypes.number,
124+
/** Set the unit of the numeric input */
125+
unit: PropTypes.string,
126+
/** Override the default handlePlus function */
127+
handlePlus: PropTypes.func,
128+
/** Override the default handleMinus function */
129+
handleMinus: PropTypes.func,
77130
};
78131

79132
CounterInput.defaultProps = {
@@ -83,11 +136,15 @@ CounterInput.defaultProps = {
83136
value: 1,
84137
step: 1,
85138
min: 1,
86-
max: null,
139+
max: undefined,
87140
recommendedMaxValue: null,
88141
onChange: noop,
89142
setError: noop,
90143
setWarning: noop,
144+
widthChars: 10,
145+
unit: '',
146+
handlePlus: null,
147+
handleMinus: null,
91148
};
92149

93150
export default CounterInput;

0 commit comments

Comments
 (0)