DEV: Support category, float, group and tag inputs for objects theme setting (#26113)

Continue from https://github.com/discourse/discourse/pull/25673 and https://github.com/discourse/discourse/pull/25811.

This PR adds support for category, float, group and tag types for schema theme settings.
This commit is contained in:
Osama Sayegh 2024-03-13 04:08:50 +03:00 committed by GitHub
parent de00c9a3d3
commit 3a4f4abdc9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 289 additions and 7 deletions

View File

@ -1,8 +1,12 @@
import Component from "@glimmer/component";
import BooleanField from "./types/boolean";
import CategoryField from "./types/category";
import EnumField from "./types/enum";
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";
export default class SchemaThemeSettingField extends Component {
get component() {
@ -11,10 +15,18 @@ export default class SchemaThemeSettingField extends Component {
return StringField;
case "integer":
return IntegerField;
case "float":
return FloatField;
case "boolean":
return BooleanField;
case "enum":
return EnumField;
case "category":
return CategoryField;
case "tag":
return TagField;
case "group":
return GroupField;
default:
throw new Error("unknown type");
}

View File

@ -0,0 +1,23 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { hash } from "@ember/helper";
import { action } from "@ember/object";
import CategoryChooser from "select-kit/components/category-chooser";
export default class SchemaThemeSettingTypeCategory extends Component {
@tracked value = this.args.value;
@action
onInput(newVal) {
this.value = newVal;
this.args.onChange(newVal);
}
<template>
<CategoryChooser
@value={{this.value}}
@onChange={{this.onInput}}
@options={{hash allowUncategorized=false}}
/>
</template>
}

View File

@ -4,12 +4,7 @@ import { action } from "@ember/object";
import ComboBox from "select-kit/components/combo-box";
export default class SchemaThemeSettingTypeEnum extends Component {
@tracked value;
constructor() {
super(...arguments);
this.value = this.args.value;
}
@tracked value = this.args.value;
get content() {
return this.args.spec.choices.map((choice) => {

View File

@ -0,0 +1,20 @@
import Component from "@glimmer/component";
import { Input } from "@ember/component";
import { on } from "@ember/modifier";
import { action } from "@ember/object";
export default class SchemaThemeSettingTypeFloat extends Component {
@action
onInput(event) {
this.args.onChange(parseFloat(event.currentTarget.value));
}
<template>
<Input
@value={{@value}}
{{on "input" this.onInput}}
@type="number"
step="0.1"
/>
</template>
}

View File

@ -0,0 +1,28 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { hash } from "@ember/helper";
import { action } from "@ember/object";
import Group from "discourse/models/group";
import GroupChooser from "select-kit/components/group-chooser";
export default class SchemaThemeSettingTypeGroup extends Component {
@tracked value = this.args.value;
@tracked groups = Group.findAll().then((groups) => {
this.groups = groups;
});
@action
onInput(newVal) {
this.value = newVal[0];
this.args.onChange(newVal[0]);
}
<template>
<GroupChooser
@content={{this.groups}}
@value={{this.value}}
@onChange={{this.onInput}}
@options={{hash maximum=1}}
/>
</template>
}

View File

@ -6,7 +6,7 @@ import { action } from "@ember/object";
export default class SchemaThemeSettingTypeInteger extends Component {
@action
onInput(event) {
this.args.onChange(event.currentTarget.value);
this.args.onChange(parseInt(event.currentTarget.value, 10));
}
<template>

View File

@ -0,0 +1,23 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { hash } from "@ember/helper";
import { action } from "@ember/object";
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}}
/>
</template>
}

View File

@ -172,12 +172,24 @@ export default function schemaAndData(version = 1) {
integer_field: {
type: "integer",
},
float_field: {
type: "float",
},
boolean_field: {
type: "boolean",
},
enum_field: {
type: "enum",
choices: ["nice", "awesome", "cool"]
},
category_field: {
type: "category",
},
group_field: {
type: "group",
},
tag_field: {
type: "tag",
}
},
};

View File

@ -2,6 +2,7 @@ import { click, fillIn, render } from "@ember/test-helpers";
import { module, test } from "qunit";
import schemaAndData from "discourse/tests/fixtures/theme-setting-schema-data";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import pretender, { response } from "discourse/tests/helpers/create-pretender";
import { queryAll } from "discourse/tests/helpers/qunit-helpers";
import selectKit from "discourse/tests/helpers/select-kit-helper";
import I18n from "discourse-i18n";
@ -292,6 +293,47 @@ module(
);
});
test("input fields are rendered even if they're not present in the data", async function (assert) {
const schema = {
name: "something",
identifier: "id",
properties: {
id: {
type: "string",
},
name: {
type: "string",
},
},
};
const data = [
{
id: "bu1",
name: "Big U",
},
{
id: "fi2",
},
];
await render(<template>
<AdminSchemaThemeSettingEditor @schema={{schema}} @data={{data}} />
</template>);
const inputFields = new InputFieldsFromDOM();
assert.strictEqual(inputFields.count, 2);
assert.dom(inputFields.fields.id.inputElement).hasValue("bu1");
assert.dom(inputFields.fields.name.inputElement).hasValue("Big U");
const tree = new TreeFromDOM();
await click(tree.nodes[1].element);
inputFields.refresh();
assert.strictEqual(inputFields.count, 2);
assert.dom(inputFields.fields.id.inputElement).hasValue("fi2");
assert.dom(inputFields.fields.name.inputElement).hasNoValue();
});
test("input fields for items at different levels", async function (assert) {
const setting = schemaAndData(2);
@ -371,6 +413,36 @@ module(
.hasValue("922229");
});
test("input fields of type float", async function (assert) {
const [schema, data] = schemaAndData(3);
await render(<template>
<AdminSchemaThemeSettingEditor @schema={{schema}} @data={{data}} />
</template>);
const inputFields = new InputFieldsFromDOM();
assert
.dom(inputFields.fields.float_field.labelElement)
.hasText("float_field");
assert.dom(inputFields.fields.float_field.inputElement).hasValue("");
await fillIn(inputFields.fields.float_field.inputElement, "6934.24");
const tree = new TreeFromDOM();
await click(tree.nodes[1].element);
inputFields.refresh();
assert.dom(inputFields.fields.float_field.inputElement).hasValue("");
tree.refresh();
await click(tree.nodes[0].element);
inputFields.refresh();
assert
.dom(inputFields.fields.float_field.inputElement)
.hasValue("6934.24");
});
test("input fields of type boolean", async function (assert) {
const setting = schemaAndData(3);
@ -428,6 +500,103 @@ module(
assert.strictEqual(enumSelector.header().value(), "nice");
});
test("input fields of type category", async function (assert) {
const [schema, data] = schemaAndData(3);
await render(<template>
<AdminSchemaThemeSettingEditor @schema={{schema}} @data={{data}} />
</template>);
const inputFields = new InputFieldsFromDOM();
const categorySelector = selectKit(
`${inputFields.fields.category_field.selector} .select-kit`
);
assert.strictEqual(categorySelector.header().value(), null);
await categorySelector.expand();
await categorySelector.selectRowByIndex(1);
const selectedCategoryId = categorySelector.header().value();
assert.ok(selectedCategoryId);
const tree = new TreeFromDOM();
await click(tree.nodes[1].element);
assert.strictEqual(categorySelector.header().value(), null);
tree.refresh();
await click(tree.nodes[0].element);
assert.strictEqual(categorySelector.header().value(), selectedCategoryId);
});
test("input fields of type tag", async function (assert) {
const [schema, data] = schemaAndData(3);
await render(<template>
<AdminSchemaThemeSettingEditor @schema={{schema}} @data={{data}} />
</template>);
const inputFields = new InputFieldsFromDOM();
const tagSelector = selectKit(
`${inputFields.fields.tag_field.selector} .select-kit`
);
assert.strictEqual(tagSelector.header().value(), null);
await tagSelector.expand();
await tagSelector.selectRowByIndex(1);
await tagSelector.selectRowByIndex(3);
assert.strictEqual(tagSelector.header().value(), "gazelle,cat");
const tree = new TreeFromDOM();
await click(tree.nodes[1].element);
assert.strictEqual(tagSelector.header().value(), null);
tree.refresh();
await click(tree.nodes[0].element);
assert.strictEqual(tagSelector.header().value(), "gazelle,cat");
});
test("input fields of type group", async function (assert) {
pretender.get("/groups/search.json", () => {
return response(200, [
{ id: 23, name: "testers" },
{ id: 74, name: "devs" },
{ id: 89, name: "customers" },
]);
});
const [schema, data] = schemaAndData(3);
await render(<template>
<AdminSchemaThemeSettingEditor @schema={{schema}} @data={{data}} />
</template>);
const inputFields = new InputFieldsFromDOM();
const groupSelector = selectKit(
`${inputFields.fields.group_field.selector} .select-kit`
);
assert.strictEqual(groupSelector.header().value(), null);
await groupSelector.expand();
await groupSelector.selectRowByValue(74);
assert.strictEqual(groupSelector.header().value(), "74");
const tree = new TreeFromDOM();
await click(tree.nodes[1].element);
assert.strictEqual(groupSelector.header().value(), null);
await groupSelector.expand();
await groupSelector.selectRowByValue(23);
assert.strictEqual(groupSelector.header().value(), "23");
tree.refresh();
await click(tree.nodes[0].element);
assert.strictEqual(groupSelector.header().value(), "74");
});
test("identifier field instantly updates in the navigation tree when the input field is changed", async function (assert) {
const setting = schemaAndData(2);