Skip to content

fix(input-number): allow editing of numbers that start with zeros #11705

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Apr 12, 2025

Conversation

josercarcamo
Copy link
Contributor

Related Issue: #9611

Summary

Allows editing of numbers that start with zeros

@github-actions github-actions bot added the bug Bug reports for broken functionality. Issues should include a reproduction of the bug. label Mar 6, 2025
@josercarcamo josercarcamo added the pr ready for visual snapshots Adding this label will run visual snapshot testing. label Mar 6, 2025
@josercarcamo josercarcamo requested a review from benelan March 6, 2025 20:46
@josercarcamo
Copy link
Contributor Author

@benelan could you please review?

Copy link
Member

@benelan benelan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some cases that don't work with this regex solution, can you take another look? Please reach out if you have any questions or if you'd like some implementation ideas!

hasTrailingDecimalSeparator && isValueDeleted
? `${newLocalizedValue}${numberStringFormatter.decimal}`
: newLocalizedValue;
const re = new RegExp(`^-?0+\\d*${numberStringFormatter.decimal}?$`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some issues with this regex:

  • The - won't work in some locales, you can use numberStringFormatter.minusSign instead
  • The 0 won't work for other numbering systems
  • The \d won't work for other numbering systems
  • There will likely be issues when the groupSeparator property is enabled. You can access the locale's group separator with numberStringFormatter.group
  • There can be an e in the number, e.g. 10e2

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the code is currently using this exact notation in lines 133-137 of number.ts:

const allLeadingZerosOptionallyNegative = /^([-0])0+(?=\d)/; const decimalOnlyAtEndOfString = /(?!^\.)\.$/; const allHyphensExceptTheStart = /(?!^-)-/g; const isNegativeDecimalOnlyZeros = /^-\b0\b\.?0*$/; const hasTrailingDecimalZeros = /0*$/;

Plus all current e2e tests pass.

Copy link
Member

@benelan benelan Mar 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plus all current e2e tests pass.

The existing tests don't cover this leading zero bug, which is why they aren't failing. However, the test you added wouldn't pass if you modified it to use the group-separator attribute. Same goes for the other cases I mentioned in my previous comment.

But the code is currently using this exact notation in lines 133-137 of number.ts:

My bad, the use of numberStringFormatter.decimal in the regex had me confused for a second. With input there are two different values:

  1. the value property, which is always in the en locale and latn numbering system
  2. the displayValue state, which is in the user-specified locale and numbering system

The regex you referenced are part of the number sanitation for the value property, which why it doesn't use the locale utils. The leading zeros get trimmed during number sanitation. After the number sanitation, the value is localized for the displayValue.

The trimmed, leading zeros need to be re-added to the displayValue, so they should be in the specified numbering system. The value property doesn't need the zeros re-added.

So the steps that need to be done for this issue are:

  1. Check for leading zeros before the number sanitation (like how we check for trailing decimals here):

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

  1. Add back trimmed, leading zeros after the localization (like we are adding localized trailing decimal zeros here):

if (origin !== "connected" && !hasTrailingDecimalSeparator) {
newLocalizedValue = addLocalizedTrailingDecimalZeros(
newLocalizedValue,
newValue,
numberStringFormatter,
);
}

Notice how the 0 is being localized into the specified numbering system in the util:

return localizedValue.padEnd(localizedValue.length + trailingDecimalZeros.length, formatter.localize("0"));

The trailing, decimal zeros logic was added in #7159, I recommend taking a look that PR's implementation and discussion.

Please don't hesitate to reach out on Teams if you have any follow up questions. The input-number logic has a lot of features and edge cases, so it can be difficult to navigate at first.

Copy link
Contributor

This PR has been automatically marked as stale because it has not had recent activity. Please close your PR if it is no longer relevant. Thank you for your contributions.

@github-actions github-actions bot added the Stale Issues or pull requests that have not had recent activity. label Mar 21, 2025
Copy link
Member

@benelan benelan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggested code that simplifies your implementation, which will hopefully make things easier to troubleshoot. Can you run with the suggested changes and see if you can finish it off from there?

Please reach out tomorrow if the suggestions don't unblock you, and I'll take another look. Thanks!

@@ -823,6 +823,9 @@ export class InputNumber
const hasTrailingDecimalSeparator =
valueHandleInteger.charAt(valueHandleInteger.length - 1) === ".";

const re = new RegExp(`^(-)?0+\\d*[${numberStringFormatter.decimal}|e]?$`);
const reResult = re.exec(valueHandleInteger);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex here is unnecessarily complex, which is likely the cause of the test failures. We just need to know the following things:

  • are there leading zeros, and if so, how many?
  • is there a minus sign?

So the lines could be simplified to something like this:

    const hasLeadingMinusSign = valueHandleInteger.charAt(0) === "-";
    const hasLeadingZeros = valueHandleInteger.match(/^-?(0+)\d/);

);
this.displayedValue += hasTrailingDecimalSeparator ? numberStringFormatter.decimal : "";
}

Copy link
Member

@benelan benelan Apr 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the regex change I suggested above, lines 851-871 could be changed to something like this:

    // adds localized trailing decimal separator
    if (hasTrailingDecimalSeparator && isValueDeleted) {
      newLocalizedValue = `${newLocalizedValue}${numberStringFormatter.decimal}`;
    }

    // adds localized leading zeros
    if (hasLeadingZeros) {
      newLocalizedValue = `${
        hasLeadingMinusSign ? newLocalizedValue.charAt(0) : ""
      }${numberStringFormatter.localize("0").repeat(hasLeadingZeros[1].length)}${
        hasLeadingMinusSign ? newLocalizedValue.slice(1) : newLocalizedValue
      }`;
    }

    this.displayedValue = newLocalizedValue;
Diff
-    //adds localized trailing decimal separator
-    this.displayedValue =
-      hasTrailingDecimalSeparator && isValueDeleted
-        ? `${newLocalizedValue}${numberStringFormatter.decimal}`
-        : newLocalizedValue;
- 
-    if (reResult) {
-      this.displayedValue = reResult[1]
-        ? `-${this.displayedValue
-            .substring(1, this.displayedValue.length)
-            .padStart(
-              valueHandleInteger.length - 1 - (hasTrailingDecimalSeparator ? 1 : 0),
-              numberStringFormatter.localize("0"),
-            )}`
-        : this.displayedValue.padStart(
-            valueHandleInteger.length,
-            numberStringFormatter.localize("0"),
-          );
-      this.displayedValue += hasTrailingDecimalSeparator ? numberStringFormatter.decimal : "";
-    }
+    // adds localized trailing decimal separator
+    if (hasTrailingDecimalSeparator && isValueDeleted) {
+      newLocalizedValue = `${newLocalizedValue}${numberStringFormatter.decimal}`;
+    }
+ 
+    // adds localized leading zeros
+    if (hasLeadingZeros) {
+      newLocalizedValue = `${
+        hasLeadingMinusSign ? newLocalizedValue.charAt(0) : ""
+      }${numberStringFormatter.localize("0").repeat(hasLeadingZeros[1].length)}${
+        hasLeadingMinusSign ? newLocalizedValue.slice(1) : newLocalizedValue
+      }`;
+    }
+ 
+    this.displayedValue = newLocalizedValue;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to use the isValueDeleted boolean for the hasLeadingZeros conditional too. Can you investigate?

@josercarcamo josercarcamo added pr ready for visual snapshots Adding this label will run visual snapshot testing. and removed pr ready for visual snapshots Adding this label will run visual snapshot testing. labels Apr 4, 2025
@josercarcamo
Copy link
Contributor Author

@benelan could you please review?

@github-actions github-actions bot removed the Stale Issues or pull requests that have not had recent activity. label Apr 5, 2025
Copy link
Member

@benelan benelan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Glad my suggestions worked, thanks for testing! The e2e tests need some changes and then this will be good to go 🚀


it(`allows editing numbers that start with zeros ${locale} locale`, async () => {
const page = await newE2EPage();
await page.setContent(html`<calcite-input-number locale=${locale}></calcite-input-number>`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI the attribute to change the locale is lang, not locale

await page.waitForChanges();
expect(await calciteInput.getProperty("value")).toBe("50011");
expect(await input.getProperty("value")).toBe("50011");
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to test this feature for every locale.

Can you remove this test and add a new one on line 1544 that does the following:

  • uses the arab numbering system
  • uses the ar locale
  • uses a minus sign
  • uses a decimal

@@ -1490,6 +1541,57 @@ describe("calcite-input-number", () => {
expect(await input.getProperty("value")).toBe(numberStringFormatter.localize(initialValue));
});

it("allows editing numbers that start with zeros and group separator", async () => {
const page = await newE2EPage();
await page.setContent(html`<calcite-input-number></calcite-input-number>`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test says it is using a group separator, but the attribute is missing from the component

Suggested change
await page.setContent(html`<calcite-input-number></calcite-input-number>`);
await page.setContent(html`<calcite-input-number group-separator></calcite-input-number>`);

@benelan benelan added the skip visual snapshots Pull requests that do not need visual regression testing. label Apr 7, 2025
expect(await calciteInput.getProperty("value")).toBe("0");
expect(await input.getProperty("value")).toBe("000");
expect(await calciteInput.getProperty("value")).toBe("7000");
expect(await input.getProperty("value")).toBe("7000");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the modified expected value here, it looks like you are deleting the group separator instead of the 7. You will need to add another ArrowLeft keypress now that there is an additional character.

Alternatively you can do this, which doesn't depend on the length of the number:

    await page.keyboard.press("Home");
    await page.keyboard.press("Delete");

The other expected values below will also need to be updated to ensure they are testing the leading zero fix.


it(`allows editing numbers that start with zeros and have decimals in the ar locale and arab numbering system`, async () => {
const page = await newE2EPage();
await page.setContent(html`<calcite-input-number lang="ar"></calcite-input-number>`);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
await page.setContent(html`<calcite-input-number lang="ar"></calcite-input-number>`);
await page.setContent(html`<calcite-input-number lang="ar" numbering-system="arab"></calcite-input-number>`);

You need to use the numbering-system attribute in order to change the numbering system. It doesn't change automatically when setting lang because some users want the latin numbering system and arab locale, for example.

Copy link
Member

@benelan benelan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed some changes to fix the arabic test, LGTM now!

@josercarcamo josercarcamo merged commit e86c99d into dev Apr 12, 2025
10 checks passed
@josercarcamo josercarcamo deleted the josercarcamo/9611-input-number-editing branch April 12, 2025 00:15
@github-actions github-actions bot added this to the 2025-04-29 - Apr Milestone milestone Apr 12, 2025
@@ -135,8 +135,14 @@
<div class="child-flex font right-aligned-text">Number Vertical</div>
<div class="child-flex">
<calcite-label scale="s">
Input Label
<calcite-input-number placeholder="Placeholder" scale="s" value="123" step="1"></calcite-input-number>
Input Label AR2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@josercarcamo for consistency, could you restore the previous HTML and either:

  • move this particular Arabic configuration to a new section or
  • omit it for now?

benelan added a commit that referenced this pull request May 14, 2025
…1705)

**Related Issue:** #9611

## Summary
Allows editing of numbers that start with zeros

---------

Co-authored-by: Ben Elan <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Bug reports for broken functionality. Issues should include a reproduction of the bug. pr ready for visual snapshots Adding this label will run visual snapshot testing. skip visual snapshots Pull requests that do not need visual regression testing.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants