Repository: vazco/uniforms
Pull request #564 (2019-07-29 16:57:41)

Made NumField adaptive to React version
A long time ago, back in #167, I had implemented special handling of numeric inputs in `NumField` because it behaved weirdly in most browsers (it was crazy in Safari). It got fixed by React in version 16, but the mechanism stayed as it was, because of backward compatibility. Now, it stung back in #558. I've decided to make it logic conditional, to enable it only in React versions which needs them.

codecov[bot]: # [Codecov](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=h1) Report
> Merging [#564](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=desc) into [master](https://codecov.io/gh/vazco/uniforms/commit/c63302c256bf0173a8bdb14ec7770e6c402c68d4?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/vazco/uniforms/pull/564/graphs/tree.svg?width=650&token=PEY7QrNpKu&height=150&src=pr)](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=tree)

```diff
@@          Coverage Diff          @@
##           master   #564   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files         162    162           
  Lines        1574   1529   -45     
=====================================
- Hits         1574   1529   -45
```


| [Impacted Files](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [packages/uniforms-bootstrap3/src/NumField.js](https://codecov.io/gh/vazco/uniforms/pull/564/diff?src=pr&el=tree#diff-cGFja2FnZXMvdW5pZm9ybXMtYm9vdHN0cmFwMy9zcmMvTnVtRmllbGQuanM=) | `100% <100%> (ø)` | :arrow_up: |
| [packages/uniforms-unstyled/src/NumField.js](https://codecov.io/gh/vazco/uniforms/pull/564/diff?src=pr&el=tree#diff-cGFja2FnZXMvdW5pZm9ybXMtdW5zdHlsZWQvc3JjL051bUZpZWxkLmpz) | `100% <100%> (ø)` | :arrow_up: |
| [packages/uniforms-bootstrap4/src/NumField.js](https://codecov.io/gh/vazco/uniforms/pull/564/diff?src=pr&el=tree#diff-cGFja2FnZXMvdW5pZm9ybXMtYm9vdHN0cmFwNC9zcmMvTnVtRmllbGQuanM=) | `100% <100%> (ø)` | :arrow_up: |
| [packages/uniforms-material/src/NumField.js](https://codecov.io/gh/vazco/uniforms/pull/564/diff?src=pr&el=tree#diff-cGFja2FnZXMvdW5pZm9ybXMtbWF0ZXJpYWwvc3JjL051bUZpZWxkLmpz) | `100% <100%> (ø)` | :arrow_up: |
| [packages/uniforms-semantic/src/NumField.js](https://codecov.io/gh/vazco/uniforms/pull/564/diff?src=pr&el=tree#diff-cGFja2FnZXMvdW5pZm9ybXMtc2VtYW50aWMvc3JjL051bUZpZWxkLmpz) | `100% <100%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=footer). Last update [c63302c...0e84988](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

codecov[bot]: # [Codecov](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=h1) Report
> Merging [#564](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=desc) into [master](https://codecov.io/gh/vazco/uniforms/commit/c63302c256bf0173a8bdb14ec7770e6c402c68d4?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/vazco/uniforms/pull/564/graphs/tree.svg?width=650&token=PEY7QrNpKu&height=150&src=pr)](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=tree)

```diff
@@          Coverage Diff          @@
##           master   #564   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files         162    162           
  Lines        1574   1529   -45     
=====================================
- Hits         1574   1529   -45
```


| [Impacted Files](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [packages/uniforms-bootstrap3/src/NumField.js](https://codecov.io/gh/vazco/uniforms/pull/564/diff?src=pr&el=tree#diff-cGFja2FnZXMvdW5pZm9ybXMtYm9vdHN0cmFwMy9zcmMvTnVtRmllbGQuanM=) | `100% <100%> (ø)` | :arrow_up: |
| [packages/uniforms-unstyled/src/NumField.js](https://codecov.io/gh/vazco/uniforms/pull/564/diff?src=pr&el=tree#diff-cGFja2FnZXMvdW5pZm9ybXMtdW5zdHlsZWQvc3JjL051bUZpZWxkLmpz) | `100% <100%> (ø)` | :arrow_up: |
| [packages/uniforms-bootstrap4/src/NumField.js](https://codecov.io/gh/vazco/uniforms/pull/564/diff?src=pr&el=tree#diff-cGFja2FnZXMvdW5pZm9ybXMtYm9vdHN0cmFwNC9zcmMvTnVtRmllbGQuanM=) | `100% <100%> (ø)` | :arrow_up: |
| [packages/uniforms-material/src/NumField.js](https://codecov.io/gh/vazco/uniforms/pull/564/diff?src=pr&el=tree#diff-cGFja2FnZXMvdW5pZm9ybXMtbWF0ZXJpYWwvc3JjL051bUZpZWxkLmpz) | `100% <100%> (ø)` | :arrow_up: |
| [packages/uniforms-semantic/src/NumField.js](https://codecov.io/gh/vazco/uniforms/pull/564/diff?src=pr&el=tree#diff-cGFja2FnZXMvdW5pZm9ybXMtc2VtYW50aWMvc3JjL051bUZpZWxkLmpz) | `100% <100%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=footer). Last update [c63302c...0e84988](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

codecov[bot]: # [Codecov](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=h1) Report
> Merging [#564](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=desc) into [master](https://codecov.io/gh/vazco/uniforms/commit/c63302c256bf0173a8bdb14ec7770e6c402c68d4?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/vazco/uniforms/pull/564/graphs/tree.svg?width=650&token=PEY7QrNpKu&height=150&src=pr)](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=tree)

```diff
@@          Coverage Diff          @@
##           master   #564   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files         162    162           
  Lines        1574   1529   -45     
=====================================
- Hits         1574   1529   -45
```


| [Impacted Files](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [packages/uniforms-bootstrap3/src/NumField.js](https://codecov.io/gh/vazco/uniforms/pull/564/diff?src=pr&el=tree#diff-cGFja2FnZXMvdW5pZm9ybXMtYm9vdHN0cmFwMy9zcmMvTnVtRmllbGQuanM=) | `100% <100%> (ø)` | :arrow_up: |
| [packages/uniforms-unstyled/src/NumField.js](https://codecov.io/gh/vazco/uniforms/pull/564/diff?src=pr&el=tree#diff-cGFja2FnZXMvdW5pZm9ybXMtdW5zdHlsZWQvc3JjL051bUZpZWxkLmpz) | `100% <100%> (ø)` | :arrow_up: |
| [packages/uniforms-bootstrap4/src/NumField.js](https://codecov.io/gh/vazco/uniforms/pull/564/diff?src=pr&el=tree#diff-cGFja2FnZXMvdW5pZm9ybXMtYm9vdHN0cmFwNC9zcmMvTnVtRmllbGQuanM=) | `100% <100%> (ø)` | :arrow_up: |
| [packages/uniforms-material/src/NumField.js](https://codecov.io/gh/vazco/uniforms/pull/564/diff?src=pr&el=tree#diff-cGFja2FnZXMvdW5pZm9ybXMtbWF0ZXJpYWwvc3JjL051bUZpZWxkLmpz) | `100% <100%> (ø)` | :arrow_up: |
| [packages/uniforms-semantic/src/NumField.js](https://codecov.io/gh/vazco/uniforms/pull/564/diff?src=pr&el=tree#diff-cGFja2FnZXMvdW5pZm9ybXMtc2VtYW50aWMvc3JjL051bUZpZWxkLmpz) | `100% <100%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=footer). Last update [c63302c...0e84988](https://codecov.io/gh/vazco/uniforms/pull/564?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/packages/uniforms-bootstrap3/src/NumField.js b/packages/uniforms-bootstrap3/src/NumField.js
index 6c65a2ca..7154394a 100644
--- a/packages/uniforms-bootstrap3/src/NumField.js
+++ b/packages/uniforms-bootstrap3/src/NumField.js
@@ -5,6 +5,7 @@ import connectField from 'uniforms/connectField';
 import wrapField from './wrapField';
 
 const noneIfNaN = x => (isNaN(x) ? undefined : x);
+const parse = (decimal, x) => noneIfNaN((decimal ? parseFloat : parseInt)(x));
 
 const Num_ = props =>
   wrapField(
@@ -27,45 +28,46 @@ const Num_ = props =>
     />
   );
 
-// NOTE: React < 16 workaround. Make it optional?
-class Num extends Component {
-  constructor() {
-    super(...arguments);
+let Num;
+// istanbul ignore next
+if (parseInt(React.version, 10) < 16) {
+  Num = class Num extends Component {
+    state = { value: '' + this.props.value };
 
-    this.state = { value: '' + this.props.value };
+    componentWillReceiveProps({ decimal, value }) {
+      if (
+        parse(decimal, value) !==
+        parse(decimal, this.state.value.replace(/[.,]+$/, ''))
+      ) {
+        this.setState({
+          value: value === undefined || value === '' ? '' : '' + value
+        });
+      }
+    }
 
-    this.onChange = this.onChange.bind(this);
-  }
+    onChange = event => {
+      const value = event.target.value.replace(/[^\d.,-]/g, '');
 
-  componentWillReceiveProps({ decimal, value }) {
-    const parse = decimal ? parseFloat : parseInt;
+      this.setState({ value });
+      this.props.onChange(parse(this.props.decimal, value));
+    };
 
-    if (
-      noneIfNaN(parse(value)) !==
-      noneIfNaN(parse(this.state.value.replace(/[.,]+$/, '')))
-    ) {
-      this.setState({
-        value: value === undefined || value === '' ? '' : '' + value
+    render() {
+      return Num_({
+        ...this.props,
+        onChange: this.onChange,
+        value: this.state.value
       });
     }
-  }
-
-  onChange({ target: { value } }) {
-    const change = value.replace(/[^\d.,-]/g, '');
-
-    this.setState({ value: change });
-    this.props.onChange(
-      noneIfNaN((this.props.decimal ? parseFloat : parseInt)(change))
-    );
-  }
-
-  render() {
-    return Num_({
-      ...this.props,
-      onChange: this.onChange,
-      value: this.state.value
+  };
+} else {
+  Num = props =>
+    Num_({
+      ...props,
+      onChange(event) {
+        props.onChange(parse(props.decimal, event.target.value));
+      }
     });
-  }
 }
 
 export default connectField(Num);
diff --git a/packages/uniforms-bootstrap4/src/NumField.js b/packages/uniforms-bootstrap4/src/NumField.js
index 6671f088..f8801669 100644
--- a/packages/uniforms-bootstrap4/src/NumField.js
+++ b/packages/uniforms-bootstrap4/src/NumField.js
@@ -5,6 +5,7 @@ import connectField from 'uniforms/connectField';
 import wrapField from './wrapField';
 
 const noneIfNaN = x => (isNaN(x) ? undefined : x);
+const parse = (decimal, x) => noneIfNaN((decimal ? parseFloat : parseInt)(x));
 
 const Num_ = props =>
   wrapField(
@@ -27,45 +28,46 @@ const Num_ = props =>
     />
   );
 
-// NOTE: React < 16 workaround. Make it optional?
-class Num extends Component {
-  constructor() {
-    super(...arguments);
+let Num;
+// istanbul ignore next
+if (parseInt(React.version, 10) < 16) {
+  Num = class Num extends Component {
+    state = { value: '' + this.props.value };
 
-    this.state = { value: '' + this.props.value };
+    componentWillReceiveProps({ decimal, value }) {
+      if (
+        parse(decimal, value) !==
+        parse(decimal, this.state.value.replace(/[.,]+$/, ''))
+      ) {
+        this.setState({
+          value: value === undefined || value === '' ? '' : '' + value
+        });
+      }
+    }
 
-    this.onChange = this.onChange.bind(this);
-  }
+    onChange = event => {
+      const value = event.target.value.replace(/[^\d.,-]/g, '');
 
-  componentWillReceiveProps({ decimal, value }) {
-    const parse = decimal ? parseFloat : parseInt;
+      this.setState({ value });
+      this.props.onChange(parse(this.props.decimal, value));
+    };
 
-    if (
-      noneIfNaN(parse(value)) !==
-      noneIfNaN(parse(this.state.value.replace(/[.,]+$/, '')))
-    ) {
-      this.setState({
-        value: value === undefined || value === '' ? '' : '' + value
+    render() {
+      return Num_({
+        ...this.props,
+        onChange: this.onChange,
+        value: this.state.value
       });
     }
-  }
-
-  onChange({ target: { value } }) {
-    const change = value.replace(/[^\d.,-]/g, '');
-
-    this.setState({ value: change });
-    this.props.onChange(
-      noneIfNaN((this.props.decimal ? parseFloat : parseInt)(change))
-    );
-  }
-
-  render() {
-    return Num_({
-      ...this.props,
-      onChange: this.onChange,
-      value: this.state.value
+  };
+} else {
+  Num = props =>
+    Num_({
+      ...props,
+      onChange(event) {
+        props.onChange(parse(props.decimal, event.target.value));
+      }
     });
-  }
 }
 
 export default connectField(Num);
diff --git a/packages/uniforms-material/src/NumField.js b/packages/uniforms-material/src/NumField.js
index 87a5ec38..22ae6a36 100644
--- a/packages/uniforms-material/src/NumField.js
+++ b/packages/uniforms-material/src/NumField.js
@@ -4,6 +4,7 @@ import connectField from 'uniforms/connectField';
 import filterDOMProps from 'uniforms/filterDOMProps';
 
 const noneIfNaN = x => (isNaN(x) ? undefined : x);
+const parse = (decimal, x) => noneIfNaN((decimal ? parseFloat : parseInt)(x));
 
 const Num_ = ({
   decimal,
@@ -44,45 +45,46 @@ Num_.defaultProps = {
   margin: 'dense'
 };
 
-// NOTE: React < 16 workaround. Make it optional?
-class Num extends Component {
-  constructor() {
-    super(...arguments);
+let Num;
+// istanbul ignore next
+if (parseInt(React.version, 10) < 16) {
+  Num = class Num extends Component {
+    state = { value: '' + this.props.value };
 
-    this.state = { value: '' + this.props.value };
+    componentWillReceiveProps({ decimal, value }) {
+      if (
+        parse(decimal, value) !==
+        parse(decimal, this.state.value.replace(/[.,]+$/, ''))
+      ) {
+        this.setState({
+          value: value === undefined || value === '' ? '' : '' + value
+        });
+      }
+    }
 
-    this.onChange = this.onChange.bind(this);
-  }
+    onChange = event => {
+      const value = event.target.value.replace(/[^\d.,-]/g, '');
 
-  componentWillReceiveProps({ decimal, value }) {
-    const parse = decimal ? parseFloat : parseInt;
+      this.setState({ value });
+      this.props.onChange(parse(this.props.decimal, value));
+    };
 
-    if (
-      noneIfNaN(parse(value)) !==
-      noneIfNaN(parse(this.state.value.replace(/[.,]+$/, '')))
-    ) {
-      this.setState({
-        value: value === undefined || value === '' ? '' : '' + value
+    render() {
+      return Num_({
+        ...this.props,
+        onChange: this.onChange,
+        value: this.state.value
       });
     }
-  }
-
-  onChange({ target: { value } }) {
-    const change = value.replace(/[^\d.,-]/g, '');
-
-    this.setState({ value: change });
-    this.props.onChange(
-      noneIfNaN((this.props.decimal ? parseFloat : parseInt)(change))
-    );
-  }
-
-  render() {
-    return Num_({
-      ...this.props,
-      onChange: this.onChange,
-      value: this.state.value
+  };
+} else {
+  Num = props =>
+    Num_({
+      ...props,
+      onChange(event) {
+        props.onChange(parse(props.decimal, event.target.value));
+      }
     });
-  }
 }
 
 export default connectField(Num);
diff --git a/packages/uniforms-semantic/src/NumField.js b/packages/uniforms-semantic/src/NumField.js
index 206cdc00..4952bb02 100644
--- a/packages/uniforms-semantic/src/NumField.js
+++ b/packages/uniforms-semantic/src/NumField.js
@@ -4,6 +4,7 @@ import connectField from 'uniforms/connectField';
 import filterDOMProps from 'uniforms/filterDOMProps';
 
 const noneIfNaN = x => (isNaN(x) ? undefined : x);
+const parse = (decimal, x) => noneIfNaN((decimal ? parseFloat : parseInt)(x));
 
 const Num_ = ({
   className,
@@ -68,45 +69,46 @@ const Num_ = ({
   </div>
 );
 
-// NOTE: React < 16 workaround. Make it optional?
-class Num extends Component {
-  constructor() {
-    super(...arguments);
+let Num;
+// istanbul ignore next
+if (parseInt(React.version, 10) < 16) {
+  Num = class Num extends Component {
+    state = { value: '' + this.props.value };
 
-    this.state = { value: '' + this.props.value };
+    componentWillReceiveProps({ decimal, value }) {
+      if (
+        parse(decimal, value) !==
+        parse(decimal, this.state.value.replace(/[.,]+$/, ''))
+      ) {
+        this.setState({
+          value: value === undefined || value === '' ? '' : '' + value
+        });
+      }
+    }
 
-    this.onChange = this.onChange.bind(this);
-  }
+    onChange = event => {
+      const value = event.target.value.replace(/[^\d.,-]/g, '');
 
-  componentWillReceiveProps({ decimal, value }) {
-    const parse = decimal ? parseFloat : parseInt;
+      this.setState({ value });
+      this.props.onChange(parse(this.props.decimal, value));
+    };
 
-    if (
-      noneIfNaN(parse(value)) !==
-      noneIfNaN(parse(this.state.value.replace(/[.,]+$/, '')))
-    ) {
-      this.setState({
-        value: value === undefined || value === '' ? '' : '' + value
+    render() {
+      return Num_({
+        ...this.props,
+        onChange: this.onChange,
+        value: this.state.value
       });
     }
-  }
-
-  onChange({ target: { value } }) {
-    const change = value.replace(/[^\d.,-]/g, '');
-
-    this.setState({ value: change });
-    this.props.onChange(
-      noneIfNaN((this.props.decimal ? parseFloat : parseInt)(change))
-    );
-  }
-
-  render() {
-    return Num_({
-      ...this.props,
-      onChange: this.onChange,
-      value: this.state.value
+  };
+} else {
+  Num = props =>
+    Num_({
+      ...props,
+      onChange(event) {
+        props.onChange(parse(props.decimal, event.target.value));
+      }
     });
-  }
 }
 
 export default connectField(Num);
diff --git a/packages/uniforms-unstyled/src/NumField.js b/packages/uniforms-unstyled/src/NumField.js
index bc5459c2..b3b4e954 100644
--- a/packages/uniforms-unstyled/src/NumField.js
+++ b/packages/uniforms-unstyled/src/NumField.js
@@ -3,6 +3,7 @@ import connectField from 'uniforms/connectField';
 import filterDOMProps from 'uniforms/filterDOMProps';
 
 const noneIfNaN = x => (isNaN(x) ? undefined : x);
+const parse = (decimal, x) => noneIfNaN((decimal ? parseFloat : parseInt)(x));
 
 const Num_ = ({
   decimal,
@@ -38,45 +39,46 @@ const Num_ = ({
   </div>
 );
 
-// NOTE: React < 16 workaround. Make it optional?
-class Num extends Component {
-  constructor() {
-    super(...arguments);
+let Num;
+// istanbul ignore next
+if (parseInt(React.version, 10) < 16) {
+  Num = class Num extends Component {
+    state = { value: '' + this.props.value };
 
-    this.state = { value: '' + this.props.value };
+    componentWillReceiveProps({ decimal, value }) {
+      if (
+        parse(decimal, value) !==
+        parse(decimal, this.state.value.replace(/[.,]+$/, ''))
+      ) {
+        this.setState({
+          value: value === undefined || value === '' ? '' : '' + value
+        });
+      }
+    }
 
-    this.onChange = this.onChange.bind(this);
-  }
+    onChange = event => {
+      const value = event.target.value.replace(/[^\d.,-]/g, '');
 
-  componentWillReceiveProps({ decimal, value }) {
-    const parse = decimal ? parseFloat : parseInt;
+      this.setState({ value });
+      this.props.onChange(parse(this.props.decimal, value));
+    };
 
-    if (
-      noneIfNaN(parse(value)) !==
-      noneIfNaN(parse(this.state.value.replace(/[.,]+$/, '')))
-    ) {
-      this.setState({
-        value: value === undefined || value === '' ? '' : '' + value
+    render() {
+      return Num_({
+        ...this.props,
+        onChange: this.onChange,
+        value: this.state.value
       });
     }
-  }
-
-  onChange({ target: { value } }) {
-    const change = value.replace(/[^\d.,-]/g, '');
-
-    this.setState({ value: change });
-    this.props.onChange(
-      noneIfNaN((this.props.decimal ? parseFloat : parseInt)(change))
-    );
-  }
-
-  render() {
-    return Num_({
-      ...this.props,
-      onChange: this.onChange,
-      value: this.state.value
+  };
+} else {
+  Num = props =>
+    Num_({
+      ...props,
+      onChange(event) {
+        props.onChange(parse(props.decimal, event.target.value));
+      }
     });
-  }
 }
 
 export default connectField(Num);


Test patch:
diff --git a/packages/uniforms-bootstrap3/__tests__/NumField.js b/packages/uniforms-bootstrap3/__tests__/NumField.js
index c4b51f00..9b2bc589 100644
--- a/packages/uniforms-bootstrap3/__tests__/NumField.js
+++ b/packages/uniforms-bootstrap3/__tests__/NumField.js
@@ -5,6 +5,11 @@ import NumField from 'uniforms-bootstrap3/NumField';
 
 import createContext from './_createContext';
 
+const expectedValueTransform =
+  parseInt(React.version, 10) < 16
+    ? x => (x === undefined ? '' : '' + x)
+    : x => x;
+
 test('<NumField> - renders an input', () => {
   const element = <NumField name="x" />;
   const wrapper = mount(element, createContext({ x: { type: Number } }));
@@ -110,7 +115,7 @@ test('<NumField> - renders an input with correct value (model)', () => {
   );
 
   expect(wrapper.find('input')).toHaveLength(1);
-  expect(wrapper.find('input').prop('value')).toBe('1');
+  expect(wrapper.find('input').prop('value')).toBe(expectedValueTransform(1));
 
   // NOTE: All following tests are here to cover hacky NumField implementation.
   const spy = jest.spyOn(global.console, 'error').mockImplementation(() => {});
@@ -124,7 +129,7 @@ test('<NumField> - renders an input with correct value (model)', () => {
     { value: 1, decimal: false },
     { value: 1, decimal: false }
   ].forEach(({ decimal = true, value }) => {
-    const valueInput = value === undefined ? '' : '' + value;
+    const valueInput = expectedValueTransform(value);
 
     wrapper.setProps({ decimal });
 
@@ -151,7 +156,7 @@ test('<NumField> - renders an input with correct value (specified)', () => {
   const wrapper = mount(element, createContext({ x: { type: Number } }));
 
   expect(wrapper.find('input')).toHaveLength(1);
-  expect(wrapper.find('input').prop('value')).toBe('2');
+  expect(wrapper.find('input').prop('value')).toBe(expectedValueTransform(2));
 });
 
 test('<NumField> - renders an input which correctly reacts on change', () => {
diff --git a/packages/uniforms-bootstrap4/__tests__/NumField.js b/packages/uniforms-bootstrap4/__tests__/NumField.js
index a1a9d32e..b43d6807 100644
--- a/packages/uniforms-bootstrap4/__tests__/NumField.js
+++ b/packages/uniforms-bootstrap4/__tests__/NumField.js
@@ -5,6 +5,11 @@ import NumField from 'uniforms-bootstrap4/NumField';
 
 import createContext from './_createContext';
 
+const expectedValueTransform =
+  parseInt(React.version, 10) < 16
+    ? x => (x === undefined ? '' : '' + x)
+    : x => x;
+
 test('<NumField> - renders an input', () => {
   const element = <NumField name="x" />;
   const wrapper = mount(element, createContext({ x: { type: Number } }));
@@ -110,7 +115,7 @@ test('<NumField> - renders an input with correct value (model)', () => {
   );
 
   expect(wrapper.find('input')).toHaveLength(1);
-  expect(wrapper.find('input').prop('value')).toBe('1');
+  expect(wrapper.find('input').prop('value')).toBe(expectedValueTransform(1));
 
   // NOTE: All following tests are here to cover hacky NumField implementation.
   const spy = jest.spyOn(global.console, 'error').mockImplementation(() => {});
@@ -124,7 +129,7 @@ test('<NumField> - renders an input with correct value (model)', () => {
     { value: 1, decimal: false },
     { value: 1, decimal: false }
   ].forEach(({ decimal = true, value }) => {
-    const valueInput = value === undefined ? '' : '' + value;
+    const valueInput = expectedValueTransform(value);
 
     wrapper.setProps({ decimal });
 
@@ -151,7 +156,7 @@ test('<NumField> - renders an input with correct value (specified)', () => {
   const wrapper = mount(element, createContext({ x: { type: Number } }));
 
   expect(wrapper.find('input')).toHaveLength(1);
-  expect(wrapper.find('input').prop('value')).toBe('2');
+  expect(wrapper.find('input').prop('value')).toBe(expectedValueTransform(2));
 });
 
 test('<NumField> - renders an input which correctly reacts on change', () => {
diff --git a/packages/uniforms-material/__tests__/NumField.js b/packages/uniforms-material/__tests__/NumField.js
index 2051e7d2..2b59a71a 100644
--- a/packages/uniforms-material/__tests__/NumField.js
+++ b/packages/uniforms-material/__tests__/NumField.js
@@ -6,6 +6,11 @@ import NumField from 'uniforms-material/NumField';
 
 import createContext from './_createContext';
 
+const expectedValueTransform =
+  parseInt(React.version, 10) < 16
+    ? x => (x === undefined ? '' : '' + x)
+    : x => x;
+
 test('<NumField> - renders a TextField', () => {
   const element = <NumField name="x" />;
   const wrapper = mount(element, createContext({ x: { type: Number } }));
@@ -111,7 +116,7 @@ test('<NumField> - renders a TextField with correct value (model)', () => {
   );
 
   expect(wrapper.find(TextField)).toHaveLength(1);
-  expect(wrapper.find(TextField).prop('value')).toBe('1');
+  expect(wrapper.find(TextField).prop('value')).toBe(expectedValueTransform(1));
 
   // NOTE: All following tests are here to cover hacky NumField implementation.
   const spy = jest.spyOn(global.console, 'error').mockImplementation(() => {});
@@ -125,7 +130,7 @@ test('<NumField> - renders a TextField with correct value (model)', () => {
     { value: 1, decimal: false },
     { value: 1, decimal: false }
   ].forEach(({ decimal = true, value }) => {
-    const valueInput = value === undefined ? '' : '' + value;
+    const valueInput = expectedValueTransform(value);
 
     wrapper.setProps({ decimal });
 
@@ -152,7 +157,7 @@ test('<NumField> - renders a TextField with correct value (specified)', () => {
   const wrapper = mount(element, createContext({ x: { type: Number } }));
 
   expect(wrapper.find(TextField)).toHaveLength(1);
-  expect(wrapper.find(TextField).prop('value')).toBe('2');
+  expect(wrapper.find(TextField).prop('value')).toBe(expectedValueTransform(2));
 });
 
 test('<NumField> - renders a TextField which correctly reacts on change', () => {
diff --git a/packages/uniforms-semantic/__tests__/NumField.js b/packages/uniforms-semantic/__tests__/NumField.js
index 9fe7c377..a85d1ace 100644
--- a/packages/uniforms-semantic/__tests__/NumField.js
+++ b/packages/uniforms-semantic/__tests__/NumField.js
@@ -5,6 +5,11 @@ import NumField from 'uniforms-semantic/NumField';
 
 import createContext from './_createContext';
 
+const expectedValueTransform =
+  parseInt(React.version, 10) < 16
+    ? x => (x === undefined ? '' : '' + x)
+    : x => x;
+
 test('<NumField> - renders an input', () => {
   const element = <NumField name="x" />;
   const wrapper = mount(element, createContext({ x: { type: Number } }));
@@ -110,7 +115,7 @@ test('<NumField> - renders an input with correct value (model)', () => {
   );
 
   expect(wrapper.find('input')).toHaveLength(1);
-  expect(wrapper.find('input').prop('value')).toBe('1');
+  expect(wrapper.find('input').prop('value')).toBe(expectedValueTransform(1));
 
   // NOTE: All following tests are here to cover hacky NumField implementation.
   const spy = jest.spyOn(global.console, 'error').mockImplementation(() => {});
@@ -124,7 +129,7 @@ test('<NumField> - renders an input with correct value (model)', () => {
     { value: 1, decimal: false },
     { value: 1, decimal: false }
   ].forEach(({ decimal = true, value }) => {
-    const valueInput = value === undefined ? '' : '' + value;
+    const valueInput = expectedValueTransform(value);
 
     wrapper.setProps({ decimal });
 
@@ -151,7 +156,7 @@ test('<NumField> - renders an input with correct value (specified)', () => {
   const wrapper = mount(element, createContext({ x: { type: Number } }));
 
   expect(wrapper.find('input')).toHaveLength(1);
-  expect(wrapper.find('input').prop('value')).toBe('2');
+  expect(wrapper.find('input').prop('value')).toBe(expectedValueTransform(2));
 });
 
 test('<NumField> - renders an input which correctly reacts on change', () => {
diff --git a/packages/uniforms-unstyled/__tests__/NumField.js b/packages/uniforms-unstyled/__tests__/NumField.js
index c3b97a52..a8369e5b 100644
--- a/packages/uniforms-unstyled/__tests__/NumField.js
+++ b/packages/uniforms-unstyled/__tests__/NumField.js
@@ -5,6 +5,11 @@ import NumField from 'uniforms-unstyled/NumField';
 
 import createContext from './_createContext';
 
+const expectedValueTransform =
+  parseInt(React.version, 10) < 16
+    ? x => (x === undefined ? '' : '' + x)
+    : x => x;
+
 test('<NumField> - renders an input', () => {
   const element = <NumField name="x" />;
   const wrapper = mount(element, createContext({ x: { type: Number } }));
@@ -110,7 +115,7 @@ test('<NumField> - renders an input with correct value (model)', () => {
   );
 
   expect(wrapper.find('input')).toHaveLength(1);
-  expect(wrapper.find('input').prop('value')).toBe('1');
+  expect(wrapper.find('input').prop('value')).toBe(expectedValueTransform(1));
 
   // NOTE: All following tests are here to cover hacky NumField implementation.
   const spy = jest.spyOn(global.console, 'error').mockImplementation(() => {});
@@ -124,7 +129,7 @@ test('<NumField> - renders an input with correct value (model)', () => {
     { value: 1, decimal: false },
     { value: 1, decimal: false }
   ].forEach(({ decimal = true, value }) => {
-    const valueInput = value === undefined ? '' : '' + value;
+    const valueInput = expectedValueTransform(value);
 
     wrapper.setProps({ decimal });
 
@@ -151,7 +156,7 @@ test('<NumField> - renders an input with correct value (specified)', () => {
   const wrapper = mount(element, createContext({ x: { type: Number } }));
 
   expect(wrapper.find('input')).toHaveLength(1);
-  expect(wrapper.find('input').prop('value')).toBe('2');
+  expect(wrapper.find('input').prop('value')).toBe(expectedValueTransform(2));
 });
 
 test('<NumField> - renders an input which correctly reacts on change', () => {
diff --git a/packages/uniforms/__tests__/connectField.js b/packages/uniforms/__tests__/connectField.js
index 6e532c00..e4e998b0 100644
--- a/packages/uniforms/__tests__/connectField.js
+++ b/packages/uniforms/__tests__/connectField.js
@@ -76,7 +76,7 @@ describe('connectField', () => {
 
   describe('when called with `baseField`', () => {
     it('inherits from `baseField`', () => {
-      /* istanbul ignore next */
+      // istanbul ignore next
       class Class {}
 
       Class.property1 = 1;