1.0.2 • Published 3 years ago

shopify-collection-filter v1.0.2

Weekly downloads
-
License
-
Repository
github
Last release
3 years ago

✨ Grafikr® Collection Filter™

Fast & highly customizable storefront-based collection filter adapted to DEUS setups. To get a quick overview, the provided example files can be immediately imported to a project and should be functional plug & play 🤓
⚠️ When building functionality on top of the filter, never alter the main package file index.js, but instead use the event hooks provided. If you encounter bugs, or if you need additional features, that are not currently possible simply by hooking into the filter's event hooks, feel free to open an issue.

📂 Example files

FileDescriptionPlacement
collection.data.liquidCollection data endpoint template. This is where the filter fetches its data from. Refer to the data template section for more information.src/templates
template-collection.liquidBarebone example of the collection page markup.src/sections
collection.cssBarebone styling for the collection page example.🤷‍♂️
collection.jsCollection page example scripts. This is where the filter is being imported and executed.🤷‍♂️

💾 Data template

Before using the filter, a data template collection.data.liquid must be created. This template serves as the data endpoint for the filter. The template must use {% layout none %}, it has to be formatted in JSON and have the same structure as regular Shopify product.json endpoints, and it should paginate over the collection's products by 1000. Also there are certain data that must be included for the filter to work. The minimal data to allow for the use of all filter types is as follows:

{% layout none %}
{%- assign c = collection -%}

{%- paginate c.products by 1000 -%}
[
  {%- for p in c.products -%}
    {
      "product_type": {{ p.type | json }},
      "vendor": {{ p.vendor | json }},
      "options": [
        {%- for o in p.options_with_values -%}
          {
            "name": {{ o.name | json }},
            "values": [
              {%- for v in o.values -%}
                {{ v | json }}{% unless forloop.last %},{% endunless %}
              {%- endfor -%}
            ]
          }{% unless forloop.last %},{% endunless %}
        {%- endfor -%}
      ],
      "price": {{ p.price | json }},
      "tags": [
        {%- for t in p.tags -%}
          {{ t | json }}{% unless forloop.last %},{% endunless %}
        {%- endfor -%}
      ],
      "created_at": {{ p.created_at | json }}
    }{% unless forloop.last %},{% endunless %}
  {%- endfor -%}
]
{%- endpaginate -%}

Datasets not needed for the specific filter being built may be omitted. E.g. if the filter will not need to include product type, the product_type entry may be left out from the template and so on. Cutting down to only the necessary data may benefit the loading speed on very large collections. Aside from the required data, any additional product data may be included, and all the data will be available on the product object passed to the product render function.


🛠 Usage

The filter is created by importing the function from shopify-collection-filter and saving its return value to a variable (if you call the function without saving its return value to a variable, you will not be able to hook into the filter events afterwards), passing a config object argument. Refer to the collection.js example file to examine the actual values of each key in this example:

import CollectionFilter from 'shopify-collection-filter';

const collectionFilter = CollectionFilter({
  collection: {
    handle,
    productCount,
  },
  defaultSort,
  pagination: {
    element: moreBtn,
    pageSize,
  },
  render: {
    func: renderProduct,
    output,
  },
  filters: [
    ...filters
  ],
});

⚙️ Required config settings

KeyValueDescription
collection.handlestringThe collection handle. You can get this from the collection liquid object.
collection.productCountintThe number of products in the collection. The filter uses this to know in advance how many pages to fetch. You can get this from the collection liquid object.
render.funcfunctionThe function to render the HTML output for each product. The function will be passed an object argument, where the product key storing the product data is the only fixed content. Additional key-value pairs can however be passed to the function if needed. Refer to render.args in the optional config settings. A minimal render function could look like: const renderProduct = (obj) => `<p>${obj.product.title}</p>`;
render.outputHTML elementThe HTML element to render the outputted product elements to.
pagination.elementHTML elementThe HTML element used to trigger the rendering of the next page. If pagination.type is set to 'scroll', the pagination.element element should always be placed at the very bottom of the product output.
pagination.typestringThe allowed values are 'button' and 'scroll'. When set to 'scroll', the next page will be rendered when the user scrolls to the element, and when set to 'button', the next page will be rendered when the user clicks the element.
filtersarrayThe array must be populated with objects containing settings for the filters to be generated. Refer to the list of filter types and their required settings.

⚙️ Optional config settings

KeyValueDescriptionDefault value
classes.groupstringThe class used by the filter to select filter groups. These are the HTML elements that the filter should render its individual filter items to.'js-group'
classes.itemstringThe class given by the filter to the rendered individual filter items.'js-item'
classes.itemActivestringThe class given by the filter to individual filter items when they are active.'is-active'
classes.itemDisabledstringThe filter gives this class to a filter item, if none of the currently filtered products match the item. The disabled items can then be hidden, greyed out, or something else by styling the class.'is-disabled'
defaultSortstringThe default sorting order of the collection. The allowed values are the same as the ones allowed for name on val on the sorting filter type.'featured'
render.argsobjectAn object of key-value pairs that will be passed to the product render function along with the product object. This can e.g. be used to pass certain variables depending on the current collection, etc.null
prerenderboolTells the filter whether the first page of products have already been rendered with liquid. If true the filter will not replace the innerHTML of the output element, unless the URL has filter query params. This renders the first page available to the user much faster.false
pagination.pageSizeintThe number of products to render per page.12
reset.elementHTML elementElement to reset/clear the filter. The filter will reset when the element is clicked.undefined

🕹 Filter objects

⚙️ Required settings (all)

The following key-value pairs are required for all filter types: Key | Value | Description ---|---|--- type | string | The type of filter to be created. The allowed values are: 'sort', 'type', 'vendor', 'option', 'tag', 'tagPrefix', and 'price'. name | string | The unique name of the filter. This is used in the generated URL params, as well as internally by the filter. The value can be any string, but two filters cannot have the same name. groupEl | HTML element | The HTML element that the filter should render its individual filter items to ⚠️ This can also be an array of HTML elements ⚠️⚠️ renderFunc | function | The function to render the filter's individual filter items. The function must return an HTML string, and some element in the returned HTML must have the attributes class="${className}" and data-value="${value}". A minimal render function could look like this: const renderFunc = (val, className) => `<p class="${className}" data-value="${val}">${val}</p>`;. This can also be an array of functions ⚠️⚠️ removeFunc | function | The function used to remove the filter from the markup in case no values are found in the current collection. The remove function is passed a filter group HTML element as its sole argument. A remove function could look like: const removeFunc = (groupEl) => groupEl.remove();. This can also be an array of functions ⚠️⚠️

⚠️ NOTE: Specifically if a <select> element is used as a filter's groupEl, that filter will be listening for changes on the groupElement itself instead of on its items (as its items should be <option> elements inside the <select>). The renderFunc for a filter using a <select> as its groupEl should therefore always return an <option> element!

⚠️⚠️ NOTE: When passing an array of HTML elements for groupEl, arrays of equal length must be passed to renderFunc and removeFunc as well. For each group element in groupEl the render and remove functions of corresponding indices will be used.

⚙️ Required settings (type-specific)

Filter typeKeyValueDescription
'sort'valsarrayThe 'sort' filter type is different from all other types in that it needs an array of values passed to it. The array must be populated with objects containing a val and a title. The value of title can be any string (allowing for translations), but the value of val must be one of the following: 'featured', 'title-ascending', 'title-descending', 'price-ascending', 'price-descending', 'created-ascending', or 'created-descending'.
'sort'renderFuncfunctionThe 'sort' filter type requires a slightly different render function. This is because the val argument passed to the function reflects the filter's vals array, which means that the argument will be an object containing values for both val and title. A render function could look like this: const renderFunc = (val, className) => `<p class="${className}" data-value="${val.val}">${val.title}</p>`;.
'option'optionNamestringThe actual name of the option used on products in the Shopify backend.
'tagPrefix'prefixstringThe tag prefix to search for. A prefix could e.g. be 'color_', which would then include all tags starting with 'color_'.
'tagPrefix'renderFuncfunctionThe 'tagPrefix' filter type requires a slightly different render function. This is because the val argument passed to the function contains both a val and a title property, allowing to both store the actual value on the item (which is the entire tag including the prefix) as well as print only the value excluding the prefix to the item. A render function could look like this: const renderFunc = (val, className) => `<div class="${className}" data-value="${val.val}">${val.title}</div>`;.
'price'renderFuncfunctionThe 'price' filter type requires a slightly different render function. The arguments passed to it will be the minimum price, the maximum price, and the filterItem class. It's not important per se which type of HTML element the price filter gets rendered as, but it makes sense e.g. to render it as an <input type="hidden">. The two value arguments must be written to the element as data-min and data-max attributes, as it is those attributes that will be used for the actual filtering. The filter can then be run by programatically manipulating the data-min and data-max attributes and then triggering a 'change' event on the element. A render function could look like this: const renderFunc = (min, max, className) => `<input type="hidden" class="${className}" data-min="${min}" data-max="${max}">`;.

⚙️ Optional settings

Filter typeKeyValueDescription
'type', 'vendor', 'option', 'tag', 'tagPrefix'singularboolIf set to true, the user will only be able to select one value at a time.
'tag'includeOnlyarrayThe 'tag' filter type can be passed an array of strings representing the tags to include. If includeOnly is put on the filter, only the tags in this array will be included in the filter.
'tag'omitarrayThe 'tag' filter type can be passed an array of strings representing specific tags to omit. These tags will then not be included in the filter.
'type', 'vendor', 'option', 'tag', 'tagPrefix'sortOrderarrayBy default filter items for the filter types 'type', 'vendor', 'option', 'tag', and 'tagPrefix' are sorted alphabetically by their values. In case a filter needs a non-alphabetical sorting, it can be given an array of manually sorted values. This is useful e.g. for size filters, where the order might need to be for instance 'S', 'M', 'L', 'XL', etc. If given a sortOrder the filter will first sort its items alphabetically, and then attempt to sort them according to the array. Filters of the types 'sort' and 'price' will not be affected if given a sortOrder.

🪝 Event hooks

A number of events are fired by the filter during the different stages of the filter. These can be hooked into by setting collectionFilter.on(...) event listeners (where collectionFilter should be replaced with whichever variable name you have saved the return value of the collection filter function to). On these events the callback function will always be passed the filter object in its current state as its sole argument. It can be used like this: collectionFilter.on('fetchProducts', (filter) => console.log(filter));. For advanced functionality, the events can also be manually triggered by running collectionFilter.emit('eventName'), where eventName should be replaced with one of the actual event names. This should however be done with caution, as it might trigger unexpected behaviour. Below is a list of all the events:

NameEventOccurence
'fetchProducts'All products have been fetched from the server.Filter start
'renderFilters'All the filters have been rendered to the markup.Filter start
'processURLParams'The URL params (if any) have been processed. Filter queries and the current page have been stored.Filter start
'setValues'All filter items found in the queries have been activated.Filter start
'filterProducts'The product data have been filtered.Filter start, Item changed
'setPagination'The products have been split into chunks reflecting the set page size.Filter start, Item changed
'itemChanged'A filter item has been changed (either by the user, or programatically by another script).Item changed
'initFilters'Event listeners have been set for all filter items.Filter start
'pageChanged'Next page has been selected.Page changed
'initPagination'Pagination has been initialized.Filter start, Item changed
'writeURLParams'URL params have been written to the URL.Item changed
'renderProducts'The products have been rendered to the markup.Filter start, Item changed
1.0.2

3 years ago

1.0.1

3 years ago