Skip to content

Latest commit

 

History

History
344 lines (246 loc) · 12.9 KB

File metadata and controls

344 lines (246 loc) · 12.9 KB

@gusto/embedded-react-sdk - Internal README

To install dependencies:

npm install

To run:

npm run dev

Local development

For components

If you just need to develop a component outside of its context within a workflow, you can use Storybook:

npm run storybook

For workflows

If you need to test your component within the larger context of a workflow, you'll need to setup Zenpayroll + GWS-Flows.

  • Follow setup instructions for Zenpayroll and start local dev server
    • In another tab run the following commands in Zenpayroll directory(these generate necessary partner account for gws-flows) :
    bundle exec rails runner "DevAccountCreator.new.create_dev_accounts"
    rake partners_api:dev_setup_for_gws_onboarding
    
  • Follow setup instructions in the readme for GWS Flows
  • in SDK, Run npm run dev:setup; if that fails, follow the instructions below to setup manually:
    • Run npm link ../gws-flows/node_modules/react in sdk folder - this is needed to avoid duplicate react instances in local development due to using npm/yarn link
    • In GWS-Flows project folder, run yarn link -r ../embedded-react-sdk
  • In SDK, run npm run dev

Now your local changes appear in GWS Flows.

To see the SDK running in GWS Flows, visit it locally and choose React SDK (New company) or React SDK (Company Onboarded) under Select a Type and click Create Demo

Components and Flows will be shown at the top of the page in a nav. Company Components will automatically appear as they are added. Select a Flow or Component to view it

How to leverage Translations

Translations are stored in /src/i18n.

The are broken out by locale (e.g. en), then by namespace, which is usually the name of the component they are used in. The source of truth is the en locale - the rest will be autogenerated by translation service.

After writing the new translations, run npm run interface to generate the types.

In your components, first use useI18n hook, which takes in component namespace and loads appropriate resource file into dictionary. After that use the useTranslation hook to access the translations in that component and any child components.

Translation Key Guidelines

Follow these conventions when creating or modifying translation keys:

Naming Convention

Use camelCase for all translation key names:

{
  "pageTitle": "Federal Tax Information",
  "federalEinLabel": "Federal EIN",
  "federalEinDescription": "Your company's Federal Employer Identification Number",
  "continueCta": "Continue"
}

Standard Suffixes

Use consistent suffixes to indicate the purpose of each key:

  • Cta - Call-to-action buttons (e.g., submitCta, continueCta, cancelCta)
  • Label - Form field labels (e.g., firstNameLabel, emailLabel)
  • Description - Help text or descriptions (e.g., federalEinDescription)
  • Title - Page or section headings (e.g., pageTitle, sectionTitle)
  • Placeholder - Input placeholders (e.g., emailPlaceholder)
  • Error - Error messages (e.g., requiredFieldError, invalidEmailError)

Grouping Related Keys

Group related keys using nested objects for better organization:

{
  "validations": {
    "firstName": "First name is required",
    "lastName": "Last name is required",
    "email": "Email address is required and must be valid",
    "address": {
      "street1": "Street address is required",
      "city": "City is required",
      "state": "State is required"
    }
  },
  "labels": {
    "openMenu": "Open menu",
    "menuLabel": "Menu"
  },
  "alerts": {
    "progressSaved": "Your progress has been saved",
    "errorEncountered": "There was a problem with your submission"
  }
}

Common grouping patterns:

  • validations - Form validation error messages
  • labels - Accessibility labels and field labels
  • alerts - Alert and notification messages
  • icons - Icon accessibility labels
  • table - Table-related labels (column headers, actions)

When to Use snake_case

Only use snake_case when required by external contracts:

  1. API Enum Values - When keys match backend API responses:
{
  "onboardingStatus": {
    "admin_onboarding_incomplete": "Admin-onboarding Incomplete",
    "self_onboarding_invited": "Self-onboarding: Invited"
  }
}
  1. i18next Pluralization - Required by i18next for plural forms:
{
  "priority_one": "{{count}}st",
  "priority_two": "{{count}}nd",
  "priority_few": "{{count}}rd",
  "priority_other": "{{count}}th"
}
  1. Programmatic Identifiers - When used as flow/step identifiers that match API:
{
  "stepTitles": {
    "add_addresses": "Add company addresses",
    "federal_tax_setup": "Company federal tax information"
  }
}

Examples

Good:

{
  "pageTitle": "Employee Profile",
  "continueCta": "Continue",
  "cancelCta": "Cancel",
  "validations": {
    "firstName": "First name is required",
    "emailFormat": "Email must be valid format"
  },
  "labels": {
    "personalInfo": "Personal Information"
  }
}

Avoid:

{
  "page_title": "Employee Profile", // ❌ Don't use snake_case
  "ContinueCTA": "Continue", // ❌ Don't capitalize suffix
  "validation_first_name": "First name...", // ❌ Don't use snake_case
  "first_name_validation": "First name...", // ❌ Poor grouping
  "lbl_personal_info": "Personal Info" // ❌ Don't abbreviate
}

Creating components

Block components

Block components are focused, reusable components that serve specific functionality. They follow these patterns:

  • Single Purpose: Each component should generally handle a specific task (e.g., list, form, etc.)
  • Base Component: Uses BaseComponent for consistent behavior and error handling
  • Compound Pattern: Exposes subcomponents (Head, List, Actions) for flexibility and composition

Examples

Flow components

Flow components compose block components and other flow components together using state machines to manage transitions. They follow these patterns:

  • State Management: Uses the Flow component with a state machine to handle transitions
  • Component Composition: Can compose both block components (e.g., list → form) and other flow components
  • Naming: Suffix with "Flow" (e.g., DocumentSigner, Locations)

For example, EmployeeOnboardingFlow composes both block components (profile, taxes) and other flow components (document signer) to create a complete onboarding experience.

Examples

Testing locally

You can run the test suite locally with the following command:

npm run test

Pull requests

When creating a pull request, use the provided PR template (.github/PULL_REQUEST_TEMPLATE.md). Here are the guidelines:

PR title format

Use conventional commits format for your PR title. PR titles are validated by CI. When a release is prepared with npm run release, commit history (which uses squash-merge PR titles) determines the version bump and populates the changelog — so PR title format directly affects how releases are documented.

Note: Commitlint runs in two places: as a pre-commit hook (via husky) validating individual commit messages, and as a CI check validating the PR title, which becomes the squash merge commit message.

Semantic versioning (semver) mapping

PR titles determine the version bump and changelog entry when a release is prepared, following semver.org specification:

During 0.x.x (pre-1.0 development):

Commit Type Version Bump When to Use
feat MINOR (0.1.0 → 0.2.0) New features or functionality
fix PATCH (0.1.0 → 0.1.1) Bug fixes
feat! or fix! (with !) MINOR (0.1.0 → 0.2.0) Breaking changes*
docs, chore, refactor, test, ci, style, perf, build, revert No bump Non-functional changes

*Per semver spec, during 0.x.x the API is considered unstable, so breaking changes bump MINOR instead of MAJOR. The jump to 1.0.0 will be an intentional decision when we're ready to declare a stable API.

Title examples

  • feat: add new component - New feature → MINOR bump
  • fix: resolve issue with form validation - Bug fix → PATCH bump
  • feat!: redesign JSX component props - Breaking change → MINOR bump (during 0.x.x)
  • chore: update dependencies - Maintenance → no version bump
  • refactor: simplify state machine logic - Code refactoring → no version bump
  • docs: update README - Documentation → no version bump

For work tied to a Jira ticket, include the ticket in the scope so the title still follows conventional commits:

  • feat(SDK-123): add payroll blocker alerts - MINOR bump
  • fix(SDK-456): correct date formatting - PATCH bump

PR description sections

  • Summary: Brief description of what the PR does and why
  • Changes: Briefly list only the most important changes (high-level only; do not enumerate every file-level change)
  • Demo: Screenshots or screen recordings showing the changes (when applicable)
  • Related: Links to Jira tickets, Figma designs, or related PRs
  • Testing: Instructions for reviewers to test the changes

Best practices

  • Keep PRs focused on a single concern when possible
  • Include visual demos (screenshots/videos) for UI changes
  • Link to relevant Jira tickets using the format [SDK-XXX](https://gustohq.atlassian.net/browse/SDK-XXX)
  • Add Storybook stories for new components
  • Include unit tests for new functionality
  • Run npm run test and npm run lint before submitting

Commits

All commits must follow commitlint enforced format: type(scope?): subject #scope is optional; multiple scopes are supported (current delimiter options: "/", "\" and ",") Following types are currently defined:

'build',
'chore',
'ci',
'docs',
'feat',
'fix',
'perf',
'refactor',
'revert',
'style',
'test'

Read more about conventional commits here

Documentation

You can find documentation on building with the Gusto Embedded React SDK in the docs directory within this repo.

Cutting a new release

Releases are prepared with release-it, which reads commits since the last release, proposes a semver bump, updates package.json and CHANGELOG.md, and creates a release branch ready for review.

Option 1: locally

From a clean main branch:

npm run release

release-it will:

  1. Show you the commits since the last release grouped by type
  2. Propose a version based on conventional commit prefixes (feat → MINOR, fix → PATCH, feat!/fix! → MINOR during 0.x.x)
  3. Ask you to confirm or override the target version
  4. Bump package.json, update package-lock.json, and prepend a new section to CHANGELOG.md
  5. Commit the changes and check out a chore/release-<version> branch automatically

Then push and open a PR:

git push -u origin chore/release-<version>
gh pr create --title "chore: release <version>"

Option 2: via GitHub Actions

Trigger the Prepare Release workflow from the Actions UI (Run workflow). It accepts an optional version override; if left blank it auto-detects from commits. The workflow creates the branch, commits the changes, and opens a PR automatically.

After the PR is merged

Publishing happens automatically. When a chore: release commit lands on main and all CI checks pass, the Publish to NPM workflow runs automatically and publishes to NPM.

If you need to trigger a publish manually (e.g. CI was re-run after the auto-trigger fired, or something went wrong), you can still run the workflow manually from the Actions UI (Run workflow).