FEATURE: Support boolean, enum and integer fields for schema theme settings (#25933)

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

This commit adds support for boolean, integer and enum types for schema theme settings.
This commit is contained in:
Osama Sayegh 2024-02-29 11:11:32 +03:00 committed by GitHub
parent ef292d1fed
commit 8bec0ca083
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 242 additions and 17 deletions

View File

@ -96,7 +96,7 @@ export default class SchemaThemeSettingEditor extends Component {
}
list.push({
name,
type: spec.type,
spec,
value: node.object[name],
});
}
@ -194,8 +194,8 @@ export default class SchemaThemeSettingEditor extends Component {
{{#each this.fields as |field|}}
<FieldInput
@name={{field.name}}
@type={{field.type}}
@value={{field.value}}
@spec={{field.spec}}
@onValueChange={{fn this.inputFieldChanged field}}
/>
{{/each}}

View File

@ -1,29 +1,34 @@
import Component from "@glimmer/component";
import { Input } from "@ember/component";
import BooleanField from "./types/boolean";
import EnumField from "./types/enum";
import IntegerField from "./types/integer";
import StringField from "./types/string";
export default class SchemaThemeSettingField extends Component {
#bufferVal;
get component() {
if (this.args.type === "string") {
return Input;
switch (this.args.spec.type) {
case "string":
return StringField;
case "integer":
return IntegerField;
case "boolean":
return BooleanField;
case "enum":
return EnumField;
default:
throw new Error("unknown type");
}
}
get value() {
return this.#bufferVal || this.args.value;
}
set value(v) {
this.#bufferVal = v;
this.args.onValueChange(v);
}
<template>
<div class="schema-field" data-name={{@name}}>
<label>{{@name}}</label>
<div class="input">
<this.component @value={{this.value}} />
<this.component
@value={{@value}}
@spec={{@spec}}
@onChange={{@onValueChange}}
/>
</div>
</div>
</template>

View File

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

View File

@ -0,0 +1,36 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
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;
}
get content() {
return this.args.spec.choices.map((choice) => {
return {
name: choice,
id: choice,
};
});
}
@action
onInput(newVal) {
this.value = newVal;
this.args.onChange(newVal);
}
<template>
<ComboBox
@content={{this.content}}
@value={{this.value}}
@onChange={{this.onInput}}
/>
</template>
}

View File

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

View File

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

View File

@ -4,6 +4,9 @@ export default class AdminCustomizeThemesSchemaController extends Controller {
data = [
{
name: "item 1",
width: 143,
is_valid: true,
enum_prop: 11,
children: [
{
name: "child 1-1",
@ -25,6 +28,9 @@ export default class AdminCustomizeThemesSchemaController extends Controller {
},
{
name: "item 2",
width: 803,
is_valid: false,
enum_prop: 22,
children: [
{
name: "child 2-1",
@ -53,6 +59,16 @@ export default class AdminCustomizeThemesSchemaController extends Controller {
name: {
type: "string",
},
width: {
type: "integer",
},
is_valid: {
type: "boolean",
},
enum_prop: {
type: "enum",
choices: [11, 22],
},
children: {
type: "objects",
schema: {

View File

@ -159,6 +159,40 @@ export default function schemaAndData(version = 1) {
],
},
];
} else if (version === 3) {
schema = {
name: "something",
identifier: "name",
properties: {
name: {
type: "string",
},
integer_field: {
type: "integer",
},
boolean_field: {
type: "boolean",
},
enum_field: {
type: "enum",
choices: ["nice", "awesome", "cool"]
}
},
};
data = [
{
name: "lamb",
integer_field: 92,
boolean_field: true,
enum_field: "awesome"
},
{
name: "cow",
integer_field: 820,
boolean_field: false,
enum_field: "cool"
},
];
} else {
throw new Error("unknown fixture version");
}

View File

@ -3,6 +3,7 @@ import { module, test } from "qunit";
import schemaAndData from "discourse/tests/fixtures/theme-setting-schema-data";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { queryAll } from "discourse/tests/helpers/qunit-helpers";
import selectKit from "discourse/tests/helpers/select-kit-helper";
import I18n from "discourse-i18n";
import AdminSchemaThemeSettingEditor from "admin/components/schema-theme-setting/editor";
@ -47,6 +48,7 @@ class InputFieldsFromDOM {
this.fields[field.dataset.name] = {
labelElement: field.querySelector("label"),
inputElement: field.querySelector(".input").children[0],
selector: `.schema-field[data-name="${field.dataset.name}"]`,
};
});
}
@ -329,6 +331,93 @@ module(
assert.dom(inputFields.fields.icon.inputElement).hasValue("asterisk");
});
test("input fields of type integer", 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.integer_field.labelElement)
.hasText("integer_field");
assert.dom(inputFields.fields.integer_field.inputElement).hasValue("92");
assert
.dom(inputFields.fields.integer_field.inputElement)
.hasAttribute("type", "number");
await fillIn(inputFields.fields.integer_field.inputElement, "922229");
const tree = new TreeFromDOM();
await click(tree.nodes[1].element);
inputFields.refresh();
assert.dom(inputFields.fields.integer_field.inputElement).hasValue("820");
tree.refresh();
await click(tree.nodes[0].element);
inputFields.refresh();
assert
.dom(inputFields.fields.integer_field.inputElement)
.hasValue("922229");
});
test("input fields of type boolean", 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.boolean_field.labelElement)
.hasText("boolean_field");
assert.dom(inputFields.fields.boolean_field.inputElement).isChecked();
await click(inputFields.fields.boolean_field.inputElement);
const tree = new TreeFromDOM();
await click(tree.nodes[1].element);
inputFields.refresh();
assert
.dom(inputFields.fields.boolean_field.labelElement)
.hasText("boolean_field");
assert.dom(inputFields.fields.boolean_field.inputElement).isNotChecked();
tree.refresh();
await click(tree.nodes[0].element);
inputFields.refresh();
assert.dom(inputFields.fields.boolean_field.inputElement).isNotChecked();
});
test("input fields of type enum", async function (assert) {
const [schema, data] = schemaAndData(3);
await render(<template>
<AdminSchemaThemeSettingEditor @schema={{schema}} @data={{data}} />
</template>);
const inputFields = new InputFieldsFromDOM();
const enumSelector = selectKit(
`${inputFields.fields.enum_field.selector} .select-kit`
);
assert.strictEqual(enumSelector.header().value(), "awesome");
await enumSelector.expand();
await enumSelector.selectRowByValue("nice");
assert.strictEqual(enumSelector.header().value(), "nice");
const tree = new TreeFromDOM();
await click(tree.nodes[1].element);
assert.strictEqual(enumSelector.header().value(), "cool");
tree.refresh();
await click(tree.nodes[0].element);
assert.strictEqual(enumSelector.header().value(), "nice");
});
test("identifier field instantly updates in the navigation tree when the input field is changed", async function (assert) {
const [schema, data] = schemaAndData(2);
await render(<template>