diff --git a/packages/core/src/common/errors.ts b/packages/core/src/common/errors.ts index a4c717a5a1..0f59460be6 100644 --- a/packages/core/src/common/errors.ts +++ b/packages/core/src/common/errors.ts @@ -26,12 +26,11 @@ export const HOTKEYS_WARN_DECORATOR_NO_METHOD = ns + ` @HotkeysTarget-decorated export const HOTKEYS_WARN_DECORATOR_NEEDS_REACT_ELEMENT = ns + ` "@HotkeysTarget-decorated components must return a single JSX.Element or an empty render.`; -export const NUMERIC_INPUT_MIN_MAX = - ns + ` requires min to be strictly less than max if both are defined.`; +export const NUMERIC_INPUT_MIN_MAX = ns + ` requires min to be no greater than max if both are defined.`; export const NUMERIC_INPUT_MINOR_STEP_SIZE_BOUND = - ns + ` requires minorStepSize to be strictly less than stepSize.`; + ns + ` requires minorStepSize to be no greater than stepSize.`; export const NUMERIC_INPUT_MAJOR_STEP_SIZE_BOUND = - ns + ` requires majorStepSize to be strictly greater than stepSize.`; + ns + ` requires stepSize to be no greater than majorStepSize.`; export const NUMERIC_INPUT_MINOR_STEP_SIZE_NON_POSITIVE = ns + ` requires minorStepSize to be strictly greater than zero.`; export const NUMERIC_INPUT_MAJOR_STEP_SIZE_NON_POSITIVE = diff --git a/packages/core/src/components/forms/numericInput.tsx b/packages/core/src/components/forms/numericInput.tsx index f9bab4c3d6..71b5ab0dc3 100644 --- a/packages/core/src/components/forms/numericInput.tsx +++ b/packages/core/src/components/forms/numericInput.tsx @@ -248,7 +248,7 @@ export class NumericInput extends AbstractPureComponent= max) { + if (min != null && max != null && min > max) { throw new Error(Errors.NUMERIC_INPUT_MIN_MAX); } if (stepSize == null) { diff --git a/packages/core/test/controls/numericInputTests.tsx b/packages/core/test/controls/numericInputTests.tsx index 2739320106..94fa08eed5 100644 --- a/packages/core/test/controls/numericInputTests.tsx +++ b/packages/core/test/controls/numericInputTests.tsx @@ -674,6 +674,25 @@ describe("", () => { }); }); + describe("if min === max", () => { + it("never changes value", () => { + const onValueChangeSpy = spy(); + const component = mount(); + // repeated interactions, no change in state + component + .find(Button) + .first() + .simulate("mousedown") + .simulate("mousedown") + .simulate("mousedown") + .simulate("mousedown") + .simulate("mousedown"); + expect(component.state().value).to.equal("2"); + expect(onValueChangeSpy.callCount).to.equal(5); + expect(onValueChangeSpy.args[0]).to.deep.equal([2, "2"]); + }); + }); + describe("clampValueOnBlur", () => { it("does not clamp or invoke onValueChange on blur if clampValueOnBlur=false", () => { // should be false by default diff --git a/packages/docs-app/src/examples/core-examples/numericInputBasicExample.tsx b/packages/docs-app/src/examples/core-examples/numericInputBasicExample.tsx index 85d770fce4..52b15914d5 100644 --- a/packages/docs-app/src/examples/core-examples/numericInputBasicExample.tsx +++ b/packages/docs-app/src/examples/core-examples/numericInputBasicExample.tsx @@ -6,6 +6,7 @@ import * as React from "react"; import { + H5, HTMLSelect, Intent, INumericInputProps, @@ -29,7 +30,7 @@ const MIN_VALUES = [ { label: "None", value: -Infinity }, { label: "-10", value: -10 }, { label: "0", value: 0 }, - { label: "10", value: 10 }, + { label: "20", value: 20 }, ]; const MAX_VALUES = [ @@ -66,7 +67,6 @@ export class NumericInputBasicExample extends React.PureComponent this.setState({ max })); private handleMinChange = handleNumberChange(min => this.setState({ min })); private handleIntentChange = handleStringChange((intent: Intent) => this.setState({ intent })); - private handleButtonPositionChange = handleStringChange((buttonPosition: INumericInputProps["buttonPosition"]) => this.setState({ buttonPosition }), ); @@ -110,14 +110,14 @@ export class NumericInputBasicExample extends React.PureComponent - +
Props
+ {this.renderSwitch("Disabled", disabled, this.toggleDisabled)} + {this.renderSwitch("Fill", fill, this.toggleFullWidth)} + {this.renderSwitch("Large", large, this.toggleLargeSize)} + {this.renderSwitch("Left icon", leftIcon != null, this.toggleLeftIcon)} {this.renderSwitch("Numeric characters only", allowNumericCharactersOnly, this.toggleNumericCharsOnly)} {this.renderSwitch("Select all on focus", selectAllOnFocus, this.toggleSelectAllOnFocus)} {this.renderSwitch("Select all on increment", selectAllOnIncrement, this.toggleSelectAllOnIncrement)} - {this.renderSwitch("Disabled", disabled, this.toggleDisabled)} - {this.renderSwitch("Left icon", leftIcon != null, this.toggleLeftIcon)} - {this.renderSwitch("Fill container", fill, this.toggleFullWidth)} - {this.renderSwitch("Large", large, this.toggleLargeSize)} {this.renderSelectMenu("Minimum value", min, MIN_VALUES, this.handleMinChange)} {this.renderSelectMenu("Maximum value", max, MAX_VALUES, this.handleMaxChange)} {this.renderSelectMenu( @@ -137,19 +137,17 @@ export class NumericInputBasicExample extends React.PureComponent, + onChange: React.FormEventHandler, ) { return ( ); } - private handleValueChange = (_valueAsNumber: number, valueAsString: string) => { - this.setState({ value: valueAsString }); - }; + private handleValueChange = (_v: number, value: string) => this.setState({ value }); }