Shopify App Bridge and React Router

Standard

I recently started building Shopify apps and ran into a problem with an embedded app and getting the URL change to reflect in the browser. The iframe would update, and I would be on the page, but the URL in the browser wouldn’t update. While not a critical feature, it’s sure annoying if you refresh the page and you can’t get the exact page on because instead of /orders/10 the URL only shows /orders/.

I spent hours researching how to do it, and found either out-of-date comments or things half done.

The not so obvious RoutePropagator is what you are looking for. After following the setup for the ClientRouter, the missing piece was adding in the RoutePropagator where I define my main <Switch> and <Route> statements.

//App.jsx
import { useRoutePropagation } from '@shopify/app-bridge-react';

function App({ location }) {
    //the magic was right here
    useRoutePropagation(location);

    return (
      <Switch>
          <Route path="/settings" component={Settings} />
          <Route path="/plan" exact component={Plan} />
          <Route path="/" exact component={Dashboard} />
      </Switch>
  );
});
//AppRouter.js
import {withRouter} from 'react-router'
import {useClientRouting} from '@shopify/app-bridge-react';
//Setup with the ClientRouter hook
function AppRouter(props) {
    const {history} = props;
    useClientRouting(history);
    return null;
}

export default withRouter(AppRouter);
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { BrowserRouter, Link } from 'react-router-dom'
import { Provider as AppBridgeProvider } from '@shopify/app-bridge-react'
import enTranslations from '@shopify/polaris/locales/en.json';
import {AppProvider as PolarisProvider} from "@shopify/polaris";
import config from './config';
import AppRouter from './AppRouter';
import { createStore, applyMiddleware } from 'redux';
import { Provider as ReduxProvider } from 'react-redux';
import '@shopify/polaris/dist/styles.css';

import rootReducer from './store/reducers/rootReducer';
const store = createStore(rootReducer, []);

const paramsSearcher = new URLSearchParams(window.location.search)
const findOrigin = paramsSearcher.get('shop') || 'test.myshopify.com';

window.sessionStorage.setItem('shopifyDomain', findOrigin);

const shopifyConfig = {
    apiKey: config.API_KEY,
    shopOrigin: findOrigin
}

const CustomLinkComponent = ({children, url, ...rest}) => {
    return (
        <Link to={url} {...rest}>{children}</Link>
    );
};

ReactDOM.render(
    <React.StrictMode>
        <BrowserRouter>
            <AppBridgeProvider config={shopifyConfig}>
                <ReduxProvider store={store}>
                    <PolarisProvider i18n={enTranslations} linkComponent={CustomLinkComponent}>
                        <AppRouter />
                        <App />
                    </PolarisProvider>
                </ReduxProvider>
        </AppBridgeProvider>
        </BrowserRouter>
    </React.StrictMode>,
    document.getElementById('root')
);

Shopify Polaris and the missing placeholder

Standard

I have recently been working on a Shopify app and came across a minor annoyance with a ResourceList that I solved, and then figured out why it was happening.

When adding a filter, it provides a search box, and all the examples had a placerholder in it, except mine did not have the placeholder.

While a minor thing I dug into the Shopify code and found I could pass in a queryPlaceholder property to set my own placeholder.

By setting the specific text it showed up just like I wanted. However in the component code you can see it will set a default placeholder if you don’t provide one.

When going back through the getting started directions, there is a spot to load english translations and that is what I was missing. It was an easy thing to miss but if you are missing a default placeholder, make sure you loaded the translations!

Interviewing after being the interviewer

Standard

I recently switched jobs after just shy of 5 years. Over those past 4+ years I have interviewed dozens of people for a job and learned a lot about the process.

The 2 way street

For starters, it helped me be relaxed during the interview process. I always tried to make sure it was a 2 way street. If a candidate doesn’t feel comfortable with me, it probably wouldn’t be a good fit. Therefore I liked to structure interviews as a conversation. The candidate would get to ask questions as they have them instead of just firing them off at the end. With my past experience, I was able to maintain that sort of things while I was being interviewed for a position.

Keeping with that idea, I had one interview where I knew it wasn’t going to be a good fit. We were already 2 or 3 in, so they bring in another member of the team to talk with and it just sort of died there. I am certainly grateful I was employed and didn’t have to take the first offer, otherwise if it came with this company, I imagine I wouldn’t be happy.

Sharing your test on Github…

You know who knows my Github username? My employer. If they see these random repositories popping up at night that are quite specific, it’s probably safe to assume they understand I am interviewing elsewhere. Now I don’t know if my employer at the time visited my Github to see what I was up to, but it definitely made me think of what I should name the repos.

What I believe is a better approach would be to keep them private and invite the interviewer or their team to review it. Github does have private repositories now.

$ Cha-Ching

The money aspect is always the weirdest. I told my wife on a few occasions that I didn’t know if I want to proceed with the ‘test’ because there was no salary range listed. If I spent 4+ hours on this test and what I need was outside what they were willing to pay it was a huge waste of time. In one instance, I knew I was talking to one of the guys at the top so I mentioned my concern before doing the test. If you are a negotiator, the downside here is you may have to throw out the number first.

While I didn’t make the job postings at my former job, it did instill in me just how frustrating it is for candidates not to know the salary range before hand.

My favorite question

If there is one thing you could change, what would it be?

Some people will give you really good answers. Sometimes it’s awkward if their boss is on the phone. My favorite time was when both the guy interviewing me answered and his boss separately answered. I really look for 2 different things when hearing the answers. I try to hear if it’s a genuine answer vs a b.s. answer and what it is. A fellow developer might be roadblocked by some process, whereas the CTO/COO wants to change a bigger picture thing.

This question also tends to lead the interviewers into rambling about their company culture. Often not trying to backpedal on their answer but just talking, and thinking about the different things they have experienced.

While I looked on and off for a while, Monday I start the new gig and I think it will be a great one. The personalities of the owners remind me of my last job where we got along quite well. While I am no longer working at a SaaS, the new company sells stuff that I needed. On Tuesday it arrives as I placed an order as a regular customer without my dev goggles on and I don’t think the fulfillment team knows who I am. It’s like Undercover Boss without the boss part.

Having a family and continuing education

Standard

Having a family definitely slows down the long nights going through an online course and digesting everything. Back in the bachelor life I could spend the entire weekend going through some course.

Now though, the kids are doing things, I am spending time with my wife, lawn care has to happen. All of this takes up time that in my single life would have been used for online learning. This means the courses take a lot longer to complete.

There is an upside to it that though. By doing a course spread of weeks instead of days, it re-enforces the concepts you learn. In this particular case, I am going through a Ruby on Rails course. By not being able to burn through it, it’s easier to retain the Ruby language and concepts of Rails.

It reminds me of when I learned to SCUBA dive. If you go on a nice Caribbean vacation, you can find places that will teach you to SCUBA dive in a weekend. If you take an 8 week course, you are much more comfortable putting the gear together as you have done it week after week. The weekend places, you do it 2 maybe 3 times, but come 6 months from now when you go diving again, that uncertainty of not remembering just how everything goes.

So in a few more weeks, when I am able to finish this course, I hope to be able to retain most of what I learned and start working on Ruby projects comfortably. The hard part of learning a new programming language is it’s so much faster to just use the language I am already proficient it.

Laravel Nova with your pipelines updated

Standard

Shortly after I added ‘Laravel Nova with your pipelines‘ I stumbled across a tweet on using an environment variable with composer. So instead of maintaining an auth.json file in the repository and using sed to replace placeholders, you can just add a COMPOSER_AUTH environment variable with the entire contents.

GitLab environment variables page

There had to be a better solution and here it was. Nice and simple.

Laravel Nova with your pipelines

Standard

If you use Laravel Nova, you undoubtedly were happy when Taylor started allowing us to use composer to install and update Nova. By doing so you create or edit your ~/.composer/auth.json file with something like the following:

{
    "http-basic": {
        "nova.laravel.com": {
            "username": "your-email@your-email.com",
            "password": "your-super-secret-token"
        }
    }
}

On your dev machine you need this file, along with your production or wherever you are building / deploying your site. Using forge, this is a simple git pull and not a complicated deployment.

I setup a GitLab pipeline so that when pushing the master branch, it will run my unit tests, then trigger forge to do the deployment.

To get composer to install and not error out means we had to add auth.json to our repository and in our build steps, copy it to the correct directory. By using variables, we don’t need to store our credentials in that file. Our auth.json is literally what we have above. We are going to replace your-email@your-email.com with our custom variable in the Gitlab CI/CD Environment Variables page.

GitLab CI variables

Here is what our test stage looks like. The ‘- sed’ lines replace our placeholders in auth.json with the variables we defined above.

phpunit:

  stage: test

  script:
    - cp auth.json ~/.composer/auth.json
    - sed -i "s/your-email@your-email.com/${NOVA_COMPOSER_EMAIL}/g" ~/.composer/auth.json
    - sed -i "s/your-super-secret-token/${NOVA_COMPOSER_TOKEN}/g" ~/.composer/auth.json
    - composer install --prefer-dist --no-ansi --no-interaction --no-progress --no-scripts
    - cp .env.testing .env
    - php artisan key:generate
    - phpunit --colors=never

There may be a more elegant way to achieve this, but this works and doesn’t jump through too many hoops.

Adding Facebook Pixel to Google Tag Manager

Standard

Adding the Facebook pixel to your Google Tag Manager account is really quite easy.

To start, let’s go to the Tags page and click the ‘New’ button. Where it is named ‘Untitled Tag’ go ahead and change that to ‘Facebook Pixel’. This is for informational purposes only.

Next, click ‘Choose a tag type to begin setup…’ and you are going to select ‘Custom HTML’. In the multi-line text box that opens, you will want to paste your Facebook pixel code there.

Down under ‘Tag firing options’, select’ Once per page’.

After that we are going to select our trigger. You are going to want to use the ‘All Pages Page View’ trigger that comes by default with your tag manager account. After that, you will want to click save.

Example facebook pixel configuration
Example Facebook Pixel setup

The last thing to do is submit your changes so they can be live.

To make sure you have it setup correctly, you can use the Facebook Pixel Helper that Facebook offers for free from the Chrome web store,

Chrome store link

Google Tag Manager ‘Page View’ tags not firing

Standard

If you landed on this page, you are probably trying to figure out why your ‘Page View’ tags aren’t firing. I ran across this problem myself, and that is why I am writing this blog post.

First up, make sure your Google Tag Manager is added to your site. This sounds dumb, but it is the first step we have to take. You should find code like below inside the <head> </head> tags of your website.

<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-MYGTMCODE');</script>
<!-- End Google Tag Manager -->

Now this is a basic setup. As long as that code is in there you should be good. Next, the question is, are you using the data layer? If you are, you of course added the following snippet of code like the examples show.

<script>
    dataLayer = [];
</script>

Now here is where the magic broke or happens. You may have skimmed over it, but that code needs to be ABOVE your Google Tag Manager code. I found that if it is below it, it will break the events and not fire correctly.

A correct implementation looks like the following. Make sure to update the tracking ID (this page example is: GTM-MYGTMCODE with your actual tracking ID.

<script>
    dataLayer = [];
</script>
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-MYGTMCODE');</script>
<!-- End Google Tag Manager -->

I place this code as high in the <head></head> tags as I can. Hopefully this solved your problem of the Page View event not firing when it should have.

Google Tag Manager and how to use it. The basics.

Standard

Have you ever wondered what Google Tag Manager (GTM) is? Does it seem complex to you? Did you notice that the new Google Analytics script references tag manager?

There are 2 main components we are going to talk about. The first is tags and the second is triggers. In the image below, we are looking at the tags section of one of my website GTM accounts. You will notice in the third column, is the ‘Firing Triggers’ column. In order for a tag to be used, it has to be triggered by some sort of event.

Google Tag Manager tags
Tags page of our GTM account.

What is a tag?

A tag is basically HTML or javascript you execute when the trigger happens. If you have ever had to add tracking code, or some sort of code to the footer of every page, once you have Google Tag Manager installed you can add it from there. The upside of using GTM is you can see what tags get fired when instead of having it hidden throughout your code.

If you have ever ran ads either on Google Adwords or Facebook, you know you need to add pixels and conversion tracking to help track how well your ads perform. Depending on the complexity of your setup, this could be achieved with no coding on your end.

What is a trigger?

A trigger is an ‘event’ that happens, and when it happens it will load a tag. Google Tag Manager comes with a ‘All Pages – Page View’ trigger out of the box. This trigger is used whenever you need some tag to fire on every single page.

You can have all sorts of triggers, such as form submissions, clicks to certain elements or links, custom events you send yourself, even timers that fire every so often.

In the image below, we have defined 2 triggers. The first trigger happens when our form with and id of “registerform” is submitted. The second trigger is a page view but when the URL is specifically our registration page. When each of these triggers are true, they each fire a tag.

Google Analytics Triggers
Google Analytics Triggers Page

Below we can see what tags the triggers fire. The ‘Facebook Pixel ViewContent’ tag is fired for the ‘Registration Page View’ trigger. As mentioned above, this trigger happens when you land on the page with ‘/register’ in the URL. Next we see the ‘GA – Register Form’ tag is fired when the ‘Register Form Submission’ trigger is used.

GTM Tag Example
Example Tags

Why use Google Tag Manager

If you are uncomfortable with editing your website, Google Tag Manager gives you an easy way to add some integrations. If you pay a company to manage your website for you, this allows you to add those integrations without having to pay a fee for a simple “add this script here” request. You can do it yourself, or have a whiz staff member do it.

Once you understand the concepts between tags, triggers, data layers and what not, you can unlock a lot of tools and not have to worry about code or updating your website.

What I use Google Tag Manager for

I use Google Tag Manager for some basic things. On TodoSage.com for example, here is what I use:

  1. Google Analytics
  2. Sending events to Google Analytics I want to track (see the register form submission trigger above)
  3. Facebook Pixel and ad tracking
  4. Hot Jar heatmap tracking

Most of these are simple embed scripts on every page and they ‘just work’. When you are just starting out, you don’t have to go crazy with your implementation. Start small and add more over time if you need to.

It’s best to only add things you need and are going to use. If you have noticed all those “we use cookie” banners, those happened because of websites doing all sorts of tracking of you that you don’t know about. It resulted in countries finally taking issue with it and demanding more transparency.