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:
parent
41dfa217ca
commit
902b8c7913
|
@ -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}}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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");
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue