Skip to content

Commit 349752f

Browse files
committed
fix(forms): allow numbers as option value on select inputs
1 parent 0ad9ae4 commit 349752f

File tree

5 files changed

+60
-43
lines changed

5 files changed

+60
-43
lines changed

demo/FormExamples.jsx

+7-4
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,13 @@ export function FormExamples() {
170170
</div>
171171

172172
<div className="row">
173-
<div className="col">
173+
<div className="col-4">
174174
<FormGroupSelect name="selectField" label="Select field (list)" options={['A', 'B', 'C']} disabled />
175175
</div>
176-
<div className="col">
176+
<div className="col-4">
177+
<FormGroupSelect name="selectNumberField" label="Select field (number list)" options={[1, 2, 3, 4]} />
178+
</div>
179+
<div className="col-4">
177180
<FormGroupSelect
178181
name="selectField2"
179182
label="Select field 2 (array of objects)"
@@ -185,7 +188,7 @@ export function FormExamples() {
185188
required
186189
/>
187190
</div>
188-
<div className="col">
191+
<div className="col-4">
189192
<FormGroupSelect
190193
name="selectField3"
191194
label="Select field 3 (function)"
@@ -197,7 +200,7 @@ export function FormExamples() {
197200
placeholder="Select one value"
198201
/>
199202
</div>
200-
<div className="col">
203+
<div className="col-4">
201204
<FormGroupSelect
202205
name="selectField4"
203206
label="Select field 4 (objects with trackBy)"

src/forms/FormGroup.jsx

+1
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ FormGroupSelect.propTypes = {
151151
PropTypes.arrayOf(
152152
PropTypes.oneOfType([
153153
PropTypes.string,
154+
PropTypes.number,
154155
PropTypes.shape({
155156
value: PropTypes.any.isRequired,
156157
label: PropTypes.node.isRequired,

src/forms/FormSelect.jsx

+9-27
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
import React, { useCallback } from 'react';
22
import PropTypes from 'prop-types';
33

4-
import { getValueByPath } from '../utils/getters-setters';
5-
import { normalizeOptions, booleanOrFunction } from './helpers/form-helpers';
4+
import {
5+
normalizeOptions,
6+
booleanOrFunction,
7+
serializeValue,
8+
getSelectedOption,
9+
getOptionsType,
10+
} from './helpers/form-helpers';
611
import { useFormControl } from './helpers/useFormControl';
712

813
export function FormSelect({
@@ -32,7 +37,7 @@ export function FormSelect({
3237
<select
3338
{...attrs}
3439
className="custom-select"
35-
onChange={handleOnChange}
40+
onChange={(e) => handleOnChange(e, getOptionsType(normalizedOptions))}
3641
value={getSelectedOption(value, normalizedOptions, trackBy)}
3742
ref={registerRef}
3843
>
@@ -52,6 +57,7 @@ FormSelect.propTypes = {
5257
PropTypes.arrayOf(
5358
PropTypes.oneOfType([
5459
PropTypes.string,
60+
PropTypes.number,
5561
PropTypes.shape({ value: PropTypes.any.isRequired, label: PropTypes.string.isRequired }),
5662
])
5763
),
@@ -68,27 +74,3 @@ function renderOptions(options, trackBy) {
6874
</option>
6975
));
7076
}
71-
72-
function getSelectedOption(value, options, trackBy) {
73-
let selectedValue = value;
74-
75-
if (trackBy) {
76-
const selectedOption = options.find(
77-
(option) => getValueByPath(option.value, trackBy) === getValueByPath(value, trackBy)
78-
);
79-
80-
if (selectedOption) {
81-
selectedValue = selectedOption.value;
82-
}
83-
}
84-
85-
return serializeValue(selectedValue);
86-
}
87-
88-
function serializeValue(value) {
89-
if (typeof value !== 'object') {
90-
return value;
91-
}
92-
93-
return JSON.stringify(value);
94-
}

src/forms/helpers/form-helpers.js

+32-10
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,10 @@ export function normalizeOptions(options, formData, extraData) {
4444
throw new Error('Select Options should be an array');
4545
}
4646

47-
return _options.map((option) => {
48-
if (typeof option !== 'string') {
49-
return option;
50-
}
51-
52-
return {
53-
value: option,
54-
label: option,
55-
};
56-
});
47+
return _options.map((option) => ({
48+
value: option.value || option,
49+
label: option.label || serializeValue(option),
50+
}));
5751
}
5852

5953
export function booleanOrFunction(property, formData) {
@@ -63,3 +57,31 @@ export function booleanOrFunction(property, formData) {
6357

6458
return property(formData);
6559
}
60+
61+
export function serializeValue(value) {
62+
if (typeof value !== 'object') {
63+
return value.toString();
64+
}
65+
66+
return JSON.stringify(value);
67+
}
68+
69+
export function getSelectedOption(value, options, trackBy) {
70+
let selectedValue = value;
71+
72+
if (trackBy) {
73+
const selectedOption = options.find(
74+
(option) => getValueByPath(option.value, trackBy) === getValueByPath(value, trackBy)
75+
);
76+
77+
if (selectedOption) {
78+
selectedValue = selectedOption.value;
79+
}
80+
}
81+
82+
return serializeValue(selectedValue);
83+
}
84+
85+
export function getOptionsType(options) {
86+
return options.length > 0 ? typeof options[0].value : undefined;
87+
}

src/forms/helpers/useFormControl.js

+11-2
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ export function useFormControl(name, type) {
2121
return {
2222
getValue: () => encode(formState.getValue(name), type),
2323
setValue,
24-
handleOnChange: ({ target }) => {
24+
handleOnChange: ({ target }, _type) => {
2525
const value = getTargetValue(target);
26+
const decodedValue = decode(value, type || _type);
2627

27-
setValue(value);
28+
setValue(decodedValue);
2829
},
2930
register,
3031
getFormData: () => formState.getFormData(),
@@ -62,6 +63,14 @@ function encode(value, type) {
6263
return value;
6364
}
6465

66+
function decode(value, type) {
67+
if (type === 'number') {
68+
return parseFloat(value);
69+
}
70+
71+
return value;
72+
}
73+
6574
function getTargetValue(target) {
6675
let value = target.type === 'checkbox' ? target.checked : target.value;
6776

0 commit comments

Comments
 (0)