Skip to content

Commit 7fdddbb

Browse files
authored
Merge pull request #45 from ericblade/dev
adding more tests, bug fixes
2 parents ea7f8c2 + c77fded commit 7fdddbb

7 files changed

Lines changed: 532 additions & 27 deletions

File tree

examples/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ const ExampleForm = ({
6969
<br />
7070
<input type="checkbox" name="disableSelectionHandling" defaultChecked={disableSelectionHandling} /> Disable selection handling
7171
<br />
72-
{/* <input type="text" name="value" placeholder="Value" defaultValue={value} />Value<br /> */}
72+
<input type="text" name="value" placeholder="Value" defaultValue={value} />Value<br />
7373
<input
7474
type="text"
7575
name="decimalSeparator"
@@ -163,7 +163,7 @@ function FormContainer() {
163163
setDisableSelectionHandling(
164164
document.getElementsByName("disableSelectionHandling")[0].checked
165165
);
166-
// setValue(document.getElementsByName('value')[0].value);
166+
setValue(document.getElementsByName('value')[0].value);
167167
setDecimalSeparator(document.getElementsByName("decimalSeparator")[0].value);
168168
setThousandSeparator(
169169
document.getElementsByName("thousandSeparator")[0].value

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ericblade/react-currency-input",
3-
"version": "1.4.4",
3+
"version": "1.4.5",
44
"description": "React component for inputting currency amounts",
55
"main": "dist/cjs/index.js",
66
"module": "dist/esm/index.js",
@@ -71,4 +71,4 @@
7171
"publishConfig": {
7272
"access": "public"
7373
}
74-
}
74+
}

playwright.config.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ export default defineConfig({
4343
testMatch: '**/mask.spec.ts',
4444
dependencies: ['base tests'],
4545
},
46+
{
47+
name: 'controlled-value tests',
48+
testMatch: '**/controlled-value.spec.ts',
49+
dependencies: ['base tests'],
50+
},
51+
{
52+
name: 'input-type tests',
53+
testMatch: '**/input-type.spec.ts',
54+
dependencies: ['base tests'],
55+
},
4656
{
4757
name: 'chromium',
4858
use: { ...devices['Desktop Chrome'] },

src/index.tsx

Lines changed: 69 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ type CurrencyInputState = {
3636
disableSelectionHandling: boolean,
3737
maskedValue: string,
3838
value: number | string, // TODO: should be string? should also have a separate float field for 'pennies'
39+
previousProps?: Readonly<CurrencyInputProps>, // Track previous props to detect changes
3940
};
4041

4142
type SelectionSnapshot = {
@@ -124,22 +125,23 @@ class CurrencyInput extends React.Component<CurrencyInputProps, CurrencyInputSta
124125
* General function used to cleanup and define the final props used for rendering
125126
* @returns CurrencyInputState
126127
*/
127-
static prepareProps({
128-
onChangeEvent,
129-
value: propValue,
130-
decimalSeparator,
131-
thousandSeparator,
132-
precision,
133-
inputType,
134-
allowNegative,
135-
allowEmpty,
136-
prefix,
137-
suffix,
138-
selectAllOnFocus,
139-
autoFocus,
140-
disableSelectionHandling: propDisableSelectionHandling,
141-
...customProps
142-
}: Readonly<CurrencyInputProps>): CurrencyInputState {
128+
static prepareProps(props: Readonly<CurrencyInputProps>): CurrencyInputState {
129+
const {
130+
onChangeEvent,
131+
value: propValue,
132+
decimalSeparator,
133+
thousandSeparator,
134+
precision,
135+
inputType,
136+
allowNegative,
137+
allowEmpty,
138+
prefix,
139+
suffix,
140+
selectAllOnFocus,
141+
autoFocus,
142+
disableSelectionHandling: propDisableSelectionHandling,
143+
...customProps
144+
} = props;
143145
let initialValue = propValue;
144146
if (initialValue === null) {
145147
initialValue = allowEmpty ? null : '';
@@ -166,7 +168,7 @@ class CurrencyInput extends React.Component<CurrencyInputProps, CurrencyInputSta
166168
);
167169

168170
const disableSelectionHandling = propDisableSelectionHandling || inputType === 'number';
169-
return { maskedValue, value, customProps, disableSelectionHandling };
171+
return { maskedValue, value, customProps, disableSelectionHandling, previousProps: props };
170172
}
171173

172174
/**
@@ -182,11 +184,57 @@ class CurrencyInput extends React.Component<CurrencyInputProps, CurrencyInputSta
182184
* @see https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops
183185
*/
184186
static getDerivedStateFromProps(nextProps: Readonly<CurrencyInputProps>, prevState: Readonly<CurrencyInputState>) {
185-
const props = { ...nextProps };
186-
if (nextProps.value !== prevState.value) {
187-
props.value = prevState.value;
187+
const previousProps = prevState.previousProps || nextProps; // First call uses the initial props snapshot
188+
189+
// Check if the VALUE prop itself changed (parent is controlling the input)
190+
const valueChanged = nextProps.value !== previousProps.value;
191+
192+
// Check if allowNegative changed (affects whether negative values are allowed)
193+
const allowNegativeChanged = nextProps.allowNegative !== previousProps.allowNegative;
194+
195+
// Check if separators or display formatting changed (these require reformatting the current value)
196+
const formatChanged =
197+
nextProps.decimalSeparator !== previousProps.decimalSeparator ||
198+
nextProps.thousandSeparator !== previousProps.thousandSeparator ||
199+
nextProps.precision !== previousProps.precision ||
200+
nextProps.prefix !== previousProps.prefix ||
201+
nextProps.suffix !== previousProps.suffix ||
202+
allowNegativeChanged;
203+
204+
if (valueChanged) {
205+
// Parent changed the value prop - use the new value
206+
const newState = CurrencyInput.prepareProps(nextProps);
207+
return { ...newState, previousProps: nextProps };
188208
}
189-
return CurrencyInput.prepareProps(props);
209+
210+
if (formatChanged) {
211+
// Display formatting or allowNegative changed - reformat the current value
212+
// First, determine the value to use. Start with the current state value,
213+
// which may be adjusted below if allowNegative was disabled and value is negative.
214+
let valueToFormat = prevState.value;
215+
216+
if (allowNegativeChanged && !nextProps.allowNegative) {
217+
// allowNegative was disabled - if current value is negative, make it positive
218+
const parsedValue = typeof prevState.value === 'number'
219+
? prevState.value
220+
: prevState.value === null
221+
? 0
222+
: CurrencyInput.stringValueToFloat(String(prevState.value), nextProps.thousandSeparator, nextProps.decimalSeparator);
223+
224+
if (parsedValue < 0) {
225+
valueToFormat = Math.abs(parsedValue);
226+
}
227+
}
228+
229+
// Reformat with new formatting and potentially adjusted value
230+
const propsWithCurrentValue = { ...nextProps, value: valueToFormat };
231+
const newState = CurrencyInput.prepareProps(propsWithCurrentValue);
232+
return { ...newState, previousProps: nextProps };
233+
}
234+
235+
// Other props changed but value and display formatting didn't
236+
// Just update the previousProps reference and preserve current state
237+
return { ...prevState, previousProps: nextProps };
190238
}
191239

192240
/**

0 commit comments

Comments
 (0)