React Widget from 0 to Launch

The one where I build the Draft widget in React.

Today seems as good a day as any to write a full-dress React widget and actually host it somewhere. I maintain a little website called WidgetWonk (https://www.widgetwonk.com/) where I host such things. I’ll be happy to take you through the entire process. I probably need to remind myself of a few things. It’s been a while since I actually added a new widget to that thing. And all the ones there currently as of this writing are Vue widgets. The whole site is built on Vue.

In my React Lesson 2 post and the subsequent parts, we built a little widget where you can type and edit text and you can commit the values as you go. This also lets you browse backward and forward through the commits. Right now it only lives in my react-examples repo here https://github.com/xerocross/react-examples on the branch tiny-text-commit-redux. I need to upgrade this thing to its own repo and give it a real name. I like single-syllable verbs. How about Draft.

To create the new repo, first I’m going to clone my react-examples branch into a new directory: git clone -b tiny-text-commit-redux --single-branch https://github.com/xerocross/react-examples.git draft. But now this will be the master branch, so git checkout -b master. Then I delete the old branch: git branch -D tiny-text-commit-redux. I could have just renamed the branch, but whatever. It’s done now.

I’ll just create a new GitHub repo on the website, call it xero.draft. Then I update my local git project to the new repo location: git remote set-url origin https://github.com/xerocross/xero.draft.git. After I do any changes to git remote, I like to explicitly look at them again just to make sure. It’s unnecessary, but I do it every time. git remote -v. Confirmed: the url for origin has been correctly updated. Now let’s do an initial push with git push --set-upstream origin master.

Over at GitHub, my repo is populated with files, including a lot of boilerplate that came ready-made when I used create-react-app to make this project. But it also has my source files in it. Turning this thing into a full dress widget that I intend to host someplace means I need to take full ownership of the package. I’m not just writing little bits of example code anymore.

It’s just generally bad practice to make large changes directly on the master branch, so I’m going to create a new branch to work on. git checkout -b cleanup. The first thing I want to do is make some scattered minor changes. Specifically:

  • Rename the package in package.json.
  • Delete the serviceWorker.js file because I’m definitely not using it. I don’t even know what it does yet, but the comments say it is optional. So: gone.
  • The serviceWorker file is referenced in the index.js file, so I remove those references.
  • Delete some code that had been commented out.
  • Add a mostly blank readme file.

Since I did clone this directory from GitHub, I need to reinstall everything. So yarn install. This makes a lockfile that will be committed to the repo, but of course the installed files themselves that go in node_modules will not be committed to the repo. Now I start the dev server yarn start just to see if I’ve already broken anything, but it looks good so far.

I want to merge these changes into master now. To do that, first we checkout master. Then, while on master we run git merge cleanup. This does a fast-forward merge automatically. Now those changes are on master, and I can push them to GitHub.

As someone who isn’t great at custom-making my own styles, I appreciate Bootstrap. However, I never use Bootstrap’s JS files. I only use the style sheets. And I’m using version 3.3.7. For my purposes here, I added some Bootstrap CDN links to my index.html file and then added the appropriate classes and such to my component templates. Ultimately, I’m probably going to lift these react components out of this single-widget project and put them into something larger, like my WidgetWonk website, so we can actually host it and view it online. For now I just add some minor stylistic changes using Bootstrap.

Because I’m not just doing this as a learning exercise anymore, I need to make a few important changes to the interface. For example, I have a “reset to last commit” button, and its effects are destructive. So before just firing off a destructive event because of a mouse click, let’s add a confirm dialog to make sure the user understands.

Now, however, one of my tests in my testing suite fails because I have changed the use path. To fire a reset-to-last-commit the user has to click the button and then click “OK” at a dialog box. I’ll need to research how one accounts for that when using react-testing-library.

I don’t see any good way to use the dom-testing-library to interact with a confirm dialog box. An alternative option is to redefine (override) the window.confirm function. I don’t see any way of doing that that seems like the correct way, but you can do the following and it seems to work. Directly inside your test function, you can include something like this:

window.confirm = () => true;
fireEvent.click(resetButton);

Since firing the click event causes the window.confirm method to be executed, it has to be overridden before firing the click event. In this case I wanted to confirm what happens when the user confirms. For each such test, I also recommend a parallel test for what happens if the user declines, in which case we can write this.

window.confirm = () => false;
fireEvent.click(resetButton);

Since I have done something so hacky as overriding a global, for good measure I put the following inside my beforeEach function.

window.confirm = ()=> {
    throw new Error("window.confirm not specified")
}

If something in one of my tests calls window.confirm and if I haven’t explicitly defined it in that particular test, then I want it to throw an error and alert me to fix this. I don’t want any unexpected cross-contamination between my tests.

I have also added a “clear all” button and method. It completely resets the state of the widget just as if you refreshed the page. I am adding several more tests to the testing suite to make sure it is working correctly.

At this point, the widget is very, very simple, but it does exactly what it says it does. We can publish this thing. It’s a good backbone onto which we can add more complex features later if we want. For now may main goal is just to see if I can put it on the web without any expected problems. One place you can host widgets such as this—and I’ve used them often before—is Heroku.

When you create a Heroku account, you can set up a command line interface tool. If you haven’t done that already, you are better off following their own guides. I am going to create a new Heroku app which I call xero-draft by executing heroku create xero-draft at the command line. Behind the scenes, it does some very modern thing with containers (sorta like virtual servers, but more efficient), so it can charge you for hosting/computation by the second. For very small hobby projects, the service is free.

Heroku uses git for pushing updates. I have mixed feelings about that actually, but it’s what we are going to do for now. After running the heroku create command a moment ago, it returned this git address to me: https://git.heroku.com/xero-draft.git. That is where I need to push changes to publish them to Heroku. The name of this remote is simply “heroku”—much as the name of my GitHub remote is “origin”.

One of my reservations about using git as the means to publish to the host is that I don’t want the master branch to be the one that is deployed. Almost nobody works that way, but by default when you push a repo to heroku, it builds and deploys your master branch. A more typical technique of deploying code is to push it through a few different stages in order. There would usually be branches for development (dev), staging (stage), master, and production (prod). Master and production are not the same thing. They become the same thing at deployment by merging master into the production branch and then deploying the production branch. So the thing you see live on a web site would most likely be something that lives on a “prod” branch in their code source. This sequence of upgrades in code is very deliberate and usually requires peer code reviews and a supervisor sign-off before anything actually gets to production and goes live.

Well—if you have millions of views per day, then that kind of control is necessary. For a tiny, one-man project it might seem strange of me to create a “prod” branch, but I’m doing it anyway, because there is a workaround for Heroku.

Right now I create a new prod branch from master, so right now prod is a duplicate of master. Now we can use the command git push heroku prod:master to push our local branch “prod” to the remote branch “master”. Heroku will only deploy the branch called master on their end. So this is necessary. Once we execute this, it will actually attempt to build and deploy the app right in front of us. Here goes.

I’m happy to say that build and deployment succeeded without any problem at all. Now my Draft widget, however simple, is hosted at https://xero-draft.herokuapp.com/. With this setup in place, whenever I want to make changes to my code in the future, I can do whatever process I want for getting code ready for master. At all times, I can keep my GitHub repo up-to-date by running git push origin. When I want to deploy changes, I checkout the prod branch, I merge master into it, then I execute the same command as above again: git push heroku prod:master.

Believe me: deployment can get a lot harder than that. This is about the simplest it ever gets. And that’s good, because I think later I’m going to add a few more features to this widget. Also, I did mention putting this on my WidgetWonk site, not just in its own little sandbox on Heroku. I think I’ll leave that for Part 2.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s