mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
DEV: Change tag type to tags type for theme object schema (#26315)
Why this change? While working on the tag selector for the theme object editor, I realised that there is an extremely high possibility that users might want to select more than one tag. By supporting the ability to select more than one tag, it also means that we get support for a single tag for free as well. What does this change do? 1. Change `type: tag` to `type: tags` and support `min` and `max` validations for `type: tags`. 2. Fix the `<SchemaThemeSetting::Types::Tags>` component to support the `min` and `max` validations
This commit is contained in:
committed by
GitHub
parent
3685eafb7a
commit
86b2e3aa3e
@@ -8,10 +8,12 @@ import FloatField from "./types/float";
|
||||
import GroupField from "./types/group";
|
||||
import IntegerField from "./types/integer";
|
||||
import StringField from "./types/string";
|
||||
import TagField from "./types/tag";
|
||||
import TagsField from "./types/tags";
|
||||
|
||||
export default class SchemaThemeSettingField extends Component {
|
||||
get component() {
|
||||
const type = this.args.spec.type;
|
||||
|
||||
switch (this.args.spec.type) {
|
||||
case "string":
|
||||
return StringField;
|
||||
@@ -25,12 +27,12 @@ export default class SchemaThemeSettingField extends Component {
|
||||
return EnumField;
|
||||
case "category":
|
||||
return CategoryField;
|
||||
case "tag":
|
||||
return TagField;
|
||||
case "tags":
|
||||
return TagsField;
|
||||
case "group":
|
||||
return GroupField;
|
||||
default:
|
||||
throw new Error("unknown type");
|
||||
throw new Error(`unknown type ${type}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { hash } from "@ember/helper";
|
||||
import { action } from "@ember/object";
|
||||
import FieldInputDescription from "admin/components/schema-theme-setting/field-input-description";
|
||||
import TagChooser from "select-kit/components/tag-chooser";
|
||||
|
||||
export default class SchemaThemeSettingTypeTag extends Component {
|
||||
@tracked value = this.args.value;
|
||||
|
||||
@action
|
||||
onInput(newVal) {
|
||||
this.value = newVal;
|
||||
this.args.onChange(newVal);
|
||||
}
|
||||
|
||||
<template>
|
||||
<TagChooser
|
||||
@tags={{this.value}}
|
||||
@onChange={{this.onInput}}
|
||||
@options={{hash allowAny=false}}
|
||||
/>
|
||||
|
||||
<FieldInputDescription @description={{@description}} />
|
||||
</template>
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
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";
|
||||
import TagChooser from "select-kit/components/tag-chooser";
|
||||
|
||||
export default class SchemaThemeSettingTypeTags extends Component {
|
||||
@tracked touched = false;
|
||||
@tracked value = this.args.value;
|
||||
required = this.args.spec.required;
|
||||
min = this.args.spec.validations?.min;
|
||||
max = this.args.spec.validations?.max;
|
||||
|
||||
@action
|
||||
onInput(newVal) {
|
||||
this.touched = true;
|
||||
this.value = newVal;
|
||||
this.args.onChange(newVal);
|
||||
}
|
||||
|
||||
get tagChooserOption() {
|
||||
return {
|
||||
allowAny: false,
|
||||
maximum: this.max,
|
||||
};
|
||||
}
|
||||
|
||||
get validationErrorMessage() {
|
||||
if (!this.touched) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
(this.min && this.value.length < this.min) ||
|
||||
(this.required && (!this.value || this.value.length === 0))
|
||||
) {
|
||||
return I18n.t("admin.customize.theme.schema.fields.tags.at_least_tag", {
|
||||
count: this.min,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
<TagChooser
|
||||
@tags={{this.value}}
|
||||
@onChange={{this.onInput}}
|
||||
@options={{this.tagChooserOption}}
|
||||
class={{if this.validationErrorMessage "--invalid"}}
|
||||
/>
|
||||
|
||||
<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>
|
||||
}
|
||||
@@ -189,8 +189,8 @@ export default function schemaAndData(version = 1) {
|
||||
group_field: {
|
||||
type: "group",
|
||||
},
|
||||
tag_field: {
|
||||
type: "tag",
|
||||
tags_field: {
|
||||
type: "tags",
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -726,34 +726,76 @@ module(
|
||||
assert.dom(categorySelector.clearButton()).exists("is clearable");
|
||||
});
|
||||
|
||||
test("input fields of type tag", async function (assert) {
|
||||
const setting = schemaAndData(3);
|
||||
test("input fields of type tags which is required", async function (assert) {
|
||||
const setting = ThemeSettings.create({
|
||||
setting: "objects_setting",
|
||||
objects_schema: {
|
||||
name: "something",
|
||||
identifier: "id",
|
||||
properties: {
|
||||
required_tags: {
|
||||
type: "tags",
|
||||
required: true,
|
||||
validations: {
|
||||
min: 2,
|
||||
max: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
value: [
|
||||
{
|
||||
required_tags: ["gazelle", "cat"],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await render(<template>
|
||||
<AdminSchemaThemeSettingEditor @themeId="1" @setting={{setting}} />
|
||||
</template>);
|
||||
|
||||
const inputFields = new InputFieldsFromDOM();
|
||||
|
||||
const tagSelector = selectKit(
|
||||
`${inputFields.fields.tag_field.selector} .select-kit`
|
||||
`${inputFields.fields.required_tags.selector} .select-kit`
|
||||
);
|
||||
|
||||
assert.strictEqual(tagSelector.header().value(), "gazelle,cat");
|
||||
|
||||
await tagSelector.expand();
|
||||
await tagSelector.selectRowByIndex(2);
|
||||
await tagSelector.collapse();
|
||||
|
||||
assert.strictEqual(tagSelector.header().value(), "gazelle,cat,dog");
|
||||
|
||||
await tagSelector.expand();
|
||||
await tagSelector.deselectItemByName("gazelle");
|
||||
await tagSelector.deselectItemByName("cat");
|
||||
await tagSelector.deselectItemByName("dog");
|
||||
await tagSelector.collapse();
|
||||
|
||||
assert.strictEqual(tagSelector.header().value(), null);
|
||||
|
||||
inputFields.refresh();
|
||||
|
||||
assert.dom(inputFields.fields.required_tags.errorElement).hasText(
|
||||
I18n.t("admin.customize.theme.schema.fields.tags.at_least_tag", {
|
||||
count: 2,
|
||||
})
|
||||
);
|
||||
|
||||
await tagSelector.expand();
|
||||
await tagSelector.selectRowByIndex(1);
|
||||
await tagSelector.selectRowByIndex(3);
|
||||
|
||||
assert.strictEqual(tagSelector.header().value(), "gazelle,cat");
|
||||
assert.strictEqual(tagSelector.header().value(), "gazelle");
|
||||
|
||||
const tree = new TreeFromDOM();
|
||||
await click(tree.nodes[1].element);
|
||||
assert.strictEqual(tagSelector.header().value(), null);
|
||||
inputFields.refresh();
|
||||
|
||||
tree.refresh();
|
||||
|
||||
await click(tree.nodes[0].element);
|
||||
assert.strictEqual(tagSelector.header().value(), "gazelle,cat");
|
||||
assert.dom(inputFields.fields.required_tags.errorElement).hasText(
|
||||
I18n.t("admin.customize.theme.schema.fields.tags.at_least_tag", {
|
||||
count: 2,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test("input fields of type group", async function (assert) {
|
||||
|
||||
@@ -30,6 +30,12 @@
|
||||
|
||||
.select-kit {
|
||||
width: 100%;
|
||||
|
||||
&.--invalid {
|
||||
summary {
|
||||
border-color: var(--danger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.schema-field__input-description {
|
||||
|
||||
Reference in New Issue
Block a user