Skip to main content

Abstract art page banner

Design System

This repository contains Salesloft's Design System, including core React components for frontend development called the UI Kit. The repository is a monorepo (see Organization) containing distinct packages for each module as well as tests, documentation, build processes, and release management.

Getting Started

All packages in this repository are namespaced under the "@rhythm" scope and published to Salesloft's private registry. You must have read access to the registry to install them. Install any package using npm:

$ npm install @rhythm/name-of-component @rhythm/name-of-service

You can then use the package in your project:

import { Component1, Component2 } from '@rhythm/name-of-component'
import { invokeService } from '@rhythm/name-of-service'

const data = invokeService()

const MyComponent = () => (
<Component1>
<Component2 data={data} />
</Component1>
)

Organization

This repository is a "monorepo" meaning it contains multiple packages that are separately published to NPM (technically, Salesloft's private registry). Rush is used to manage the repository. Each package is responsible for its own build process but all tests and documentation are managed at the top-level.

Developing

Preparing your machine to develop for this repository is fairly straightforward.

Initial Setup

Prepare your machine by satisfying the following prerequisites:

  1. Set up your npm authentication token
  2. Install rush globally using the command npm install --global @microsoft/rush

Then, run the following commands in a terminal:

$ rush update
$ rush rebuild

Let's look at what those commands are doing. First, we instructed Rush to install dependencies for each of our packages and link any packages that depend on another in the repository. Second, we ran the build process for each package. It's also recommended to run these two commands after pulling from master.

We still need a way to view our components and iterate on them locally. For that, we use Storybook. In another terminal, run the following:

$ pnpm install --prefix .config/storybook
$ rush storybook

NOTE: The rush storybook command supports loading specific packages as well. Run rush storybook --help for more information.

With these two processes running, you have everything needed to start developing.

Daily Development

There are two categories of commands you'll typically run when developing on a daily basis: global commands and package commands.

Global Commands

Global commands can be run in any directory in the repo and will apply to the repository as a whole. The most commonly used global commands are below:

  • rush update: install dependencies for each package and link inter-repository dependencies
  • rush rebuild: run npm run build in each package
  • rush build: run npm run build in packages that have changed since the previous build
  • rush change: generate changelog entries
  • rush check: check to see if any packages contain mismatched dependency versions

Package Commands

Package commands should be run in a package's directory and apply only to that package. The most commonly used package commands are below:

  • rush add: add a dependency to a package; if already installed in another package, matches the version
  • rushx SCRIPT: analogous to running npm run SCRIPT

Storybook

Storybook is a fantastic tool both for developing and documenting components. When developing, Storybook allows us to create components in isolation which helps re-enforce a component mindset as well as remove the need for a full application environment to build components. Storybook also allows us to document components in various states to demonstrate the full capabilities of the components we create. Additionally, we utilize many Storybook plugins to extend its functionality.

Linting

This repository uses both Prettier and ESLint to maintain its code standards. Both tools will check code before pushing to a remote using a git pre-push hook. During development, you can do one of two things: 1) enable Prettier and ESLint in your IDE of choice (the IDE should pull in the repository configuration) or 2) run Prettier and ESLint via the command line as you go. If you decide to do the latter, run the following commands in a terminal:

$ npm run prettier:format
$ npm run eslint:fix

These will not only check but also automatically fix any errors found in the repository. Note that ESLint can't always automatically resolve issues so some manual work may be required to fix the issues it raises.

Adding a Package

We use a lightweight templating tool called plop to rapidly scaffold out new packages. To create a new package, run the following command and follow the prompts:

$ npm run plop

Prereleases

This repository supports publishing prerelease versions of packages for testing. This should only be used when doing final integration tests with a product.

  • 🚨 Do not use prerelease branches for normal development 🚨
  • 🚨 Do not create a PR for a prerelease branch 🚨

To publish a prerelease, perform the following steps:

  1. Create a special prerelease branch with the name following the pattern prerelease/<slug> where <slug> is a short descriptor of the feature. (e.g. prerelease/new-button)
  2. Push your branch and verify it built successfully in GitHub.
  3. Find your pre-release package name in the #rhythm-builds channel.
    • All prerelease packages are versioned with a suffix of -<slug>.<commithash> (e.g. 1.1.0-new-button.81e3b44.)
    • The <commithash> suffix uses the first 7 characters of the commit SHA.
    • Only changed packages will be published. Subsequent pushes to a release branch will publish new prerelease versions.
  4. Install your prerelease package in the target app by updating the package.json with the prerelease package name and running npm install. Make sure and commit both the updated package and package-lock files. (e.g. change the version of @rhythm/inputs to your release package name 1.1.0-new-button.81e3b44 and npm install to update your package-lock.json)
  5. Now QA can test your changes made to the package in the target application's branch.
  6. Once this passes QA, you can merge your changes here, which bumps the version of the changed package and publishes the new version number in the #rhythm-builds channel. (e.g. merging a change in buttons might bump the inputs package from "@rhythm/inputs": "^2.16.1" to "@rhythm/inputs": "^2.16.2")
  7. Implement this change in your target app by updating to your new version of the changed package in the package.json and getting a new dev approval. (e.g. update the @rhythm/inputs package in Rhapsody's package.json to the new version "^2.16.2")
  8. Then you can merge the PR into your target application.

NOTE: Don't forget to join the #rhythm-builds channel in Slack to see published prerelease versions and version bumps.

Adding a Changelog Entry

Each pull request requires a changelog entry to be included. Rush provides a CLI utility for generating changelog entries and will assist you in generating them for each package that has changed on your branch. Rush will then use the changelog entries to accomplish two tasks: update changelogs for each package during the release process and track which type of version bump (major, minor, patch) to use on each package being released. Changelog entries should be generated after all changes have been committed.

Format

When writing a changelog entry, adhere to the following best practices adapted from Rush's Recommended Practices:

  • Use the present simple tense using the imperative ("command") mood.
  • Write from the perspective of an external audience who may be unfamiliar with implementation details of your package
  • Focus on scenario outcomes ("Add wildcard support to search") instead of code changes ("Add regular expression support to searchHelper")
  • Start with a verb. These verbs are recommended:
    • Add: when you introduce or expose a new feature, export, component, etc.
    • Remove: when you fully removed something and it can no longer be used.
    • Deprecate: when you plan on removing something, but it is still accessible.
    • Fix an issue with/where…: when you fixed a bug.
    • Improve: when you made an existing thing better.
    • Update: when you refresh something, but don’t necessarily make it better.
    • Upgrade: when upgrading the version of a dependency.
    • Initial/Beta release of…: when releasing a brand-new package.
  • Don’t use the word bug. Use issue instead.
  • Don’t add a trailing period unless you have two or more sentences.

Here are examples of good changelog entries:

  • Add FlatButton component
  • Improve usage documentation for setupHeadlessComponent
  • Deprecate the iconLeft button prop. Use iconPosition instead.
  • Upgrade from styled-components 3 to styled-components 4
  • Initial release of the theme package

CLI Command

To generate changelog entries, run the following command and follow the prompts:

$ rush change

The prompts are more helpful if local master branch is up to date. Rush compares current branch with master to detect changed packages.

Manually Adding Changelog Entries

Warning: this is a hack and should only be used if you absolutely know what you're doing. There may be times when you need to manually trigger a package release, e.g. changing a build configuration. You can do this by creating a changelog entry by hand with the appropriate comment and release type (major, minor, patch).

To manually create a changelog entry, perform the following steps:

  1. Create a file with the path common/changes/@rhythm/<PACKAGENAME>/<BRANCHNAME>_<YEAR>-<MONTH>-<DAY>-<HOUR>-<MINUTE>.json e.g. common/changes/@rhythm/chips/rg__remove-build-watch-command_2019-05-16-02-21.json

  2. Include the following contents in the file:

    {
    "changes": [
    {
    "comment": "<COMMENT>",
    "packageName": "@rhythm/<PACKAGENAME>",
    "type": "<RELEASETYPE>"
    }
    ],
    "packageName": "@rhythm/<PACKAGENAME>",
    "email": "<YOUREMAIL>"
    }

Publishing

Publishing is handled by our CI/CD pipeline. Publishing should never happen on a local machine. Rush will automatically identify which packages need to be published and which version each should be bumped to. It knows which packages need publishing based on the files that have been changed since the last release. It know which version to bump each package to based on the changelog entries.

Testing the Publishing Process

Advanced: Because we publish to a private registry and our publishing process depends on pushing back to the repository's remote, testing the publishing process requires some setup. The work is divided into two steps. First, we need to set up a local registry and point npm to it. Second, we need to point the repository's remote to a special local repository.

Let's start by spinning up a local registry using the verdaccio tool. This is the tool we use to host our private registry as well. In a new terminal window, run the following command:

$ npx verdaccio

To finish the registry setup, we need to ensure any commands using npm point to our local registry. Run the following commands to switch to the test .npmrc file:

$ mv .npmrc .npmrc.bak
$ cp ./.config/.npmrc.test .npmrc

Next, we need to create a local mirror to serve as a remote repository and point the origin remote to it. Run the following commands to complete this process:

$ git init --bare ~/tmp-repo.git
$ git remote rename origin origin-backup
$ git remote add origin ~/tmp-repo.git

The local environment is now set up. When testing is complete, run the following commands to reset the NPM and git configurations:

$ mv .npmrc.bak .npmrc
$ git remote remove origin
$ git remote rename origin-backup origin
$ rm -rf ~/tmp-repo.git

Codemods

With each breaking change for a package, our goal is to provide a codemod to help you with the migration process. In order to do this, we utilize a library called jscodeshift.

$ npx jscodeshift [OPTION]... -t TRANSFORM_PATH OLD_CODE_PATH...

Example: Buttons initial migration codemod running on Rhapsody (Running from the src directory on your local machine)

$ npx jscodeshift -t design-system/packages/codemods/initialMigration.js rhapsody/src/ --ignore-pattern=_angular

FAQ

Q) Where can I find components that already exist for the UI Kit? A) For an up to date list of all UI Kit components, please check our public Storybook.

Q) What is the process for building new UI Kit components? A) More information can be found about this process in Confluence or by joining the #rhythm-components Slack channel. In short, design leadership typically proposes these by navigating to the Issues tab in Github, clicking New Issue, then creating a new New UI Kit Component Proposal and filling out all of the relevant sections.

Q) How do I file a bug for the UI Kit? A) In the Issues tab in Github, click New Issue, then create a new Bug Report and fill out all of the relevant sections.

Glossary

  • monorepo: a repository containing multiple packages that are independently published to an NPM registry
  • top-level: any command, code, or configuration that is executed or managed by the entire repository
  • package-level: any command, code, or configuration that is executed or managed by each package individually
  • Rush: a tool for managing monorepos
  • Storybook: a tool for developing and documenting components in isolation
  • Babel: a JavaScript transpiler

Additional Resources