UX: Using CategoryChooser for param param_input (#306)

This change changes the category selector to use CategoryChooser instead
of a bare text input to improve the user experience.

Related meta link: https://meta.discourse.org/t/wishlist-param-dropdown-for-data-explorer-query/253883/28
This commit is contained in:
锦心 2024-08-13 16:03:26 +08:00 committed by GitHub
parent 41dfa217ca
commit 902b8c7913
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 88 additions and 30 deletions

View File

@ -64,6 +64,14 @@
/> />
<span class="param-name">{{@info.identifier}}</span> <span class="param-name">{{@info.identifier}}</span>
{{else if (eq this.type "category_id")}}
<CategoryChooser
@value={{this.value}}
@onChange={{this.updateValue}}
name={{@info.identifier}}
/>
<span class="param-name">{{@info.identifier}}</span>
{{else}} {{else}}
<TextField <TextField
@value={{this.value}} @value={{this.value}}

View File

@ -19,7 +19,7 @@ const layoutMap = {
user_id: "user_id", user_id: "user_id",
post_id: "string", post_id: "string",
topic_id: "generic", topic_id: "generic",
category_id: "generic", category_id: "category_id",
group_id: "generic", group_id: "generic",
badge_id: "generic", badge_id: "generic",
int_list: "generic", int_list: "generic",
@ -62,24 +62,27 @@ export default class ParamInput extends Component {
this.args.updateParams(this.args.info.identifier, this.boolValue); this.args.updateParams(this.args.info.identifier, this.boolValue);
} }
} else { } else {
this.value = this.value = this.normalizeValue(initialValue);
this.args.info.type === "category_id"
? this.dasherizeCategoryId(initialValue)
: initialValue;
this.args.updateParams(this.args.info.identifier, this.value); this.args.updateParams(this.args.info.identifier, this.value);
} }
} else { } else {
// if no parsed params then get and set default values // if no parsed params then get and set default values
const defaultValue = this.args.info.default; const defaultValue = this.args.info.default;
this.value = this.value = this.normalizeValue(defaultValue);
this.args.info.type === "category_id"
? this.dasherizeCategoryId(defaultValue)
: defaultValue;
this.boolValue = defaultValue !== "false"; this.boolValue = defaultValue !== "false";
this.nullableBoolValue = defaultValue; this.nullableBoolValue = defaultValue;
} }
} }
normalizeValue(value) {
switch (this.args.info.type) {
case "category_id":
return this.digitalizeCategoryId(value);
default:
return value;
}
}
get type() { get type() {
const type = this.args.info.type; const type = this.args.info.type;
if ((type === "time" || type === "date") && !allowsInputTypeTime()) { if ((type === "time" || type === "date") && !allowsInputTypeTime()) {
@ -135,18 +138,8 @@ export default class ParamInput extends Component {
case "category_id": case "category_id":
if (isPositiveInt) { if (isPositiveInt) {
return !!this.site.categories.find((c) => c.id === intVal); return !!this.site.categories.find((c) => c.id === intVal);
} else if (/\//.test(value)) {
const match = /(.*)\/(.*)/.exec(value);
if (!match) {
return false;
}
const result = Category.findBySlug(
dasherize(match[2]),
dasherize(match[1])
);
return !!result;
} else { } else {
return !!Category.findBySlug(dasherize(value)); return false;
} }
case "group_id": case "group_id":
const groups = this.site.get("groups"); const groups = this.site.get("groups");
@ -163,13 +156,25 @@ export default class ParamInput extends Component {
return this.site.get("groups"); return this.site.get("groups");
} }
dasherizeCategoryId(value) { digitalizeCategoryId(value) {
value = String(value || ""); value = String(value || "");
const isPositiveInt = /^\d+$/.test(value); const isPositiveInt = /^\d+$/.test(value);
if (!isPositiveInt && value !== dasherize(value)) { if (!isPositiveInt) {
return dasherize(value); if (/\//.test(value)) {
const match = /(.*)\/(.*)/.exec(value);
if (!match) {
value = null;
} else {
value = Category.findBySlug(
dasherize(match[2]),
dasherize(match[1])
)?.id;
}
} else {
value = Category.findBySlug(dasherize(value))?.id;
}
} }
return value; return value?.toString();
} }
@action @action
@ -177,12 +182,9 @@ export default class ParamInput extends Component {
// handle selectKit inputs as well as traditional inputs // handle selectKit inputs as well as traditional inputs
const value = input.target ? input.target.value : input; const value = input.target ? input.target.value : input;
if (value.length) { if (value.length) {
this.value = this.value = this.normalizeValue(value.toString());
this.args.info.type === "category_id"
? this.dasherizeCategoryId(value.toString())
: value.toString();
} else { } else {
this.value = value; this.value = this.normalizeValue(value);
} }
this.args.updateParams(this.args.info.identifier, this.value); this.args.updateParams(this.args.info.identifier, this.value);

View File

@ -63,6 +63,12 @@ RSpec.describe "Param input", type: :system, js: true do
::DiscourseDataExplorer::Parameter ::DiscourseDataExplorer::Parameter
.create_from_sql(ALL_PARAMS_SQL) .create_from_sql(ALL_PARAMS_SQL)
.each { |param| expect(page).to have_css(".query-params [name=\"#{param.identifier}\"]") } .each do |param|
if !param.nullable && param.type != :boolean && param.default.nil?
expect(page).to have_css(".query-params .param.invalid [name=\"#{param.identifier}\"]")
else
expect(page).to have_css(".query-params .param.valid [name=\"#{param.identifier}\"]")
end
end
end end
end end

View File

@ -0,0 +1,42 @@
import { render } from "@ember/test-helpers";
import hbs from "htmlbars-inline-precompile";
import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import selectKit from "discourse/tests/helpers/select-kit-helper";
const values = {};
function updateParams(identifier, value) {
values[identifier] = value;
}
module("Data Explorer Plugin | Component | param-input", function (hooks) {
setupRenderingTest(hooks);
test("Renders the categroy_id type correctly", async function (assert) {
this.setProperties({
info: {
identifier: "category_id",
type: "category_id",
default: null,
nullable: false,
},
initialValues: {},
params: {},
updateParams,
});
await render(hbs`<ParamInput
@params={{this.params}}
@initialValues={{this.initialValues}}
@info={{this.info}}
@updateParams={{this.updateParams}}
/>`);
const categoryChooser = selectKit(".category-chooser");
await categoryChooser.expand();
await categoryChooser.selectRowByValue(2);
assert.strictEqual(values.category_id, "2");
});
});