New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details
Socket
Book a DemoSign in
Socket

@procore/ai-translation-utils

Package Overview
Dependencies
Maintainers
303
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@procore/ai-translation-utils

Utility functions for AI translation services

latest
Source
npmnpm
Version
0.3.1
Version published
Weekly downloads
172
56.36%
Maintainers
303
Weekly downloads
 
Created
Source

@procore/ai-translation-utils

AG Grid data-table integration utilities for [@procore/ai-translations](https://github.com/procore/platform-internationalization-js-monorepo). Provides type-safe column-level translation toggling, cell rendering, progress popups, and menu option factories -- all wired through AG Grid's GridApi and React refs.

Table of Contents

Quick Start

Installation

yarn add @procore/ai-translation-utils

Hello World -- A Fully Typed AG Grid with AI Translation

import React, { useRef } from 'react';
import type { GridApi, ColDef } from '@ag-grid-community/core';
import { AgGridReact } from '@ag-grid-community/react';
import { AITranslationProvider } from '@procore/ai-translations';
import {
  handleGridReady,
  createAIMenuOptions,
  TranslatableCell,
  TranslationProgressPopup,
  ModelDownloadProgressPopup,
  type AIMenuOption,
} from '@procore/ai-translation-utils';

export function TranslatableGrid() {
  const gridApiRef = useRef<GridApi | null>(null);

  const aiMenuOptions: AIMenuOption[] = createAIMenuOptions(gridApiRef, {
    translate: 'Translate Column',
    highlight: 'Highlight Translations',
  });

  const columnDefs: ColDef[] = [
    {
      field: 'description',
      cellRenderer: TranslatableCell,
      headerComponentParams: { menuOptions: aiMenuOptions },
    },
  ];

  return (
    <AITranslationProvider>
      <AgGridReact
        columnDefs={columnDefs}
        rowData={[{ description: 'Hello, world!' }]}
        onGridReady={(params) => handleGridReady(params, gridApiRef)}
      />
      <TranslationProgressPopup />
      <ModelDownloadProgressPopup />
    </AITranslationProvider>
  );
}

Every export above is explicitly typed. gridApiRef is MutableRefObject<GridApi | null>, aiMenuOptions is AIMenuOption[], and columnDefs uses AG Grid's ColDef -- the compiler enforces correctness at every integration point.

Core Architecture

The package is organized into a single data-table module that exposes three layers: state management, UI components, and a menu factory.

Module Structure

src/
├── index.ts                  # Barrel: re-exports everything from data-table/
└── data-table/
    ├── index.ts              # Public API barrel
    ├── columnFeatureState.ts # WeakMap-backed per-grid, per-column state
    ├── aiMenuOptions.ts      # Menu option factory (depends on columnFeatureState)
    ├── TranslatableCell.tsx   # AG Grid cell renderer (reads columnFeatureState)
    ├── TranslationProgressPopup.tsx
    ├── ModelDownloadProgressPopup.tsx
    └── popupShared.tsx        # Internal: shared styles, ProgressBar, ErrorBoundary

Class & Interface Diagram

classDiagram
    direction LR

    class AIMenuOption {
        <<interface>>
        +label: string
        +value: string
        +action(colDef: ColDef | null) void
    }

    class columnFeatureState {
        <<module>>
        -translationStateMap: WeakMap~GridApi, Record~string, boolean~~
        -highlightStateMap: WeakMap~GridApi, Record~string, boolean~~
        +isColumnTranslationEnabled(gridApi, field) boolean
        +toggleColumnTranslation(gridApi, field) void
        +isColumnHighlightEnabled(gridApi, field) boolean
        +toggleColumnHighlight(gridApi, field) void
        +clearColumnFeatureStates(gridApi) void
        +handleGridReady(params, gridApiRef) void
    }

    class createAIMenuOptions {
        <<function>>
        +createAIMenuOptions(gridApiRef, labels) AIMenuOption[]
    }

    class TranslatableCell {
        <<component>>
        +TranslatableCell(params: ICellRendererParams) JSX.Element
        +extractText(value: unknown) string
    }

    class TranslationProgressPopup {
        <<component>>
        +label? string
        +TranslationProgressPopup(props) JSX.Element
    }

    class ModelDownloadProgressPopup {
        <<component>>
        +label? string
        +ModelDownloadProgressPopup(props) JSX.Element
    }

    class ProgressErrorBoundary {
        <<internal>>
        +state: hasError boolean
        +getDerivedStateFromError() state
        +render() ReactNode | null
    }

    createAIMenuOptions --> columnFeatureState : calls toggle functions
    createAIMenuOptions --> AIMenuOption : returns
    TranslatableCell --> columnFeatureState : reads state
    TranslatableCell --> AITranslateText : delegates rendering
    TranslationProgressPopup --> ProgressErrorBoundary : wrapped by
    ModelDownloadProgressPopup --> ProgressErrorBoundary : wrapped by
    TranslationProgressPopup --> useAITranslation : reads translationProgress
    ModelDownloadProgressPopup --> useAITranslation : reads modelDownloadProgress

API Reference

Exported Types & Interfaces

Type / InterfaceSourcePropertiesDescription
AIMenuOptionaiMenuOptions.tslabel: string, value: string, action: (colDef: ColDef | null) => voidDescribes a single menu entry for AG Grid column headers. action receives the column definition so toggle logic can identify which column to operate on.
TranslationProgressPopupPropsTranslationProgressPopup.tsxlabel?: stringOptional props for TranslationProgressPopup. label overrides the default "Translating..." text.
ModelDownloadProgressPopupPropsModelDownloadProgressPopup.tsxlabel?: stringOptional props for ModelDownloadProgressPopup. label overrides the default "Downloading AI Model..." text.

Dependency types used in public signatures (from peer packages, not re-exported):

TypeSource PackageUsage
GridApi@ag-grid-community/coreGrid instance passed to all column state functions
GridReadyEvent@ag-grid-community/coreEvent parameter for handleGridReady
ColDef@ag-grid-community/coreColumn definition received by AIMenuOption.action
ICellRendererParams@ag-grid-community/coreProps received by TranslatableCell
MutableRefObject<T>reactReact ref holding GridApi | null

Column Feature State Functions

All state is stored in module-scoped WeakMap<GridApi, Record<string, boolean>> instances. This means state is automatically garbage-collected when a GridApi is disposed and is isolated per grid instance.

FunctionSignatureReturnsDescription
isColumnTranslationEnabled(gridApi: GridApi | null | undefined, field: string | undefined) => booleanbooleanReturns true if translation is active for field. Safely returns false for nullish inputs.
toggleColumnTranslation(gridApi: GridApi | null | undefined, field: string | undefined) => voidvoidFlips the translation flag for field and calls gridApi.refreshCells(). No-op if gridApi, field, or the column definition is missing.
isColumnHighlightEnabled(gridApi: GridApi | null | undefined, field: string | undefined) => booleanbooleanReturns true if highlighting is active for field.
toggleColumnHighlight(gridApi: GridApi | null | undefined, field: string | undefined) => voidvoidFlips the highlight flag for field and refreshes cells.
clearColumnFeatureStates(gridApi: GridApi | null | undefined) => voidvoidResets both translation and highlight maps for the grid. Refreshes only columns that had active states.
handleGridReady(params: GridReadyEvent, gridApiRef: MutableRefObject<GridApi | null>) => voidvoidStores params.api in the ref and registers a paginationChanged listener that auto-clears feature states.

Menu Options Factory

FunctionSignatureReturns
createAIMenuOptions(gridApiRef: MutableRefObject<GridApi | null>, labels: { translate: string; highlight: string }) => AIMenuOption[]AIMenuOption[]

Returns two AIMenuOption entries (translate and highlight). Each option's action closure captures the gridApiRef and calls the corresponding toggle function from columnFeatureState. The labels parameter allows localization of menu text.

const options = createAIMenuOptions(gridApiRef, {
  translate: 'Traducir columna', // Spanish
  highlight: 'Resaltar traducciones',
});

Cell Renderer & Utilities

TranslatableCell

(params: ICellRendererParams) => JSX.Element;

AG Grid cell renderer component. Reads column-level translation/highlight state from the GridApi on every render, extracts display text via extractText, and delegates to AITranslateText from @procore/ai-translations. Uses React.memo internally to avoid unnecessary re-renders of the translation component.

extractText

(value: unknown) => string;

Coerces an AG Grid cell value into a display string:

Input TypeBehavior
stringReturned as-is
Array<{ label?: string } | unknown>Each element: uses .label if object with label key, otherwise String(v); joined with ', '
{ label?: string }Returns .label ?? ''
Anything elseReturns ''

Progress Popups

Both components consume the useAITranslation() hook from @procore/ai-translations and render fixed-position popups. Each is wrapped in a ProgressErrorBoundary that renders null on error, preventing translation UI failures from crashing the host app.

ComponentHook DataVisibilityProgress Bar Color
TranslationProgressPopuptranslationProgressHidden when null or total === 0#f47e42 (orange)
ModelDownloadProgressPopupmodelDownloadProgressHidden when null or isComplete#3b82f6 (blue)

Props

Both components accept an optional label prop. When omitted, the default label is used.

PropTypeRequiredDefault (TranslationProgressPopup)Default (ModelDownloadProgressPopup)
labelstringNo"Translating...""Downloading AI Model..."
// Default labels
<TranslationProgressPopup />
<ModelDownloadProgressPopup />

// Custom labels (e.g. for localization)
<TranslationProgressPopup label="Traduciendo..." />
<ModelDownloadProgressPopup label="Descargando modelo..." />

Workflow: Request Flow

The following sequence diagram shows how a user toggling "Translate Column" from a column header menu flows through the system:

sequenceDiagram
    participant User
    participant AGGrid as AG Grid Column Menu
    participant Factory as createAIMenuOptions
    participant State as columnFeatureState
    participant WeakMap as WeakMap<GridApi, Record>
    participant Cell as TranslatableCell
    participant AIText as AITranslateText

    Note over Factory: At mount time, factory creates menu options
    Factory->>State: Captures toggleColumnTranslation in action closure

    User->>AGGrid: Clicks "Translate Column"
    AGGrid->>Factory: Invokes action(colDef)
    Factory->>State: toggleColumnTranslation(gridApi, colDef.field)
    State->>WeakMap: Flip field flag in translationStateMap
    State->>AGGrid: gridApi.refreshCells({ columns: [field], force: true })

    AGGrid->>Cell: Re-renders TranslatableCell(params)
    Cell->>State: isColumnTranslationEnabled(api, field) → true
    Cell->>State: isColumnHighlightEnabled(api, field)
    Cell->>Cell: extractText(params.value) → display string
    Cell->>AIText: <AITranslateText text shouldTranslate showHighlight />
    AIText-->>User: Translated text rendered in cell

Grid Initialization Flow

sequenceDiagram
    participant App
    participant AGGrid as AG Grid
    participant Handler as handleGridReady
    participant Ref as gridApiRef (MutableRefObject)
    participant State as columnFeatureState

    AGGrid->>Handler: onGridReady(params)
    Handler->>Ref: gridApiRef.current = params.api
    Handler->>AGGrid: api.addEventListener('paginationChanged', callback)

    Note over AGGrid: User navigates to page 2
    AGGrid->>State: paginationChanged → clearColumnFeatureStates(gridApi)
    State->>State: Reset both WeakMaps for this grid
    State->>AGGrid: refreshCells for previously active columns

Type Safety & Generics

WeakMap State Isolation (columnFeatureState.ts)

The central design decision in columnFeatureState.ts is using WeakMap<GridApi, Record<string, boolean>> for both translation and highlight state:

const translationStateMap = new WeakMap<GridApi, Record<string, boolean>>();
const highlightStateMap = new WeakMap<GridApi, Record<string, boolean>>();

This delivers three type-safety guarantees:

  • Key type constraint -- Only GridApi instances can be used as keys. Passing a plain object or null is a compile-time error at the WeakMap level; the exported functions add runtime guards (if (!gridApi) return) for the null | undefined union that consumers pass.
  • Value type contract -- Record<string, boolean> enforces that column field names (string) always map to boolean flags. This prevents accidental storage of richer objects or numeric states.
  • Automatic cleanup -- WeakMap allows the JavaScript engine to garbage-collect entries when the GridApi instance is no longer referenced. This is critical in SPAs where grids mount/unmount; no manual teardown is needed.

Nullable Parameter Patterns

Every public function accepts GridApi | null | undefined and string | undefined rather than requiring non-null values. This is deliberate -- it matches how AG Grid hands back these values in practice (e.g., colDef?.field is string | undefined, and a ref starts as null). The functions guard internally, so consumers don't need to litter their code with null checks:

// No need for: if (gridApi && field) { toggleColumnTranslation(gridApi, field); }
// Just call it directly -- the function handles nullish values safely:
toggleColumnTranslation(gridApiRef.current, colDef?.field);

MutableRefObject<GridApi | null>

The createAIMenuOptions and handleGridReady functions accept MutableRefObject<GridApi | null> -- React's useRef return type. This ensures:

  • The ref's .current property is always typed as GridApi | null
  • The ref object itself is stable across renders (referential equality)
  • Action closures in menu options always read the latest GridApi via the ref, avoiding stale closure bugs

AIMenuOption.action and ColDef

The action callback is typed as (colDef: ColDef | null) => void. The ColDef | null union accounts for cases where the menu is invoked without a column context. Implementations use optional chaining (colDef?.field) to safely extract the field name.

Advanced Patterns

Custom Menu Options

Extend the menu options array with your own entries that follow the AIMenuOption interface:

import type { AIMenuOption } from '@procore/ai-translation-utils';
import { createAIMenuOptions } from '@procore/ai-translation-utils';

const baseOptions = createAIMenuOptions(gridApiRef, {
  translate: 'Translate',
  highlight: 'Highlight',
});

const customOption: AIMenuOption = {
  label: 'Export Translations',
  value: 'export',
  action: (colDef) => {
    if (colDef?.field) {
      exportTranslationsForColumn(colDef.field);
    }
  },
};

const allOptions: AIMenuOption[] = [...baseOptions, customOption];

Custom Cell Renderer Wrapping extractText

Build on extractText to create a cell renderer with additional formatting:

import {
  extractText,
  isColumnTranslationEnabled,
} from '@procore/ai-translation-utils';
import type { ICellRendererParams } from '@ag-grid-community/core';

export function CustomTranslatableCell(params: ICellRendererParams) {
  const field = params.colDef?.field;
  const isTranslating = isColumnTranslationEnabled(params.api, field);
  const text = extractText(params.value);

  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
      {isTranslating && <TranslationIcon />}
      <span>{text}</span>
    </div>
  );
}

Multi-Grid State Isolation

Because state is keyed by GridApi via WeakMap, multiple grids on the same page are automatically isolated:

function DualGridPage() {
  const gridApiRefA = useRef<GridApi | null>(null);
  const gridApiRefB = useRef<GridApi | null>(null);

  // Each grid gets its own independent translation/highlight state
  const menuA = createAIMenuOptions(gridApiRefA, {
    translate: 'Translate',
    highlight: 'Highlight',
  });
  const menuB = createAIMenuOptions(gridApiRefB, {
    translate: 'Translate',
    highlight: 'Highlight',
  });

  return (
    <>
      <AgGridReact
        onGridReady={(p) => handleGridReady(p, gridApiRefA)} /* ... */
      />
      <AgGridReact
        onGridReady={(p) => handleGridReady(p, gridApiRefB)} /* ... */
      />
    </>
  );
}

Programmatic State Control

Bypass the menu UI to toggle translation from external controls:

import {
  toggleColumnTranslation,
  isColumnTranslationEnabled,
} from '@procore/ai-translation-utils';

function TranslateAllButton({
  gridApiRef,
  fields,
}: {
  gridApiRef: MutableRefObject<GridApi | null>;
  fields: string[];
}) {
  const handleClick = () => {
    for (const field of fields) {
      if (!isColumnTranslationEnabled(gridApiRef.current, field)) {
        toggleColumnTranslation(gridApiRef.current, field);
      }
    }
  };

  return <button onClick={handleClick}>Translate All Columns</button>;
}

Developer Experience

Prerequisites

This package is part of the platform-internationalization-js-monorepo

Scripts

CommandDescription
yarn buildBuilds the package via hammer lib:build (tsup/ESBuild). Produces dist/modern/ (ESM + CJS with conditional exports) and dist/legacy/ (fallback entry points).
yarn devStarts a watch-mode dev build (hammer lib:start --env=development).
yarn testBuilds first, then runs Jest tests with coverage.
yarn test:nodepsRuns tests without a preceding build (useful during development).
yarn test:watchRuns tests in watch mode.
yarn lintRuns both ESLint (lint:code) and TypeScript type checking (lint:types).
yarn formatFormats all files with Prettier.
yarn format:checkChecks formatting without writing changes.
yarn analyzeBuilds with metafile output, then opens an interactive bundle visualizer.
yarn cleanRemoves the dist/ directory.

Build Output: dist/ vs src/

The src/ directory contains TypeScript source files. The build process (tsup via Hammer) compiles these into two output directories:

dist/
├── modern/          # Used by package.json "exports" field
│   ├── index.mjs    # ESM bundle (import)
│   ├── index.js     # CJS bundle (require)
│   ├── index.d.mts  # ESM type declarations
│   └── index.d.ts   # CJS type declarations
└── legacy/          # Used by package.json "main"/"module"/"types" fields
    ├── index.js     # CJS entry
    ├── index.mjs    # ESM entry
    └── index.d.ts   # Type declarations
  • Modern consumers (Node 16+, bundlers with exports support) resolve via dist/modern/.
  • Legacy consumers fall back to dist/legacy/ via the main, module, and types fields.
  • Only the dist/ directory is published to npm ("files": ["dist"]).

Testing

Tests use Jest (via @procore/hammer-test-jest) with React Testing Library and jest-dom matchers. Test files live alongside source code in __tests__/ directories.

src/data-table/__tests__/
├── columnFeatureState.test.ts       # Unit tests with mocked GridApi
├── aiMenuOptions.test.ts            # Tests with mocked columnFeatureState
├── TranslatableCell.test.tsx         # Component rendering tests
├── TranslationProgressPopup.test.tsx # Popup visibility/progress tests
└── ModelDownloadProgressPopup.test.tsx

Key testing patterns used:

  • GridApi methods are mocked via Jest (jest.fn()) since AG Grid is a peer dependency
  • @procore/ai-translations is mocked at the module level to control hook return values
  • columnFeatureState is mocked in aiMenuOptions.test.ts to isolate the factory logic

Peer Dependencies

PackageVersionPurpose
@ag-grid-community/core>= 31AG Grid types and API (GridApi, ColDef, ICellRendererParams, GridReadyEvent)
@procore/ai-translations>= 0.6.0AITranslateText component, useAITranslation hook, AITranslationProvider context
react>= 17React runtime for components, hooks, and refs

License

SEE LICENSE IN LICENSE

FAQs

Package last updated on 02 Apr 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts