Table of Contents
# Build the library
$ yarn run library:build
# Build and watch library files for changes
$ yarn run library:build:watch
# Run TypeScript and SCSS linters
$ yarn run library:lint
# Run all TypeScript tests
$ yarn run library:test
# Run all TypeScript tests and output coverage
$ yarn run library:test:ci:local
# Start the demo project and watch demo and lib files for changes
# (don't forget to build the library first!)
$ yarn run demo:start
# Test the demo app
$ yarn run demo:test
# Lint the demo app
$ yarn run demo:lint
# Create a commit with a helpful CLI tool
$ yarn run cm
# Add yourself as a contributor
# See contribution types here: http://bnj.bz/3C1S0A0d1c3U
$ yarn run contributors:add [your-github-handle] [contribution-type]
$ yarn run contributors:generate
Check package.json for all available commands
yarn install && yarn run library:build && yarn run demo:start
- Navigate to
http://localhost:4200/components/
- Select a component from the menu (top right)
- Create a directory using the component name:
projects/library/button/
- Necessary files:
button.module.ts
- Class name:
TsButtonModule
- Class name:
button.component.ts
- Class name:
TsButtonComponent
- Class name:
button.component.scss
button.component.html
- Necessary files:
- Import and export
button.component.ts
insidebutton.module.ts
- Add
TsButtonComponent
to the exports and declarations ofbutton.module.ts
. - Comment all methods, constants &
@Input
s using the supported JSDoc style. - Add a usage example in the component documentation with every possible input and output included.
- Add the new component to available scopes in
tooling/cz-config.js
- Update the status for the component in the components table
Note: Our base branch, release
, is always deployable.
- Branches should have a brief but comprehensive name.
- The name should clearly encompass the work that the branch will contain. A name like
user-view
may make sense now, but some time later it will be difficult to know what work was done on the branch. A better name could be27-create-user-detail-view
. Be as specific as possible, while keeping the length as short as possible.
- The name should clearly encompass the work that the branch will contain. A name like
- Branches should have the associated GitHub issue number in the name.
- This is a helpful reference to the issue details. More importantly, it forces everyone to create the issue before the work begins. If it is worth doing, than it deserves an issue. This creates better historical data and more importantly, gives everyone visibility into the work being done.
- The number should be at the beginning of the branch name:
45-update-payment-gateways
. This enables quick auto-completion in most terminals.
- Checkout
release
- Pull
release
- Create a feature branch from
release
(see branch naming)
- Commit all work on the feature branch
- Create a pull request at any time (just don't request a review until you are finished)
- Keep the remote up to date with your latest changes. Committing often locally is good, but those commits should be pushed to the remote (i.e. GitHub) at least once a day so that the code is available to all engineers.
- If there are conflicts, merge
release
into the feature branch- Only do this if there are conflicts
- See Pull Requests for more information
- Verify all linters run successfully
- Verify all tests are passing and code coverage did not decrease (bonus points if it increases)
- If you haven't yet, create a pull request from the feature branch into
release
- Add as much information into the pull request body as possible
- Request a review
Hotfixes follow the same strategy as features.
When code gets merged to release
, many of our projects are automatically versioned and released. In
order to give our tooling the information it needs, we write our commit messages in a specific
format. This has the added benefit of improving the readability of our commit history.
# The format:
type(scope): message
# Examples:
fix(Button): Aria label is now correctly read by JAWS
feat(Tooltip): Add a tooltip component
chore(): Bump lodash version
For a friendlier prompt, you can run yarn run cm
in the repo. This will ask several questions
and then construct the commit in the proper format for you. The prompt will allow you to choose
from a list of types and then a list of scopes. This is much easier than trying to remember all
possible scopes.
Optional: Installing committizen (
yarn add -g committizen
) will allow you to rungit cz
or even alias to your preferred git command.
You must use one of the defined types since the types have specific meaning to the automatic versioning tool.
- A type
fix
will increase the patch version (x.x.1
) - A type
feat
will increase the minor version (x.1.x
)
To see all valid types for a project, look for the file cz-config.js
.
When a commit contains a breaking change, it must be included in the commit message body (not the
title). Both words should be uppercase and the comment body should include everything that other
engineers may need to know: BREAKING CHANGE: A description of the breaking change
. This could
include comments, images, code examples, and more. Generally speaking, more information is
better. (Note: when using the cli prompt, it will ask you about any breaking changes and add the
prefix BREAKING CHANGES:
automatically)
Learn more about the automatic versioning tools we use:
All projects should pass all available linters before committing.
TypeScript & JavaScript projects use ESLint and SASS/SCSS projects use Stylelint.
To edit configuration, look for the files .eslintrc
, or stylelint.config.js
. These files should not be edited
without a discussion with the team.
Look at the scripts section in the project's package.json
for the command to run tests.
All projects should work towards full test coverage. This is often not practical in a quickly changing software startup, but a project should never dip below 80% coverage.
We use Jest for unit tests.
Look at the scripts section in the project's package.json
for the command to run tests.
When it is time merge a branch into release
, create a pull request from the feature into release
.
- At the top of the pull request, link to the original issue.
- If the ZenHub extension is installed in your browser, you can attach an issue to the PR via the built in controls
- If the pull request includes more than one item, include a high level list of what was done.
- If the pull request covers UI changes, include a GIF or image to clearly show the change (bonus points for before and after images).
- Request a review from someone on the UI team (any code owners will be notified by default).
- A pull request may be opened before the work is complete. This makes it easier to get feedback
while the work is in progress. Include
WIP:
at the beginning of the pull request title, add theDO NOT MERGE
label so that it is not accidentally merged andcc/ @mention
anyone that should take a look. - There are two options to check for merge conflicts between your branch and release:
- Create a pull request against release. (Note: This will cause any associated CI service to begin building the feature branch on every push)
- Use GitHub's compare view:
https://github.com/GetTerminus/terminus-ui/compare/your-branch-name...release
- The pull request body, just like the issue body, is the single source of truth. Any discussions, decisions or relevant information should be added to the pull request body immediately.
Releases are handled automatically when code is merged to release
. Never merge code to release
that is
not production ready!
- Semantic Release looks at all commits since the last tag on
release
. - Based on those commits it will bump the version number appropriately.
- A changelog is generated in the release notes on Github.
- The new version is published to NPM under the
next
tag. - When the new functionality is verified, it is tagged as
latest
:npm dist-tag add @terminus/ui@<version to promote> latest
NOTE: currently
yarn tag
outputs an error even though the tagging seems to work. Because of this, we will continue using NPM for tagging.
You can view the currently published files using the unpkg CDN.
By default all releases are tagged as @next
on NPM. This allows us to test the release with consumer partners before
promoting for all consumers. To promote the most recent @next
release to @latest
, run the release script prefixed
with a valid GitHub token:
# NOTE: This is meant to be run from the repo root - not on CI.
# Pass your GitHub token as `GH_TOKEN` with the call:
$ GH_TOKEN=[YOUR_TOKEN] tooling/ci/promote-next-and-deploy.sh
NOTE: A personal GitHub token can be generated by visiting https://github.com/settings/tokens.
See the full script for more context.
When writing code, an engineer should be mindful that others will need to understand and edit this code in the future. For this reason we don't assume that the future editor has the context.
Modern tooling will handle cleaning up comments and white space so we write for humans - not machines.
-
Always include a JSDoc-style comment above all methods/functions:
/** * Get a customer by ID * * @param customerId - The customer's ID * @returns The customer object */
-
For general notes, always include the prefix if one applies:
// NOTE: A general informational note. Not for every comment; just when there is an important piece of unusual information. // TODO: A note about missing logic. // HACK: A note about a 'hacky' solution. // FIXME: A note about something that needs refactoring. // BUG: A note about a bug. Include the associated issue number in the note.
-
All engineers should do their best to never need most of these note types. However, if needed, always include the reason the note is needed. Remember, more information is better!
We don't use the type tag since the types are documented ina the TypeScript code.
The @deprecated
flag can be used to mark deprecated code. It should be accompanied by a migration note if possible.
/**
* @deprecated This method has been merged into `myNewMethod()`
*/
The @ignore
flag should be used on all code that is not part of the library (such as test host components etc).
/**
* @ignore
*/
export class MyTestHost {}
The @internal
flag should be used on code that is not ignored (as it is part of the library) but still should not
appear in the generated documentation.
/**
* @internal
*/
export function myPrivateHelperFunction() {}
The @link
flag can be used to link to other components or web locations.
/**
* Component B.
*
* Used by {@link ComponentA}
* Learn more at [Google]{@link http://www.google.com}
*/
export class ComponentB() {}
Usage docs should be added to all components and directives. These docs are created by adding a markdown file named to match the component or directive:
// component:
foo.component.ts
// usage docs:
foo.component.md
When the documentation published, this usage doc will be automatically consumed by the API docs generation.
When editing or creating usage documentation, add headlines and appropriate content, then run yarn run docs:toc
to update (or generate)
the table of contents.
Any code merged to the release
branch gets published under the next
tag:
$ yarn add @terminus/ui@next
Once the code is ready to be promoted to latest
a member of our NPM organization can promote it:
# Replace `0.0.0` with the version to promote
$ npm dist-tag add @terminus/ui@0.0.0 latest
Most code style is enforced through ESLint, TSLint and Stylelint, so make sure to run all of the linters.
When writing a class, this is the basic order of items:
- Private properties
- Public properties
- Getters (setter goes before getter, which is before the associated private property)
- View references (
@ViewChild
etc) @Input
s@Output
s- Constructor
- Lifecycle methods (in the order they are called by Angular) (
ngOnInit
etc) - Public methods
- Protected or static methods
- Private methods
Prefixing private methods and properties is a common pattern. We have opted to rely on TypeScript's member access rather than the underscore method.
Caveat: The one place we do use the underscore is to prefix the private property that is used with a getter and setter:
public set active(v: boolean) {
this._foo = v;
}
public get active(): boolean {
return this._foo;
}
private _foo = false;
Our setters always come before the getter; which is in turn before the private property. (See the above code example)
Decorators go on their own line above the item they are decorating:
@Input()
public foo;
- Always create an issue for things you work on. If it is worth spending time on, it is worth creating an issue for it since that enables other people to learn and help. You can always edit the description or close it when the problem is something different or disappears.
- Always include a link to the original issue when submitting a pull request.
- If two issues are related, cross link them (a link from each issue to the other one). Put the link at the top of each issue's description with a short mention of the relationship (Report, etc.). If there are more than 2 issues, use one issue as the central one and cross link all issues to this one.
- After a discussion about a feature update the issue body with the consensus or final conclusions. This makes it much easier to see the state of an issue for everyone involved in the implementation and prevents confusion and discussion later on.
- Submit the smallest item of work that makes sense. When creating an issue describe the smallest fix possible, put suggestions for enhancements in separate issues and link them.
- Do not leave issues open for a long time, issues should be actionable and realistic. If you are assigned to an issue but don't have time to work on it, remove your assignment so that the next available engineer can pick it up.
- Make a conscious effort to prioritize your work. The priority of items depends on multiple factors: Is there a team member waiting for the answer? What is the impact if you delay it? How many people does it affect, etc.?
- If the project is using milestones, pick issues from the current milestone.
- Assign an issue to yourself as soon as you start to work on it, but not before that time. If you complete part of an issue and need someone else to take the next step, re-assign the issue to that person.
- When re-assigning an issue, make sure that the issue body contains the latest information. The issue body should be the single source of truth.
- When working on an issue, ask for feedback from your peers. For example, if you're a designer and you propose a design, ping a fellow designer to review your work. If they approve, you can move it to the next step. If they suggest changes, you get the opportunity to improve your design. This promotes collaboration and advances everyone's skills.
- Even when something is not done, share it internally so people can comment early and prevent
rework. Mark the pull request
Work In Progress
orWIP
so it is not merged by accident. - When you create a pull request, mention the issue(s) that it solves in the description. After the merge, if any followup actions are required on the issue like reporting back to any customers or writing documentation, avoid auto closing it by including text such as 'Fixes #1' or 'Closes #1'.
- When the pull request is complete, remove the
WIP
prefix and assign the pull request to someone to review and merge it. You can still make changes based on feedback, but by removing the WIP prefix it clarifies that the main body of work has been completed. - If a pull request is assigned to you and there is a merge conflict, consider trying to resolve it yourself instead of asking the pull request creator to resolve the conflict. If it is easy to resolve, you avoid a round trip between you and the creator, and the pull request gets merged sooner. This is a suggestion, not an obligation.
- If you ask a question to a specific person, always start the comment by mentioning them; this will ensure they see it and other people will understand they don't have to respond.
- Do not close an issue until it is fully complete, which means code has been merged, tested, all issue trackers are updated, and any documentation is written and merged.
- When closing an issue, leave a comment explaining why you are closing the issue.
- If you notice that the tests for the
release
branch of any project are failing (red) or broken (green as a false positive), fixing this takes priority over everything else development related, since everything we do while test are broken may break functionality, or introduce new bugs and security issues. If the problem cannot be fixed by you within a few hours, because if it is too much work for one person and/or you have other priorities, create an issue, post about it in development Slack channel.
π Happy Coding! π