Skip to content

Commit

Permalink
feat(input-number): add integer property (#7646)
Browse files Browse the repository at this point in the history
**Related Issue:** #6706

## Summary

Add an `integer` property to `calcite-input-number` which prevents
decimals and exponential notation.
  • Loading branch information
benelan authored Aug 31, 2023
1 parent b52b575 commit cd66a6d
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { E2EPage, newE2EPage } from "@stencil/core/testing";
import { E2EElement, E2EPage, EventSpy, newE2EPage } from "@stencil/core/testing";
import { KeyInput } from "puppeteer";
import { html } from "../../../support/formatting";
import {
Expand Down Expand Up @@ -544,8 +544,8 @@ describe("calcite-input-number", () => {
});

describe("mouse events on arrow buttons", () => {
let input;
let calciteInputNumberInput;
let input: E2EElement;
let calciteInputNumberInput: EventSpy;

beforeEach(async () => {
await page.setContent(html`<calcite-input-number value="0"></calcite-input-number>`);
Expand Down Expand Up @@ -1631,7 +1631,7 @@ describe("calcite-input-number", () => {
expect(await button.getProperty("disabled")).toBe(true);
expect(await input.getProperty("disabled")).toBe(false);

await input.setProperty("disabled", true);
input.setProperty("disabled", true);
await input.callMethod("setFocus");
await page.waitForChanges();
await typeNumberValue(page, "2");
Expand All @@ -1640,7 +1640,7 @@ describe("calcite-input-number", () => {
expect(await button.getProperty("disabled")).toBe(true);
expect(await input.getProperty("disabled")).toBe(true);

await input.setProperty("disabled", false);
input.setProperty("disabled", false);
await page.waitForChanges();
await input.callMethod("setFocus");
await page.waitForChanges();
Expand All @@ -1650,7 +1650,7 @@ describe("calcite-input-number", () => {
expect(await button.getProperty("disabled")).toBe(true);
expect(await input.getProperty("disabled")).toBe(false);

await button.setProperty("disabled", false);
button.setProperty("disabled", false);
await page.waitForChanges();
await input.callMethod("setFocus");
await page.waitForChanges();
Expand All @@ -1660,7 +1660,7 @@ describe("calcite-input-number", () => {
expect(await button.getProperty("disabled")).toBe(false);
expect(await input.getProperty("disabled")).toBe(false);

await input.setProperty("disabled", true);
input.setProperty("disabled", true);
await page.waitForChanges();
await input.callMethod("setFocus");
await page.waitForChanges();
Expand All @@ -1671,6 +1671,33 @@ describe("calcite-input-number", () => {
expect(await input.getProperty("disabled")).toBe(true);
});

it("integer property prevents decimals and exponential notation", async () => {
const page = await newE2EPage();
await page.setContent(`<calcite-input-number integer value="1.2" step="0.01"></calcite-input-number>`);

const input = await page.find("calcite-input-number");
const numberHorizontalItemUp = await page.find(
"calcite-input-number >>> .number-button-item[data-adjustment='up']"
);

await input.callMethod("setFocus");
await page.waitForChanges();

expect(await input.getProperty("value")).toBe("12"); // test initial value

await typeNumberValue(page, "3.4e-5");
await page.waitForChanges();
expect(await input.getProperty("value")).toBe("12345"); // test user input

input.setProperty("value", "-9.8e-7");
await page.waitForChanges();
expect(await input.getProperty("value")).toBe("-987"); // test directly setting value

await numberHorizontalItemUp.click();
await page.waitForChanges();
expect(await input.getProperty("value")).toBe("-986"); // test incrementing
});

describe("is form-associated", () => {
formAssociated("calcite-input-number", {
testValue: 5,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ export class InputNumber
/** When `true`, the icon will be flipped when the element direction is right-to-left (`"rtl"`). */
@Prop({ reflect: true }) iconFlipRtl = false;

/** When `true`, restricts the component to integer numbers only and disables exponential notation. */
@Prop() integer = false;

/** Accessible name for the component's button or hyperlink. */
@Prop() label: string;

Expand Down Expand Up @@ -540,7 +543,9 @@ export class InputNumber
): void {
const { value } = this;
const adjustment = direction === "up" ? 1 : -1;
const inputStep = this.step === "any" ? 1 : Math.abs(this.step || 1);
const stepHandleInteger =
this.integer && this.step !== "any" ? Math.round(this.step) : this.step;
const inputStep = stepHandleInteger === "any" ? 1 : Math.abs(stepHandleInteger || 1);
const inputVal = new BigDecimal(value !== "" ? value : "0");
const nudgedValue = inputVal.add(`${inputStep * adjustment}`);

Expand Down Expand Up @@ -616,7 +621,10 @@ export class InputNumber
};
const delocalizedValue = numberStringFormatter.delocalize(value);
if (nativeEvent.inputType === "insertFromPaste") {
if (!isValidNumber(delocalizedValue)) {
if (
!isValidNumber(delocalizedValue) ||
(this.integer && (delocalizedValue.includes("e") || delocalizedValue.includes(".")))
) {
nativeEvent.preventDefault();
}
this.setNumberValue({
Expand Down Expand Up @@ -675,15 +683,15 @@ export class InputNumber
useGrouping: this.groupSeparator,
};

if (event.key === numberStringFormatter.decimal) {
if (event.key === numberStringFormatter.decimal && !this.integer) {
if (!this.value && !this.childNumberEl.value) {
return;
}
if (this.value && this.childNumberEl.value.indexOf(numberStringFormatter.decimal) === -1) {
return;
}
}
if (/[eE]/.test(event.key)) {
if (/[eE]/.test(event.key) && !this.integer) {
if (!this.value && !this.childNumberEl.value) {
return;
}
Expand Down Expand Up @@ -836,9 +844,16 @@ export class InputNumber

const isValueDeleted =
this.previousValue?.length > value.length || this.value?.length > value.length;
const hasTrailingDecimalSeparator = value.charAt(value.length - 1) === ".";

const valueHandleInteger = this.integer ? value.replace(/[e.]/g, "") : value;

const hasTrailingDecimalSeparator =
valueHandleInteger.charAt(valueHandleInteger.length - 1) === ".";

const sanitizedValue =
hasTrailingDecimalSeparator && isValueDeleted ? value : sanitizeNumberString(value);
hasTrailingDecimalSeparator && isValueDeleted
? valueHandleInteger
: sanitizeNumberString(valueHandleInteger);

const newValue =
value && !sanitizedValue
Expand Down

0 comments on commit cd66a6d

Please sign in to comment.