Magidoc

Comparison Splitter

The JAVASCRIPT_MAPPER_SPLIT_COMPONENTS display type expands a single comparison metadata field into multiple rows (or collapsible groups) in the comparison view. Use it when the stored value is itself a list of features, attributes, or measurements that should each get their own row, with values, translated yes / no labels, or tick / cross icons per product.

This display type only applies inside the comparison view. Selecting it on a PDP block has no effect — the engine PDP renderer falls through to plain text rendering.

Authoring contract

#

  • You write a function body only. Cloudshelf wraps your body as new Function('input', body) at runtime, so any code you paste must produce its output by return -ing from the top of the body. A standalone arrow expression like (input) => ({ result: [] }) parses and runs without throwing, but it just creates and discards an anonymous function — the wrapper's return value is undefined and the engine falls back to legacy rendering for the field. Same caveat for a full function () { ... } declaration: it parses, but the wrapping function still has no top-level return .
  • You receive a single parameter named input , an object whose keys carry whatever context the splitter has at that site (input.products , ...). New keys may be added in future, so destructure or read them defensively.
  • Errors, malformed return shapes, or empty results fall back to legacy single-row rendering for that field; the rest of the comparison view keeps rendering.

Input

#

    
  

input.products is the array of products being compared, in the order they appear in the comparison view. The array length is between 1 and 3 (not padded). For each product you get:

  • The product-level metadata for the field's key (productMetadata ), or null if the product doesn't carry a value at that scope.
  • Every variant's metadata for the same key, in variants[] .
  • The selected variant id, if the shopper has picked one.

raw is the original metadata string as Cloudshelf received it. parsed is JSON.parse(raw) on success, or the raw string itself on parse failure.

Output

#

    
  

Each cell value is one of:

Value
Renders as
"CS_YES"
Translated yes text
"CS_NO"
Translated no text
"CS_CHECK"
A green tick icon
"CS_CROSS"
A red cross icon
null (or omitted)
Em dash ()
Any other string
DOMPurify-sanitised HTML

Returning { result: [] } causes the engine to fall back to legacy single-row rendering for the field — so it's safe to bail out when a value looks unfamiliar. Throwing an exception, returning a non-object, or returning rows with the wrong shape also falls back gracefully and logs an error.

Testing in the manager

#

Open the metadata field in the comparison edit sheet, set its display type to Comparison Splitter , and click Edit Splitter. The editor pulls up to five recent values for that metadata key from your live catalogue (or accepts a manual JSON paste when no samples are available) and re-runs your splitter against the chosen sample on every keystroke. The output { result: [...] } is pretty-printed in the Rendered output panel underneath the editor.

The preview only mocks a single product (variants: [] , selectedVariantId: null ), so any logic that genuinely needs two products to differ (e.g. union of values across products, ticks based on cross-product comparison) won't show that difference until you open the live comparison view with multiple products.

Examples

#

Yes mode for an array of strings

#

    
  

Attribute table for a plain object

#

    
  

Collapsible groups for a record-list

#

    
  

Boolean flags become translated yes / no

#

    
  

Default splitter

#

When the admin first switches a field to Comparison Splitter , Cloudshelf populates the editor with a built-in default body. It auto-detects four common shapes:

  • Array of primitives (["leather", "vegan"] ) — produces yes rows: each distinct value becomes a row, with CS_YES in the column for products whose array contains it. The built-in comments show how to switch this to CS_CHECK when icon rendering is preferred.
  • Plain object ({ areaSize: "16000m2", weight: "5kg" } ) — produces a value table: each key becomes a row, with that key's value in each product's column. Booleans render as CS_YES / CS_NO by default. The built-in comments show how to switch booleans to CS_CHECK / CS_CROSS .
  • Array of single-key objects ([{ areaSize: "16000m2" }, { weight: "5kg" }] ) — flattened then treated as a plain object.
  • Array of multi-key records ([{ groupName: "Engine", name: "Output", value: "1.4 kW" }, ...] ) — labels and values are inferred by cardinality and string length; if a low-cardinality key is present (e.g. groupName ) it becomes a collapsible group header.

If none of those shapes match, the default returns { result: [] } and the field falls back to legacy rendering. Edit the default freely — selecting Reset to default reinstates it.

Limitations

#

  • No module imports inside splitter code.
  • Splitter execution is synchronous — await , Promise , setTimeout etc are not supported.
  • Groups cannot nest. A Group may contain Row s, but a Group.rows entry that is itself a group is dropped.
  • Heavy computation on large datasets may impact comparison render performance.
  • Preview output in the manager only mocks a single product, so cross-product behaviour can differ from live runtime.
  • Always escape user-controlled strings if you return arbitrary HTML in cells. Cells are run through DOMPurify, but escaping at the source is still the safe default.

Troubleshooting

#

  • Field renders as a single row of raw JSON: your splitter returned { result: [] } , threw, or returned a malformed shape. The engine falls back to legacy text rendering in those cases — open the manager preview to see the actual output and any error.
  • metadata is not defined : the splitter has its own input shape — there's no input.metadata . Read product values from input.products[i].productMetadata?.parsed .
  • SyntaxError: function statement requires a name : you've pasted a full function () { ... } declaration. Remove the outer function (...) and trailing } — splitters are body-only.
  • All cells show em dashes: check that the colN keys (col1 , col2 , col3 ) match the indices of input.products and that the values are strings ("CS_YES" or "CS_CHECK" , the literal string), not bare booleans on cells you intended to be free-text.
  • Group is missing from the output: an entry whose rows field isn't a plain Row[] is dropped (nested groups are not supported in v1).