development
Development Guide

Development Guide

This guide covers the development workflow, tools, and best practices for contributing to GridSheet.

Development Setup

Prerequisites

  • Node.js: Version 22.x or later
  • pnpm: For package management
  • Git: For version control

Installation

# Clone the repository
git clone https://github.com/your-org/gridsheet.git
cd gridsheet
 
# Install dependencies
pnpm install

Development Scripts

# Start Storybook development server
pnpm dev
 
# Start documentation development server
pnpm doc
 
# Run unit tests
pnpm test
 
# Run e2e tests (uses server)
pnpm e2e
 
# Run linting and apply prettier
pnpm lint:fix

Testing Strategy

Testing Policy

GridSheet follows this testing strategy:

  • E2E tests (Playwright): The default for all features — component behavior, user interactions, and integration between components.
  • Unit tests (Jest): Used only for packages/functions. Every formula function in that package must have a corresponding Jest unit test.

E2E Test Example

import { test, expect } from '@playwright/test';
import { ctrl, drag, paste } from './utils';
 
test('time + delta, time + number(days)', async ({ page }) => {
  await page.goto('http://localhost:5233/iframe.html?id=basic-simple--sheet&viewMode=story');
  const a4 = page.locator("[data-address='A4']");
  const b4 = page.locator("[data-address='B4']");
  const c4 = page.locator("[data-address='C4']");
  const a5 = page.locator("[data-address='A5']");
 
  expect(await a4.locator('.gs-cell-rendered').textContent()).toBe('2022-03-05 12:34:56');
  expect(await b4.locator('.gs-cell-rendered').textContent()).toBe('11:11:11');
  expect(await c4.locator('.gs-cell-rendered').textContent()).toBe('2022-03-05 23:46:07');
  expect(await a5.locator('.gs-cell-rendered').textContent()).toBe('2022-03-04 23:34:56');
});

Available Utilities:

The E2E test suite provides utility functions for common operations:

  • ctrl: Helper for Ctrl key combinations
  • drag: Helper for drag and drop operations
  • paste: Helper for paste operations

These utilities are available in e2e/utils.ts and can be imported to simplify test writing.

Formula Functions

Creating Custom Formula Functions

When adding new formula functions to GridSheet, follow these guidelines:

Package Location Strategy

To keep the distribution size of @gridsheet/react-core as compact as possible, it only includes essential, minimum functions. All future function additions must be made to the @gridsheet/functions package (packages/functions).

Google Spreadsheet Compatibility

When adding new functions, prioritize those that are also supported by Google Spreadsheet:

  • Target Functions: Focus on functions that exist in Google Sheets
  • Specification Matching: Follow Google Sheets' function specifications and behavior
  • Parameter Compatibility: Match argument types, order, and validation rules
  • Return Value Consistency: Ensure return values match Google Sheets' behavior
  • Error Handling: Implement the same error conditions and messages

BaseFunction Inheritance

All custom formula functions must extend the BaseFunction class:

import { BaseFunction, ensureString } from '@gridsheet/react-core';
import type { FunctionArgumentDefinition } from '@gridsheet/react-core';
 
class CustomFunction extends BaseFunction {
  // Required properties for documentation and the function guide UI
  example = 'CUSTOM_FUNCTION("value", 123)';
  description = 'Performs a custom operation on the input values';
  category = 'text' as const;  // math | statistics | text | time | lookup | information | logical | other
  defs: FunctionArgumentDefinition[] = [
    { name: 'value', description: 'The input value to process', acceptedTypes: ['string'] },
    { name: 'number', description: 'A numeric parameter', acceptedTypes: ['number'] },
  ];
 
  // Required main implementation
  protected main(value: string, number: number) {
    // Your custom logic here
    return `Processed: ${value} with ${number}`;
  }
}

Note: The built-in validate() method automatically checks argument count and types based on defs. Override it only when you need custom coercion logic beyond what defs provides.

Documentation Properties

Every formula function must include these properties:

  • example: Shows how to use the function in a formula
  • description: A string describing what the function does (used in the FunctionGuide UI)
  • category: Classification (math, statistics, text, time, lookup, information, logical, other)
  • defs: Array of FunctionArgumentDefinition objects with name, description, and optional type constraints

Function Registration

Register custom functions via the additionalFunctions option in useBook (or createBook):

import { useBook } from '@gridsheet/react-core';
 
const book = useBook({
  additionalFunctions: {
    CUSTOM_FUNCTION: CustomFunction,
  },
});

For apps that want all extended functions from the @gridsheet/functions package:

import { useSpellbook } from '@gridsheet/react-core/spellbook';
 
// useSpellbook pre-loads all extended functions and accepts extra ones
const book = useSpellbook({
  additionalFunctions: { CUSTOM_FUNCTION: CustomFunction },
});

Documentation

API Documentation

# Start documentation development server
pnpm doc
 
# Build documentation
pnpm build:docs

Storybook

# Start Storybook development server
pnpm dev
 
# Build Storybook
pnpm build:storybook

Contributing

Pull Request Process

  1. Create Feature Branch: git checkout -b feature/new-feature
  2. Make Changes: Implement your feature
  3. Run Tests: Ensure all tests pass (pnpm test)
  4. Update Documentation: Update relevant docs
  5. Submit PR: Create pull request with description

Code Review Checklist

  • Code follows style guidelines
  • Tests are included and passing
  • Documentation is updated
  • No breaking changes (or documented)
  • Performance impact considered

Release Process

  1. Version Bump: pnpm set-version <version> (updates all packages, commits, and tags)
  2. Push to master: git push origin master --tags
  3. GitHub Actions (release.yaml) will automatically build and publish all packages to npm

Recommended: Release via GitHub Actions rather than manually. The CI pipeline handles build order and dependency resolution correctly.

If you must publish manually, always use pnpm publish (not npm publish). pnpm publish automatically converts workspace:* references to actual version numbers. npm publish will leave workspace:* as-is, causing installation failures for users.