From 8bec0ca0839a818cb4c95739d90ce8e1e48cda5f Mon Sep 17 00:00:00 2001 From: Osama Sayegh Date: Thu, 29 Feb 2024 11:11:32 +0300 Subject: [PATCH] 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. --- .../schema-theme-setting/editor.gjs | 4 +- .../components/schema-theme-setting/field.gjs | 35 ++++---- .../schema-theme-setting/types/boolean.gjs | 15 ++++ .../schema-theme-setting/types/enum.gjs | 36 ++++++++ .../schema-theme-setting/types/integer.gjs | 15 ++++ .../schema-theme-setting/types/string.gjs | 15 ++++ .../admin-customize-themes-schema.js | 16 ++++ .../fixtures/theme-setting-schema-data.js | 34 +++++++ .../editor-test.gjs | 89 +++++++++++++++++++ 9 files changed, 242 insertions(+), 17 deletions(-) create mode 100644 app/assets/javascripts/admin/addon/components/schema-theme-setting/types/boolean.gjs create mode 100644 app/assets/javascripts/admin/addon/components/schema-theme-setting/types/enum.gjs create mode 100644 app/assets/javascripts/admin/addon/components/schema-theme-setting/types/integer.gjs create mode 100644 app/assets/javascripts/admin/addon/components/schema-theme-setting/types/string.gjs diff --git a/app/assets/javascripts/admin/addon/components/schema-theme-setting/editor.gjs b/app/assets/javascripts/admin/addon/components/schema-theme-setting/editor.gjs index 38d98d1e08f..f1b9b9bfcc7 100644 --- a/app/assets/javascripts/admin/addon/components/schema-theme-setting/editor.gjs +++ b/app/assets/javascripts/admin/addon/components/schema-theme-setting/editor.gjs @@ -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|}} {{/each}} diff --git a/app/assets/javascripts/admin/addon/components/schema-theme-setting/field.gjs b/app/assets/javascripts/admin/addon/components/schema-theme-setting/field.gjs index e033f99cadf..e7702ae00bc 100644 --- a/app/assets/javascripts/admin/addon/components/schema-theme-setting/field.gjs +++ b/app/assets/javascripts/admin/addon/components/schema-theme-setting/field.gjs @@ -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); - } - diff --git a/app/assets/javascripts/admin/addon/components/schema-theme-setting/types/boolean.gjs b/app/assets/javascripts/admin/addon/components/schema-theme-setting/types/boolean.gjs new file mode 100644 index 00000000000..21d10b58557 --- /dev/null +++ b/app/assets/javascripts/admin/addon/components/schema-theme-setting/types/boolean.gjs @@ -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); + } + + +} diff --git a/app/assets/javascripts/admin/addon/components/schema-theme-setting/types/enum.gjs b/app/assets/javascripts/admin/addon/components/schema-theme-setting/types/enum.gjs new file mode 100644 index 00000000000..077064ca4fe --- /dev/null +++ b/app/assets/javascripts/admin/addon/components/schema-theme-setting/types/enum.gjs @@ -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); + } + + +} diff --git a/app/assets/javascripts/admin/addon/components/schema-theme-setting/types/integer.gjs b/app/assets/javascripts/admin/addon/components/schema-theme-setting/types/integer.gjs new file mode 100644 index 00000000000..761bb10114d --- /dev/null +++ b/app/assets/javascripts/admin/addon/components/schema-theme-setting/types/integer.gjs @@ -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); + } + + +} diff --git a/app/assets/javascripts/admin/addon/components/schema-theme-setting/types/string.gjs b/app/assets/javascripts/admin/addon/components/schema-theme-setting/types/string.gjs new file mode 100644 index 00000000000..0b427c6b063 --- /dev/null +++ b/app/assets/javascripts/admin/addon/components/schema-theme-setting/types/string.gjs @@ -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); + } + + +} diff --git a/app/assets/javascripts/admin/addon/controllers/admin-customize-themes-schema.js b/app/assets/javascripts/admin/addon/controllers/admin-customize-themes-schema.js index 1c6098693a5..878a8b69360 100644 --- a/app/assets/javascripts/admin/addon/controllers/admin-customize-themes-schema.js +++ b/app/assets/javascripts/admin/addon/controllers/admin-customize-themes-schema.js @@ -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: { diff --git a/app/assets/javascripts/discourse/tests/fixtures/theme-setting-schema-data.js b/app/assets/javascripts/discourse/tests/fixtures/theme-setting-schema-data.js index 7add942a8ef..a445c838c19 100644 --- a/app/assets/javascripts/discourse/tests/fixtures/theme-setting-schema-data.js +++ b/app/assets/javascripts/discourse/tests/fixtures/theme-setting-schema-data.js @@ -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"); } diff --git a/app/assets/javascripts/discourse/tests/integration/components/admin-schema-theme-setting/editor-test.gjs b/app/assets/javascripts/discourse/tests/integration/components/admin-schema-theme-setting/editor-test.gjs index fbe35dacbd4..d11243cb73f 100644 --- a/app/assets/javascripts/discourse/tests/integration/components/admin-schema-theme-setting/editor-test.gjs +++ b/app/assets/javascripts/discourse/tests/integration/components/admin-schema-theme-setting/editor-test.gjs @@ -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(); + + 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(); + + 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(); + + 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(