DEV: Use Category's async method in param-input (#310)
This commit refactors the category id normalization of param-input to use `Category.asyncFindBySlugPath`. Now, the category id input will use an async query when the default/initial value is category slug. Co-authored-by: Natalie <1555215+nattsw@users.noreply.github.com>
This commit is contained in:
parent
1d991c6192
commit
b47ba7ea60
|
@ -1,8 +1,9 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import EmberObject, { action } from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { dasherize } from "@ember/string";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
|
||||
import Form from "discourse/components/form";
|
||||
import Category from "discourse/models/category";
|
||||
import I18n from "I18n";
|
||||
|
@ -49,22 +50,20 @@ const ERRORS = {
|
|||
function digitalizeCategoryId(value) {
|
||||
value = String(value || "");
|
||||
const isPositiveInt = /^\d+$/.test(value);
|
||||
if (!isPositiveInt) {
|
||||
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;
|
||||
}
|
||||
if (!isPositiveInt && value.trim()) {
|
||||
return Category.asyncFindBySlugPath(dasherize(value))
|
||||
.then((res) => res.id)
|
||||
.catch((err) => {
|
||||
if (err.jqXHR?.status === 404) {
|
||||
throw new ParamValidationError(
|
||||
`${ERRORS.NO_SUCH_CATEGORY}: ${value}`
|
||||
);
|
||||
} else {
|
||||
throw new Error(err.errorThrow || err.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
return value?.toString();
|
||||
return value;
|
||||
}
|
||||
|
||||
function normalizeValue(info, value) {
|
||||
|
@ -158,36 +157,72 @@ export default class ParamInputForm extends Component {
|
|||
infoOf = {};
|
||||
form = null;
|
||||
|
||||
promiseNormalizations = [];
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
const initialValues = this.args.initialValues;
|
||||
for (const info of this.args.paramInfo) {
|
||||
const identifier = info.identifier;
|
||||
|
||||
// access parsed params if present to update values to previously ran values
|
||||
let initialValue;
|
||||
if (initialValues && identifier in initialValues) {
|
||||
initialValue = initialValues[identifier];
|
||||
} else {
|
||||
// if no parsed params then get and set default values
|
||||
initialValue = info.default;
|
||||
}
|
||||
this.data[identifier] = normalizeValue(info, initialValue);
|
||||
this.paramInfo.push({
|
||||
...info,
|
||||
validation: validationOf(info),
|
||||
validate: this.validatorOf(info),
|
||||
component: componentOf(info),
|
||||
});
|
||||
this.infoOf[identifier] = info;
|
||||
}
|
||||
this.initializeParams();
|
||||
|
||||
this.args.onRegisterApi?.({
|
||||
submit: this.submit,
|
||||
allNormalized: Promise.allSettled(this.promiseNormalizations),
|
||||
});
|
||||
}
|
||||
|
||||
initializeParams() {
|
||||
this.args.paramInfo.forEach((info) => {
|
||||
const identifier = info.identifier;
|
||||
const pinfo = this.createParamInfo(info);
|
||||
|
||||
this.paramInfo.push(pinfo);
|
||||
this.infoOf[identifier] = info;
|
||||
|
||||
const normalized = this.getNormalizedValue(info);
|
||||
|
||||
if (normalized instanceof Promise) {
|
||||
this.handlePromiseNormalization(normalized, pinfo);
|
||||
} else {
|
||||
this.data[identifier] = normalized;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
createParamInfo(info) {
|
||||
return EmberObject.create({
|
||||
...info,
|
||||
validation: validationOf(info),
|
||||
validate: this.validatorOf(info),
|
||||
component: componentOf(info),
|
||||
});
|
||||
}
|
||||
|
||||
getNormalizedValue(info) {
|
||||
const initialValues = this.args.initialValues;
|
||||
const identifier = info.identifier;
|
||||
return normalizeValue(
|
||||
info,
|
||||
initialValues && identifier in initialValues
|
||||
? initialValues[identifier]
|
||||
: info.default
|
||||
);
|
||||
}
|
||||
|
||||
handlePromiseNormalization(promise, pinfo) {
|
||||
this.promiseNormalizations.push(promise);
|
||||
pinfo.set("loading", true);
|
||||
this.data[pinfo.identifier] = null;
|
||||
|
||||
promise
|
||||
.then((res) => this.form.set(pinfo.identifier, res))
|
||||
.catch((err) =>
|
||||
this.form.addError(pinfo.identifier, {
|
||||
title: pinfo.identifier,
|
||||
message: err.message,
|
||||
})
|
||||
)
|
||||
.finally(() => pinfo.set("loading", false));
|
||||
}
|
||||
|
||||
getErrorFn(info) {
|
||||
const isPositiveInt = (value) => /^\d+$/.test(value);
|
||||
const VALIDATORS = {
|
||||
|
@ -321,6 +356,10 @@ export default class ParamInputForm extends Component {
|
|||
as |field|
|
||||
>
|
||||
<info.component @field={{field}} @info={{info}} />
|
||||
<ConditionalLoadingSpinner
|
||||
@condition={{info.loading}}
|
||||
@size="small"
|
||||
/>
|
||||
</form.Field>
|
||||
</div>
|
||||
{{/each}}
|
||||
|
|
|
@ -1,26 +1,20 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { action } from "@ember/object";
|
||||
import CategoryChooser from "select-kit/components/category-chooser";
|
||||
|
||||
export default class GroupListInput extends Component {
|
||||
@tracked value;
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.value = this.args.field.value;
|
||||
}
|
||||
|
||||
@action
|
||||
update(id) {
|
||||
this.value = id;
|
||||
this.args.field.set(id);
|
||||
export default class CategoryIdInput extends Component {
|
||||
// CategoryChooser will try to modify the value of value,
|
||||
// triggering a setting-on-hash error. So we have to do the dirty work.
|
||||
get data() {
|
||||
return {
|
||||
value: this.args.field.value,
|
||||
};
|
||||
}
|
||||
|
||||
<template>
|
||||
<@field.Custom id={{@field.id}}>
|
||||
<CategoryChooser
|
||||
@value={{this.value}}
|
||||
@onChange={{this.update}}
|
||||
@value={{this.data.value}}
|
||||
@onChange={{@field.set}}
|
||||
name={{@info.identifier}}
|
||||
/>
|
||||
</@field.Custom>
|
||||
|
|
|
@ -359,11 +359,6 @@ export default class PluginsExplorerController extends Controller {
|
|||
this.form = form;
|
||||
}
|
||||
|
||||
@action
|
||||
updateParams(identifier, value) {
|
||||
this.selectedItem.set(`params.${identifier}`, value);
|
||||
}
|
||||
|
||||
@action
|
||||
updateSearch(value) {
|
||||
this.search = value;
|
||||
|
|
|
@ -130,13 +130,6 @@ export default class GroupReportsShowController extends Controller {
|
|||
});
|
||||
}
|
||||
|
||||
// This is necessary with glimmer's one way data stream to get the child's
|
||||
// changes of 'params' to bubble up.
|
||||
@action
|
||||
updateParams(identifier, value) {
|
||||
this.set(`model.params.${identifier}`, value);
|
||||
}
|
||||
|
||||
@action
|
||||
onRegisterApi(form) {
|
||||
this.form = form;
|
||||
|
|
|
@ -60,7 +60,7 @@ const InputTestCases = [
|
|||
tests: [
|
||||
{
|
||||
input: null,
|
||||
data_null: undefined,
|
||||
data_null: "",
|
||||
error: ERRORS.REQUIRED,
|
||||
},
|
||||
{
|
||||
|
@ -111,8 +111,9 @@ module("Data Explorer Plugin | Component | param-input", function (hooks) {
|
|||
initialValues: config.initial
|
||||
? { [testcase.type]: config.initial }
|
||||
: {},
|
||||
onRegisterApi: ({ submit }) => {
|
||||
onRegisterApi: ({ submit, allNormalized }) => {
|
||||
this.submit = submit;
|
||||
this.allNormalized = allNormalized;
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -124,6 +125,8 @@ module("Data Explorer Plugin | Component | param-input", function (hooks) {
|
|||
@onRegisterApi={{this.onRegisterApi}}
|
||||
/>`);
|
||||
|
||||
await this.allNormalized;
|
||||
|
||||
if (config.initial || config.default) {
|
||||
const data = await this.submit();
|
||||
const val = config.initial || config.default;
|
||||
|
@ -201,4 +204,34 @@ module("Data Explorer Plugin | Component | param-input", function (hooks) {
|
|||
await fillIn(`[name="string"]`, "");
|
||||
assert.rejects(this.submit());
|
||||
});
|
||||
|
||||
test("async normalizion", async function (assert) {
|
||||
this.setProperties({
|
||||
param_info: [
|
||||
{
|
||||
identifier: "category_id",
|
||||
type: "category_id",
|
||||
default: "support",
|
||||
nullable: false,
|
||||
},
|
||||
],
|
||||
initialValues: {},
|
||||
onRegisterApi: (paramInputApi) => {
|
||||
this.paramInputApi = paramInputApi;
|
||||
},
|
||||
});
|
||||
|
||||
await render(hbs`
|
||||
<ParamInputForm
|
||||
@initialValues={{this.initialValues}}
|
||||
@paramInfo={{this.param_info}}
|
||||
@onRegisterApi={{this.onRegisterApi}}
|
||||
/>`);
|
||||
|
||||
await this.paramInputApi.allNormalized;
|
||||
|
||||
this.paramInputApi.submit().then((res) => {
|
||||
assert.strictEqual(res.category_id, "1003");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue