diff --git a/framework/base/src/main/java/org/apache/ofbiz/base/conversion/NumberConverters.java b/framework/base/src/main/java/org/apache/ofbiz/base/conversion/NumberConverters.java
index ad6a5032d3..0a44683d23 100644
--- a/framework/base/src/main/java/org/apache/ofbiz/base/conversion/NumberConverters.java
+++ b/framework/base/src/main/java/org/apache/ofbiz/base/conversion/NumberConverters.java
@@ -32,7 +32,7 @@
public class NumberConverters implements ConverterLoader {
private static Number fromString(String str, Locale locale) throws ConversionException {
- NumberFormat nf = NumberFormat.getNumberInstance(locale);
+ NumberFormat nf = NumberFormat.getNumberInstance(Locale.getDefault());
if (nf instanceof DecimalFormat) {
// CHECKSTYLE_OFF: ALMOST_ALL
((DecimalFormat) nf).setParseBigDecimal(true);
diff --git a/framework/widget/dtd/widget-form.xsd b/framework/widget/dtd/widget-form.xsd
index 2be59b4fc7..3e9ee92b92 100644
--- a/framework/widget/dtd/widget-form.xsd
+++ b/framework/widget/dtd/widget-form.xsd
@@ -1560,6 +1560,11 @@ under the License.
+
+
+ Controls the "step" attribute of the input element. Ignored if the "type" attribute of the field is not "number". See https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/step
+
+
Controls the pattern attribute of the input element. See https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/pattern
diff --git a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelFormField.java b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelFormField.java
index 830ac1444e..f2f9cb947d 100644
--- a/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelFormField.java
+++ b/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelFormField.java
@@ -366,7 +366,7 @@ public String getEntry(Map context, String defaultValu
if (retVal != null) {
// format string based on the user's locale and time zone
if (retVal instanceof Double || retVal instanceof Float || retVal instanceof BigDecimal) {
- NumberFormat nf = NumberFormat.getInstance(locale);
+ NumberFormat nf = NumberFormat.getInstance(Locale.getDefault());
nf.setMaximumFractionDigits(10);
return nf.format(retVal);
} else if (retVal instanceof java.sql.Date) {
@@ -5918,6 +5918,7 @@ public static class TextField extends FieldInfo {
private final SubHyperlink subHyperlink;
private final String type;
private final String pattern;
+ private final String step;
public TextField(Element element, ModelFormField modelFormField) {
super(element, modelFormField);
@@ -5925,6 +5926,7 @@ public TextField(Element element, ModelFormField modelFormField) {
this.defaultValue = FlexibleStringExpander.getInstance(element.getAttribute("default-value"));
this.mask = element.getAttribute("mask");
this.type = element.getAttribute("type");
+ this.step = element.getAttribute("step");
this.pattern = element.getAttribute("pattern");
Integer maxlength = null;
String maxlengthStr = element.getAttribute("maxlength");
@@ -5964,6 +5966,7 @@ protected TextField(int fieldSource, int size, Integer maxlength, ModelFormField
this.defaultValue = FlexibleStringExpander.getInstance("");
this.mask = "";
this.type = "";
+ this.step = "";
this.pattern = "";
this.maxlength = maxlength;
this.placeholder = FlexibleStringExpander.getInstance("");
@@ -5978,6 +5981,7 @@ protected TextField(int fieldSource, int size, Integer maxlength, String type, M
this.defaultValue = FlexibleStringExpander.getInstance("");
this.mask = "";
this.type = type;
+ this.step = "";
this.pattern = "";
this.maxlength = maxlength;
this.placeholder = FlexibleStringExpander.getInstance("");
@@ -5992,6 +5996,7 @@ private TextField(int fieldSource, int fieldType, ModelFormField modelFormField)
this.defaultValue = FlexibleStringExpander.getInstance("");
this.mask = "";
this.type = "";
+ this.step = "";
this.pattern = "";
this.maxlength = null;
this.placeholder = FlexibleStringExpander.getInstance("");
@@ -6014,6 +6019,7 @@ protected TextField(TextField original, ModelFormField modelFormField) {
this.defaultValue = original.defaultValue;
this.mask = original.mask;
this.type = original.type;
+ this.step = original.step;
this.pattern = original.pattern;
this.placeholder = original.placeholder;
this.size = original.size;
@@ -6135,6 +6141,14 @@ public String getType() {
return this.type;
}
+ /**
+ * Gets step.
+ * @return the step
+ */
+ public String getStep() {
+ return this.step;
+ }
+
/**
* Gets pattern.
* @return the pattern
diff --git a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilder.java b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilder.java
index 2c91b3032b..e53f28e348 100644
--- a/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilder.java
+++ b/framework/widget/src/main/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilder.java
@@ -255,6 +255,13 @@ public RenderableFtl textField(final Map context, final ModelFor
if (UtilValidate.isEmpty(type)) {
type = "text";
}
+ String step = "";
+ if (type.equals("number")) {
+ step = textField.getStep();
+ if (UtilValidate.isEmpty(step)) {
+ step = "any";
+ }
+ }
String pattern = "";
if (List.of("text", "email", "url", "tel").contains(type)) {
pattern = textField.getPattern();
@@ -308,6 +315,7 @@ public RenderableFtl textField(final Map context, final ModelFor
.stringParameter("name", name)
.stringParameter("className", String.join(" ", classes))
.stringParameter("type", type)
+ .stringParameter("step", step)
.stringParameter("pattern", pattern)
.stringParameter("alert", alert)
.stringParameter("value", value)
diff --git a/framework/widget/src/test/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilderTest.java b/framework/widget/src/test/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilderTest.java
index 13a15f5ee6..37144fe702 100644
--- a/framework/widget/src/test/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilderTest.java
+++ b/framework/widget/src/test/java/org/apache/ofbiz/widget/renderer/macro/RenderableFtlFormElementsBuilderTest.java
@@ -252,6 +252,7 @@ public void textFieldDefaultTypeIsText(@Mocked final ModelFormField.TextField te
final RenderableFtl renderableFtl = renderableFtlFormElementsBuilder.textField(Map.of("session", httpSession), textField, true);
assertThat(renderableFtl, MacroCallMatcher.hasName("renderTextField"));
assertThat(renderableFtl, MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndStringValue("type", "text")));
+ assertThat(renderableFtl, MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndStringValue("step", "")));
}
@Test
@@ -266,6 +267,23 @@ public void textFieldTypeNumber(@Mocked final ModelFormField.TextField textField
final RenderableFtl renderableFtl = renderableFtlFormElementsBuilder.textField(Map.of("session", httpSession), textField, true);
assertThat(renderableFtl, MacroCallMatcher.hasName("renderTextField"));
assertThat(renderableFtl, MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndStringValue("type", "number")));
+ assertThat(renderableFtl, MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndStringValue("step", "any")));
+ }
+
+ @Test
+ public void textFieldTypeNumberCustomStep(@Mocked final ModelFormField.TextField textField) {
+ new Expectations() {
+ {
+ textField.getType(); result = "number";
+ textField.getStep(); result = "0.1";
+ httpSession.getAttribute("delegatorName"); result = "DelegatorName";
+ }
+ };
+
+ final RenderableFtl renderableFtl = renderableFtlFormElementsBuilder.textField(Map.of("session", httpSession), textField, true);
+ assertThat(renderableFtl, MacroCallMatcher.hasName("renderTextField"));
+ assertThat(renderableFtl, MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndStringValue("type", "number")));
+ assertThat(renderableFtl, MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndStringValue("step", "0.1")));
}
@Test
@@ -280,6 +298,7 @@ public void textFieldTypeEmail(@Mocked final ModelFormField.TextField textField)
final RenderableFtl renderableFtl = renderableFtlFormElementsBuilder.textField(Map.of("session", httpSession), textField, true);
assertThat(renderableFtl, MacroCallMatcher.hasName("renderTextField"));
assertThat(renderableFtl, MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndStringValue("type", "email")));
+ assertThat(renderableFtl, MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndStringValue("step", "")));
}
@Test
@@ -294,6 +313,7 @@ public void textFieldTypeUrl(@Mocked final ModelFormField.TextField textField) {
final RenderableFtl renderableFtl = renderableFtlFormElementsBuilder.textField(Map.of("session", httpSession), textField, true);
assertThat(renderableFtl, MacroCallMatcher.hasName("renderTextField"));
assertThat(renderableFtl, MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndStringValue("type", "url")));
+ assertThat(renderableFtl, MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndStringValue("step", "")));
}
@Test
@@ -308,6 +328,7 @@ public void textFieldTypeTel(@Mocked final ModelFormField.TextField textField) {
final RenderableFtl renderableFtl = renderableFtlFormElementsBuilder.textField(Map.of("session", httpSession), textField, true);
assertThat(renderableFtl, MacroCallMatcher.hasName("renderTextField"));
assertThat(renderableFtl, MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndStringValue("type", "tel")));
+ assertThat(renderableFtl, MacroCallMatcher.hasParameters(MacroCallParameterMatcher.hasNameAndStringValue("step", "")));
}
@Test
diff --git a/themes/common-theme/template/macro/HtmlFormMacroLibrary.ftl b/themes/common-theme/template/macro/HtmlFormMacroLibrary.ftl
index a9db6463b5..19fce9d59f 100644
--- a/themes/common-theme/template/macro/HtmlFormMacroLibrary.ftl
+++ b/themes/common-theme/template/macro/HtmlFormMacroLibrary.ftl
@@ -43,7 +43,7 @@ under the License.
#macro>
<#macro renderHyperlinkField>#macro>
-<#macro renderTextField type pattern name className alert value="" textSize="" maxlength="" id="" event="" action=""
+<#macro renderTextField type step pattern name className alert value="" textSize="" maxlength="" id="" event="" action=""
disabled=false clientAutocomplete="" ajaxUrl="" ajaxEnabled="" mask="" tabindex="" readonly="" required=false
placeholder="" delegatorName="default">
@@ -69,6 +69,7 @@ under the License.
<#if tabindex?has_content> tabindex="${tabindex}"#if><#rt/>
<#if required?has_content && required> required#if>
<#if pattern?has_content> pattern="${pattern}"#if>
+ <#if step?has_content> step="${step}"#if>
/><#t/>
#macro>