Bring Your Own (BYO) 7.24 Implementation Example

Overview

The Bring Your Own (BYO) 7.24 implementation examples page contains a list of arguments for a custom element. Use this list as a base for building your own components using our BYO Component SDK guide.

The Bring Your Own framework is intended for developers who have a strong understanding of the Unqork Designer Platform and JavaScript JavaScript is an object-oriented computer programming language. It is most-commonly used for interactive effects in the browser..

This example is for Creators Also known as Unqork Users, or Designer Users; is anyone who is inside the Unqork platform. using the 7.24 Unqork Designer Platform release. For environments using the 7.22 release, view our BYO 7.22 Implementation Examples.

Ag Grid

Settings

Setting Object Type Description

value

string componentKey

The value of the grid data. This version accepts a string A string is an object that represents a sequence of characters. Strings typically hold data represented in text form., but it can be defined as needed.

columnDefinitions

string componentKey

The definitions of the columns from the key and label data.

Events

Event Name Event Value Description

Cell Clicked

onAgGridCellClicked

Emitted when a cell is clicked. The context (payload) includes details on what was clicked.

Manifest JSON Example

Copy
{
  "name": "UnqorkAgGrid",
  "main": "agGrid.js",
  "type": "custom",
  "productType": "BYO",
  "description": "AG Grid integration with Unqork",
  "components": [
    {
      "name": "AG Grid",
      "type": "agGrid",
      "description": "AG Grid component for Unqork",
      "model": {
        "type": "object",
        "properties": {
          "columnDefinitions": {
            "name": "Column Definitions",
            "type": "array",
            "description": "Column definitions for this grid (currently only supporting key and label)",
            "items": {
              "type": "object",
              "properties": {
                "key": {
                  "type": "string",
                  "description": "Unique key for the column"
                },
                "label": {
                  "type": "string",
                  "description": "Display label for the column"
                }
              }
            }
          },
          "value": {
            "name": "Grid Data (JSON)",
            "type": "string",
            "description": "JSON string of data for the grid"
          }
        },
        "required": ["columnDefinitions"]
      },
      "events": [
        {
          "name": "Cell Clicked",
          "type": "onAgGridCellClicked",
          "description": "This event is triggered when the user clicks on a grid cell.",
          "stability": "STABLE"
        }
      ]
    }
  ]
}

Implementation Example

Copy
// << AgGrid lib needs to be imported/insertend here >>
// << For readability purposes it was removed >>

class UnqorkAgGridComponent extends HTMLElement {
  constructor() {
    super()
    this.attachShadow({ mode: 'open' })

    this.gridOptions = {
      columnDefs: [],
      rowData: [],
      
    }
  }

  /**
   * This is the only required method to integrate with the Unqork system
   * 
   * @param {*} api The interface to the Unqork Runtime, used for both
   *                monitoring state and emitting events
   */
  initialize(api) {
    this.config = api.state.currentState()
    this.api = api

    if (this.config.columnDefinitions?.length) {
      const defs = this._normalizeInput(this.config.columnDefinitions)
      this.gridOptions.columnDefs = this._formatColumnDefinitions(defs)
    }

    if (this.config.value?.length) {
      this.gridOptions.rowData = this._normalizeInput(this.config.value)
    }

    this.renderGrid(this.shadowRoot, this.gridOptions)
    this.subscribeColumnDefs()
    this.subscribeData()
  }

  /**
   * Subscribe to the source for column definitions
   * 
   * This can be made more robust by first using
   * api.state.state$('columnDefsReference') to subscribe to
   * any changes to that value (i.e. the target key), and then using
   * resolveByKey$ to resolve the current value of that key, but
   * keeping it simple for example here
   */
  subscribeColumnDefs() {
    this
      .api
      .state
      .state$('columnDefinitions').subscribe((columnDefs) => {
        console.log('AG Grid column definitions updated to', columnDefs)
        // This _should_ be an array, but sometimes components try to set it as a
        // JSON string instead, so we handle that here
        const value = typeof(columnDefs) === 'string' ? JSON.parse(columnDefs) : columnDefs
        this.gridOptions = {
          rowData: this.gridOptions.rowData,
          columnDefs: value ? this._formatColumnDefinitions(value) : [],
        }
        this.updateGrid(this.gridOptions)
      })
  }

  subscribeData() {
    this.api.state.state$('value').subscribe((rowData) => {
      console.log('AG Grid value updated to', rowData)
      // We support setting this as a JSON string or already parsed array of objects
      // so handle that here
      const value = typeof(rowData) === 'string' ? JSON.parse(rowData) : rowData
      this.gridOptions = {
        rowData: value || [],
        columnDefs: this.gridOptions.columnDefs,
      }
      this.updateGrid(this.gridOptions)
    })
  }

  updateGrid(gridOptions) {
    console.log('Updating AG Grid', gridOptions)
    this.gridApi.updateGridOptions(gridOptions)
  }

  async renderGrid(document, gridOptions) {
    console.log('Rendering AG Grid component', {gridOptions, agGrid, windowAgGrid: window.agGrid})

    document.innerHTML = this.view()
    this.gridEl = document.querySelector('#myGrid')
    this.gridApi = window.agGrid.createGrid(this.gridEl, gridOptions)

    // Set this here so that it doesn't overridden when we update gridOptions
    this.gridApi.addEventListener('cellClicked', (ev) => {
      console.log('Emitting AG Grid Cell Clicked Event', ev)
      this.emitCellClickedEvent(ev)
    })

    console.log('Created AG Grid', {
      document,
      gridOptions,
      gridEl: this.gridEl,
      gridApi: this.gridApi,
    })
  }

  // Emit an event when a cell is clicked
  // Full docs: https://www.ag-grid.com/javascript-data-grid/grid-events/#reference-selection-cellClicked
  emitCellClickedEvent = (ev) => {
    this.api.events.emit({
      name: 'onAgGridCellClicked',
      payload: ev
    })
  }

  view() {
    return `
      <div id="myGrid" style="height: 500px"></div>
    `
  }

  /**
   * Input data can be either JSON string or actual data, due to
   * Unqork data constraints, so ensure it's proper data before
   * using
   */
  _normalizeInput(val) {
    return typeof(val) === 'string' ? JSON.parse(val) : val
  }

  /**
   * We store column definitions with standard key/label properties
   * But AG Grid using different property names, so we map it here
   */
  _formatColumnDefinitions(defs) {
    return defs.map(({key, label}) => {
      return {
        field: key,
        headerName: label ?? key
      }
    })
  }
}

// This definition defines the state settings (model) for this component
// It mirrors the fuller description in manifest.json and will
// eventually be used to auto-generate the manifest.json JSON schema
class AgGridDefinition {
  columnDefsReference
  dataReference
}

// Actual export that exposes both the view layer and the model
// and is consumed by the Unqork Runtime for both configuration and
// execution
// All of these are `async` to allow for us to load them on demand
// without impacting initial page load performance
export const agGrid = {
  model: async () => AgGridDefinition,
  view: async () => UnqorkAgGridComponent,
}

// This definition describes the schema of the event that is emitted
// when the user clicks on an item
class CellClicked {
  name = 'onAgGridCellClicked'
  payload = {
    index,
    text,
  }
}

// This export is used to expose the event to the Unqork Runtime
export const onAgGridCellClicked = {
  model: async () => CellClicked,
}