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 effective variant id for this field (selectedVariantId ). Comparison entries do not currently retain a shopper's PDP variant selection. When product-level metadata is absent, the engine therefore uses the first variant carrying this metadata key; otherwise this value is null .

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).