[IMP] web: model field selector: search on string and name in debug mode
We allow the user to make a fuzzy search on the string and the name of the fields when the props isDebugMode is true. Part-of: odoo/odoo#121688
This commit is contained in:
@@ -8,11 +8,13 @@ import { sortBy } from "@web/core/utils/arrays";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
|
||||
class Page {
|
||||
constructor(resModel, fieldDefs, previousPage = null, selectedName = null) {
|
||||
constructor(resModel, fieldDefs, options = {}) {
|
||||
this.resModel = resModel;
|
||||
this.fieldDefs = fieldDefs;
|
||||
const { previousPage = null, selectedName = null, isDebugMode } = options;
|
||||
this.previousPage = previousPage;
|
||||
this.selectedName = selectedName;
|
||||
this.isDebugMode = isDebugMode;
|
||||
this.sortedFieldNames = sortBy(Object.keys(fieldDefs), (key) => fieldDefs[key].string);
|
||||
this.fieldNames = this.sortedFieldNames;
|
||||
this.query = "";
|
||||
@@ -74,11 +76,13 @@ class Page {
|
||||
this.query = query;
|
||||
this.fieldNames = this.sortedFieldNames;
|
||||
if (query) {
|
||||
this.fieldNames = fuzzyLookup(
|
||||
query,
|
||||
this.fieldNames,
|
||||
(key) => this.fieldDefs[key].string
|
||||
);
|
||||
this.fieldNames = fuzzyLookup(query, this.fieldNames, (key) => {
|
||||
const vals = [this.fieldDefs[key].string];
|
||||
if (this.isDebugMode) {
|
||||
vals.push(key);
|
||||
}
|
||||
return vals;
|
||||
});
|
||||
}
|
||||
this.resetFocusedFieldName();
|
||||
}
|
||||
@@ -149,7 +153,10 @@ export class ModelFieldSelectorPopover extends Component {
|
||||
this.state.page.selectedName = fieldDef.name;
|
||||
const { resModel, fieldDefs } = modelsInfo.at(-1);
|
||||
this.openPage(
|
||||
new Page(resModel, this.filter(fieldDefs, this.state.page.path), this.state.page)
|
||||
new Page(resModel, this.filter(fieldDefs, this.state.page.path), {
|
||||
previousPage: this.state.page,
|
||||
isDebugMode: this.props.isDebugMode,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -166,7 +173,9 @@ export class ModelFieldSelectorPopover extends Component {
|
||||
async loadPages(resModel, path) {
|
||||
if (typeof path !== "string" || !path.length) {
|
||||
const fieldDefs = await this.fieldService.loadFields(resModel);
|
||||
return new Page(resModel, this.filter(fieldDefs, path));
|
||||
return new Page(resModel, this.filter(fieldDefs, path), {
|
||||
isDebugMode: this.props.isDebugMode,
|
||||
});
|
||||
}
|
||||
const { isInvalid, modelsInfo, names } = await this.fieldService.loadPath(resModel, path);
|
||||
switch (isInvalid) {
|
||||
@@ -174,14 +183,21 @@ export class ModelFieldSelectorPopover extends Component {
|
||||
throw new Error(`Invalid model name: ${resModel}`);
|
||||
case "path": {
|
||||
const { resModel, fieldDefs } = modelsInfo[0];
|
||||
return new Page(resModel, this.filter(fieldDefs, path), null, path);
|
||||
return new Page(resModel, this.filter(fieldDefs, path), {
|
||||
selectedName: path,
|
||||
isDebugMode: this.props.isDebugMode,
|
||||
});
|
||||
}
|
||||
default: {
|
||||
let page = null;
|
||||
for (let index = 0; index < names.length; index++) {
|
||||
const name = names[index];
|
||||
const { resModel, fieldDefs } = modelsInfo[index];
|
||||
page = new Page(resModel, this.filter(fieldDefs, path), page, name);
|
||||
page = new Page(resModel, this.filter(fieldDefs, path), {
|
||||
previousPage: page,
|
||||
selectedName: name,
|
||||
isDebugMode: this.props.isDebugMode,
|
||||
});
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,22 @@
|
||||
|
||||
import { unaccent } from "./strings";
|
||||
|
||||
/**
|
||||
* @param {string} pattern
|
||||
* @param {string|string[]} strs
|
||||
* @returns {number}
|
||||
*/
|
||||
function match(pattern, strs) {
|
||||
if (!Array.isArray(strs)) {
|
||||
strs = [strs];
|
||||
}
|
||||
let globalScore = 0;
|
||||
for (const str of strs) {
|
||||
globalScore = Math.max(globalScore, _match(pattern, str));
|
||||
}
|
||||
return globalScore;
|
||||
}
|
||||
|
||||
/**
|
||||
* This private function computes a score that represent the fact that the
|
||||
* string contains the pattern, or not
|
||||
@@ -17,7 +33,7 @@ import { unaccent } from "./strings";
|
||||
* @param {string} str
|
||||
* @returns {number}
|
||||
*/
|
||||
function match(pattern, str) {
|
||||
function _match(pattern, str) {
|
||||
let totalScore = 0;
|
||||
let currentScore = 0;
|
||||
const len = str.length;
|
||||
@@ -47,7 +63,7 @@ function match(pattern, str) {
|
||||
* @template T
|
||||
* @param {string} pattern
|
||||
* @param {T[]} list
|
||||
* @param {(element: T) => string} fn
|
||||
* @param {(element: T) => (string|string[])} fn
|
||||
* @returns {T[]}
|
||||
*/
|
||||
export function fuzzyLookup(pattern, list, fn) {
|
||||
@@ -72,5 +88,5 @@ export function fuzzyLookup(pattern, list, fn) {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function fuzzyTest(pattern, string) {
|
||||
return match(pattern, string) !== 0;
|
||||
return _match(pattern, string) !== 0;
|
||||
}
|
||||
|
||||
@@ -831,4 +831,35 @@ QUnit.module("Components", (hooks) => {
|
||||
);
|
||||
assert.containsNone(target, ".o_model_field_selector_warning");
|
||||
});
|
||||
|
||||
QUnit.test("search on field string and name in debug mode", async (assert) => {
|
||||
patchWithCleanup(browser, { setTimeout: (fn) => fn() }); // for debouncedSearchFields
|
||||
serverData.models.partner.fields.ucit = {
|
||||
type: "char",
|
||||
string: "Some string",
|
||||
searchable: true,
|
||||
};
|
||||
class Parent extends Component {
|
||||
static components = { ModelFieldSelector };
|
||||
static template = xml`
|
||||
<ModelFieldSelector
|
||||
readonly="false"
|
||||
resModel="'partner'"
|
||||
path="'foo'"
|
||||
isDebugMode="true"
|
||||
/>
|
||||
`;
|
||||
}
|
||||
await mountComponent(Parent);
|
||||
await openModelFieldSelectorPopover(target);
|
||||
await editInput(
|
||||
target,
|
||||
".o_model_field_selector_popover .o_model_field_selector_popover_search input",
|
||||
"uct"
|
||||
);
|
||||
assert.deepEqual(getDisplayedFieldNames(target), [
|
||||
"Productproduct_id (many2one)",
|
||||
"Some stringucit (char)",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user