Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
July 28, 2020 03:42 pm GMT

Build your First Typescript Package in Under 10 Minutes

I've been putting off building my own Typescript packages for a while.

Not for a lack of ideas, but because I know modern Javascript/Typescript development is a mess. Just check the size of your node_modules directory after starting a default React project to see what I mean, over 200MB of dependencies just to get started! Or better yet, try to start a React project without create-react-app.

It would take me days to configure my own Typescript package with Babel, Prettier, Rollup, ESLint, Jest, etc. just the way I want it. Not to mention probably cost me my sanity.

Alt Text

Then I stumbled onto TSDX.

After reading the README, I was able to publish an npm package complete with tests in a single evening.

This guide is a simplified version of what I learned publishing my first package. By the end of this tutorial you should have a published and tested Typescript package in the NPM registry.

NPM Registry

First you'll need to create an NPM account and configure it to use in your command line. Start with this short guide to configure your account and login via the command line with npm login if you haven't done so.

What we're Building

Since this tutorial is aimed at beginners, we're going to be building something simple. A reusable React component with Jest tests, types, and Github actions:

Alt Text

Truly awe inspiring, I know.

Live Demo

Final Source Code

Setup

Let's bootstrap our TSDX project from the command line:

npx tsdx create toggle

At the prompt, select React:
Alt Text

After the dependencies are installed, let's make sure we can start the project in development/watch mode:

cd togglenpm start

You should now have a functioning package!

File Structure

> tree -L 2 -I node_modules. LICENSE README.md dist    index.d.ts    index.js    toggle.cjs.development.js    toggle.cjs.development.js.map    toggle.cjs.production.min.js    toggle.cjs.production.min.js.map    toggle.esm.js    toggle.esm.js.map example    index.html    index.tsx    package.json    tsconfig.json package-lock.json package.json src    index.tsx test    blah.test.tsx tsconfig.json

The default project is pretty minimalist. There are a few directories/files that are important to know though.

Directories

  • src: This is where all of the source files that will be built live
  • example: An example playground to test your component/package
  • dist: What will get built and published to npm. You shouldn't really have to touch this directory and it should be excluded from source control.
  • test: Your tests

Files

  • src/index.tsx: Your main source file that will be built. This needs to import all your other source files
  • package.json: Dependencies/all configuration for your package
  • example/package.json: Dependencies for your playground (these will not be published to npm)
  • example/index.tsx: File that loads your package for the playground
  • test/blah.test.tsx: Example test file
  • README.md: Generated README with a lot of useful information for reference.

Toggle Component

To keep with React best practices, we'll make a separate file for our component.

Copy and paste the following code into src/Toggle.tsx:

// Inside src/Toggle.tsximport React, { FC } from 'react';export const Toggle: FC = () => {  return (    <label className="switch">      <input type="checkbox" />      <span className="slider round"></span>    </label>  );};

Nothing crazy here, just a default HTML checkbox. Let's export our component from our index.tsx file which is the main file that will be used in the package.

// src/index.tsxexport * from './Toggle';

TSDX projects come with an example folder to help you visualize your component in a browser. This is what we'll use as a sandbox for our component as well. Since we changed the name of the component, we'll have to update the example import:

// example/index.tsximport 'react-app-polyfill/ie11';import * as React from 'react';import * as ReactDOM from 'react-dom';import { Toggle } from '../src/index'; //  Change our import const App = () => {  return (    <div>      <Toggle />{/*  Change to use your new component*/}    </div>  );};ReactDOM.render(<App />, document.getElementById('root'));

Now let's run this sandbox environment to see what we have:

cd examplenpm inpm start

Navigate to http://localhost:1234. You should see a checkbox!

Alt Text

Styling

Let's add some styles to our sweet checkbox now. Open a new file called Toggle.css inside of the src directory and copy the following styles into it:

/* src/Toggle.css *//* The switch - the box around the slider */.switch {  position: relative;  display: inline-block;  width: 60px;  height: 34px;}/* Hide default HTML checkbox */.switch input {  opacity: 0;  width: 0;  height: 0;}/* The slider */.slider {  position: absolute;  cursor: pointer;  top: 0;  left: 0;  right: 0;  bottom: 0;  background-color: #ccc;  -webkit-transition: .4s;  transition: .4s;}.slider:before {  position: absolute;  content: "";  height: 26px;  width: 26px;  left: 4px;  bottom: 4px;  background-color: white;  -webkit-transition: .4s;  transition: .4s;}input:checked + .slider {  background-color: #2196F3;}input:focus + .slider {  box-shadow: 0 0 1px #2196F3;}input:checked + .slider:before {  -webkit-transform: translateX(26px);  -ms-transform: translateX(26px);  transform: translateX(26px);}/* Rounded sliders */.slider.round {  border-radius: 34px;}.slider.round:before {  border-radius: 50%;}

Let's import those styles into our Toggle.tsx component. We will need to install the rollup-plugin-postcss to tell Rollup how to compile CSS as part of our package:

npm i -D rollup-plugin-postcss

Now create a file called tsdx.config.js in the root of your project and paste the following code:

// tsdx.config.jsconst postcss = require('rollup-plugin-postcss');module.exports = {  rollup(config, options) {    config.plugins.push(      postcss({        plugins: [],      })    );    return config;  },};

Now we can import our styles using ESM imports:

// src/Toggle.tsximport React, { FC } from 'react';import './Toggle.css'; //  Import our new stylesexport const Toggle: FC = () => {  return (    <label className="switch">      <input type="checkbox" />      <span className="slider round"></span>    </label>  );};

Save and refresh your browser.

Alt Text

Tada!

Component Props

But what if we want to actually do something with the state of our toggle component? It's not very useful as is.

Let's add component props in order to give us more flexibility:

// src/Toggle.tsximport React, { FC } from 'react';require('./Toggle.css');export type ToggleType = {  isOn: boolean;  handleChange: () => void;};export const Toggle: FC<ToggleType> = ({ isOn, handleChange }) => {  return (    <label className="switch">      <input checked={isOn} onChange={handleChange} type="checkbox" />      <span className="slider round"></span>    </label>  );};

We can now pass props into the component and manage the state of it. Our types will automatically be built and included as part of our project, since we are exporting ToggleType.

Let's update our playground to contain this state and make sure the toggle still works:

// example/index.tsximport 'react-app-polyfill/ie11';import React, { useState } from 'react';import * as ReactDOM from 'react-dom';import { Toggle } from '../src/index';const App = () => {  const [isOn, setIsOn] = useState(false);  return (    <div>      <Toggle isOn={isOn} handleChange={() => setIsOn(!isOn)} />    </div>  );};ReactDOM.render(<App />, document.getElementById('root'));

Now we're handling state outside of the component. This means we can change the toggle state anywhere by simply calling setIsOn(!isOn).

Tests

We're ready to publish our package, however let's make sure we have a functioning test first. We want people to contribute to your project and we don't want to test the functionality in our sandbox every time a new PR is opened.

Let's rename the blah.test.tsx file to toggle.test.tsx and update our react-dom render method:

// src/tests/blah.test.tsx -> src/tests/toggle.test.tsximport * as React from 'react';import * as ReactDOM from 'react-dom';import { Toggle } from '../src';describe('it', () => {  it('renders without crashing', () => {    const div = document.createElement('div');    ReactDOM.render(<Toggle isOn={false} handleChange={() => {}} />, div);    ReactDOM.unmountComponentAtNode(div);  });});

In order for Jest to be able to read CSS files, we'll need to install a package to allow us to mock these files:

npm i -D identity-obj-proxy

And then edit our package.json to reflect this:

// package.json...  "jest": {    "moduleNameMapper": {      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",      "\\.(css|less|scss|sass)$": "identity-obj-proxy"    }  },...

See Jest docs for more on why this is necessary. We should have a functioning test now, from your root level directory:

npm test

Huzzah!

Alt Text

The only problem is that this is just testing that the component mounts and doesn't break the app doing so. What we really want to test is that the toggle functionality and isOn prop works.

We can use react-testing-library to test our component prop functionality:

npm i -D @testing-library/react @testing-library/jest-dom

Let's update our test file to use some of these new testing methods. We'll be using the render and fireEvent methods:

// test/toggle.test.tsximport * as React from 'react';import { render, fireEvent } from '@testing-library/react';import { Toggle } from '../src';it('Should render the toggle and be clickable between states', () => {  // mock onChange function  const onChange = jest.fn();  const { getByTestId, rerender } = render(    <Toggle isOn={false} handleChange={onChange} />  );  // checkbox and parent label components  const checkbox = getByTestId('Toggle');  const label = getByTestId('Toggle-label');  // isOn=false should mean it's unchecked  expect(checkbox).toHaveProperty('checked', false);  // Clicking from off -> on  fireEvent.click(label);  expect(onChange).toHaveBeenCalledTimes(1);  // isOn=true should mean it's checked  rerender(<Toggle isOn={true} handleChange={onChange} />);  expect(checkbox).toHaveProperty('checked', true);  // Clicking from on -> off  fireEvent.click(label);  expect(onChange).toHaveBeenCalledTimes(2);});

If this is a bit confusing or if you're unfamiliar with react-testing-library, that's okay. All we're really doing here is rendering the component and making sure isOn reflects a checked state and that our handleChange function is called every time on click.

Double check that it still works:

npm test

Publish

Let's edit our package.json to contain some of your own personalized information. You'll want to make sure you update the version, author, and name. The name should be unique and not taken in the NPM registry:

// package.json...author: Frodo Baggins,name: frodo-toggle,version: 1.0.0,...

The last step is to publish now!

npm publish

If you get an error, it's likely that you either need to a) login again via npm login or b) change the package name to be unique. If you want to see if the package name is available, try searching for it in the npm registry.

Congratulations, you are now a Typescript package author.

Anyone can now install your package from the command line by running:

npm i your-toggle-lib # replace this with your package name

Next Steps

There are a few things you could do to make this package better from here. If you're planning on allowing outside contributors you may want to tweak the default Github action that comes with TSDX to run your test suite on new PRs. This will make sure that outside contributors are not merging in broken changes.

Other possible next steps:

  • Add props to change the color and add labels to the toggle button.
  • Add a size prop with "small", "medium", and "large" options.
  • Add different transitions based on a prop.
  • Add styled-components instead of css

The world is your oyster!

Configuration is the most painful part of any project, but libraries like TSDX and create-react-app are amazing at lowering the barrier of entry for newcomers and lazy people (like me). No one likes spending a day fighting with configuration files. Hopefully this guide gives you a bit more confidence that you can write your own packages. I look forward to seeing your projects on Github and npm!


Original Link: https://dev.to/g_abud/publish-a-typescript-package-in-under-10-minutes-4940

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To