app/javascript/js/controllers/filter_controller.js

Summary

Maintainability
A
0 mins
Test Coverage
import { Controller } from '@hotwired/stimulus'
import URI from 'urijs'

export default class extends Controller {
  static targets = ['urlRedirect']

  static values = {
    keepFiltersPanelOpen: Boolean,
  }

  PARAM_KEY = 'encoded_filters'

  // eslint-disable-next-line class-methods-use-this
  uriParams() {
    return URI(window.location.toString()).query(true)
  }

  viaResourceName() {
    return this.uriParams().via_resource_name
  }

  uriParam(param) {
    const viaResourceName = this.viaResourceName()

    if (viaResourceName) return `${this.viaResourceName}_${param}`

    return param
  }

  decode(filters) {
    return JSON.parse(
      new TextDecoder().decode(
        Uint8Array.from(atob(
          decodeURIComponent(filters),
        ), (m) => m.codePointAt(0)),
      ),
    )
  }

  encode(filtered) {
    return encodeURIComponent(
      btoa(
        String.fromCodePoint(
          ...new TextEncoder().encode(
            JSON.stringify(filtered),
          ),
        ),
      ),
    )
  }

  changeFilter() {
    const value = this.getFilterValue()
    const filterClass = this.getFilterClass()

    // Get the `encoded_filters` param for all params
    let filters = this.uriParams()[this.uriParam(this.PARAM_KEY)]

    // Decode the filters
    if (filters) {
      filters = this.decode(filters)
    } else {
      filters = {}
    }

    // Get the values for this particular filter
    filters[filterClass] = value

    const filtered = Object.keys(filters)
      // Filter out the filters without a value
      .filter((key) => filters[key] !== null)
      .reduce((obj, key) => {
        obj[key] = filters[key]

        return obj
      }, {})

    let encodedFilters

    // Encode the filters and their values
    if (filtered && Object.keys(filtered).length > 0) {
      encodedFilters = this.encode(filtered)
    }

    this.navigateToURLWithFilters(encodedFilters)
  }

  navigateToURLWithFilters(encodedFilters) {
    // Create a new URI with them
    const url = new URI(this.urlRedirectTarget.href)

    const query = {
      ...url.query(true),
    }

    if (this.keepFiltersPanelOpenValue) {
      // eslint-disable-next-line camelcase
      query.keep_filters_panel_open = this.keepFiltersPanelOpenValue ? 1 : null
    }

    // force to go to the first page if the filters changed
    query.page = 1

    if (encodedFilters) {
      query[this.PARAM_KEY] = encodedFilters
    } else {
      delete query[this.PARAM_KEY]
    }

    url.query(query)

    this.urlRedirectTarget.href = url.readable().toString()
    this.urlRedirectTarget.click()
  }
}