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 installDevelopment 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:fixTesting 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 combinationsdrag: Helper for drag and drop operationspaste: 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 ondefs. Override it only when you need custom coercion logic beyond whatdefsprovides.
Documentation Properties
Every formula function must include these properties:
example: Shows how to use the function in a formuladescription: A string describing what the function does (used in the FunctionGuide UI)category: Classification (math,statistics,text,time,lookup,information,logical,other)defs: Array ofFunctionArgumentDefinitionobjects 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:docsStorybook
# Start Storybook development server
pnpm dev
# Build Storybook
pnpm build:storybookContributing
Pull Request Process
- Create Feature Branch:
git checkout -b feature/new-feature - Make Changes: Implement your feature
- Run Tests: Ensure all tests pass (
pnpm test) - Update Documentation: Update relevant docs
- 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
- Version Bump:
pnpm set-version <version>(updates all packages, commits, and tags) - Push to master:
git push origin master --tags - 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(notnpm publish).pnpm publishautomatically convertsworkspace:*references to actual version numbers.npm publishwill leaveworkspace:*as-is, causing installation failures for users.