Skip to content

collectFields() ignores multiple elements with the same fs-list-field when not inside a nested collection list #789

@victorguleshov

Description

@victorguleshov

Description
When a CMS collection item contains multiple elements with the same fs-list-field attribute (e.g., fs-list-field="category"), only the first element's value is stored in item.fields. The remaining elements are collected into item.fieldElements but never used for filtering.

This is a regression — the same setup was working correctly in a previous version of @finsweet/attributes@2.

Environment
Script: https://cdn.jsdelivr.net/npm/@finsweet/attributes@2/attributes.js
Version: 2.6.32 (loaded via @2 — latest)
Steps to reproduce
Create a CMS collection list with items that have multiple [fs-list-field="category"] elements per item:

Webinar
Webinar On-Demand
On-Demand Content
Add radio filters with fs-list-value="On-Demand Content".

Click the "On-Demand Content" filter — returns 0 results even though 30 items have that value.

Root cause
In the minified source of collectFields(), the logic that determines single vs. multi-value is:

let u = i.closest(listSelector);
if (this.element.contains(u)) {
// Multi-value path — pushes to array
this.fields[r].value.push(a);
} else {
// Single-value path — first element wins, rest are silently ignored
this.fields[r] ||= { fieldKey: r, type: s, rawValue: o, value: a };
}
The multi-value path only activates when the field element is inside a nested collection list (multi-reference) contained within the item. If multiple field elements with the same key are regular sibling divs (not inside a nested .w-dyn-list), the ||= assignment stores only the first value and discards the rest.

Meanwhile, item.fieldElements[key] correctly stores all matching elements — but item.fields[key] (which filtering relies on) does not.

Observed behavior

item.fieldElements.category → [{value: "Webinar"}, {value: "Webinar On-Demand"}, {value: "On-Demand Content"}]
item.fields.category → {type: "text", value: "Webinar", rawValue: "Webinar"}
Expected behavior

item.fields.category → {type: "text", value: ["Webinar", "Webinar On-Demand", "On-Demand Content"], rawValue: [...]}
When multiple elements share the same fs-list-field key, all values should be collected into an array in item.fields, regardless of whether they are inside a nested collection list or not. This matches the TypeScript definition where ListItemField.value is typed as string | string[].

Current workaround
Patching via addHook('start', ...) to sync fieldElements into fields:

window.FinsweetAttributes ||= [];
window.FinsweetAttributes.push([
'list',
(listInstances) => {
listInstances.forEach((listInstance) => {
listInstance.addHook('start', (items) => {
items.forEach((item) => {
if (!item.fieldElements) return;
for (const [key, elements] of Object.entries(item.fieldElements)) {
if (elements.length > 1) {
item.fields[key] = {
type: item.fields[key]?.type || 'text',
rawValue: elements.map((fe) => fe.element?.textContent || String(fe.value)),
value: elements.map((fe) => fe.value),
};
}
}
});
return items;
});
});
},
]);
Suggested fix
In collectFields(), when this.element.contains(u) is false and this.fields[r] already exists, convert the existing single value to an array and push the new value — instead of silently skipping it via ||=.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions