Tried to add an FAQ section to our collection using the following code:
Sections/faq.liquid
{%- comment -%}
Section published at https://sections.design/blogs/shopify/faq-rich-snippets-section
Get the latest version: https://github.com/mirceapiturca/Sections/tree/master/FAQ
{%- endcomment -%}
{%- comment -%} ---------------- THE CSS ---------------- {%- endcomment -%}
{%- assign id = '#shopify-section-' | append: section.id -%}
{% style %}
{{ id }} {
background: {{ section.settings.background_color }};
--panel-bg: {{ section.settings.panel_color }};
--border-color: {{ section.settings.border_color }};
--question-color: {{ section.settings.q_color }};
--answer-color: {{ section.settings.a_color }};
{%- assign min = section.settings.q_size_small -%}
{%- assign max = section.settings.q_size_large -%}
{%- assign min_rem = min | append: 'rem' -%}
{%- assign max_rem = max | append: 'rem' -%}
--title-font-size: clamp({{ min_rem }}, calc({{ min_rem }} + ({{ max }} - {{ min }}) * ((100vw - 25rem) / (64 - 25))), {{ max_rem }});
}
{% endstyle %}
{%- comment -%} ---------------- THE MARKUP ---------------- {%- endcomment -%}
{%- for block in section.blocks -%}
{%- if block.settings.title != blank and block.settings.content != blank -%}
{%- if block.settings.checkbox_expanded == true -%}
{%- assign expanded = 'true' -%}
{%- assign hidden = '' -%}
{%- else -%}
{%- assign expanded = 'false' -%}
{%- assign hidden = 'hidden' -%}
{%- endif -%}
##
{{ block.settings.content }}
{%- endif -%}
{%- endfor -%}
{%- comment -%} -------------- THE RICH SCHEMA ------------- {%- endcomment -%}
{%- if section.settings.enable_rich_schema -%}
{%- endif -%}
{%- comment -%} ---------------- THE CONFIG ---------------- {%- endcomment -%}
{%- comment -%} ---------------- THE SETTINGS ---------------- {%- endcomment -%}
{% schema %}
{
"name": "FAQ",
"class": "sd-faq",
"tag": "article",
"settings": [
{
"type": "header",
"content": "Rich schema"
},
{
"type": "checkbox",
"id": "enable_rich_schema",
"default": true,
"label": "Enable FAQ rich schema?"
},
{
"type": "header",
"content": "Dimensions"
},
{
"type": "text",
"id": "max_width",
"label": "Max width",
"default": "64rem"
},
{
"type": "range",
"id": "margin_top",
"min": 0,
"max": 10,
"step": 0.1,
"unit": "rem",
"label": "Margin top",
"default": 1
},
{
"type": "range",
"id": "margin_bottom",
"min": 0,
"max": 10,
"step": 0.1,
"unit": "rem",
"label": "Margin bottom",
"default": 1
},
{
"type": "header",
"content": "Colors"
},
{
"type": "color",
"id": "background_color",
"label": "Background color",
"default": "#ffffff"
},
{
"type": "color",
"id": "border_color",
"label": "Border color",
"default": "#eeeeee"
},
{
"type": "color",
"id": "panel_color",
"label": "Panel color",
"default": "#fdfdfd"
},
{
"type": "header",
"content": "Question"
},
{
"type": "range",
"id": "q_size_small",
"min": 1,
"max": 2,
"step": 0.1,
"unit": "rem",
"label": "Small devices font size",
"default": 1
},
{
"type": "range",
"id": "q_size_large",
"min": 1,
"max": 3,
"step": 0.1,
"unit": "rem",
"label": "Large devices font size",
"default": 1.4
},
{
"type": "color",
"id": "q_color",
"label": "Question text color"
},
{
"type": "header",
"content": "Answer"
},
{
"type": "color",
"id": "a_color",
"label": "Answer text color"
}
],
"blocks": [
{
"type": "faq",
"name": "FAQ",
"settings": [
{
"type": "checkbox",
"id": "checkbox_expanded",
"default": false,
"label": "Expanded?"
},
{
"type": "text",
"id": "title",
"label": "FAQ title",
"default": "FAQ title"
},
{
"type": "richtext",
"id": "content",
"label": "FAQ content",
"default": "
FAQ content
"
}
]
}
],
"presets": [
{
"name": "FAQ"
}
]
}
{% endschema %}
{%- comment -%} ------------------ THE JS ----------------- {%- endcomment -%}
{%- comment -%} ---------------- THE NO-JS ---------------- {%- endcomment -%}
{%- comment -%} ---------------- THE EDITOR ------------------ {%- endcomment -%}
{%- if request.design_mode -%}
{%- endif -%}
assets/faq.js
(function FAQ(SectionsDesign) {
'use strict';
if (SectionsDesign.faq) return;
SectionsDesign.faq = {};
var support = getSupport();
var init = function() {
var configNodes = Array.from(document.querySelectorAll('[data-faq-config]'));
configNodes.map(configNode => {
var section = compose(publicAPI, setEvents, getBlocks, getConfig)(configNode);
SectionsDesign.faq[section.id] = section;
});
}
init();
function publicAPI(config) {
return {
id: config.sectionId,
config: config,
blocks: zipObj(config.blockIds, config.blocks),
init: init
}
}
//**************************
/**
* Click event.
* @param {Object} block Section block elements and methods.
* {Object} Section block elements and methods.
*/
function blockEvents(block) {
if (block.trigger.hasAttribute('data-faq-init')) return block;
block.trigger.setAttribute('data-faq-init', true);
block.trigger.addEventListener('click', triggerClick);
function triggerClick() {
toggle(block);
}
return block;
}
/**
* Toggle block state.
* @param {Object} block Section block elements and methods.
* {Object} Section block elements and methods.
*/
function toggle(block) {
block.collapsed ? expand(block) : collapse(block);
return block;
}
/**
* Expand block.
* @param {Object} block Section block elements and methods.
* {Object} Section block elements and methods.
*/
function expand(block) {
block.button.setAttribute('aria-expanded', true);
block.panel.removeAttribute('hidden');
animate(block.panel, 'normal');
return block;
}
/**
* Collapse block.
* @param {Object} block Section block elements and methods.
* {Object} Section block elements and methods.
*/
function collapse(block) {
block.button.setAttribute('aria-expanded', false);
block.panel.setAttribute('hidden', '');
animate(block.panel, 'reverse');
return block;
}
/**
* Collapse block.
* @param {Object} block Section block elements and methods.
* {boolean} Collapsed block state.
*/
function isCollapsed(block) {
return Boolean(block.button.getAttribute('aria-expanded') === 'false');
}
/**
* Collapse block.
* @param {HTMLElement} element Block panel element to animate.
* @param {String} direction Animation direction, normal or reverse.
* {undefined} Nothing to return.
*/
function animate(element, direction) {
if (!support.WebAnimations) return;
element.setAttribute('data-is-animating', true);
element.animate([
{ height: 0 },
{ height: element.offsetHeight + 'px' }],
{ duration: 240,
fill: 'both',
easing: 'cubic-bezier(0.4, 0.0, 0.2, 1)',
direction: direction
}
).onfinish = function() {
element.removeAttribute('data-is-animating');
this.cancel();
};
}
//**************************
/**
* Maps all block IDs to exposed block API.
* @param {Object} config Section and section blocks IDs.
* {Object} config Section and section blocks IDs.
*/
function getBlocks(config) {
config.blocks = config.blockIds.map(block);
return config;
}
/**
* Create section block API.
* @param {String} blockId Section Liquid block ID.
* {Object} block elements and methods.
*/
function block(blockId) {
return {
trigger: document.querySelector('[data-faq-trigger="' + blockId + '"]'),
button: document.querySelector('[data-faq-button="' + blockId + '"]'),
panel: document.querySelector('[data-faq-panel="' + blockId + '"]'),
get collapsed() { return isCollapsed(this) },
select: function select() { return expand(this) },
deselect: function deselect() { return collapse(this) }
}
}
/**
* Adds event listeners to block elements.
* @param {Object} config Section and section blocks IDs.
* {Object} config Section and section blocks IDs.
*/
function setEvents(config) {
config.blocks.forEach(blockEvents);
return config;
}
/**
* Pass the Liquid assigned section variabiles.
* @param {HTMLElement} element Configuration script node.
* {Object} Section and section blocks IDs.
*/
function getConfig(node) {
return JSON.parse(node.innerHTML);
}
/**
* Feature detection.
* {Object} Browser support.
*/
function getSupport() {
return {
WebAnimations: (typeof Element.prototype.animate === 'function')
}
}
//**************************
/**
* Creates a new object out of a list of keys and a list of values.
* Key/value pairing is truncated to the length of the shorter of the two lists.
*
* zipObj(['a', 'b', 'c'], [1, 2, 3]); //=> {a: 1, b: 2, c: 3}
* @param {Array} keys The array that will be properties on the output object.
* @param {Array} values The list of values on the output object.
* {Object} The object made by pairing up same-indexed elements of `keys` and `values`.
*/
function zipObj(keys, values) {
return keys.reduce(
function zipObj(acc, key, idx) {
acc[key] = values[idx];
return acc;
}, {}
)
}
/**
* Performs right-to-left function composition.
* The rightmost function may have any arity, the remaining functions must be unary.
*
* function plus1(n) {return n + 1};
* function plus2(n) {return n + 2};
* compose(plus2,plus1)(1) => 4
* {Function} Composed function
*/
function compose() {
var funcs = Array.prototype.slice.call(arguments).reverse();
return function() {
return funcs.slice(1).reduce(function(res, fn) {
return fn(res);
}, funcs[0].apply(undefined, arguments));
};
}
})(window.SectionsDesign = window.SectionsDesign || {});
How do I get it to display on collection page? I added the line {% include ‘faq’ %} to collection template, but get the error Liquid error (sections/collection-template line 12): Could not find asset snippets/faq.liquid