mirror of
https://github.com/wekan/wekan.git
synced 2026-11-08 19:35:19 -05:00
Typing '#test' in the add-card / edit-card-title field did not suggest a label named 'Testing': the label search matched with a case-SENSITIVE label.name.indexOf(term) (and label.color.indexOf(term)), so only '#Test' matched 'Testing'. The board's '@' member autocomplete already matches case-insensitively; this brings the '#' label autocomplete in line. Extracted the match into a pure, unit-testable models/lib/labelAutocomplete.js (labelMatchesTerm / filterLabelsByTerm) that lowercases both the term and the label name/color, mirroring the other models/lib pure helpers. Tests: tests/labelAutocompleteMatch.test.cjs covers the exact #5116 scenario (#test -> Testing), color-fallback matching, empty-term-shows-all, order preservation, and negatives (non-substring, missing name/color, null label, non-array), plus a source guard that listBody.js routes through the shared matcher. 11 checks pass. Thanks to rjl9 and xet7 ! Fixes #5116,
38 lines
1.7 KiB
JavaScript
38 lines
1.7 KiB
JavaScript
'use strict';
|
|
|
|
// Pure helper for the card-title "#" label autocomplete (#5116): typing
|
|
// "#test" in the add-card / edit-card-title field should suggest a label named
|
|
// "Testing". The original match used a case-SENSITIVE `label.name.indexOf(term)`,
|
|
// so "#test" (lowercase) never matched "Testing" (capital T) even though
|
|
// "#Test" did. Extracted so the matching is unit-testable in plain Node without
|
|
// Meteor/jQuery/textcomplete (mirrors models/lib/sortCardsByTitle.js and the
|
|
// other models/lib pure helpers).
|
|
//
|
|
// The board's "@" member autocomplete already matches case-insensitively via
|
|
// toLowerCase()/includes(); this brings the "#" label autocomplete in line.
|
|
|
|
// Does `label` match the typed `term`? Case-insensitive substring match against
|
|
// the label's name and its color (color is the fallback shown when a label has
|
|
// no name). A missing name/color is treated as an empty string so it never
|
|
// throws. An empty term matches every label (the menu shows all labels before
|
|
// anything is typed), matching the previous ''.indexOf('') === 0 behaviour.
|
|
function labelMatchesTerm(label, term) {
|
|
if (!label) return false;
|
|
const needle = String(term == null ? '' : term).toLowerCase();
|
|
const name = String(label.name == null ? '' : label.name).toLowerCase();
|
|
const color = String(label.color == null ? '' : label.color).toLowerCase();
|
|
return name.indexOf(needle) > -1 || color.indexOf(needle) > -1;
|
|
}
|
|
|
|
// Filter a list of board labels by the typed term, preserving input order.
|
|
// Does not mutate the input labels.
|
|
function filterLabelsByTerm(labels, term) {
|
|
if (!Array.isArray(labels)) return [];
|
|
return labels.filter(label => labelMatchesTerm(label, term));
|
|
}
|
|
|
|
module.exports = {
|
|
labelMatchesTerm,
|
|
filterLabelsByTerm,
|
|
};
|