Skip to content

feat(ui5-number-input): introduce NumberInput component#13799

Open
GDamyanov wants to merge 22 commits into
mainfrom
number-input
Open

feat(ui5-number-input): introduce NumberInput component#13799
GDamyanov wants to merge 22 commits into
mainfrom
number-input

Conversation

@GDamyanov

@GDamyanov GDamyanov commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Introduces a new ui5-number-input component as the dedicated numeric input primitive, and refactors ui5-step-input to be built on top of it.

New Component: ui5-number-input

A numeric input field ui5-number-input has been extracted as a standalone component. It handles all numeric input logic internally:

  • Number formatting via locale-aware NumberFormat
  • Min/max validation with value-state-change event (preventable)
  • Keyboard navigation: arrow keys, Ctrl/Shift modifiers, Page Up/Down
  • Mouse wheel support
  • Spin button acceleration on long-press
  • Increment/decrement buttons (opt-in via _showStepButtons)
  • Form association support
  • Full accessibility (ARIA labels, value state messages)

Refactored: ui5-step-input

ui5-step-input is now a thin wrapper around ui5-number-input, delegating all numeric logic to it

A new private property is added in the ui5-number-input:

_showStepButtons is a @Private, noAttribute boolean property on ui5-number-input (defaults to false) that controls whether the increment/decrement buttons are rendered. When false, ui5-number-input renders as a plain numeric input with no step buttons — suitable for standalone use. ui5-step-input sets _showStepButtons={true} in its template, which activates the buttons and the associated CSS modifier class (ui5-number-input-root--with-buttons). This means the step button UI lives entirely inside ui5-number-input; ui5-step-input does not render any buttons of its own.

Breaking Changes

BREAKING CHANGE: ui5-step-input shadow DOM internals changed

ui5-step-input now composes ui5-number-input internally instead of ui5-input.
Any CSS, querySelector, or ::part() targeting the previous inner [ui5-input]
(.ui5-step-input-root, .ui5-step-input-input) will no longer work and must be

@GDamyanov GDamyanov self-assigned this Jul 8, 2026
@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 8, 2026 11:29 — with GitHub Actions Inactive
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

👋 Heads-up: dev close is in effect

Thanks for the contribution! This repository is currently in dev close ahead of release next (scheduled 2026-07-09, UTC). See the release schedule for the full timeline.

This PR appears to introduce public-API changes (detected by diffing the Custom Elements Manifest against the latest published version on npm):

@ui5/webcomponents

  • ➕ added element: NumberInput
  • ➕ added property: accessibleName
  • ➕ added property: accessibleNameRef
  • ➕ added property: disabled
  • ➕ added property: max
  • ➕ added property: min
  • ➕ added property: name
  • ➕ added property: placeholder
  • ➕ added property: readonly
  • ➕ added property: required
  • ➕ added property: step
  • ➕ added property: value
  • ➕ added property: valuePrecision
  • ➕ added property: valueState
  • ➕ added event: change
  • ➕ added event: input
  • ➕ added event: value-state-change
  • ➕ added slot: valueStateMessage
  • ➕ added attribute: accessible-name
  • ➕ added attribute: accessible-name-ref
  • ➕ added attribute: disabled
  • ➕ added attribute: max
  • ➕ added attribute: min
  • ➕ added attribute: name
  • ➕ added attribute: placeholder
  • ➕ added attribute: readonly
  • ➕ added attribute: required
  • ➕ added attribute: step
  • ➕ added attribute: value
  • ➕ added attribute: value-precision
  • ➕ added attribute: value-state
  • 🔄 changed attribute value-state (type)

Could you please hold off on merging into main until the release ships? Public-API changes are best landed in the next dev cycle so they don't slip into the release at the last minute. Once the release is out, this PR is good to go.

If this change must ship in the current release, please request a review from one or two members of @UI5/ui5-team-webc so the team can sign off explicitly.

💬 False positive? If you believe this PR doesn't actually change the public API (e.g. only internal refactoring, or an entry the detector mis-attributed), please reply on this thread — your feedback helps us improve the detection during this trial run.

Posted automatically by the Dev Close Notice workflow.

@sap-ui5-webcomponents-release

Copy link
Copy Markdown

@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 8, 2026 11:40 — with GitHub Actions Inactive
@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 8, 2026 11:44 — with GitHub Actions Inactive
@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 8, 2026 12:04 — with GitHub Actions Inactive
@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 8, 2026 12:36 — with GitHub Actions Inactive
@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 8, 2026 12:39 — with GitHub Actions Inactive
@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 8, 2026 13:40 — with GitHub Actions Inactive
@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 8, 2026 14:05 — with GitHub Actions Inactive
@GDamyanov
GDamyanov requested a review from hinzzx July 8, 2026 14:26
@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 9, 2026 14:16 — with GitHub Actions Inactive
@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 9, 2026 19:39 — with GitHub Actions Inactive
@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 10, 2026 06:21 — with GitHub Actions Inactive
@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 10, 2026 06:49 — with GitHub Actions Inactive
@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 10, 2026 07:26 — with GitHub Actions Inactive

@hinzzx hinzzx left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

  • We should also register the NumberInput in ReactPlayground;

Comment thread packages/main/src/NumberInput.ts Outdated
_onButtonFocusOut() {
setTimeout(() => {
if (!this._inputFocused && !this.shadowRoot!.activeElement) {
this.inputOuter.removeAttribute("focused");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This must be property/template-driven. Using remove/setAttribute is a forbidden pattern.

Just use this.focused = false, and add the focused={this.focused} in the tempalte.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 97d1410

} from "@ui5/webcomponents-base/dist/Keys.js";
import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js";
import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js";
import ValueState from "@ui5/webcomponents-base/dist/types/ValueState.js";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

import type ValueState, then wherever used, replace with template string literals.

ValueState.None → `${ValueState.None}`

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

import type is erased entirely at compile time — there's no runtime object, so when we are setting some value to a property of type ValueState, we should use strings, we cannot use the enum anyway.

Comment thread packages/main/src/NumberInput.ts Outdated
this._validate();
this._setButtonState();
this.focused = true;
this.inputOuter.setAttribute("focused", "");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Here it should also be property/template-driven. We should not use remove/setattribute.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in e7a0665

Comment thread packages/main/src/NumberInput.ts Outdated
return {
patternMismatch: this.value !== 0 && !this._isValueWithCorrectPrecision,
rangeOverflow: this.max !== undefined && this.value >= this.max,
rangeUnderflow: this.min !== undefined && this.value <= this.min,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

form-validity boundaries are inverted: rangeOverflow: value >= max, rangeUnderflow: value <= min,
this means a value exactly at min or max (e.g. max=10, value=10) is flagged form-invalid; clamping to max leaves it permanently invalid.

Think we should remove the =

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in f0c7f00

onFocusOut={this._onfocusout}
onWheel={this._onMouseWheel}
<NumberInput
_showStepButtons={true}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The shadow DOM surface changed.

Step-input's internals went from [ui5-input] (.ui5-step-input-root/-input) to a nested [ui5-number-input].

Any consumer CSS, querySelector, or ::part targeting the old internals breaks.

I think this is a BREAKING CHANGE, which we should document somewhere.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Noted in PR description, so this information should go in Changelog.md

bubbles: true,
cancelable: true,
})
@event("_request-submit", {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

internal event is used as the public coordination channel between two public components

_setDefaultInputValueIfNeeded() {
if (this.input.value === "") {
const defaultValue = this._formatNumber(this.min || 0);
this.input.value = defaultValue;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

the same applies here

return !Number.isNaN(parsedValue) && !/, {2,}/.test(typedValue);
}

_decSpin(e: MouseEvent) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think the whole spin functionality lack tests

}

onExitDOM() {
this._cleanupLanguageChangeHandler();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we should reset the spin there.

this._resetSpin()

--_ui5_number_input_error_focused_background: var(--sapField_Focus_Background);
--_ui5_number_input_button_state_hover_background_color: var(--sapField_Background);
--_ui5_number_input_error_hover_background: var(--sapField_Background);
--_ui5_number_input_border_style: 1px solid var(--sapField_BorderColor);;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

two ;s

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 74a1366

--_ui5_number_input_error_warning_information_border_style: none;
--_ui5_number_input_after_border_style: var(--_ui5_input_error_warning_border_style);
--_ui5_number_input_error_background_color: var(--_ui5_input_input_background_color);
--_ui5_number_input_error_background_color_hover: var(--_ui5_number_input_input_error_background_color);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this is not used anywhere

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

All params are used

@@ -0,0 +1,32 @@
:host {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we should add: --_ui5_number_input_min_width: 7.25rem;

@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 17, 2026 09:55 — with GitHub Actions Inactive
@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 17, 2026 10:05 — with GitHub Actions Inactive
@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 17, 2026 10:07 — with GitHub Actions Inactive
@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 17, 2026 10:10 — with GitHub Actions Inactive
@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 17, 2026 10:14 — with GitHub Actions Inactive
@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 17, 2026 10:17 — with GitHub Actions Inactive
@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 17, 2026 10:23 — with GitHub Actions Inactive
@GDamyanov
GDamyanov temporarily deployed to netlify-preview July 17, 2026 10:28 — with GitHub Actions Inactive
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants