Contributing to TouchSpin
October 16, 2025 ยท View on GitHub
Thank you for your interest in contributing to TouchSpin! This guide covers how to propose changes, coding standards, our testing approach, and how to author new renderers. For a workspace-wide overview (including sibling repositories), see AGENTS.md in this repository.
How to Contribute
Reporting Issues
- Use GitHub Issues: describe the problem clearly and concisely.
- Include environment details: OS, browser(s)/version(s), Node/Yarn versions.
- Provide reproduction steps and a minimal example (HTML/JS snippet or CodeSandbox link).
- Attach console logs, screenshots, or videos if relevant.
- Mention which package(s) are affected (core, renderer-bootstrap5, jquery adapter, etc.).
Submitting Pull Requests
- Fork the repo and create a topic branch from
main(e.g.feat/renderer-tailwind-halo). - Keep PRs focused and small; one logical change per PR.
- Follow commit best practices: concise subject, informative body.
- Required local checks before opening a PR:
yarn installyarn lintyarn typecheckyarn buildyarn workspaces foreach -A exec npm pack --dry-run(packaging smoke test)
- Use
yarn changesetto record version bumps for any package that changes behaviour or build outputs. - Tests are welcome but optional for release-engineering PRs (this audit focuses on packaging only).
- Do not commit generated
dist/artifacts; prepack scripts build them during publishing. - Use Yarn 4 (Berry) with PnP (already configured in the repo).
Our Testing Philosophy
We use a Gherkin-style testing approach with comprehensive behavior documentation:
- Plan first: Start with
test.skip()scenarios documenting all behaviors - One behavior per test: Focus on single, testable outcomes
- Helper-driven: Use our Step Lexicon for consistent interactions
- Coverage-focused: Aim for 100% test coverage on new features
See our Writing Gherkin Tests Guide for detailed guidance.
Coding Standards
- TypeScript, strict and explicit: prefer explicit types; avoid
any. - SOLID principles: small, composable, single-responsibility units.
- Naming:
- Interfaces:
Renderer,RendererOptions(noIprefix). - Abstract classes:
AbstractRenderer. - Files:
Renderer.ts,AbstractRenderer.ts,RendererOptions.tsfor option bags.
- Interfaces:
- Renderer event wiring via
data-touchspin-injectedattributes (no class-based wiring). - Side effects: keep minimal; prefer pure functions where possible.
Package Setup Requirements
All testable packages (renderers, component wrappers, plugins) MUST include:
- Build Scripts: Each package must support both production (
build) and test (build:test) builds - Test Build Configuration: A
tsconfig.testbuild.jsonfile for test compilation - Artifacts Manifest: Both
dist/anddevdist/directories must containartifacts.json - Consistent Structure: Standard directory layout with
src/,dist/,devdist/, andtests/
๐ Complete Requirements: See Package Requirements Guide for detailed setup instructions.
Renderer Authoring Guide
Renderers provide the UI layer around the input element and delegate all logic to core. They must align with the canonical core API exported from @touchspin/core/renderer.
Contract and Base Class
import { AbstractRenderer, type Renderer } from '@touchspin/core/renderer';
export default class MyRenderer extends AbstractRenderer implements Renderer {
init(): void {
// 1) Build DOM and set this.wrapper
this.wrapper = this.buildUI();
// 2) Locate UI controls
const up = this.wrapper.querySelector('[data-touchspin-injected="up"]') as HTMLElement | null;
const down = this.wrapper.querySelector('[data-touchspin-injected="down"]') as HTMLElement | null;
// 3) Delegate events to core
this.core.attachUpEvents(up);
this.core.attachDownEvents(down);
// 4) React to settings
this.core.observeSetting('prefix', (v) => this.updatePrefix(v));
this.core.observeSetting('postfix', (v) => this.updatePostfix(v));
}
}
Notes:
- Implement
init();finalizeWrapperAttributes()is called by core after initialization. - Optionally implement
teardown()for renderer-specific cleanup; callsuper.teardown()when overriding.
Naming Conventions
- Package name:
@touchspin/renderer-<flavor>(e.g.,@touchspin/renderer-bootstrap5). - Default export: export your renderer class as the default.
- CSS filename: emit
dist/touchspin-<flavor>.css(documented in README andpackage.jsonviastylefield and./cssexport if applicable).
Examples and Examples Hub
- Add a runnable example under
packages/renderer-<flavor>/example/index.html. - Ensure it imports the built UMD or module output and the CSS.
- The global examples hub (
yarn dev) lists examples recursively; verify your example appears and works. - Each renderer package can also be run individually via
yarn dev:<flavor>if configured.
Smoke Tests (Playwright)
- Add basic Playwright smoke tests that:
- Load the example page.
- Wait for
data-touchspin-injectedattributes to indicate readiness. - Click up/down buttons and assert input value changes.
- Cover vertical buttons and disabled/readonly states where applicable.
- Run locally with
yarn test(oryarn test:devto iterate).
Quality Checklist
- Builds cleanly:
yarn build. - Dist artifacts are stable (CSS/JS names).
- Example appears in
/exampleshub and works in major browsers. - Adheres to canonical
Rendererinterface; extendsAbstractRenderer. - Minimal DOM side effects; only use
data-touchspin-injectedfor event wiring.
Build & Types Rules
- ESM-only. No CJS/
require. jQuery plugin ships an extra IIFE file for script-tag users; it is not exported. - Dual-build:
- Prod:
tsupJS +tsc --emitDeclarationOnlyfor.d.ts. - Tests/Coverage: plain
tscโdevdistwithsourceMap + inlineSources.
- Prod:
- Topological builds: Always run
yarn build:types:allthenyarn build:js:all. Never parallelize types. - Type resolution: Consumers use
moduleResolution: "Bundler"; do not usetsconfig.pathsto reachcore. - Exports: For every public subpath, declare
{ types, import }inpackage.json#exports. Core exposes"."and"./renderer". - Tests: Use
PLAYWRIGHT_TSCONFIG=tsconfig.playwright.jsonandTS_BUILD_TARGET=dev. - Coverage:
yarn coverageruns run+merge+report+open; always usesdevdist.
Local Dev Server & Base URL
- The dev server runs on
http://localhost:8866by convention and is started byyarn dev. - In browser-evaluated helpers (e.g., inside
page.evaluate), prefer origin-relative resolution:- Example:
const origin = location.origin; await import(new URL(coreUrl, origin).href) - Avoid hard-coding
http://localhost:8866inside evaluated code.
- Example:
Porting Policy (Parity with Source)
When porting behavior between the legacy jQuery plugin (src/jquery.touchspin.js) and the new core/wrapper packages:
- In PR descriptions, include links or references to the exact source lines that the change mirrors.
- Call out any intentional deviations in behavior and justify them.
- Prefer moving code and preserving semantics over rewrites to minimize drift.