@redmountaintec/ysui v0.1.5
Table of Contents
- General Info
- Technologies
- Setup
- Installing Additional Dependencies
- Folder Structure
- Available Scripts
- Publishing Your Changes
- Versioning
- Typescript & Constant Declarations
- Testing
- Implementation
General Info
The purpose of this project is to build/provide custom components that can be implemented throughout all of our applications.
Technologies
- React
- Typescript
- MUI
- npm
- Emotion
- date-fns
Setup
Currently, development is happening with npm
version 16 or higher.
If you don't have npm installed, then install it locally: nodejs or via asdf website
- If you're using asdf to control your node version for other projects, you can set your shell to version 16 via:
then set your nodejs version per shell via:asdf install nodejs 16.13.0
asdf shell nodejs 16.13.0
Because our component library is private, you'll need to add the .env
variables in order to publish successfully. These variables are only valid for 30 days so chances are if these don't work, we'll need to generate a new NPM_TOKEN
.
Create a .env file with the
GH_TOKEN
andNPM_TOKEN
variables. These values can be found hereInstall dependencies
npm install
Start up Storybook
npm run storybook
Installing Dependencies
The generated project includes React, ReactDOM, Typescript, @mui/material, @emotion/react, @emotion/styled and date-fns as devDependencies. You may install other dependencies (for example, React Router) with npm:
npm install --save-dev react-router
Whenever a devDependency is installed, be sure to add it as a peer dependency as well. Our intent is to keep this component library lightweight in order to improve performance and to avoid duplication of node_modules.
Available Scripts
In the project directory, you can run:
npm run storybook
Runs the app in the development mode. Open http://localhost:6006 to view it in the browser.
The page will reload if you make edits. You will also see any lint errors in the console.
npm test
Launches the test runner in the interactive watch mode. See the section about running tests for more information.
npm run build
Builds the app for production to the build
folder.
It correctly bundles React in production mode and optimizes the build for the best performance.
Folder structure
When building out your component, you should include a main folder (named appropriately) with all the related files nested inside.
- Required
- Optional
Below is an example of how your file structure should appear when creating your component or customHook:
my-app/
README.md
node_modules/
dist/
src/
components/
Button/
Button.ias.ts
Button.stories.tsx
Button.test.tsx
Button.testIds.ts
index.tsx
customHooks/
useDelay/
useDelay.test.tsx
index.tsx
index.ts
Versioning
By default, the project is set to autoincrement the patch version upon every push/merge to the main branch. When there is a breaking change for a previous component, the minor version should be manually incremented by one in the package.json.
[major].[minor].[patch]
An example of a breaking change would include the following:
- Change in default behavior of a prop eg.(disabled?: boolean => disabled: boolean)
- Addition/removal of a required prop to a previously built component
- Required peerDependency for the project
When creating your pull request, you would need to increment the minor version of the pacakge.json by one
0.1.29
would need to be changed to 0.2.0
prior to creating your pull request.
Major versions should never be changed.
Publishing your changes
In order for your changes to be published and available via npm, your feature branch must be merged into main. For the project to publish your changes, your exports for the components and any interface/typings you want included must be exported from the main index.ts file in the src directory:
Create a feature branch off develop
Make and commit the changes to your feature branch
Create your PR to the develop branch
Once your PR is merged, merge the develop branch into main
Typescript & Constant Declarations
Typescript Identifiers - I like to follow the typescript standards listed below. You can review these in more detail if you’d like here
The one caveat is, I like prepending an I character in front of the Interface identifiers
- UpperCamelCase
- Interface - interface variables should be UpperCamelCase with an “I” character prepended eg. IButtonProps
- class
- type
- enum
- decorator
- type parameters
- lowerCamelCase
- variable
- parameter
- function
- method
- property
- module alias
- CONSTANT_CASE
- Global constant values
- enum values
- Redux Actions
- UpperCamelCase
Testing
Writing Tests
To create tests, add it()
(or test()
) blocks with the name of the test and its code. The describe()
command allows you to group related tests, producing cleaner outputs.
Jest provides a built-in expect()
global function for making assertions. A basic test could look like this:
import { fireEvent, render, screen } from '@testing-library/react';
import { KlsButton } from '@youscience/khaleesi'
describe('KlsButton', () => {
it('is disabled', () => {
render(
<KlsButton
color='primary'
disabled
label='Hello world!'
onClick={() => {}}
testId="KLSBUTTON"
variant='contained'
/>
);
const button = screen.getByTestId(
"KLSBUTTON"
) as HTMLButtonElement;
expect(button).toBeDisabled();
});
})
All expect()
matchers supported by Jest are extensively documented here.
You can also use jest.fn()
and expect(fn).toBeCalled()
to create “spies” or mock functions.
Testing Components
There is a broad spectrum of component testing techniques. They range from a “smoke test” verifying that a component renders without throwing, to shallow rendering and testing some of the output, to full rendering and testing component lifecycle and state changes.
Different projects choose different testing tradeoffs based on how often components change, and how much logic they contain. If you haven’t decided on a testing strategy yet, we recommend that you start with creating simple smoke tests for your components:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
});
This test mounts a component and makes sure that it didn’t throw during rendering. Tests like this provide a lot of value with very little effort so they are great as a starting point, and this is the test you will find in src/App.test.js
.
When you encounter bugs caused by changing components, you will gain a deeper insight into which parts of them are worth testing in your application. This might be a good time to introduce more specific tests asserting specific expected output or behavior.
Best practices for our component unit tests, include testing component functionality and the cause/effect of actions performed on the component itself. Don't test the component with static props/variables like in the KlsButton example above, it will always be disabled. A more appropriate example would be to test what happens if the disabled button is clicked:
import { fireEvent, render, screen } from '@testing-library/react';
import { KlsButton } from '@youscience/khaleesi'
import { Input } from '@mui/material'
describe('KlsButton', () => {
it('should be disabled & not allow button to be clicked', () => {
const onClickMock = jest.fn();
render(
<KlsButton
color='primary'
disabled
label='Hello world!'
onClick={onClickMock}
testId="KLSBUTTON"
variant='contained'
/>
);
const button = screen.getByTestId("KLSBUTTON") as HTMLButtonElement;
// expect the buttton mock to not be clicked
expect(onClickMock).toBeCalledTimes(0);
expect(button).toBeDisabled();
// attempt to click the button
fireEvent.click(button);
// expect the function to not be called even if clicked while disabled
expect(onClickMock).not.toHaveBeenCalled();
});
})
This ensures the custom component you built will not call the function provided to the button if it is disabled.
Implementation
Importing a Component from @youscience/khaleesi
Because our component library is private, you'll need to add a .npmrc
file in root of the project in order to have access
- Sign up or login to your npm account
- Add a custom
Read Only
token to your npm account create npm tokens
- You will get an access error if you're not invited to the @youscience organization. Reach out to Brandon Allred @ brandon.allred@youscience.com for access to the organization.
- Create a .npmrc file in ~/$HOME/user
Add the Read Only token you generated, the registry and scope. It should look similar to this.
// registry.npmjs.org/:_authToken=npm_YOUR_TOKEN_HERE scope=@youscience @youscience:registry=https://registry.npmjs.org/
Install @youscience/khaleesi package
npm install
You should now be able to install the @youscience/khaleesi package & utilize the components
import { useState } from 'react' import { useDebounce } from '@youscience/khaleesi' import { TextField } from '@mui/material' export function Form() { const [name, setName] = useState('') const onChange = useDebounce(setName, 500) <TextField label="Name" onChange={onChange} ... /> }
License
@youscience/khaleesi is MIT licensed