--- title: "Bring Your Own (BYO) 7.26 Implementation Examples" slug: "bring-your-own-byo-726-implementation-examples" updated: 2025-12-03T18:58:34Z published: 2025-12-09T16:46:27Z canonical: "docs.unqork.io/bring-your-own-byo-726-implementation-examples" --- > ## Documentation Index > Fetch the complete documentation index at: https://docs.unqork.io/llms.txt > Use this file to discover all available pages before exploring further. # Bring Your Own (BYO) 7.26 Implementation Examples > [!WARNING] > The [Bring Your Own](/v1/docs/introduction-to-the-bring-your-own-byo-framework) feature is intended for developers who have a strong understanding of the Unqork Designer Platform and JavaScript. ## Overview The **Bring Your Own (BYO) 7.26 implementation examples** page contains example implementations of a component, event, and operation. Refer to these exampless when building your components with the [BYO Component SDK](/v1/docs/bring-your-own-byo-component-sdk-software-development-kit) guide. AG Grid ComponentTask List ComponentRoot Level ComponentAvg & Round Operation ## AG Grid Component This example displays the customization options, `manifest.json`, and implementation of a custom AG Grid component. ### Configuration | Settings | Object Type | Description | | --- | --- | --- | | `columnDefinitions` | string componentKey | The definitions of the columns from the `key` and `label` data. | | `value` | string componentKey | The value of the grid data. This version accepts a string, but it can be defined as needed. | ### Events | Event Name | Event Value | Description | | --- | --- | --- | | Cell Clicked | `agGridCellClicked` | Add a custom event that emits on cell click. **Event Payload**: Add the details of the cell that was clicked into the Event Payload to use in logic: - Row of the cell that was clicked. - Column of the cell that was clicked. - Value of the cell that was click. | ## Example AG Grid Package Click on each of the tabs below to view the JSON and Javascript required to implement an AG Grid: ### Manifest JSON Example > Get an in-depth understanding of `manifest.json` specifications in our [Bring Your Own (BYO): Understanding the manifest.json File](/v1/docs/bring-your-own-byo-understanding-the-manifestjson-file) article. ```json { "name": "AgGrid", "version": "1.0.0", "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": "AG Grid Cell Clicked", "type": "agGridCellClicked", "description": "This event is triggered when the user clicks on a grid cell.", "stability": "STABLE" } ] } ] } ``` ### agGrid.js The implementation javascript file name must match the `manifest.json`'s `main` value. In this example, the `agGrid.js` file matches line four in the manifest. ```javascript 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 `
` } /** * 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 = 'agGridCellClicked' payload = { index, text, } } // This export is used to expose the event to the Unqork Runtime export const agGridCellClicked = { model: async () => CellClicked, } ``` > Understand what's required to build a custom component in [BYO Component SDK](/v1/docs/bring-your-own-byo-component-sdk-software-development-kit) article. ## Task List Component This example displays the customization options, `manifest.json`, and implementation of a custom Task List component. This asset also contains an event and an operation. > Discover how to create a custom task list component in our [How To Example: Bring Your Own Task List](/v1/docs/bring-your-own-byo-component-sdk-software-development-kit) article. ### Configuration | Settings | Type | Object Type | Description | | --- | --- | --- | --- | | `label` | Input | string componentKey | The title that displays above the task list. Default value is "Tasks". | | `value` | Input | array componentKey | An array of objects that defines the task items in the list. Each object in the array contains the following: - `text` (string): The text of the task item. - `completed` (boolean): Whether the task item's checkbox is checked. Default value is `false` (unchecked). | ### Event | Event Name | Event Value | Description | | --- | --- | --- | | Task List Item Toggled | `taskListItemToggled` | An event that emits when a user checks or unchecks a task item. The event includes a payload with the text and completed status of the updated item. **Event Payload**: Adds the item name of the item that was clicked. | ### Operation | Event Name | Event Value | Description | | --- | --- | --- | | Task List Toggle Task List Item | `toggleTaskListItem` | Toggles or explicitly sets the completed state of a single item in a target task list list. Includes the following settings: - `targetKey` (string, required): component key of the task list to modify. - `item` (number\|string, required) : either the index of the item or the exact text to match. - `completed` (boolean, optional): A boolean to set the `completed` property of item. If not provided, toggles the existing value to the opposite. For example, `true` (checked) becomes `false` (unchecked), or, `false` (unchecked) becomes `true` (checked). | ## Example Task List Package Click on each of the tabs below to view the JSON and Javascript required to implement a Task List component, including an event and operation: ### Manifest JSON Example > Get an in-depth understanding of `manifest.json` specifications in our [Bring Your Own (BYO): Understanding the manifest.json File](/v1/docs/bring-your-own-byo-understanding-the-manifestjson-file) article. ```json { "name": "TaskList", "version": "1.0.0", "main": "taskList.js", "type": "custom", "productType": "BYO", "description": "A simple task list component with an event and an operation.", "components": [ { "name": "Example Task List", "type": "taskList", "description": "Simple task list with checkboxes.", "model": { "type": "object", "properties": { "label": { "name": "Label", "type": "string", "description": "Label of Task List", "default": "Tasks" }, "value": { "name": "Value", "type": "array", "description": "Array of task items", "items": { "type": "object", "properties": { "text": { "type": "string", "description": "Text to display for the task item" }, "completed": { "type": "boolean", "description": "Whether the item is completed", "default": false } }, "required": ["text"] }, "default": [] } }, "required": ["value"] }, "events": [ { "name": "Example Task List Item Toggled", "type": "taskListItemToggled", "description": "Emitted when an end-user checks/unchecks a task item.", "stability": "STABLE", "model": { "type": "object", "properties": { "name": { "const": "taskListItemToggled", "type": "string", "description": "Name of the event" }, "payload": { "type": "object", "description": "The event payload.", "properties": { "item": { "type": "object", "description": "A task list item.", "properties": { "text": { "type": "string", "description": "Task name."}, "completed": { "type": "boolean", "description": "Whether this task is completed."} } } }, "required": [ "item" ] } } } } ], "operations": [ { "name": "Example Task List Toggle Task List Item", "type": "toggleTaskListItem", "description": "Toggle (or set) the completion state of a specific item on a target task list.", "stability": "STABLE", "model": { "type": "object", "properties": { "options": { "type": "object", "properties": { "targetKey": { "name": "Target Key", "type": "string", "description": "Component key of the task list to modify" }, "item": { "name": "Item", "description": "Index (number) or text (string) of the item to change", "type": ["number", "string"] }, "completed": { "name": "Completed", "type": "boolean", "description": "Optional explicit completed value; if omitted, the value is toggled" } } }, "type": { "const": "toggleTaskListItem", "type": "string" } }, "required": ["targetKey", "item"] } } ] } ] } ``` ### taskList.js The implementation JavaScript file name must match the `manifest.json`'s `main` value. In this example, the `taskList.js` file matches line four in the manifest. ```javascript class UnqorkTaskListComponent extends HTMLElement { constructor() { super() this.attachShadow({ mode: 'open' }) this.items = [] this.label = 'Tasks' } /** * 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.api = api this.config = api.state.currentState() // Initial label and value from config (if provided) if (this.config.label != null) this.label = String(this.config.label) if (this.config.value != null) this.items = this._normalizeItems(this.config.value) this.render(this.shadowRoot, this.label, this.items) // Subscribe to state changes such as SET_PROPERTY this.subscribeLabel() this.subscribeValue() } /** * Subscribes to changes in the `label` property of the component's state. * * This method listens to the `state$('label')` observable for updates to the `label` property. * When a new value is emitted, it updates the `label` property of the component and re-renders the component. * * @returns {void} */ subscribeLabel() { this.api.state.state$('label').subscribe((label) => { this.label = label ?? '' this.shadowRoot.innerHTML = this.view(this.label, this.items) }) } /** * Subscribes to changes in the `value` property of the component's state. * * This method listens to the `state$('value')` observable for updates to the `value` property. * When a new value is emitted, it normalizes the value into an array of items and updates the `items` property. * It then re-renders the component with the updated items. * * @returns {void} */ subscribeValue() { this.api.state.state$('value').subscribe((value) => { this.items = this._normalizeItems(value) this.shadowRoot.innerHTML = this.view(this.label, this.items) }) } /** * Normalizes the input value into an array of objects with a specific structure. * * This method ensures that the input `value` is converted into an array of objects, * where each object contains the following properties: * - text: The text to actually display in the taskList item (defaults to an empty string if missing). * - completed: A boolean indicating whether the item is completed (defaults to `false` if missing). * * @param value - The input value to normalize. Can be a JSON string or an array. * @returns The normalized array of items. */ _normalizeItems(value) { const arr = typeof value === 'string' ? JSON.parse(value) : value if (!Array.isArray(arr)) return [] return arr.map((item) => ({ text: String(item?.text ?? ''), completed: Boolean(item?.completed), })) } render(root, label, items) { root.innerHTML = this.view(label, items) root.addEventListener('change', (ev) => { const el = ev.target if (!(el instanceof HTMLInputElement) || el.type !== 'checkbox') return const idx = Number(el.getAttribute('data-index')) if (Number.isNaN(idx) || !this.items[idx]) return const updatedItem = { ...this.items[idx], completed: el.checked } const next = [...this.items] next[idx] = updatedItem // Update state with new item value this.api.state.set({ value: next }) // Emit taskListItemToggled event this.emitItemToggledEvent(updatedItem) }) } /** * Emits a `taskListItemToggled` event when a to-do list item is toggled. * * This method triggers a domain event with the name `taskListItemToggled` and includes * the toggled item's details in the event payload. The payload contains the updated item * with its `text` and `completed` properties. * */ emitItemToggledEvent = (item) => { this.api.events.emit({ name: 'taskListItemToggled', payload: { item }, }) } // The component view view(label, items) { const list = items .map( (item, i) => `