domo-toolkit

Contributing to Domo Toolkit

Thanks for your interest in contributing! Whether it’s reporting a bug, suggesting a feature, or submitting code, this guide covers everything you need to know.

Reporting Issues

Found a bug or have a feature request? Open an issue on GitHub. An issue is just a post describing what bug you ran into or what you’d like to see – no coding or technical knowledge required. You’ll need a free GitHub account to create one. Include as much detail as you can (what you were doing, what happened, what you expected) and I’ll take it from there.

Bug Reports

Include as much of the following as you can:

Feature Requests

Describe the problem you’re trying to solve, not just the solution you have in mind. Context about your workflow helps prioritize and design the right approach.

Submitting Pull Requests

Getting Started

  1. Fork the repository
  2. Clone your fork and create a branch from main:

    git clone https://github.com/<your-username>/domo-toolkit.git
    cd domo-toolkit
    git checkout -b your-branch-name
    
  3. Install dependencies and start the dev server:

    yarn
    yarn dev
    
  4. Load the extension from the dist directory (see Development Setup)

Branch Naming

Use a short, descriptive branch name that reflects the change. For example:

Commit Messages

Write clear, descriptive commit messages. Start with what the change does, not what file was edited. A single commit covering a cohesive set of changes is preferred over many small ones, but split unrelated changes into separate commits.

Good:

Avoid:

PR Guidelines

Code Review

All PRs are reviewed before merging. You may be asked to make changes – this is normal and part of the process. Keep an eye on your PR for comments and requested changes.

Helpful Developer Information

Tech Stack

Category Technology Version
Framework React 19.2.4
Bundler Vite 7.3.0
Extension Plugin @crxjs/vite-plugin 2.0.3
UI Library @heroui/react 3.0.0-beta.8
CSS Tailwind CSS 4.1.18
Icons @tabler/icons-react 3.36.1
Virtualization @tanstack/react-virtual 3.13.18
Linter ESLint 10.0.2
Formatter Prettier 3.7.4

Project Structure

src/
├── assets/             # Static assets and CSS
├── components/         # Shared React components
│   ├── functions/      # Action button implementations
│   ├── options/        # Settings page components
│   └── views/          # View components used in side panel for data discovery features
├── data/               # Release information used for new release badge and page
├── hooks/              # Custom React hooks
├── models/             # Data classes (DomoObject, DomoContext, DomoObjectType)
├── options/            # Settings/options page
├── popup/              # Popup UI (click on extension icon)
├── services/           # Domo API service functions
├── sidepanel/          # Side panel UI (contextual panel alongside pages)
├── utils/              # Utility functions
├── background.js       # Service worker (background script)
└── contentScript.js    # Content script (injected into Domo pages)

Architecture

The extension has four main execution contexts:

  1. Popup - Small interface when extension icon is clicked
  2. Side Panel - Persistent panel alongside Domo pages with richer UI
  3. Content Script - Injected into Domo pages; detects objects, applies favicons
  4. Background Service Worker - Handles message passing, maintains tab context cache

Data Flow

Content Script (detects page context)
  → Background Service Worker (message relay, caches context)
  → Popup/Sidepanel (receives context via chrome.runtime messages)
  → User triggers action → Services execute via content script

Core Models

Development Setup

# Clone the repository (or your fork)
git clone https://github.com/brycewc/domo-toolkit.git
cd domo-toolkit

# Install dependencies
yarn          # or: npm install

# Start dev server (with HMR)
yarn dev      # or: npm run dev

# Build for production
yarn build    # or: npm run build

# Preview production build
yarn preview  # or: npm run preview

Load the extension in Chrome or Edge:

  1. Navigate to chrome://extensions/ or edge://extensions
  2. Enable “Developer mode”
  3. Click “Load unpacked” and select the dist directory

Code Conventions

Linting (via ESLint)

The project uses ESLint with three plugin layers configured in eslint.config.js:

Key sorting rules enforced by perfectionist:

Unused variables must be prefixed with _ (e.g., _event, _unused). Caught errors are exempt.

Run ESLint to check files:

npx eslint --no-warn-ignored <file-paths>

Formatting (via Prettier)

File Organization

React Patterns

Model Classes

Styling

Key Patterns

Executing Code in Page Context

Services need to inherit user session, authentication, and permissions by running in the page context:

import { executeInPage } from '@/utils/executeInPage';

const result = await executeInPage(
  (arg1, arg2) => {
    // This runs in the Domo page context
    return fetch('/api/endpoint').then((r) => r.json());
  },
  [arg1, arg2],
  tabId
);

Message Passing

Popup and sidepanel listen for context updates from the background service worker. Messages are filtered by currentTabId so each view only responds to updates for the tab it’s displaying:

useEffect(() => {
  const handleMessage = (message, sender, sendResponse) => {
    if (message.type === 'TAB_CONTEXT_UPDATED') {
      if (message.tabId === currentTabId) {
        const context = DomoContext.fromJSON(message.context);
        setCurrentContext(context);
      }
      sendResponse({ received: true });
      return true;
    }
    return false;
  };

  chrome.runtime.onMessage.addListener(handleMessage);
  return () => chrome.runtime.onMessage.removeListener(handleMessage);
}, [currentTabId]);

Status Bar Pattern

Status messages are managed by the useStatusBar hook in the parent App component (popup or sidepanel). Child components receive an onStatusUpdate callback prop and call it to display transient alerts:

// In the parent App component (popup/App.jsx or sidepanel/App.jsx)
const { statusBar, showStatus, hideStatus } = useStatusBar();

// Pass showStatus down as a prop
<ActionButtons onStatusUpdate={showStatus} />
<ContextFooter onStatusUpdate={showStatus} />

// Render StatusBar alongside content (animated overlay)
{statusBar.visible && (
  <StatusBar
    title={statusBar.title}
    description={statusBar.description}
    status={statusBar.status}
    timeout={statusBar.timeout}
    onClose={hideStatus}
  />
)}

Inside action components, call the callback to show a status message:

// In any action component
onStatusUpdate('Copied', 'Object ID copied to clipboard', 'success', 3000);

Extension Permissions

Permission Purpose
sidePanel Open side panel UI
storage Persist settings (sync) and cache (local)
scripting Inject and execute scripts in pages
activeTab Access current tab info
clipboardRead/Write Copy object IDs
cookies Clear Domo cookies
webNavigation Listen for navigation events
webRequest Detect 431 errors for auto cookie clearing

Host permission: *://*.domo.com/*

Configuration Files

File Purpose
vite.config.js Vite config with CRXJS, Tailwind, and path aliases
manifest.config.js Chrome extension manifest v3 (permissions, content scripts, etc.)
eslint.config.js ESLint config with perfectionist sorting and stylistic rules
.prettierrc Prettier formatting rules

Documentation