mirror of
https://github.com/discourse/discourse.git
synced 2025-02-17 08:52:46 +08:00
DEV: Add validation message to integer fields in theme object editora (#26284)
Why this change?
This is a continuation of 8de869630f
.
In our schema, we support the `min` and `max` validation
rules like so:
```
some_objects_setting
type: objects
schema:
name: some_object
properties:
id:
type: integer
validations:
min: 5
max: 10
```
While the validations used to validate the objects on the server side,
we should also add client side validation for better UX.
This commit is contained in:
parent
8de869630f
commit
a30d73f255
|
@ -1,18 +1,80 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { Input } from "@ember/component";
|
||||
import { on } from "@ember/modifier";
|
||||
import { action } from "@ember/object";
|
||||
import { and, not } from "truth-helpers";
|
||||
import I18n from "discourse-i18n";
|
||||
import FieldInputDescription from "admin/components/schema-theme-setting/field-input-description";
|
||||
|
||||
export default class SchemaThemeSettingTypeInteger extends Component {
|
||||
@tracked touched = false;
|
||||
@tracked value = this.args.value;
|
||||
min = this.args.spec.validations?.min;
|
||||
max = this.args.spec.validations?.max;
|
||||
required = this.args.spec.required;
|
||||
|
||||
@action
|
||||
onInput(event) {
|
||||
this.args.onChange(parseInt(event.currentTarget.value, 10));
|
||||
this.touched = true;
|
||||
let newValue = parseInt(event.currentTarget.value, 10);
|
||||
|
||||
if (isNaN(newValue)) {
|
||||
newValue = null;
|
||||
}
|
||||
|
||||
this.value = newValue;
|
||||
this.args.onChange(newValue);
|
||||
}
|
||||
|
||||
get validationErrorMessage() {
|
||||
if (!this.touched) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.value) {
|
||||
if (this.required) {
|
||||
return I18n.t("admin.customize.theme.schema.fields.required");
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.min && this.value < this.min) {
|
||||
return I18n.t("admin.customize.theme.schema.fields.number.too_small", {
|
||||
count: this.min,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.max && this.value > this.max) {
|
||||
return I18n.t("admin.customize.theme.schema.fields.number.too_large", {
|
||||
count: this.max,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
<Input @value={{@value}} {{on "input" this.onInput}} @type="number" />
|
||||
<Input
|
||||
@value={{this.value}}
|
||||
{{on "input" this.onInput}}
|
||||
@type="number"
|
||||
inputmode="numeric"
|
||||
pattern="[0-9]*"
|
||||
max={{this.max}}
|
||||
min={{this.min}}
|
||||
required={{this.required}}
|
||||
/>
|
||||
|
||||
<FieldInputDescription @description={{@description}} />
|
||||
<div class="schema-field__input-supporting-text">
|
||||
{{#if (and @description (not this.validationErrorMessage))}}
|
||||
<FieldInputDescription @description={{@description}} />
|
||||
{{/if}}
|
||||
|
||||
{{#if this.validationErrorMessage}}
|
||||
<div class="schema-field__input-error">
|
||||
{{this.validationErrorMessage}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -63,11 +63,15 @@ class InputFieldsFromDOM {
|
|||
refresh() {
|
||||
this.fields = {};
|
||||
this.count = 0;
|
||||
|
||||
[...queryAll(".schema-field")].forEach((field) => {
|
||||
this.count += 1;
|
||||
|
||||
this.fields[field.dataset.name] = {
|
||||
labelElement: field.querySelector(".schema-field__label"),
|
||||
inputElement: field.querySelector(".schema-field__input").children[0],
|
||||
countElement: field.querySelector(".schema-field__input-count"),
|
||||
errorElement: field.querySelector(".schema-field__input-error"),
|
||||
selector: `.schema-field[data-name="${field.dataset.name}"]`,
|
||||
};
|
||||
});
|
||||
|
@ -449,59 +453,96 @@ module(
|
|||
<AdminSchemaThemeSettingEditor @themeId="1" @setting={{setting}} />
|
||||
</template>);
|
||||
|
||||
const fieldSelector =
|
||||
".schema-field[data-name='id'] .schema-field__input";
|
||||
const inputFields = new InputFieldsFromDOM();
|
||||
|
||||
assert.dom(`${fieldSelector} .schema-field__input-count`).hasText("3/5");
|
||||
assert.dom(inputFields.fields.id.labelElement).hasText("id*");
|
||||
assert.dom(inputFields.fields.id.countElement).hasText("3/5");
|
||||
|
||||
await fillIn(`${fieldSelector} input`, "1");
|
||||
await fillIn(inputFields.fields.id.inputElement, "1");
|
||||
|
||||
assert.dom(`${fieldSelector} .schema-field__input-error`).hasText(
|
||||
assert.dom(inputFields.fields.id.countElement).hasText("1/5");
|
||||
|
||||
inputFields.refresh();
|
||||
|
||||
assert.dom(inputFields.fields.id.errorElement).hasText(
|
||||
I18n.t("admin.customize.theme.schema.fields.string.too_short", {
|
||||
count: 2,
|
||||
})
|
||||
);
|
||||
|
||||
await fillIn(`${fieldSelector} input`, "");
|
||||
await fillIn(inputFields.fields.id.inputElement, "");
|
||||
|
||||
assert.dom(`${fieldSelector} .schema-field__input-count`).hasText("0/5");
|
||||
assert.dom(inputFields.fields.id.countElement).hasText("0/5");
|
||||
|
||||
assert
|
||||
.dom(`${fieldSelector} .schema-field__input-error`)
|
||||
.dom(inputFields.fields.id.errorElement)
|
||||
.hasText(I18n.t("admin.customize.theme.schema.fields.required"));
|
||||
});
|
||||
|
||||
test("input fields of type integer", async function (assert) {
|
||||
const setting = schemaAndData(3);
|
||||
const setting = ThemeSettings.create({
|
||||
setting: "objects_setting",
|
||||
objects_schema: {
|
||||
name: "something",
|
||||
identifier: "id",
|
||||
properties: {
|
||||
id: {
|
||||
type: "integer",
|
||||
required: true,
|
||||
validations: {
|
||||
max: 10,
|
||||
min: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
value: [
|
||||
{
|
||||
id: 6,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await render(<template>
|
||||
<AdminSchemaThemeSettingEditor @themeId="1" @setting={{setting}} />
|
||||
</template>);
|
||||
|
||||
const inputFields = new InputFieldsFromDOM();
|
||||
|
||||
assert.dom(inputFields.fields.id.labelElement).hasText("id*");
|
||||
assert.dom(inputFields.fields.id.inputElement).hasValue("6");
|
||||
|
||||
assert
|
||||
.dom(inputFields.fields.integer_field.labelElement)
|
||||
.hasText("integer_field");
|
||||
assert.dom(inputFields.fields.integer_field.inputElement).hasValue("92");
|
||||
assert
|
||||
.dom(inputFields.fields.integer_field.inputElement)
|
||||
.dom(inputFields.fields.id.inputElement)
|
||||
.hasAttribute("type", "number");
|
||||
await fillIn(inputFields.fields.integer_field.inputElement, "922229");
|
||||
|
||||
const tree = new TreeFromDOM();
|
||||
await click(tree.nodes[1].element);
|
||||
await fillIn(inputFields.fields.id.inputElement, "922229");
|
||||
|
||||
inputFields.refresh();
|
||||
|
||||
assert.dom(inputFields.fields.integer_field.inputElement).hasValue("820");
|
||||
assert.dom(inputFields.fields.id.errorElement).hasText(
|
||||
I18n.t("admin.customize.theme.schema.fields.number.too_large", {
|
||||
count: 10,
|
||||
})
|
||||
);
|
||||
|
||||
await fillIn(inputFields.fields.id.inputElement, "0");
|
||||
|
||||
inputFields.refresh();
|
||||
|
||||
assert.dom(inputFields.fields.id.errorElement).hasText(
|
||||
I18n.t("admin.customize.theme.schema.fields.number.too_small", {
|
||||
count: 5,
|
||||
})
|
||||
);
|
||||
|
||||
await fillIn(inputFields.fields.id.inputElement, "");
|
||||
|
||||
tree.refresh();
|
||||
await click(tree.nodes[0].element);
|
||||
inputFields.refresh();
|
||||
|
||||
assert
|
||||
.dom(inputFields.fields.integer_field.inputElement)
|
||||
.hasValue("922229");
|
||||
.dom(inputFields.fields.id.errorElement)
|
||||
.hasText(I18n.t("admin.customize.theme.schema.fields.required"));
|
||||
});
|
||||
|
||||
test("input fields of type float", async function (assert) {
|
||||
|
|
|
@ -5651,6 +5651,9 @@ en:
|
|||
required: "*required"
|
||||
string:
|
||||
too_short: "must be at least %{count} characters"
|
||||
number:
|
||||
too_small: "must be greater than or equal to %{count}"
|
||||
too_large: "must be less than or equal to %{count}"
|
||||
|
||||
colors:
|
||||
select_base:
|
||||
|
|
Loading…
Reference in New Issue
Block a user