diff --git a/app/assets/javascripts/admin/addon/components/site-settings/integer.hbs b/app/assets/javascripts/admin/addon/components/site-settings/integer.hbs
index a3a297a4976..ba2b0c92260 100644
--- a/app/assets/javascripts/admin/addon/components/site-settings/integer.hbs
+++ b/app/assets/javascripts/admin/addon/components/site-settings/integer.hbs
@@ -1,4 +1,9 @@
-
+
{{html-safe this.setting.description}}
\ No newline at end of file
diff --git a/app/assets/javascripts/discourse/app/components/number-field.js b/app/assets/javascripts/discourse/app/components/number-field.js
index c3ae066df64..38838358ef8 100644
--- a/app/assets/javascripts/discourse/app/components/number-field.js
+++ b/app/assets/javascripts/discourse/app/components/number-field.js
@@ -26,13 +26,27 @@ const ALLOWED_KEYS = [
export default TextField.extend({
classNameBindings: ["invalid"],
- keyDown: function (e) {
+ keyDown: function (event) {
return (
- ALLOWED_KEYS.includes(e.key) ||
- (e.key === "-" && parseInt(this.get("min"), 10) < 0)
+ ALLOWED_KEYS.includes(event.key) ||
+ (event.key === "-" && this._minNumber && this._minNumber < 0)
);
},
+ get _minNumber() {
+ if (!this.get("min")) {
+ return;
+ }
+ return parseInt(this.get("min"), 10);
+ },
+
+ get _maxNumber() {
+ if (!this.get("max")) {
+ return;
+ }
+ return parseInt(this.get("max"), 10);
+ },
+
@discourseComputed("number")
value: {
get(number) {
diff --git a/app/assets/javascripts/discourse/tests/integration/components/number-field-test.js b/app/assets/javascripts/discourse/tests/integration/components/number-field-test.js
new file mode 100644
index 00000000000..9c12e71fc44
--- /dev/null
+++ b/app/assets/javascripts/discourse/tests/integration/components/number-field-test.js
@@ -0,0 +1,62 @@
+import { fillIn, render, triggerKeyEvent } from "@ember/test-helpers";
+import { hbs } from "ember-cli-htmlbars";
+import { module, test } from "qunit";
+import { setupRenderingTest } from "discourse/tests/helpers/component-test";
+
+module("Integration | Component | number-field", function (hooks) {
+ setupRenderingTest(hooks);
+
+ test("number field", async function (assert) {
+ this.set("value", 123);
+
+ await render(hbs`
+
+ `);
+
+ await fillIn(".number-field-test", "33");
+
+ assert.equal(
+ this.get("value"),
+ 33,
+ "value is changed when the input is a valid number"
+ );
+
+ await fillIn(".number-field-test", "");
+ await triggerKeyEvent(".number-field-test", "keydown", 66); // b
+
+ assert.equal(
+ this.get("value"),
+ "",
+ "value is cleared when the input is NaN"
+ );
+ });
+
+ test("number field | min value", async function (assert) {
+ this.set("value", "");
+
+ await render(hbs`
+
+ `);
+
+ await triggerKeyEvent(".number-field-test", "keydown", 189); // -
+ await triggerKeyEvent(".number-field-test", "keydown", 49); // 1
+
+ assert.equal(
+ this.get("value"),
+ "",
+ "value is cleared when the input is less than the min"
+ );
+
+ await render(hbs`
+
+ `);
+
+ await fillIn(".number-field-test", "-1");
+
+ assert.equal(
+ this.get("value"),
+ "-1",
+ "negative input allowed when min is negative"
+ );
+ });
+});
diff --git a/lib/site_settings/type_supervisor.rb b/lib/site_settings/type_supervisor.rb
index db4bfa054a1..c06bd3bfb52 100644
--- a/lib/site_settings/type_supervisor.rb
+++ b/lib/site_settings/type_supervisor.rb
@@ -180,6 +180,17 @@ class SiteSettings::TypeSupervisor
end
end
+ if type == :integer
+ result[:min] = @validators[name].dig(:opts, :min) if @validators[name].dig(
+ :opts,
+ :min,
+ ).present?
+ result[:max] = @validators[name].dig(:opts, :max) if @validators[name].dig(
+ :opts,
+ :max,
+ ).present?
+ end
+
result[:allow_any] = @allow_any[name] if type == :list
result[:choices] = @choices[name] if @choices.has_key? name
diff --git a/spec/lib/site_settings/type_supervisor_spec.rb b/spec/lib/site_settings/type_supervisor_spec.rb
index b1d416c18d6..c12be5dc9c2 100644
--- a/spec/lib/site_settings/type_supervisor_spec.rb
+++ b/spec/lib/site_settings/type_supervisor_spec.rb
@@ -418,7 +418,7 @@ RSpec.describe SiteSettings::TypeSupervisor do
before do
settings.setting(:type_null, nil)
- settings.setting(:type_int, 1)
+ settings.setting(:type_int, 1, min: -10, max: 10)
settings.setting(:type_true, true)
settings.setting(:type_float, 2.3232)
settings.setting(:type_string, "string")
@@ -433,24 +433,31 @@ RSpec.describe SiteSettings::TypeSupervisor do
it "returns null type" do
expect(settings.type_supervisor.type_hash(:type_null)[:type]).to eq "null"
end
+
it "returns int type" do
expect(settings.type_supervisor.type_hash(:type_int)[:type]).to eq "integer"
end
+
it "returns bool type" do
expect(settings.type_supervisor.type_hash(:type_true)[:type]).to eq "bool"
end
+
it "returns float type" do
expect(settings.type_supervisor.type_hash(:type_float)[:type]).to eq "float"
end
+
it "returns string type" do
expect(settings.type_supervisor.type_hash(:type_string)[:type]).to eq "string"
end
+
it "returns url_list type" do
expect(settings.type_supervisor.type_hash(:type_url_list)[:type]).to eq "url_list"
end
+
it "returns textarea type" do
expect(settings.type_supervisor.type_hash(:type_textarea)[:textarea]).to eq true
end
+
it "returns enum type" do
expect(settings.type_supervisor.type_hash(:type_enum_choices)[:type]).to eq "enum"
end
@@ -474,5 +481,10 @@ RSpec.describe SiteSettings::TypeSupervisor do
expect(hash[:valid_values]).to eq %w[a b]
expect(hash[:translate_names]).to eq false
end
+
+ it "returns int min/max values" do
+ expect(settings.type_supervisor.type_hash(:type_int)[:min]).to eq(-10)
+ expect(settings.type_supervisor.type_hash(:type_int)[:max]).to eq(10)
+ end
end
end