<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Gatsby on Haseeb Majid</title>
    <link>https://haseebmajid.dev/tags/gatsby/</link>
    <description>Recent content in Gatsby on Haseeb Majid</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Thu, 17 Nov 2022 00:00:00 +0000</lastBuildDate><atom:link href="https://haseebmajid.dev/tags/gatsby/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Why I moved from Gatsby to Hugo for this blog?</title>
      <link>https://haseebmajid.dev/posts/2022-11-17-why-i-moved-from-gatsby-to-hugo-for-this-blog/</link>
      <pubDate>Thu, 17 Nov 2022 00:00:00 +0000</pubDate>
      <author>Me</author>
      <guid>https://haseebmajid.dev/posts/2022-11-17-why-i-moved-from-gatsby-to-hugo-for-this-blog/</guid>
      <description>&lt;h1 id=&#34;about-this-site&#34;&gt;About This Site&lt;/h1&gt;
&lt;p&gt;This site was built with &lt;a href=&#34;https://gohugo.io/&#34;&gt;hugo&lt;/a&gt; and the &lt;a href=&#34;https://github.com/hmajid2301/hugo-PaperModX&#34;&gt;PaperModX Theme&lt;/a&gt; (using a fork of a fork at the moment).&lt;/p&gt;
&lt;p&gt;I decided to go with an existing theme rather than creating my own this time, to one save time but also to give the
site a more consistent feel. I am no designer and I felt my last website (v3), really felt like a bunch of different
websites thrown together. It was a great way to learn React, TailwindCSS and a bunch of other technologies.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h1 id="about-this-site">About This Site</h1>
<p>This site was built with <a href="https://gohugo.io/">hugo</a> and the <a href="https://github.com/hmajid2301/hugo-PaperModX">PaperModX Theme</a> (using a fork of a fork at the moment).</p>
<p>I decided to go with an existing theme rather than creating my own this time, to one save time but also to give the
site a more consistent feel. I am no designer and I felt my last website (v3), really felt like a bunch of different
websites thrown together. It was a great way to learn React, TailwindCSS and a bunch of other technologies.</p>
<h2 id="technologies-used">Technologies Used</h2>
<ul>
<li><a href="https://gohugo.io/">Hugo</a>
<ul>
<li><a href="https://github.com/hmajid2301/hugo-PaperModX">PaperModX Theme</a>
<ul>
<li>My fork (of a fork, of a fork)</li>
</ul>
</li>
</ul>
</li>
<li><a href="https://www.goatcounter.com/">Goatcounter</a> for Analytics</li>
<li>Hosted by <a href="https://www.netlify.com/">Netlify</a></li>
<li>Using <a href="https://www.netlifycms.org">NetlifyCMS</a> for content management</li>
</ul>
<h2 id="why-move-to-hugo">Why Move to Hugo?</h2>
<p>I also had a bunch of issues even building the <a href="https://gitlab.com/hmajid2301/portfolio-site/-/pipelines">site recently</a>.
I had a scheduled job to rebuild it so that the stats page would update with the most viewed articles etc.
The final straw for me was not being to easily add a new page for talks. I recently was lucky enough to give a
talk at Europython and wanted to share that on my website but realised with my old Gatsby site that would be a bit of
a pain to add. I could also no longer easily upgrade my site to the latest version of Gatsby v4 due to the old
plugins I relied on.
Mostly it was essentially an issue with me, that I no longer wanted to put in the effort to maintain the site.</p>
<p>So I decided to take a look at something easier to maintain but that would still look great. I have been
learning Golang and decided to take a look at Hugo. One thing I noticed right away was how fast it was to
build the site. Roughly speaking the old site used to take ~120 seconds to build and this site takes &lt; 1 second.</p>
<p>Anecdotally I noticed the hot reloader seems to work better but again I was using a v2 of Gatsby.
Overall I am pretty happy with this new site. Using Hugo archetypes and page-bundles. I have moved
all of my articles within this repo, making it far easier to create new blog posts and test draft posts.</p>
<p>So hopefully I will be blogging more often 🤣 (I&rsquo;m looking at you 2 posts in 2022, as of the time of writing)!</p>
<h2 id="-older-iterations">👴 Older iterations:</h2>
<p>You can find older iterations of this site here:</p>
<ul>
<li><a href="https://v1.haseebmajid.dev">Version 1</a></li>
<li><a href="https://v2.haseebmajid.dev">Version 2</a></li>
<li><a href="https://v3.haseebmajid.dev">Version 3</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Testing a Gatsby application with Cypress on Gitlab CI</title>
      <link>https://haseebmajid.dev/posts/2021-03-22-testing-a-gatsby-application-with-cypress-on-gitlab-ci/</link>
      <pubDate>Mon, 22 Mar 2021 00:00:00 +0000</pubDate>
      <author>Me</author>
      <guid>https://haseebmajid.dev/posts/2021-03-22-testing-a-gatsby-application-with-cypress-on-gitlab-ci/</guid>
      <description>&lt;p&gt;In this blog post, we will go over how we can automatically test a Gatsby site end-to-end (e2e), using Cypress on Gitlab CI.&lt;/p&gt;
&lt;h1 id=&#34;introduction&#34;&gt;Introduction&lt;/h1&gt;
&lt;h2 id=&#34;gatsby&#34;&gt;Gatsby&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://www.gatsbyjs.com&#34;&gt;Gatsby&lt;/a&gt; is a static site generator (SSG) built upon React. It allows us to create &amp;ldquo;blazing&amp;rdquo; fast websites.
In this example, we will use a simple blog starter template available and add a Cypress test.&lt;/p&gt;
&lt;h2 id=&#34;cypress&#34;&gt;Cypress&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Fast, easy and reliable testing for anything that runs in a browser. - Cypress README&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>In this blog post, we will go over how we can automatically test a Gatsby site end-to-end (e2e), using Cypress on Gitlab CI.</p>
<h1 id="introduction">Introduction</h1>
<h2 id="gatsby">Gatsby</h2>
<p><a href="https://www.gatsbyjs.com">Gatsby</a> is a static site generator (SSG) built upon React. It allows us to create &ldquo;blazing&rdquo; fast websites.
In this example, we will use a simple blog starter template available and add a Cypress test.</p>
<h2 id="cypress">Cypress</h2>
<blockquote>
<p>Fast, easy and reliable testing for anything that runs in a browser. - Cypress README</p>
</blockquote>
<p><a href="http://cypress.io/">Cypress</a> allows us to test a web application, how a real user would use the application.
Cypress will be used to test our Gatsby application, though if it&rsquo;s a site you can test it using Cypress.</p>
<h2 id="gitlab-ci">Gitlab CI</h2>
<p><a href="https://docs.gitlab.com/ee/ci/">Gitlab CI</a> is a continuous integration pipeline that will allow us to run our
tests automatically, such as when we merge code into the master branch.</p>
<h1 id="getting-started">Getting Started</h1>
<h2 id="gatsby-1">Gatsby</h2>
<p>Create a new Gatsby site, using this default Gatsby starter:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gatsby new gatsby-starter-blog https://github.com/gatsbyjs/gatsby-starter-blog
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> gatsby-starter-blog
</span></span></code></pre></div><h3 id="optional-typescript">(Optional) Typescript</h3>
<p>Adding Typescript to a Gatsby web application.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add typescript @types/react @types/react-dom @types/node -D
</span></span><span class="line"><span class="cl">yarn add gatsby-plugin-typescript
</span></span></code></pre></div><p>Add the following to your <code>gatsby-config.js</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">resolve</span><span class="o">:</span> <span class="sb">`gatsby-plugin-typescript`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">isTSX</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// defaults to false
</span></span></span><span class="line"><span class="cl">        <span class="nx">jsxPragma</span><span class="o">:</span> <span class="sb">`jsx`</span><span class="p">,</span> <span class="c1">// defaults to &#34;React&#34;
</span></span></span><span class="line"><span class="cl">        <span class="nx">allExtensions</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// defaults to false
</span></span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Then create a new file <code>tsconfig.json</code> (in the project root, where the <code>gatsby-config.js</code> is).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;compilerOptions&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;module&#34;</span><span class="p">:</span> <span class="s2">&#34;commonjs&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;target&#34;</span><span class="p">:</span> <span class="s2">&#34;esnext&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;jsx&#34;</span><span class="p">:</span> <span class="s2">&#34;preserve&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;lib&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;dom&#34;</span><span class="p">,</span> <span class="s2">&#34;esnext&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;strict&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;noEmit&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;isolatedModules&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;esModuleInterop&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;noUnusedLocals&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;allowJs&#34;</span><span class="p">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;exclude&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;node_modules&#34;</span><span class="p">,</span> <span class="s2">&#34;public&#34;</span><span class="p">,</span> <span class="s2">&#34;.cache&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="cypress-1">Cypress</h2>
<p>Now to finally add Cypress to our application so we can test it. First, install the dependencies.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add -D cypress cypress-axe axe-core start-server-and-test
</span></span><span class="line"><span class="cl"><span class="c1"># Add types</span>
</span></span><span class="line"><span class="cl">yarn add -D @types/cypress-axe
</span></span></code></pre></div><p>Next, let&rsquo;s create a <code>cypress.json</code> folder in the project root.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;baseUrl&#34;</span><span class="p">:</span> <span class="s2">&#34;http://localhost:8000/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="nt">&#34;integrationFolder&#34;</span><span class="p">:</span> <span class="s2">&#34;cypress/e2e&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Next, let&rsquo;s add some new &ldquo;scripts&rdquo; to the <code>package.json</code> file.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line hl"><span class="cl">  <span class="s2">&#34;scripts&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;cy:open&#34;</span><span class="p">:</span> <span class="s2">&#34;cypress open&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;cy:run&#34;</span><span class="p">:</span> <span class="s2">&#34;cypress run&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;build&#34;</span><span class="p">:</span> <span class="s2">&#34;gatsby build&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;develop&#34;</span><span class="p">:</span> <span class="s2">&#34;gatsby develop&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;format&#34;</span><span class="p">:</span> <span class="s2">&#34;prettier --write \&#34;**/*.{js,jsx,ts,tsx,json,md}\&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;start&#34;</span><span class="p">:</span> <span class="s2">&#34;npm run develop&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;serve&#34;</span><span class="p">:</span> <span class="s2">&#34;gatsby serve&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;clean&#34;</span><span class="p">:</span> <span class="s2">&#34;gatsby clean&#34;</span><span class="p">,</span>
</span></span><span class="line hl"><span class="cl">    <span class="nt">&#34;test&#34;</span><span class="p">:</span> <span class="s2">&#34;echo \&#34;Write tests! -&gt; https://gatsby.dev/unit-testing\&#34; &amp;&amp; exit 1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;test:e2e&#34;</span><span class="p">:</span> <span class="s2">&#34;start-server-and-test &#39;yarn develop&#39; http://localhost:8000 &#39;yarn cy:open&#39;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;test:e2e:ci&#34;</span><span class="p">:</span> <span class="s2">&#34;start-server-and-test &#39;yarn develop&#39; http://localhost:8000 &#39;yarn cy:run&#39;&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span></code></pre></div><p>These scripts allow us to start Cypress, <code>cy:open</code> opens a GUI to visualise our tests whereas <code>cy:run</code> does it all
in the terminal (the browser runs in headless mode). Where we will run <code>test:e2e:ci</code> in our CI pipeline, here we use
the <code>start-server-and-test</code> command to start our Gatsby server using <code>yarn develop</code>. Then we run <code>cy:run</code> to
start our tests.</p>
<h3 id="structure">Structure</h3>
<p>Create a new folder called <code>cypress</code> which will look something like this.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">.
</span></span><span class="line"><span class="cl">├── e2e
</span></span><span class="line"><span class="cl">│   └── accessibility.test.ts
</span></span><span class="line"><span class="cl">├── fixtures
</span></span><span class="line"><span class="cl">│   └── graphql.json
</span></span><span class="line"><span class="cl">├── plugins
</span></span><span class="line"><span class="cl">│   └── index.js
</span></span><span class="line"><span class="cl">├── support
</span></span><span class="line"><span class="cl">│   ├── commands.js
</span></span><span class="line"><span class="cl">│   ├── index.d.ts
</span></span><span class="line"><span class="cl">│   └── index.js
</span></span><span class="line"><span class="cl">└── tsconfig.json
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir -p cypress/support
</span></span></code></pre></div><p>Create a file at <code>cypress/support/commands.js</code></p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">Cypress</span><span class="p">.</span><span class="nx">Commands</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="sb">`assertRoute`</span><span class="p">,</span> <span class="p">(</span><span class="nx">route</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">cy</span><span class="p">.</span><span class="nx">url</span><span class="p">().</span><span class="nx">should</span><span class="p">(</span><span class="sb">`equal`</span><span class="p">,</span> <span class="sb">`</span><span class="si">${</span><span class="nb">window</span><span class="p">.</span><span class="nx">location</span><span class="p">.</span><span class="nx">origin</span><span class="si">}${</span><span class="nx">route</span><span class="si">}</span><span class="sb">`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>Add some custom types for Cypress <code>index.d.ts</code> if you are using Typescript.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="c1">/// &lt;reference types=&#34;cypress&#34; /&gt;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">declare</span> <span class="kr">namespace</span> <span class="nx">Cypress</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">interface</span> <span class="nx">Chainable</span><span class="p">&lt;</span><span class="nt">Subject</span><span class="p">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm">     * Assert the current URL
</span></span></span><span class="line"><span class="cl"><span class="cm">     * @param route
</span></span></span><span class="line"><span class="cl"><span class="cm">     * @example cy.assertRoute(&#39;/page-2&#39;)
</span></span></span><span class="line"><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="cl">    <span class="nx">assertRoute</span><span class="p">(</span><span class="nx">route</span>: <span class="kt">string</span><span class="p">)</span><span class="o">:</span> <span class="nx">Chainable</span><span class="p">&lt;</span><span class="nt">any</span><span class="p">&gt;;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm">     * Waits for Gatsby to finish the route change, in order to ensure event handlers are properly setup
</span></span></span><span class="line"><span class="cl"><span class="cm">     */</span>
</span></span><span class="line"><span class="cl">    <span class="nx">waitForRouteChange</span><span class="p">()</span><span class="o">:</span> <span class="nx">Chainable</span><span class="p">&lt;</span><span class="nt">any</span><span class="p">&gt;;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Next create the <code>index.js</code> file, which should look something like this.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;cypress-axe&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;./commands&#34;</span>
</span></span></code></pre></div><p>Next, let&rsquo;s create a plugin folder <code>mkdir -p cypress/plugins</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="c1">// / &lt;reference types=&#34;cypress&#34; /&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">// ***********************************************************
</span></span></span><span class="line"><span class="cl"><span class="c1">// This example plugins/index.js can be used to load plugins
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// You can change the location of this file or turn off loading
</span></span></span><span class="line"><span class="cl"><span class="c1">// the plugins file with the &#39;pluginsFile&#39; configuration option.
</span></span></span><span class="line"><span class="cl"><span class="c1">//
</span></span></span><span class="line"><span class="cl"><span class="c1">// You can read more here:
</span></span></span><span class="line"><span class="cl"><span class="c1">// https://on.cypress.io/plugins-guide
</span></span></span><span class="line"><span class="cl"><span class="c1">// ***********************************************************
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// This function is called when a project is opened or re-opened (e.g. due to
</span></span></span><span class="line"><span class="cl"><span class="c1">// the project&#39;s config changing)
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="cm">/**
</span></span></span><span class="line"><span class="cl"><span class="cm"> * @type {Cypress.PluginConfig}
</span></span></span><span class="line"><span class="cl"><span class="cm"> */</span>
</span></span><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">(</span><span class="nx">on</span><span class="p">,</span> <span class="nx">config</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// `on` is used to hook into various events Cypress emits
</span></span></span><span class="line"><span class="cl">  <span class="c1">// `config` is the resolved Cypress config
</span></span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">config</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Now finally let&rsquo;s create our tests folder <code>mkdir -p cypress/e2e</code>.</p>
<h3 id="cypress-axe">cypress-axe</h3>
<p>In this blog post we won&rsquo;t go over any complicated Cypress test we will simply use <code>cypress-axe</code> to test
the accessibility of our (a11y) of our website.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="c1">/// &lt;reference types=&#34;../support/index&#34; /&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">/// &lt;reference types=&#34;cypress&#34; /&gt;
</span></span></span><span class="line"><span class="cl"><span class="c1">/// &lt;reference types=&#34;@types/cypress-axe&#34; /&gt;
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">describe</span><span class="p">(</span><span class="s2">&#34;Component accessibility test&#34;</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">it</span><span class="p">(</span><span class="s2">&#34;Main Page&#34;</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">cy</span><span class="p">.</span><span class="nx">visit</span><span class="p">(</span><span class="s2">&#34;/&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">cy</span><span class="p">.</span><span class="nx">wait</span><span class="p">(</span><span class="mi">500</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">cy</span><span class="p">.</span><span class="nx">injectAxe</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="nx">cy</span><span class="p">.</span><span class="nx">checkA11y</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">      <span class="nx">include</span><span class="o">:</span> <span class="p">[[</span><span class="s2">&#34;#___gatsby&#34;</span><span class="p">]],</span>
</span></span><span class="line"><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="cl">  <span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="p">})</span>
</span></span></code></pre></div><p>Note the <code>///</code> comments at the top used to add types for cypress. The test above will go to our
home page and test if it has any a11y violations and if so will fail the test.</p>
<p>We can now run our tests locally by running this command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn run test:e2e
</span></span></code></pre></div><h2 id="gitlab-ci-1">Gitlab CI</h2>
<p>Now how can we automate this, so the tests will run say every time we make changes on the master branch to make
sure we haven&rsquo;t broken any a11y. Create a new <code>.gitlab-ci.yml</code> or add the following job to an existing CI file.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">node:12.14.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">variables</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">CYPRESS_CACHE_FOLDER</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;$CI_PROJECT_DIR/cache/Cypress&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">cache</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">${CI_COMMIT_REF_SLUG}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">paths</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">cache/Cypress</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">node_modules</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">stages</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">test</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">before_script</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">yarn install</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">tests</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">cypress/browsers:node12.14.1-chrome83-ff77</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">stage</span><span class="p">:</span><span class="w"> </span><span class="l">test</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">script</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">yarn test:e2e:ci</span><span class="w">
</span></span></span></code></pre></div><p>I won&rsquo;t go into the details of what makes up a Gitlab CI file. At the top of the file, we will cache the <code>node_modules</code>
file so we can share it between the job and the Cypress cache.
The job itself is very simple it uses a <code>cypress/browsers:node12.14.1-chrome83-ff77</code> Docker
image which provides a headless chrome browser that Cypress can leverage to run the tests.
As we won&rsquo;t have access to a GUI in the Gitlab CI runner. The <code>tests</code> job is very simple it runs <code>yarn test:e2e:ci</code> to
run our Cypress tests.</p>
<p>That&rsquo;s it, quite simple to add Cypress tests that run in our CI pipeline.</p>
<h2 id="appendix">Appendix</h2>
<ul>
<li><a href="https://gitlab.com/hmajid2301/blog/-/tree/main/content/posts/2021-03-22-testing-a-gatsby-application-with-cypress-on-gitlab-ci/source_code">Example source code</a></li>
<li><a href="http://cypress.io/">Cypress</a></li>
<li><a href="https://gitlab.com/hmajid2301/portfolio-site/-/jobs/1080367107">Example Job</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>How to add a ToC in Gatsby</title>
      <link>https://haseebmajid.dev/posts/2020-11-11-how-to-add-a-toc-in-gatsby/</link>
      <pubDate>Wed, 11 Nov 2020 00:00:00 +0000</pubDate>
      <author>Me</author>
      <guid>https://haseebmajid.dev/posts/2020-11-11-how-to-add-a-toc-in-gatsby/</guid>
      <description>&lt;p&gt;A lot of people, I included, are using Gatsby to build their own blogs. One of the things I wanted to add to my blog
was a table of contents (ToC) 📝. A ToC will show you all the headings of an article and when you click on a heading it&amp;rsquo;ll
take you directly to that heading.
It&amp;rsquo;s a nice little feature to have on your blog, which makes it easier for users to navigate and find the information
they are looking for.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>A lot of people, I included, are using Gatsby to build their own blogs. One of the things I wanted to add to my blog
was a table of contents (ToC) 📝. A ToC will show you all the headings of an article and when you click on a heading it&rsquo;ll
take you directly to that heading.
It&rsquo;s a nice little feature to have on your blog, which makes it easier for users to navigate and find the information
they are looking for.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden">
  <iframe src="https://yewtu.be/embed/YrUeiD4YO5E"
    style="position:absolute;top:0;left:0;width:100%;height:100%;border:0" allowfullscreen></iframe>
</div>

<h2 id="prerequisite">Prerequisite</h2>
<p>So before we get started you can find the <a href="https://gitlab.com/hmajid2301/">source code here</a>.
In this article, I will be using the
<a href="https://www.gatsbyjs.com/starters/gatsbyjs/gatsby-starter-blog/">gatsby-starter-blog</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># If you don&#39;t have the CLI installed, run this command.</span>
</span></span><span class="line"><span class="cl">npm -g install gatsby-cli
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">gatsby new my-gatsby-project https://github.com/gatsbyjs/gatsby-starter-blog
</span></span></code></pre></div><p>If you already have an existing Gatsby site, you can make the changes directly there instead of
using this starter.</p>
<h2 id="plugins">Plugins</h2>
<p>We need to get some extra plugins installed for the ToC to work properly.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add gatsby-remark-autolink-headers gatsby-plugin-emotion
</span></span></code></pre></div><p>The plugin <code>gatsby-remark-autolink-headers</code> turns all of the headers into anchor links. This means we can link to the
headers.</p>
<details
  class="notice info"
  open="true"
>
    <summary class="notice-title">Emotion</summary>
  
  You only need to add the emotion plugin if you want to use emotionjs, which is a css-in-js solution.
You will see this later when we look at the <code>toc.js</code> component.
</details>

<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line hl"><span class="cl"><span class="sb">`gatsby-plugin-emotion`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="sb">`gatsby-plugin-smoothscroll`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">resolve</span><span class="o">:</span> <span class="sb">`gatsby-transformer-remark`</span><span class="p">,</span>
</span></span><span class="line hl"><span class="cl">    <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// ...
</span></span></span><span class="line"><span class="cl">            <span class="sb">`gatsby-remark-autolink-headers`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="c1">// ...
</span></span></span></code></pre></div><p>Our header elements with the <code>autolinks</code> plugin will now look something like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;header-1&#34;</span> <span class="na">style</span><span class="o">=</span><span class="s">&#34;position:relative;&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#header-1&#34;</span> <span class="na">aria-label</span><span class="o">=</span><span class="s">&#34;header 1 permalink&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;anchor before&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&gt;</span>...<span class="p">&lt;/</span><span class="nt">a</span>
</span></span><span class="line"><span class="cl">  <span class="p">&gt;</span>Header 1
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
</span></span></code></pre></div><h2 id="toc">ToC</h2>
<p>First, let&rsquo;s design the ToC element. This component is just a presentation component, it doesn&rsquo;t contain any state logic.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">styled</span> <span class="nx">from</span> <span class="s2">&#34;@emotion/styled&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">React</span> <span class="nx">from</span> <span class="s2">&#34;react&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">tw</span> <span class="nx">from</span> <span class="s2">&#34;twin.macro&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">ToC</span> <span class="o">=</span> <span class="p">({</span> <span class="nx">headings</span> <span class="p">})</span> <span class="p">=&gt;</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">Toc</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">Title</span><span class="p">&gt;</span><span class="nx">Table</span> <span class="k">of</span> <span class="nx">contents</span><span class="p">&lt;/</span><span class="nt">Title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">InnerScroll</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="nx">headings</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">heading</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">heading</span><span class="p">.</span><span class="nx">depth</span> <span class="o">&gt;</span> <span class="mi">4</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">return</span> <span class="p">&lt;</span><span class="nt">div</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">ToCElement</span> <span class="na">key</span><span class="o">=</span><span class="p">{</span><span class="nx">heading</span><span class="p">.</span><span class="nx">value</span><span class="p">}&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">ToCLink</span>
</span></span><span class="line"><span class="cl">              <span class="na">href</span><span class="o">=</span><span class="p">{</span><span class="sb">`#</span><span class="si">${</span><span class="nx">heading</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/\s+/g</span><span class="p">,</span> <span class="s2">&#34;-&#34;</span><span class="p">).</span><span class="nx">toLowerCase</span><span class="p">()</span><span class="si">}</span><span class="sb">`</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">{</span><span class="nx">heading</span><span class="p">.</span><span class="nx">value</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">ToCLink</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">ToCElement</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">})}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">InnerScroll</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">Toc</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">Toc</span> <span class="o">=</span> <span class="nx">styled</span><span class="p">.</span><span class="nx">ul</span><span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">  </span><span class="si">${</span><span class="nx">tw</span><span class="sb">`bg-white fixed hidden lg:flex flex-col rounded p-3 my-3`</span><span class="si">}</span><span class="sb">;
</span></span></span><span class="line"><span class="cl"><span class="sb">  width: 20rem;
</span></span></span><span class="line"><span class="cl"><span class="sb">  left: calc(50% + 400px);
</span></span></span><span class="line"><span class="cl"><span class="sb">  top: 80px;
</span></span></span><span class="line"><span class="cl"><span class="sb">  max-height: 30vh;
</span></span></span><span class="line"><span class="cl"><span class="sb">`</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">Title</span> <span class="o">=</span> <span class="nx">tw</span><span class="p">.</span><span class="nx">h2</span><span class="sb">`text-2xl mb-2`</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">ToCElement</span> <span class="o">=</span> <span class="nx">tw</span><span class="p">.</span><span class="nx">li</span><span class="sb">`p-1 leading-5 ml-4 mb-4 mr-4 leading-3 list-none`</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">ToCLink</span> <span class="o">=</span> <span class="nx">tw</span><span class="p">.</span><span class="nx">a</span><span class="sb">`hover:text-black transition duration-300 no-underline`</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">InnerScroll</span> <span class="o">=</span> <span class="nx">styled</span><span class="p">.</span><span class="nx">div</span><span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">  scrollbar-width: thin;
</span></span></span><span class="line"><span class="cl"><span class="sb">  scrollbar-color: #367ee9 rgba(48, 113, 209, 0.3);
</span></span></span><span class="line"><span class="cl"><span class="sb">  overflow: hidden auto;
</span></span></span><span class="line"><span class="cl"><span class="sb">`</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">ToC</span>
</span></span></code></pre></div><p>Let&rsquo;s break this component down. It receives a <code>heading</code> props, which it expects to be a list of the <code>headings</code>
from the markdown documents. A heading is an element starting with <code>#</code>, the more <code>#</code>s the lower the heading, for example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl"><span class="gh"># Heading 1
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">## Heading 2
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">### Heading 3
</span></span></span></code></pre></div><p>We use the <code>heading.map</code> which will create an element for each of the <code>headings</code> in the list. If it&rsquo;s a &ldquo;heading 5&rdquo; or lower,
we will simply return an empty div, <code>heading.depth &gt; 4</code>. This is so that the ToC doesn&rsquo;t become too &ldquo;big&rdquo; and
which would make it harder to use/navigate.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">ToC</span> <span class="o">=</span> <span class="p">({</span> <span class="nx">headings</span> <span class="p">})</span> <span class="p">=&gt;</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="o">&lt;</span><span class="nx">Toc</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="nx">Title</span><span class="o">&gt;</span><span class="nx">Table</span> <span class="k">of</span> <span class="nx">contents</span><span class="o">&lt;</span><span class="err">/Title&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="nx">InnerScroll</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="nx">headings</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">heading</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">heading</span><span class="p">.</span><span class="nx">depth</span> <span class="o">&gt;</span> <span class="mi">4</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">return</span> <span class="o">&lt;</span><span class="nx">div</span> <span class="o">/&gt;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">          <span class="o">&lt;</span><span class="nx">ToCElement</span> <span class="nx">key</span><span class="o">=</span><span class="p">{</span><span class="nx">heading</span><span class="p">.</span><span class="nx">value</span><span class="p">}</span><span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="o">&lt;</span><span class="nx">ToCLink</span>
</span></span><span class="line"><span class="cl">              <span class="nx">href</span><span class="o">=</span><span class="p">{</span><span class="sb">`#</span><span class="si">${</span><span class="nx">heading</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/\s+/g</span><span class="p">,</span> <span class="s2">&#34;-&#34;</span><span class="p">).</span><span class="nx">toLowerCase</span><span class="p">()</span><span class="si">}</span><span class="sb">`</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="o">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">{</span><span class="nx">heading</span><span class="p">.</span><span class="nx">value</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="o">&lt;</span><span class="err">/ToCLink&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="o">&lt;</span><span class="err">/ToCElement&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="p">})}</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;</span><span class="err">/InnerScroll&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="o">&lt;</span><span class="err">/Toc&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span></code></pre></div><p>If it&rsquo;s a heading 1-4, we create a list element (<code>&lt;li&gt;</code>) with a link (<code>&lt;a&gt;</code>) inside of it. This will be a single
heading within our ToC. Below is an example ToC:</p>
<p><img
        loading="lazy"
        src="/posts/2020-11-11-how-to-add-a-toc-in-gatsby/images/example_toc.png"
        type=""
        alt="Example ToC"
        
      /></p>
<p>The heading data for the ToC above will look something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">headings</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">value</span><span class="o">:</span> <span class="s2">&#34;Header 1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">depth</span><span class="o">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">value</span><span class="o">:</span> <span class="s2">&#34;Header 2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">depth</span><span class="o">:</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">];</span>
</span></span></code></pre></div><p>As discussed earlier we are using the <code>autolink</code> headers plugin. This plugin auto-generates anchor links for all of our header. We will use the <code>href</code> attribute to link to these headers in our ToC.</p>
<details
  class="notice info"
  open="true"
>
    <summary class="notice-title">href</summary>
  
  <p>The <code>href</code> link we replace all the whitespace with <code>-</code> so <code>&quot;Heading 1&quot;</code> becomes the anchor link <code>#heading-1</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">ToCLink</span> <span class="na">href</span><span class="o">=</span><span class="p">{</span><span class="sb">`#</span><span class="si">${</span><span class="nx">heading</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/\s+/g</span><span class="p">,</span> <span class="s2">&#34;-&#34;</span><span class="p">).</span><span class="nx">toLowerCase</span><span class="p">()</span><span class="si">}</span><span class="sb">`</span><span class="p">}&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span><span class="nx">heading</span><span class="p">.</span><span class="nx">value</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">ToCLink</span><span class="p">&gt;</span>
</span></span></code></pre></div>
</details>

<h3 id="twin-macro--emotionjs">Twin Macro &amp; EmotionJS</h3>
<p>Now in the above <code>ToC</code> component, you see elements like <code>&lt;ToCElement&gt;</code>, <code>&lt;InnerScroll&gt;</code> and <code>&lt;ToC&gt;</code>.
Where are these components coming from? Well, this is why I said we need to use the gatsby emotion plugin.
This is the css-in-js components, these components above are twin.macro or emotionjs components.
To use it within our code run the following commands:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add twin.macro @emotion/core @emotion/styled
</span></span><span class="line"><span class="cl">npx tailwind init
</span></span><span class="line"><span class="cl">vim package.json
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;babelMacros&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;twin&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;config&#34;</span><span class="p">:</span> <span class="s2">&#34;tailwind.config.js&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;preset&#34;</span><span class="p">:</span> <span class="s2">&#34;emotion&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;dataTwProp&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;debugPlugins&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;debug&#34;</span><span class="p">:</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><details
  class="notice info"
  open="true"
>
    <summary class="notice-title">TailwindCSS with Gatsby</summary>
  
  Gatsby have a good tutorial <a href="https://www.gatsbyjs.com/docs/tailwind-css/">here</a>, on how to integrate
TailwindCSS with a Gatsby site.
</details>

<p>The <code>twin.macro</code> library allows us to use <a href="https://tailwindcss.com/"><code>TailwindCSS</code></a>. Tailwind provides us with
many pre-generated classes that we can then leverage within our code. Here I am assuming you are somewhat familiar with how it works.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">Toc</span> <span class="o">=</span> <span class="nx">styled</span><span class="p">.</span><span class="nx">ul</span><span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">  </span><span class="si">${</span><span class="nx">tw</span><span class="sb">`bg-white fixed hidden lg:flex flex-col rounded p-3 my-3`</span><span class="si">}</span><span class="sb">;
</span></span></span><span class="line"><span class="cl"><span class="sb">  width: 20rem;
</span></span></span><span class="line"><span class="cl"><span class="sb">  left: calc(50% + 400px);
</span></span></span><span class="line"><span class="cl"><span class="sb">  top: 80px;
</span></span></span><span class="line"><span class="cl"><span class="sb">  max-height: 30vh;
</span></span></span><span class="line"><span class="cl"><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">Title</span> <span class="o">=</span> <span class="nx">tw</span><span class="p">.</span><span class="nx">h2</span><span class="sb">`text-2xl mb-2`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">ToCElement</span> <span class="o">=</span> <span class="nx">tw</span><span class="p">.</span><span class="nx">li</span><span class="sb">`p-1 leading-5 ml-4 mb-4 mr-4 leading-3 list-none`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">ToCLink</span> <span class="o">=</span> <span class="nx">tw</span><span class="p">.</span><span class="nx">a</span><span class="sb">`hover:text-black transition duration-300 no-underline`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">InnerScroll</span> <span class="o">=</span> <span class="nx">styled</span><span class="p">.</span><span class="nx">div</span><span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">  scrollbar-width: thin;
</span></span></span><span class="line"><span class="cl"><span class="sb">  scrollbar-color: #367ee9 rgba(48, 113, 209, 0.3);
</span></span></span><span class="line"><span class="cl"><span class="sb">  overflow: hidden auto;
</span></span></span><span class="line"><span class="cl"><span class="sb">`</span><span class="p">;</span>
</span></span></code></pre></div><p>This is how we can style the scrollbar. The first colour is the colour of the scrollbar and the second colour is the
the background colour of the scrollbar.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">InnerScroll</span> <span class="o">=</span> <span class="nx">styled</span><span class="p">.</span><span class="nx">div</span><span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">  scrollbar-width: thin;
</span></span></span><span class="line"><span class="cl"><span class="sb">  scrollbar-color: #367ee9 rgba(48, 113, 209, 0.3);
</span></span></span><span class="line"><span class="cl"><span class="sb">  overflow: hidden auto;
</span></span></span><span class="line"><span class="cl"><span class="sb">`</span><span class="p">;</span>
</span></span></code></pre></div><details
  class="notice info"
  open="true"
>
    <summary class="notice-title">div</summary>
  
  <p>Gatsby have a good tutorial <a href="https://www.gatsbyjs.com/docs/tailwind-css/">here</a>, on how to integrate
The <code>styled.div</code> this means <code>InnerScroll</code> when translated to HTML code will be <code>&lt;div&gt;</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;css-91zyin-InnerScroll eqpue8b4&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">li</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;css-12965kf-ToCElement eqpue8b2&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;#header-1&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;css-14n9u33-ToCLink eqpue8b3&#34;</span><span class="p">&gt;</span>Header 1<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span></code></pre></div>
</details>

<p>Another interesting component to look at is the <code>ToC</code>. This combined twin.macro and emotionjs
so <code>width</code> CSS is using emotionjs and we are using twin.macro with <code>{tw</code>&hellip;<code>}</code>. Where we fill in
the <code>tw</code> with the tailwind styles we want to apply. In the example below, <code>fixed</code> will make
the position of the element fixed.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">Toc</span> <span class="o">=</span> <span class="nx">styled</span><span class="p">.</span><span class="nx">ul</span><span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">  </span><span class="si">${</span><span class="nx">tw</span><span class="sb">`bg-white fixed hidden lg:flex flex-col rounded p-3 my-3`</span><span class="si">}</span><span class="sb">;
</span></span></span><span class="line"><span class="cl"><span class="sb">  width: 20rem;
</span></span></span><span class="line"><span class="cl"><span class="sb">  left: calc(50% + 400px);
</span></span></span><span class="line"><span class="cl"><span class="sb">  top: 80px;
</span></span></span><span class="line"><span class="cl"><span class="sb">  max-height: 30vh;
</span></span></span><span class="line"><span class="cl"><span class="sb">`</span><span class="p">;</span>
</span></span></code></pre></div><h2 id="global-style">Global Style</h2>
<p>One small change we need to make to allow our scrolling to be smoother is in our global styles,
whether that should be a CSS file or a css-in-js etc. In this example, it&rsquo;ll be the <code>style.css</code> file that comes with
the start. We need to add the following properties:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="nt">html</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">scroll-behavior</span><span class="p">:</span> <span class="kc">smooth</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="err">//</span> <span class="err">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This CSS property will stop the scrolling from feeling jerky and instead will be far smoother. So instead of jumping to the header, we click on a header in the <code>ToC</code> and it&rsquo;ll scroll smoothly to that header.</p>
<h2 id="blog-template">Blog Template</h2>
<p>Finally, we need to add the ToC element to our blog template.</p>
<h3 id="gatsby-node">Gatsby Node</h3>
<p>Remember that with Gatsby in the <code>gatsby-node.js</code> file, we can create a new page for each markdown file found. Here
is the logic that creates a page for each markdown file found by the <code>markdown-remark</code> plugin. As you can see, we use
<code>blog-post.js</code> file as the template for each of our blog posts.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">exports</span><span class="p">.</span><span class="nx">createPages</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">({</span> <span class="nx">graphql</span><span class="p">,</span> <span class="nx">actions</span><span class="p">,</span> <span class="nx">reporter</span> <span class="p">})</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">{</span> <span class="nx">createPage</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">actions</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Define a template for blog post
</span></span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">blogPost</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="sb">`./src/templates/blog-post.js`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Get all markdown blog posts sorted by date
</span></span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">graphql</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">      {
</span></span></span><span class="line"><span class="cl"><span class="sb">        allMarkdownRemark(
</span></span></span><span class="line"><span class="cl"><span class="sb">          sort: { fields: [frontmatter___date], order: ASC }
</span></span></span><span class="line"><span class="cl"><span class="sb">          limit: 1000
</span></span></span><span class="line"><span class="cl"><span class="sb">        ) {
</span></span></span><span class="line"><span class="cl"><span class="sb">          nodes {
</span></span></span><span class="line"><span class="cl"><span class="sb">            id
</span></span></span><span class="line"><span class="cl"><span class="sb">            fields {
</span></span></span><span class="line"><span class="cl"><span class="sb">              slug
</span></span></span><span class="line"><span class="cl"><span class="sb">            }
</span></span></span><span class="line"><span class="cl"><span class="sb">          }
</span></span></span><span class="line"><span class="cl"><span class="sb">        }
</span></span></span><span class="line"><span class="cl"><span class="sb">      }
</span></span></span><span class="line"><span class="cl"><span class="sb">    `</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">posts</span> <span class="o">=</span> <span class="nx">result</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">allMarkdownRemark</span><span class="p">.</span><span class="nx">nodes</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Create blog posts pages
</span></span></span><span class="line"><span class="cl">  <span class="c1">// But only if there&#39;s at least one markdown file found at &#34;content/blog&#34; (defined in gatsby-config.js)
</span></span></span><span class="line"><span class="cl">  <span class="c1">// `context` is available in the template as a prop and as a variable in GraphQL
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">posts</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">posts</span><span class="p">.</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">post</span><span class="p">,</span> <span class="nx">index</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">createPage</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">        <span class="nx">path</span><span class="o">:</span> <span class="nx">post</span><span class="p">.</span><span class="nx">fields</span><span class="p">.</span><span class="nx">slug</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">component</span><span class="o">:</span> <span class="nx">blogPost</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h3 id="gatsby-config">Gatsby Config</h3>
<p>To allow the remark plugin to &ldquo;see&rdquo; the markdown files, we need to source them. There are a few ways to do this,
I normally use <a href="/blog/gatsby-articles-git-gitlab-ci">git to source my plugins</a>. In this case, we will add all
the markdown files in the <code>content/blog</code> folder.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">resolve</span><span class="o">:</span> <span class="sb">`gatsby-source-filesystem`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">path</span><span class="o">:</span> <span class="sb">`</span><span class="si">${</span><span class="nx">__dirname</span><span class="si">}</span><span class="sb">/content/blog`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">name</span><span class="o">:</span> <span class="sb">`blog`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="blog-post">Blog Post</h3>
<p>So now back to our <code>blog-post.js</code>, let us add our <code>ToC</code> component to the blog post template.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">ToC</span> <span class="nx">from</span> <span class="s2">&#34;../components/toc&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// ...
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">post</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">markdownRemark</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">Layout</span> <span class="na">location</span><span class="o">=</span><span class="p">{</span><span class="nx">location</span><span class="p">}</span> <span class="na">title</span><span class="o">=</span><span class="p">{</span><span class="nx">siteTitle</span><span class="p">}&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">article</span> <span class="na">className</span><span class="o">=</span><span class="s">&#34;blog-post&#34;</span> <span class="na">itemScope</span> <span class="na">itemType</span><span class="o">=</span><span class="s">&#34;http://schema.org/Article&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">ToC</span> <span class="na">headings</span><span class="o">=</span><span class="p">{</span><span class="nx">post</span><span class="p">.</span><span class="nx">headings</span><span class="p">}</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">article</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">Layout</span><span class="p">&gt;;</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></div><p>Let&rsquo;s also adjust the GraphQL query so we can get the heading data that the ToC component requires.
Add the <code>headings</code> field to get the value and the depth fields.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-graphql" data-lang="graphql"><span class="line"><span class="cl"><span class="py">markdownRemark</span><span class="p">(</span><span class="py">id</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="py">eq</span><span class="p">:</span><span class="w"> </span><span class="nv">$id</span><span class="w"> </span><span class="p">})</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="py">id</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="py">excerpt</span><span class="p">(</span><span class="py">pruneLength</span><span class="p">:</span><span class="w"> </span><span class="nc">160</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="py">html</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="py">headings</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="py">value</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="py">depth</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="py">frontmatter</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="py">title</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="py">date</span><span class="p">(</span><span class="py">formatString</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;MMMM DD, YYYY&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="py">description</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>That&rsquo;s that we added a ToC to our Gatsby Site. We can a bunch of other things to improve it. Such as
styling it to make it look better. I also hide my ToC when the width decreases. So you only see a ToC when
browsing the site on a Laptop/Desktop.</p>
<h2 id="appendix">Appendix</h2>
<ul>
<li><a href="https://gitlab.com/hmajid2301/blog/-/tree/main/content/posts/2020-11-11-how-to-add-a-toc-in-gatsby/source_code">Example source code</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>How to source your Gatsby posts from another repo</title>
      <link>https://haseebmajid.dev/posts/2020-09-18-how-to-source-your-gatsby-posts-from-another-repo/</link>
      <pubDate>Fri, 18 Sep 2020 00:00:00 +0000</pubDate>
      <author>Me</author>
      <guid>https://haseebmajid.dev/posts/2020-09-18-how-to-source-your-gatsby-posts-from-another-repo/</guid>
      <description>&lt;p&gt;In this article, we will go over how you can manage your markdown blog posts from another git repository (repo). Separate to the git repository for your Gatsby site.
This is the same process that I use to manage &lt;a href=&#34;https://gitlab.com/hmajid2301/articles&#34;&gt;this repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So what this entails is the source code for my Gatsby site is in a repo called &lt;code&gt;portfolio-site&lt;/code&gt; on Gitlab.
Then I have another repo for all of my blog posts (in markdown) called &lt;code&gt;articles&lt;/code&gt;. During build time of the
Gatsby blog, we will import the markdown files from our &lt;code&gt;articles&lt;/code&gt; git repo and use it as a source of data for
our Gatsby blog.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>In this article, we will go over how you can manage your markdown blog posts from another git repository (repo). Separate to the git repository for your Gatsby site.
This is the same process that I use to manage <a href="https://gitlab.com/hmajid2301/articles">this repo</a>.</p>
<p>So what this entails is the source code for my Gatsby site is in a repo called <code>portfolio-site</code> on Gitlab.
Then I have another repo for all of my blog posts (in markdown) called <code>articles</code>. During build time of the
Gatsby blog, we will import the markdown files from our <code>articles</code> git repo and use it as a source of data for
our Gatsby blog.</p>
<h2 id="git-plugin">Git Plugin</h2>
<p>First, install the <a href="https://www.gatsbyjs.com/plugins/gatsby-source-git/?=git">Gatsby git plugin</a>, so that we can source our data from git.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add gatsby-source-git
</span></span></code></pre></div><p>Then add the plugin to your <code>gatsby-config.js</code> to tell it where to source its data from.</p>
<details
  class="notice warning"
  open="true"
>
    <summary class="notice-title">Gatsby Filesystem</summary>
  
  You need to use the <code>gatsby-source-filesystem</code> before the <code>gatsby-source-git</code>.
You can read more about it <a href="https://github.com/stevetweeddale/gatsby-source-git/issues/22">here at this Github issue</a>.
</details>

<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">resolve</span><span class="o">:</span> <span class="sb">`gatsby-source-git`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">name</span><span class="o">:</span> <span class="sb">`Articles`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">remote</span><span class="o">:</span> <span class="s2">&#34;https://gitlab.com/hmajid2301/articles.git&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">branch</span><span class="o">:</span> <span class="sb">`master`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">patterns</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;**/*&#34;</span><span class="p">,</span> <span class="s2">&#34;!**/*/index.md&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span>
</span></span></code></pre></div><p>In our example, I will use <a href="https://gitlab.com/hmajid2301/articles">this</a>, the same repo this blog post originates from.
You can specify the branch to use if you want. The most interesting bit is the <code>patterns</code> section. This is where you can
specify which files to include and which to ignore <code>[&quot;**/*&quot;, &quot;!**/*/index.md&quot;]</code>. In this example, I want to ignore
all files called <code>index.md</code> because these are the ones that I use in my example Gatsby blogs in this repo for.
You can read more about the <a href="https://github.com/mrmlnc/fast-glob">pattern here</a>.</p>
<h2 id="graphql">GraphQL</h2>
<p>We can now check if the articles are being imported correctly by using the GraphQL IDE that comes with Gatsby.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn develop
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Go to localhost:8000/__graphql</span>
</span></span></code></pre></div><p>Then run the following query.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-graphql" data-lang="graphql"><span class="line"><span class="cl"><span class="kd">query</span><span class="w"> </span><span class="nc">MyQuery</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="py">allMarkdownRemark</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="py">edges</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="py">node</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="py">fileAbsolutePath</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>You should see output like this, where it will list all of your blog posts/markdown files. Here you can verify your git
repo is being sourced correctly.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;allMarkdownRemark&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;edges&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&#34;node&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;fileAbsolutePath&#34;</span><span class="p">:</span> <span class="s2">&#34;/home/haseeb/projects/personal/articles/35. Gatsby source git/source_code/.cache/gatsby-source-git/Articles/1. Expo with VirtualBox and Genymotion/README.md&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&#34;node&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;fileAbsolutePath&#34;</span><span class="p">:</span> <span class="s2">&#34;/home/haseeb/projects/personal/articles/35. Gatsby source git/source_code/.cache/gatsby-source-git/Articles/11. React Navigation with React Native/README.md&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&#34;node&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;fileAbsolutePath&#34;</span><span class="p">:</span> <span class="s2">&#34;/home/haseeb/projects/personal/articles/35. Gatsby source git/source_code/.cache/gatsby-source-git/Articles/13. REST API using OpenAPI, Flask &amp; Connexions/README.md&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// ...
</span></span></span><span class="line"><span class="cl">      <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="gatsby-node">Gatsby Node</h2>
<p>Now make sure you have logic in your <code>gatsby-node.js</code> file to create a blog post page for every
markdown file that we source, i.e. one blog post for every item in the list above.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">exports</span><span class="p">.</span><span class="nx">createPages</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">({</span> <span class="nx">graphql</span><span class="p">,</span> <span class="nx">actions</span> <span class="p">})</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">{</span> <span class="nx">createPage</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">actions</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">blogPost</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="sb">`./src/templates/blog-post.js`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">graphql</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">      {
</span></span></span><span class="line"><span class="cl"><span class="sb">        allMarkdownRemark(
</span></span></span><span class="line"><span class="cl"><span class="sb">          sort: { fields: [frontmatter___date], order: DESC }
</span></span></span><span class="line"><span class="cl"><span class="sb">          limit: 1000
</span></span></span><span class="line"><span class="cl"><span class="sb">        ) {
</span></span></span><span class="line"><span class="cl"><span class="sb">          edges {
</span></span></span><span class="line"><span class="cl"><span class="sb">            node {
</span></span></span><span class="line"><span class="cl"><span class="sb">              frontmatter {
</span></span></span><span class="line"><span class="cl"><span class="sb">                title
</span></span></span><span class="line"><span class="cl"><span class="sb">                slug
</span></span></span><span class="line"><span class="cl"><span class="sb">              }
</span></span></span><span class="line"><span class="cl"><span class="sb">            }
</span></span></span><span class="line"><span class="cl"><span class="sb">          }
</span></span></span><span class="line"><span class="cl"><span class="sb">        }
</span></span></span><span class="line"><span class="cl"><span class="sb">      }
</span></span></span><span class="line"><span class="cl"><span class="sb">    `</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">errors</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">throw</span> <span class="nx">result</span><span class="p">.</span><span class="nx">errors</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Create blog posts pages.
</span></span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">posts</span> <span class="o">=</span> <span class="nx">result</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">allMarkdownRemark</span><span class="p">.</span><span class="nx">edges</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">posts</span><span class="p">.</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">post</span><span class="p">,</span> <span class="nx">index</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">previous</span> <span class="o">=</span> <span class="nx">index</span> <span class="o">===</span> <span class="nx">posts</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">?</span> <span class="kc">null</span> <span class="o">:</span> <span class="nx">posts</span><span class="p">[</span><span class="nx">index</span> <span class="o">+</span> <span class="mi">1</span><span class="p">].</span><span class="nx">node</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">next</span> <span class="o">=</span> <span class="nx">index</span> <span class="o">===</span> <span class="mi">0</span> <span class="o">?</span> <span class="kc">null</span> <span class="o">:</span> <span class="nx">posts</span><span class="p">[</span><span class="nx">index</span> <span class="o">-</span> <span class="mi">1</span><span class="p">].</span><span class="nx">node</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">createPage</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">      <span class="nx">path</span><span class="o">:</span> <span class="nx">post</span><span class="p">.</span><span class="nx">node</span><span class="p">.</span><span class="nx">frontmatter</span><span class="p">.</span><span class="nx">slug</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">component</span><span class="o">:</span> <span class="nx">blogPost</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">context</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">slug</span><span class="o">:</span> <span class="nx">post</span><span class="p">.</span><span class="nx">node</span><span class="p">.</span><span class="nx">frontmatter</span><span class="p">.</span><span class="nx">slug</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">previous</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">next</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h2 id="gitlab-ci">Gitlab CI</h2>
<p>So every time we make a change in our article repo we want to trigger a rebuild of our site. Since I use Gitlab, I will
show you how you can do this with Gitlab CI. Every commit on the master branch, on our repo that contains our
articles, will trigger a rebuild on the Gatsby repo.</p>
<details
  class="notice warning"
  open="true"
>
    <summary class="notice-title">Assumption</summary>
  
  This next section assumes that you use Gitlab to host your repos.
It also assumes that for your Gatsby blog you use Gitlab CI to build/publish it.
</details>

<p>For example, in my use case <a href="https://gitlab.com/hmajid2301/articles">the article repo</a> will trigger a rebuild for
<a href="https://gitlab.com/hmajid2301/portfolio-site/-/tree/7258fe7ca1366024f17da5952077cdc00f00a3a8">the Gatsby repo</a>.</p>
<p>First, go to your Gatsby repo then go to <code>Settings &gt; CI/CD &gt; Pipeline triggers</code>. Then create a new pipeline trigger,
save the newly created token to your CI/CD variables.</p>
<p><div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden">
  <iframe src="https://yewtu.be/embed/JbAk6xpBRxc"
    style="position:absolute;top:0;left:0;width:100%;height:100%;border:0" allowfullscreen></iframe>
</div>

<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden">
  <iframe src="https://yewtu.be/embed/X9m8UxmZgy8"
    style="position:absolute;top:0;left:0;width:100%;height:100%;border:0" allowfullscreen></iframe>
</div>
</p>
<p>Then also copy the <code>cURL</code> command shown and add the following to your <code>.gitlab-ci.yml</code>, with the <code>cURL</code> command.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">stages</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">build</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">rebuild:portfolio-site</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">stage</span><span class="p">:</span><span class="w"> </span><span class="l">build</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">curlimages/curl</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">only</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">master</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">script</span><span class="p">:</span><span class="w">
</span></span></span><span class="line hl"><span class="cl"><span class="w">    </span>- <span class="s2">&#34;curl -X POST -F token=${TRIGGER_TOKEN} -F ref=master https://gitlab.com/api/v4/projects/19260161/trigger/pipeline&#34;</span></span></span></code></pre></div>
<p>Make sure you replace <code>19260161</code> with the project ID of your Gatsby blog, as this is the repo we want to trigger a
rebuild of. This means every time we push a new commit (i.e. an article) to the master branch of the articles,
it will trigger the pipeline to run on our Gatsby blog.</p>
<p>This will mean when it runs <code>yarn build</code> or <code>gatsby build</code> it&rsquo;ll source the markdown data from the latest commit on
our article git repo and will have the new article or whatever changes were made. The <code>.gitlab-ci</code> for our
Gatsby blog may look something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">node:12.13.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">cache</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">key</span><span class="p">:</span><span class="w"> </span><span class="l">${CI_COMMIT_REF_SLUG}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">paths</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">node_modules</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">stages</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">build</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">deploy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">before_script</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">yarn install</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">build:site</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">stage</span><span class="p">:</span><span class="w"> </span><span class="l">build</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">only</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">master</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">script</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">yarn run build</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">artifacts</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">paths</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">public</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">deploy:site</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">stage</span><span class="p">:</span><span class="w"> </span><span class="l">deploy</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">only</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">master</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">script</span><span class="p">:</span><span class="w">
</span></span></span><span class="line hl"><span class="cl"><span class="w">    </span>- <span class="l">npm i -g netlify-cli</span><span class="w">
</span></span></span><span class="line hl"><span class="cl"><span class="w">    </span>- <span class="l">yarn deploy --site $NETLIFY_SITE_ID --auth $NETLIFY_PERSONAL_TOKEN --message &#34;$CI_COMMIT_TITLE&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">dependencies</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">build:site</span></span></span></code></pre></div>
<details
  class="notice tip"
  open="true"
>
    <summary class="notice-title">Artifcats</summary>
  
  The <code>deploy:site</code> job uses the build artifacts from the previous <code>build:site</code> job which has the site data stored in the <code>public</code>
folder. Due to the sites default settings on Netlify, this is what is uploaded when we use <code>netlify-cli</code>.
</details>

<p>I build and deploy the site from Gitlab CI to save the build minutes on Netlify. All you need to do this is to get your
<code>NETLIFY_SITE_ID</code> and create a <code>NETLIFY_PERSONAL_TOKEN</code> that can make the API request to publish the site on your behalf.</p>
<p><img
        loading="lazy"
        src="/posts/2020-09-18-how-to-source-your-gatsby-posts-from-another-repo/images/gatsby-blog-ci.png"
        type=""
        alt="Gatsby Blog CI"
        
      /></p>
<details
  class="notice info"
  open="true"
>
    <summary class="notice-title">Gitlab CI</summary>
  
  You can, of course, change the <code>deploy:site</code> job to suit how you want to deploy your site, i.e. Gitlab pages, Github Pages, Vercel etc.
</details>

<h2 id="netlify">Netlify</h2>
<p>If you don&rsquo;t want to use Gitlab CI to build and publish your Gatsby blog and want to force a rebuild on Netlify.
Then you can do the following; Every time we push a new commit to the master branch on the article repo. We can
use a webhook to trigger a rebuild of our site on Netlify. To do this select your website on the Netlify GUI.
Then <code>Settings</code> &gt; <code>Build &amp; deploy</code> &gt; <code>Build hooks</code>. Add a new build hook. Then copy the <code>cURL</code> command,
so your article repo <code>.gitlab-ci.yml</code> now looks something like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yml" data-lang="yml"><span class="line"><span class="cl"><span class="nt">stages</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span>- <span class="l">build</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">rebuild:portfolio-site</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">stage</span><span class="p">:</span><span class="w"> </span><span class="l">build</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">curlimages/curl</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">only</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="l">master</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">script</span><span class="p">:</span><span class="w">
</span></span></span><span class="line hl"><span class="cl"><span class="w">    </span>- <span class="l">curl -X POST -d {} https://api.netlify.com/build_hooks/5f5e9c4f495aebe573c39aef</span></span></span></code></pre></div>
<p>You will want to turn <code>5f5e9c4f495aebe573c39aef</code> into a CI/CD variable, else anyone can force a rebuild of your site.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden">
  <iframe src="https://yewtu.be/embed/7KRihyulbTQ"
    style="position:absolute;top:0;left:0;width:100%;height:100%;border:0" allowfullscreen></iframe>
</div>

<p>That&rsquo;s it, we learnt how we can manage our markdown articles in a separate repo to our Gatsby blog! We went over how we
can also automate the rebuild of our site using Gitlab CI and Netlify.</p>
<h2 id="appendix">Appendix</h2>
<ul>
<li><a href="https://gitlab.com/hmajid2301/blog/-/tree/main/content/posts/2020-09-18-how-to-source-your-gatsby-posts-from-another-repo/source_code">Example source code</a></li>
<li>Example <a href="https://gitlab.com/hmajid2301/portfolio-site/-/tree/7258fe7ca1366024f17da5952077cdc00f00a3a8">Gatsby blog</a> repo</li>
<li>Example <a href="https://gitlab.com/hmajid2301/articles">Articles</a> repo</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Add an &#39;edit post&#39; button to your Gatsby blog</title>
      <link>https://haseebmajid.dev/posts/2020-09-07-add-an-edit-post-button-to-your-gatsby-blog/</link>
      <pubDate>Mon, 07 Sep 2020 00:00:00 +0000</pubDate>
      <author>Me</author>
      <guid>https://haseebmajid.dev/posts/2020-09-07-add-an-edit-post-button-to-your-gatsby-blog/</guid>
      <description>&lt;p&gt;In this article, we will look at how we can add an &amp;ldquo;edit post&amp;rdquo; button, to your Gatsby blog. When this button is clicked it will take the user to your markdown file, on github/gitlab that was used to generate the blog post they are currently viewing.&lt;/p&gt;
&lt;div style=&#34;position:relative;padding-bottom:56.25%;height:0;overflow:hidden&#34;&gt;
  &lt;iframe src=&#34;https://yewtu.be/embed/rALo_BzGKs8&#34;
    style=&#34;position:absolute;top:0;left:0;width:100%;height:100%;border:0&#34; allowfullscreen&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h2 id=&#34;setup&#34;&gt;Setup&lt;/h2&gt;
&lt;p&gt;Before we add the edit button to a Gatsby blog, let&amp;rsquo;s set up a simple Gatsby site using the &lt;code&gt;Gatsby blog starter&lt;/code&gt;.
You can skip this step and add the button to an existing site.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>In this article, we will look at how we can add an &ldquo;edit post&rdquo; button, to your Gatsby blog. When this button is clicked it will take the user to your markdown file, on github/gitlab that was used to generate the blog post they are currently viewing.</p>
<div style="position:relative;padding-bottom:56.25%;height:0;overflow:hidden">
  <iframe src="https://yewtu.be/embed/rALo_BzGKs8"
    style="position:absolute;top:0;left:0;width:100%;height:100%;border:0" allowfullscreen></iframe>
</div>

<h2 id="setup">Setup</h2>
<p>Before we add the edit button to a Gatsby blog, let&rsquo;s set up a simple Gatsby site using the <code>Gatsby blog starter</code>.
You can skip this step and add the button to an existing site.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm -g install gatsby-cli
</span></span><span class="line"><span class="cl">gatsby new my-blog-starter https://github.com/gatsbyjs/gatsby-starter-blog
</span></span></code></pre></div><p>If you don&rsquo;t use the start above, you will need to make sure you have the <code>gatsby-source-filesystem</code> plugin installed. To import our markdown files. Your <code>gatsby-config.js</code> looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">resolve</span><span class="o">:</span> <span class="sb">`gatsby-source-filesystem`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">path</span><span class="o">:</span> <span class="sb">`</span><span class="si">${</span><span class="nx">__dirname</span><span class="si">}</span><span class="sb">/content/blog`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">name</span><span class="o">:</span> <span class="sb">`blog`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span></code></pre></div><p>Then make sure you also have the <code>gatsby-transformer-remark</code> plugin installed
and it should be in your <code>gatsby-config.js</code> like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">resolve</span><span class="o">:</span> <span class="sb">`gatsby-transformer-remark`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// ...
</span></span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span></code></pre></div><h2 id="optional-blog-post">(Optional) Blog Post</h2>
<p>Let&rsquo;s assume our <code>gatsby-node.js</code> file looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">exports</span><span class="p">.</span><span class="nx">createPages</span> <span class="o">=</span> <span class="kr">async</span> <span class="p">({</span> <span class="nx">graphql</span><span class="p">,</span> <span class="nx">actions</span> <span class="p">})</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">{</span> <span class="nx">createPage</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">actions</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">blogPost</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="sb">`./src/templates/blog-post.js`</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">graphql</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">      {
</span></span></span><span class="line"><span class="cl"><span class="sb">        allMarkdownRemark(
</span></span></span><span class="line"><span class="cl"><span class="sb">          sort: { fields: [frontmatter___date], order: DESC }
</span></span></span><span class="line"><span class="cl"><span class="sb">          limit: 1000
</span></span></span><span class="line"><span class="cl"><span class="sb">        ) {
</span></span></span><span class="line"><span class="cl"><span class="sb">          edges {
</span></span></span><span class="line"><span class="cl"><span class="sb">            node {
</span></span></span><span class="line"><span class="cl"><span class="sb">              fields {
</span></span></span><span class="line"><span class="cl"><span class="sb">                slug
</span></span></span><span class="line"><span class="cl"><span class="sb">              }
</span></span></span><span class="line"><span class="cl"><span class="sb">              frontmatter {
</span></span></span><span class="line"><span class="cl"><span class="sb">                title
</span></span></span><span class="line"><span class="cl"><span class="sb">              }
</span></span></span><span class="line"><span class="cl"><span class="sb">            }
</span></span></span><span class="line"><span class="cl"><span class="sb">          }
</span></span></span><span class="line"><span class="cl"><span class="sb">        }
</span></span></span><span class="line"><span class="cl"><span class="sb">      }
</span></span></span><span class="line"><span class="cl"><span class="sb">    `</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">errors</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">throw</span> <span class="nx">result</span><span class="p">.</span><span class="nx">errors</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Create blog posts pages.
</span></span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">posts</span> <span class="o">=</span> <span class="nx">result</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">allMarkdownRemark</span><span class="p">.</span><span class="nx">edges</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">posts</span><span class="p">.</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">post</span><span class="p">,</span> <span class="nx">index</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">previous</span> <span class="o">=</span> <span class="nx">index</span> <span class="o">===</span> <span class="nx">posts</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span> <span class="o">?</span> <span class="kc">null</span> <span class="o">:</span> <span class="nx">posts</span><span class="p">[</span><span class="nx">index</span> <span class="o">+</span> <span class="mi">1</span><span class="p">].</span><span class="nx">node</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">next</span> <span class="o">=</span> <span class="nx">index</span> <span class="o">===</span> <span class="mi">0</span> <span class="o">?</span> <span class="kc">null</span> <span class="o">:</span> <span class="nx">posts</span><span class="p">[</span><span class="nx">index</span> <span class="o">-</span> <span class="mi">1</span><span class="p">].</span><span class="nx">node</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">createPage</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">      <span class="nx">path</span><span class="o">:</span> <span class="nx">post</span><span class="p">.</span><span class="nx">node</span><span class="p">.</span><span class="nx">fields</span><span class="p">.</span><span class="nx">slug</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">component</span><span class="o">:</span> <span class="nx">blogPost</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">context</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">slug</span><span class="o">:</span> <span class="nx">post</span><span class="p">.</span><span class="nx">node</span><span class="p">.</span><span class="nx">fields</span><span class="p">.</span><span class="nx">slug</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">previous</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">next</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>This is how we create a new blog post for each of our markdown files. You can read more about how
markdown works with <a href="https://www.gatsbyjs.com/docs/adding-markdown-pages/">Gatsby here</a>.</p>
<p>Also let&rsquo;s use a simple template file for your blogs posts. So our <code>blog-post.js</code> looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">React</span> <span class="nx">from</span> <span class="s2">&#34;react&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">Link</span><span class="p">,</span> <span class="nx">graphql</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;gatsby&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ...
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">BlogPostTemplate</span> <span class="o">=</span> <span class="p">({</span> <span class="nx">data</span><span class="p">,</span> <span class="nx">pageContext</span><span class="p">,</span> <span class="nx">location</span> <span class="p">})</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">post</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">markdownRemark</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">siteTitle</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">site</span><span class="p">.</span><span class="nx">siteMetadata</span><span class="p">.</span><span class="nx">title</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">{</span> <span class="nx">previous</span><span class="p">,</span> <span class="nx">next</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">pageContext</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">Layout</span> <span class="na">location</span><span class="o">=</span><span class="p">{</span><span class="nx">location</span><span class="p">}</span> <span class="na">title</span><span class="o">=</span><span class="p">{</span><span class="nx">siteTitle</span><span class="p">}&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">SEO</span>
</span></span><span class="line"><span class="cl">        <span class="na">title</span><span class="o">=</span><span class="p">{</span><span class="nx">post</span><span class="p">.</span><span class="nx">frontmatter</span><span class="p">.</span><span class="nx">title</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="na">description</span><span class="o">=</span><span class="p">{</span><span class="nx">post</span><span class="p">.</span><span class="nx">frontmatter</span><span class="p">.</span><span class="nx">description</span> <span class="o">||</span> <span class="nx">post</span><span class="p">.</span><span class="nx">excerpt</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// ...
</span></span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">Layout</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">BlogPostTemplate</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">pageQuery</span> <span class="o">=</span> <span class="nx">graphql</span><span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">  query BlogPostBySlug($slug: String!) {
</span></span></span><span class="line"><span class="cl"><span class="sb">    site {
</span></span></span><span class="line"><span class="cl"><span class="sb">      siteMetadata {
</span></span></span><span class="line"><span class="cl"><span class="sb">        title
</span></span></span><span class="line"><span class="cl"><span class="sb">      }
</span></span></span><span class="line"><span class="cl"><span class="sb">    }
</span></span></span><span class="line"><span class="cl"><span class="sb">    markdownRemark(fields: { slug: { eq: $slug } }) {
</span></span></span><span class="line"><span class="cl"><span class="sb">      id
</span></span></span><span class="line"><span class="cl"><span class="sb">      excerpt(pruneLength: 160)
</span></span></span><span class="line"><span class="cl"><span class="sb">      html
</span></span></span><span class="line"><span class="cl"><span class="sb">      frontmatter {
</span></span></span><span class="line"><span class="cl"><span class="sb">        title
</span></span></span><span class="line"><span class="cl"><span class="sb">        date(formatString: &#34;MMMM DD, YYYY&#34;)
</span></span></span><span class="line"><span class="cl"><span class="sb">        description
</span></span></span><span class="line"><span class="cl"><span class="sb">      }
</span></span></span><span class="line"><span class="cl"><span class="sb">    }
</span></span></span><span class="line"><span class="cl"><span class="sb">  }
</span></span></span><span class="line"><span class="cl"><span class="sb">`</span><span class="p">;</span>
</span></span></code></pre></div><h2 id="edit-button">Edit Button</h2>
<p>Ok, now we need two pieces of information the location of our project on git where our
markdown files are stored. In this example, it&rsquo;s here <code>https://gitlab.com/hmajid2301/articles</code>. We also need the path to the markdown file in the git repo. So we can combine
these two pieces of information together to get a URL to the markdown file on git.</p>
<p>First, we need a way to get the file path of the markdown file, we can do this with using our GraphQL query.
The same query we use to get other information such as title and contents. All we need to add is <code>fileAbsolutePath</code>
to the <code>markdownRemark</code> part of our query. This will return, as the name suggests, the absolute path to the file,
i.e. <code>/home/haseeb/projects/personal/articles/34. Gatsby edit button/source_code/content/blog/hello-world/index.md</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">pageQuery</span> <span class="o">=</span> <span class="nx">graphql</span><span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">  query BlogPostBySlug($slug: String!) {
</span></span></span><span class="line"><span class="cl"><span class="sb">    site {
</span></span></span><span class="line"><span class="cl"><span class="sb">      siteMetadata {
</span></span></span><span class="line"><span class="cl"><span class="sb">        title
</span></span></span><span class="line"><span class="cl"><span class="sb">      }
</span></span></span><span class="line"><span class="cl"><span class="sb">    }
</span></span></span><span class="line"><span class="cl"><span class="sb">    markdownRemark(fields: { slug: { eq: $slug } }) {
</span></span></span><span class="line"><span class="cl"><span class="sb">      id
</span></span></span><span class="line"><span class="cl"><span class="sb">      excerpt(pruneLength: 160)
</span></span></span><span class="line hl"><span class="cl"><span class="sb">      html
</span></span></span><span class="line"><span class="cl"><span class="sb">      fileAbsolutePath
</span></span></span><span class="line"><span class="cl"><span class="sb">      frontmatter {
</span></span></span><span class="line"><span class="cl"><span class="sb">        title
</span></span></span><span class="line"><span class="cl"><span class="sb">        date(formatString: &#34;MMMM DD, YYYY&#34;)
</span></span></span><span class="line"><span class="cl"><span class="sb">        description
</span></span></span><span class="line"><span class="cl"><span class="sb">      }
</span></span></span><span class="line"><span class="cl"><span class="sb">    }
</span></span></span><span class="line"><span class="cl"><span class="sb">  }
</span></span></span><span class="line"><span class="cl"><span class="sb">`</span><span class="p">;</span></span></span></code></pre></div>
<p>Now we need a way to use this file path to link to this page on Gitlab. Since I know that
<code>articles/</code> is a git repo, we want to remove <code>/home/haseeb/projects/personal/articles</code>
from <code>/home/haseeb/projects/personal/articles/34. Gatsby edit button/source_code/content/blog/hello-world/index.md</code>.</p>
<p>Then assuming the git URL of our repo, where the markdown files exists, is <code>https://gitlab.com/hmajid2301/articles</code>. The path to our markdown file on git could be something like
<code>https://gitlab.com/hmajid2301/articles/-/blob/master/34. Gatsby edit button/source_code/content/blog/hello-world/index.md</code>.</p>
<p>So let&rsquo;s add logic to our <code>blog-post.js</code> file to generate this git URL. After we have
updated our GraphQL query, we can add the some logic to our code to workout the git URL path.
Let&rsquo;s create a new function called <code>getGitMarkdownUrl()</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">BlogPostTemplate</span> <span class="o">=</span> <span class="p">({</span> <span class="nx">data</span><span class="p">,</span> <span class="nx">pageContext</span><span class="p">,</span> <span class="nx">location</span> <span class="p">})</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">post</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">markdownRemark</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">siteTitle</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">site</span><span class="p">.</span><span class="nx">siteMetadata</span><span class="p">.</span><span class="nx">title</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">{</span> <span class="nx">previous</span><span class="p">,</span> <span class="nx">next</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">pageContext</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">function</span> <span class="nx">getGitMarkdownUrl</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">pathConst</span> <span class="o">=</span> <span class="s2">&#34;/articles/&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">gitURL</span> <span class="o">=</span> <span class="s2">&#34;https://gitlab.com/hmajid2301/articles&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">sliceIndex</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl">      <span class="nx">post</span><span class="p">.</span><span class="nx">fileAbsolutePath</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="nx">pathConst</span><span class="p">)</span> <span class="o">+</span> <span class="nx">pathConst</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">markdownFileGitPath</span> <span class="o">=</span> <span class="nx">post</span><span class="p">.</span><span class="nx">fileAbsolutePath</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="nx">sliceIndex</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">blogPostOnGit</span> <span class="o">=</span> <span class="sb">`</span><span class="si">${</span><span class="nx">gitURL</span><span class="si">}</span><span class="sb">/-/blob/master/</span><span class="si">${</span><span class="nx">markdownFileGitPath</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">blogPostOnGit</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">gitMarkdownUrl</span> <span class="o">=</span> <span class="nx">getGitMarkdownUrl</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// ....
</span></span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><blockquote>
<p>Warn: Don&rsquo;t forget to change the <code>gitURL</code> variable in your project!</p>
</blockquote>
<p>Where the following two lines remove everything before <code>/articles/</code>, so we get
<code>34. Gatsby edit button/source_code/content/blog/hello-world/index.md</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">sliceIndex</span> <span class="o">=</span> <span class="nx">post</span><span class="p">.</span><span class="nx">fileAbsolutePath</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="nx">pathConst</span><span class="p">)</span> <span class="o">+</span> <span class="nx">pathConst</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">markdownFileGitPath</span> <span class="o">=</span> <span class="nx">post</span><span class="p">.</span><span class="nx">fileAbsolutePath</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="nx">sliceIndex</span><span class="p">);</span>
</span></span></code></pre></div><p>Then we combine this with our git URL to end up with the path to the markdown file <code>https://gitlab.com/hmajid2301/articles/-/blob/master/34. Gatsby edit button/source_code/content/blog/hello-world/index.md</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">blogPostOnGit</span> <span class="o">=</span> <span class="sb">`</span><span class="si">${</span><span class="nx">gitURL</span><span class="si">}</span><span class="sb">/-/blob/master/</span><span class="si">${</span><span class="nx">markdownFileGitPath</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span></code></pre></div><p>Finally, all we need to do is add the edit button and have it link to this <code>gitMarkdownUrl</code>. You can do something like
this below:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="p">{</span><span class="nx">gitMarkdownUrl</span><span class="p">}</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;noreferrer&#34;</span> <span class="na">target</span><span class="o">=</span><span class="s">&#34;_blank&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">EDIT</span> <span class="nx">THIS</span> <span class="nx">POST</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>If you want to make it look fancier, you can use <code>react-icons</code> to get a proper edit icon (as shown in the gif above).</p>
<p>That&rsquo;s it! That&rsquo;s all we needed to do when the user clicks on the edit button it&rsquo;ll take them to the git repo where
the markdown files exist. They can then perhaps fork the project make their edit and open a new merge or pull request
(GitLab vs GitHub) and add in the changes they want (if approved by you).</p>
<h2 id="appendix">Appendix</h2>
<ul>
<li><a href="https://gitlab.com/hmajid2301/blog/-/tree/main/content/posts/2020-09-07-add-an-edit-post-button-to-your-gatsby-blog/source_code">Example source code</a></li>
<li><a href="https://haseebmajid.dev/">Site in video</a></li>
<li><a href="https://gitlab.com/hmajid2301/portfolio-site">Source code</a> for site in video</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>How to add offline search to a Gatsby blog</title>
      <link>https://haseebmajid.dev/posts/2020-08-20-how-to-add-offline-search-to-a-gatsby-blog/</link>
      <pubDate>Thu, 20 Aug 2020 00:00:00 +0000</pubDate>
      <author>Me</author>
      <guid>https://haseebmajid.dev/posts/2020-08-20-how-to-add-offline-search-to-a-gatsby-blog/</guid>
      <description>&lt;p&gt;&lt;img
        loading=&#34;lazy&#34;
        src=&#34;../../posts/2020-08-20-how-to-add-offline-search-to-a-gatsby-blog/images/main.gif&#34;
        type=&#34;&#34;
        alt=&#34;Search Gif&#34;
        
      /&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s take a look at how we can add offline local search 🔍 to a Gatsby blog. There are two main types of search we can
use an offline search like &lt;code&gt;elasticlunr&lt;/code&gt; and external API search engines like &lt;code&gt;ElasticSearch&lt;/code&gt;. These are typically more
scalable but also more expensive.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can find more info &lt;a href=&#34;https://www.gatsbyjs.com/docs/adding-search/#reach-skip-nav&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this article, I will show you how to add offline search to your Gatsby blog using &lt;code&gt;elasticlunr&lt;/code&gt;. This means your
website needs to be indexed locally and will increase the bundle size as this index needs to be loaded by the client but
with the scale and size of personal blogs (100s, not 1000s of blog post) this shouldn&amp;rsquo;t make a massive difference.
We will also look at how we can add highlighting to our search results.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p><img
        loading="lazy"
        src="/posts/2020-08-20-how-to-add-offline-search-to-a-gatsby-blog/images/main.gif"
        type=""
        alt="Search Gif"
        
      /></p>
<p>Let&rsquo;s take a look at how we can add offline local search 🔍 to a Gatsby blog. There are two main types of search we can
use an offline search like <code>elasticlunr</code> and external API search engines like <code>ElasticSearch</code>. These are typically more
scalable but also more expensive.</p>
<blockquote>
<p>You can find more info <a href="https://www.gatsbyjs.com/docs/adding-search/#reach-skip-nav">here</a>.</p>
</blockquote>
<p>In this article, I will show you how to add offline search to your Gatsby blog using <code>elasticlunr</code>. This means your
website needs to be indexed locally and will increase the bundle size as this index needs to be loaded by the client but
with the scale and size of personal blogs (100s, not 1000s of blog post) this shouldn&rsquo;t make a massive difference.
We will also look at how we can add highlighting to our search results.</p>
<blockquote>
<p>Note that you need to be careful with offline search because the entire search index has to be brought into the client, which can affect the bundle size significantly - GatsbyJS</p>
</blockquote>
<h2 id="setup">Setup</h2>
<p>Before we add search Gatsby blog, let&rsquo;s setup a simple Gatsby site using the <code>Gatsby blog starter</code>, you can of course
skip this step and add search to an existing site.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm -g install gatsby-cli
</span></span><span class="line"><span class="cl">gatsby new my-blog-starter https://github.com/gatsbyjs/gatsby-starter-blog
</span></span></code></pre></div><h2 id="markdown">Markdown</h2>
<p>The search component will use the data within our markdown and index it, so that the client can search with this
data later. In this example I will assume your markdown files look something like the example below:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="l">Hello World</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">date</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;2015-05-01&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">tags</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;food&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;duck&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">This is my first post on my new fake blog! How exciting!
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">I&#39;m sure I&#39;ll write a lot more interesting things in the future.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">...
</span></span></code></pre></div><p>The top part of a markdown file between the <code>---</code> is known as the front matter, often we can access this data as a
key/value (like a Python dictionary).</p>
<blockquote>
<p>Note: In this example, we will be using the <a href="https://www.gatsbyjs.com/plugins/gatsby-transformer-remark/?=markdown"><code>MarkdownRemark</code></a>, but you can use search for anything just adjust the examples below as required.</p>
</blockquote>
<h2 id="search">Search</h2>
<p>Now onto adding search to our site.</p>
<h3 id="elasticlunr">Elasticlunr</h3>
<p>We will use <code>elasticlunr</code> for our offline/local search. Luckily there is a Gatsby plugin we can use, which makes
integrating it into our site very easy. First install the following plugin and the library:
<code>yarn add @gatsby-contrib/gatsby-plugin-elasticlunr-search elasticlunr</code>.</p>
<p>Then open your <code>gatsby-config.js</code> and add the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">resolve</span><span class="o">:</span> <span class="sb">`@gatsby-contrib/gatsby-plugin-elasticlunr-search`</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">fields</span><span class="o">:</span> <span class="p">[</span><span class="sb">`title`</span><span class="p">,</span> <span class="sb">`tags`</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">      <span class="nx">resolvers</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">MarkdownRemark</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">title</span><span class="o">:</span> <span class="p">(</span><span class="nx">node</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">node</span><span class="p">.</span><span class="nx">frontmatter</span><span class="p">.</span><span class="nx">title</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">tags</span><span class="o">:</span> <span class="p">(</span><span class="nx">node</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">node</span><span class="p">.</span><span class="nx">frontmatter</span><span class="p">.</span><span class="nx">tags</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">path</span><span class="o">:</span> <span class="p">(</span><span class="nx">node</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="nx">node</span><span class="p">.</span><span class="nx">frontmatter</span><span class="p">.</span><span class="nx">slug</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span></code></pre></div><p>Here we are telling the search plugin what (GraphQL) fields to index. In this example, we want to index
the title and tags. We could also index the content if we wanted by adding the following line after path
<code>html: (node) =&gt; node.internal.content,</code> and adding <code>html</code> to the <code>fields</code> array. You can index
any field available in GraphQL, provided by the <code>MarkdownRemark</code> plugin (or whichever plugin you are using).</p>
<h4 id="graphql-optional">GraphQL (Optional)</h4>
<p>Slight aside here but if you wish to explore and take a look at the data available/provided by the <code>MarkdownRemark</code> plugin, you can start your
Gatsby site, typically using <code>yarn develop</code> and once the command has finished doing it&rsquo;s magic 🎉, visit this page
<code>http://localhost:8000/___graphql</code>. This provides us with our GraphQL playground (an IDE) and is a great way to understand what is going
on with our GraphQL queries if you don&rsquo;t understand.</p>
<p>For example, if you type the following into the main field and press the play button at the top.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-graphql" data-lang="graphql"><span class="line"><span class="cl"><span class="kd">query</span><span class="w"> </span><span class="nc">MyQuery</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="py">allMarkdownRemark</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="py">sort</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="py">order</span><span class="p">:</span><span class="w"> </span><span class="nc">DESC</span><span class="p">,</span><span class="w"> </span><span class="py">fields</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="py">frontmatter___date</span><span class="p">]</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="py">filter</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="py">frontmatter</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="py">title</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="py">ne</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;Uses&#34;</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="py">edges</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="py">node</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="py">id</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="py">excerpt</span><span class="p">(</span><span class="py">pruneLength</span><span class="p">:</span><span class="w"> </span><span class="nc">100</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="py">frontmatter</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="py">date</span><span class="p">(</span><span class="py">formatString</span><span class="p">:</span><span class="w"> </span><span class="s">&#34;YYYY-MM-DD&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="py">title</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="py">tags</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>You should see something like (in this example):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;data&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;allMarkdownRemark&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;edges&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&#34;node&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;1a7e02d4-620a-5268-8149-2d8cbf26a20a&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;excerpt&#34;</span><span class="p">:</span> <span class="s2">&#34;Far far away, behind the word mountains, far from the countries Vokalia and\nConsonantia, there live…&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;frontmatter&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="nt">&#34;date&#34;</span><span class="p">:</span> <span class="s2">&#34;2015-05-28&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="nt">&#34;title&#34;</span><span class="p">:</span> <span class="s2">&#34;New Beginnings&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="nt">&#34;tags&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;deer&#34;</span><span class="p">,</span> <span class="s2">&#34;horse&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&#34;node&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;fe83f167-8f86-51fe-a981-c5189625e270&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;excerpt&#34;</span><span class="p">:</span> <span class="s2">&#34;Wow! I love blogging so much already. Did you know that “despite its name, salted duck eggs can also…&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;frontmatter&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="nt">&#34;date&#34;</span><span class="p">:</span> <span class="s2">&#34;2015-05-06&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="nt">&#34;title&#34;</span><span class="p">:</span> <span class="s2">&#34;My Second Post!&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="nt">&#34;tags&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;food&#34;</span><span class="p">,</span> <span class="s2">&#34;blog&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&#34;node&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="s2">&#34;4e865c18-e797-5da8-a46d-902949a00c7f&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;excerpt&#34;</span><span class="p">:</span> <span class="s2">&#34;This is my first post on my new fake blog! How exciting! I’m sure I’ll write a lot more interesting…&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="nt">&#34;frontmatter&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="nt">&#34;date&#34;</span><span class="p">:</span> <span class="s2">&#34;2015-05-01&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="nt">&#34;title&#34;</span><span class="p">:</span> <span class="s2">&#34;Hello World&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="nt">&#34;tags&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;food&#34;</span><span class="p">,</span> <span class="s2">&#34;duck&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;extensions&#34;</span><span class="p">:</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>As you can see this is a very familiar structure to the one we described in our search config above. If you play
around with the fields on the left-hand side of the IDE, you should be able to get a better understanding of all
fields you can index.</p>
<h2 id="logic">Logic</h2>
<p>Now we will add the relevant JSX components we need for search to our site.</p>
<h3 id="tailwindcss-optional">TailwindCSS (Optional)</h3>
<p>You can follow this <a href="https://www.gatsbyjs.com/docs/tailwind-css/">tutorial</a> to add TailwindCSS.
We will add TailwindCSS to this Gatsby project and we will use this to style our components.
First install the following dependencies:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add tailwindcss gatsby-plugin-postcss @emotion/core @emotion/styled gatsby-plugin-emotion
</span></span><span class="line"><span class="cl">yarn add -D twin.macro <span class="c1"># twin.macro allows us to use css-in-js a bit like emotion/styled-components except for tailwind</span>
</span></span><span class="line"><span class="cl">npx tailwindcss init
</span></span></code></pre></div><p>Then add the following to your <code>gatsby-config.js</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span><span class="sb">`gatsby-plugin-postcss`</span><span class="p">,</span> <span class="sb">`gatsby-plugin-emotion`</span><span class="p">],</span>
</span></span></code></pre></div><p>Then create a new file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">vim main.css
</span></span><span class="line"><span class="cl"><span class="c1">#...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Contents of the file</span>
</span></span><span class="line"><span class="cl">@tailwind base<span class="p">;</span>
</span></span><span class="line"><span class="cl">@tailwind components<span class="p">;</span>
</span></span><span class="line"><span class="cl">@tailwind utilities<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># ...</span>
</span></span></code></pre></div><p>Then add the following line to <code>gatsby-browser.js</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;./src/main.css&#34;</span><span class="p">;</span>
</span></span></code></pre></div><p>Finally create a new file <code>postcss.config.js</code> and add the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span><span class="nx">require</span><span class="p">(</span><span class="s2">&#34;tailwindcss&#34;</span><span class="p">)],</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><h3 id="components">Components</h3>
<p>We will create all of the components in the following <code>src/components</code> folder.
First, let&rsquo;s create the <code>Input.jsx</code> component for the text input, which looks something like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">React</span> <span class="nx">from</span> <span class="s2">&#34;react&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">tw</span> <span class="nx">from</span> <span class="s2">&#34;twin.macro&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">Input</span> <span class="o">=</span> <span class="nx">React</span><span class="p">.</span><span class="nx">forwardRef</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="p">({</span> <span class="nx">className</span><span class="p">,</span> <span class="nx">label</span><span class="p">,</span> <span class="nx">onChange</span><span class="p">,</span> <span class="nx">placeholder</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="nx">value</span> <span class="p">},</span> <span class="nx">ref</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">TextInput</span>
</span></span><span class="line"><span class="cl">      <span class="na">ref</span><span class="o">=</span><span class="p">{</span><span class="nx">ref</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="na">aria</span><span class="err">-</span><span class="na">label</span><span class="o">=</span><span class="p">{</span><span class="nx">label</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="na">className</span><span class="o">=</span><span class="p">{</span><span class="sb">`bg-background text-header placeholder-main </span><span class="si">${</span><span class="nx">className</span><span class="si">}</span><span class="sb">`</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="na">onChange</span><span class="o">=</span><span class="p">{</span><span class="nx">onChange</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="na">placeholder</span><span class="o">=</span><span class="p">{</span><span class="nx">placeholder</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="na">type</span><span class="o">=</span><span class="s">&#34;text&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="na">value</span><span class="o">=</span><span class="p">{</span><span class="nx">value</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">TextInput</span> <span class="o">=</span> <span class="nx">tw</span><span class="p">.</span><span class="nx">input</span><span class="sb">`inline px-2 h-full w-full text-left inline text-lg transition duration-300`</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">Input</span>
</span></span></code></pre></div><p>Since we are using <code>twin.macro</code> we can use syntax like <code>const TextInput = tw.input``</code>. Hence we can use the name <code>TextInput</code>.
in our component, where <code>TextInput</code> is just an input with some tailwindcss styles we&rsquo;ve defined.</p>
<p>Note we added a React forward ref so that, we can autofocus on this input later on.
So when the input is shown to the client we are already focused into the input.</p>
<p>Next, let&rsquo;s create a component for <code>SearchItem.jsx</code>. This is a single search item found.
In this case, we will only show the title and read more button. Note we are using the
<code>react-highlight-words</code> library to highlight words from the search query.</p>
<p>The prop <code>query</code> is the search query the user typed in. In the <code>Highlighter</code> component the <code>searchWords</code> prop
is given a list of words to highlight, hence we need to split the string into an array. For example, if we
had the search query <code>&quot;A blog post&quot;</code>, it would become <code>[&quot;A&quot;, &quot;blog&quot;, &quot;post&quot;]</code>, and will highlight either of
those words in the title (A, blog or post).</p>
<blockquote>
<p>Note: Again you can extend this to include perhaps a description of the blog post (first 160 characters) etc. We are just keeping it simple for this example.</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">Link</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;gatsby&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">React</span> <span class="nx">from</span> <span class="s2">&#34;react&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Highlighter</span> <span class="nx">from</span> <span class="s2">&#34;react-highlight-words&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">tw</span> <span class="nx">from</span> <span class="s2">&#34;twin.macro&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">SearchItem</span> <span class="o">=</span> <span class="p">({</span> <span class="nx">path</span><span class="p">,</span> <span class="nx">title</span><span class="p">,</span> <span class="nx">query</span> <span class="p">})</span> <span class="p">=&gt;</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">SearchItemContainer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">SearchTitle</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">Link</span>
</span></span><span class="line"><span class="cl">        <span class="na">className</span><span class="o">=</span><span class="s">&#34;hover:text-white hover:bg-blue-500 hover:p-1 rounded&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">to</span><span class="o">=</span><span class="p">{</span><span class="nx">path</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">Highlighter</span>
</span></span><span class="line"><span class="cl">          <span class="na">autoEscape</span>
</span></span><span class="line"><span class="cl">          <span class="na">highlightStyle</span><span class="o">=</span><span class="p">{{</span> <span class="nx">backgroundColor</span><span class="o">:</span> <span class="s2">&#34;#ffd54f&#34;</span> <span class="p">}}</span>
</span></span><span class="line"><span class="cl">          <span class="na">searchWords</span><span class="o">=</span><span class="p">{</span><span class="nx">query</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s2">&#34; &#34;</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl">          <span class="na">textToHighlight</span><span class="o">=</span><span class="p">{</span><span class="nx">title</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">Link</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">SearchTitle</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">ReadMore</span> <span class="na">className</span><span class="o">=</span><span class="s">&#34;hover:text-blue-500 text-lg py-2&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;button&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">Link</span> <span class="na">to</span><span class="o">=</span><span class="p">{</span><span class="nx">path</span><span class="p">}&gt;</span><span class="nx">Read</span> <span class="nx">More</span><span class="p">&lt;/</span><span class="nt">Link</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">ReadMore</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">SearchItemContainer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">SearchItemContainer</span> <span class="o">=</span> <span class="nx">tw</span><span class="p">.</span><span class="nx">div</span><span class="sb">`my-10`</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">SearchTitle</span> <span class="o">=</span> <span class="nx">tw</span><span class="p">.</span><span class="nx">h2</span><span class="sb">`text-2xl font-semibold`</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">ReadMore</span> <span class="o">=</span> <span class="nx">tw</span><span class="p">.</span><span class="nx">button</span><span class="sb">`hover:text-blue-500 text-lg py-2`</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">SearchItem</span>
</span></span></code></pre></div><p>Next, we have a component we will call <code>SearchItems.jsx</code>, which will be a list of the search results and look
something like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">React</span> <span class="nx">from</span> <span class="s2">&#34;react&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">SearchItem</span> <span class="nx">from</span> <span class="s2">&#34;./SearchItem&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">SearchItems</span> <span class="o">=</span> <span class="p">({</span> <span class="nx">results</span><span class="p">,</span> <span class="nx">query</span> <span class="p">})</span> <span class="p">=&gt;</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="nx">results</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">page</span> <span class="p">=&gt;</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">li</span> <span class="na">key</span><span class="o">=</span><span class="p">{</span><span class="nx">page</span><span class="p">.</span><span class="nx">id</span><span class="p">}&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">SearchItem</span> <span class="na">path</span><span class="o">=</span><span class="p">{</span><span class="sb">`</span><span class="si">${</span><span class="nx">page</span><span class="p">.</span><span class="nx">path</span><span class="si">}</span><span class="sb">`</span><span class="p">}</span> <span class="na">query</span><span class="o">=</span><span class="p">{</span><span class="nx">query</span><span class="p">}</span> <span class="na">title</span><span class="o">=</span><span class="p">{</span><span class="nx">page</span><span class="p">.</span><span class="nx">title</span><span class="p">}</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">li</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">))}</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">ul</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">SearchItems</span>
</span></span></code></pre></div><p>Now onto the main component, the component that will actually work out the results to show
to the client. We will call this component <code>Search.jsx</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">Index</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;elasticlunr&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">React</span><span class="p">,</span> <span class="p">{</span> <span class="nx">useState</span><span class="p">,</span> <span class="nx">useEffect</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;react&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">tw</span> <span class="nx">from</span> <span class="s2">&#34;twin.macro&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Input</span> <span class="nx">from</span> <span class="s2">&#34;./Input&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">SearchItems</span> <span class="nx">from</span> <span class="s2">&#34;./SearchItems&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">Search</span> <span class="o">=</span> <span class="p">({</span> <span class="nx">searchIndex</span> <span class="p">})</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">index</span> <span class="o">=</span> <span class="nx">Index</span><span class="p">.</span><span class="nx">load</span><span class="p">(</span><span class="nx">searchIndex</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">[</span><span class="nx">query</span><span class="p">,</span> <span class="nx">setQuery</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">[</span><span class="nx">results</span><span class="p">,</span> <span class="nx">setResults</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">([])</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">searchInput</span> <span class="o">=</span> <span class="nx">React</span><span class="p">.</span><span class="nx">createRef</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">useEffect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">searchResults</span><span class="p">(</span><span class="s2">&#34;blog&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nx">searchInput</span><span class="p">.</span><span class="nx">current</span><span class="p">.</span><span class="nx">focus</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span> <span class="p">[])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">function</span> <span class="nx">searchResults</span><span class="p">(</span><span class="nx">searchQuery</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">index</span><span class="p">.</span><span class="nx">search</span><span class="p">(</span><span class="nx">searchQuery</span><span class="p">,</span> <span class="p">{</span> <span class="nx">expand</span><span class="o">:</span> <span class="kc">true</span> <span class="p">}).</span><span class="nx">map</span><span class="p">(({</span> <span class="nx">ref</span> <span class="p">})</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span> <span class="nx">index</span><span class="p">.</span><span class="nx">documentStore</span><span class="p">.</span><span class="nx">getDoc</span><span class="p">(</span><span class="nx">ref</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="cl">    <span class="nx">setResults</span><span class="p">(</span><span class="nx">res</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">SearchContainer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">SearchInputContainer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">Input</span>
</span></span><span class="line"><span class="cl">          <span class="na">ref</span><span class="o">=</span><span class="p">{</span><span class="nx">searchInput</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">          <span class="na">className</span><span class="o">=</span><span class="s">&#34;px-2&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">label</span><span class="o">=</span><span class="s">&#34;Search&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">onChange</span><span class="o">=</span><span class="p">{</span><span class="nx">event</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="kr">const</span> <span class="nx">searchQuery</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">value</span>
</span></span><span class="line"><span class="cl">            <span class="nx">setQuery</span><span class="p">(</span><span class="nx">searchQuery</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="nx">searchResults</span><span class="p">(</span><span class="nx">searchQuery</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">          <span class="p">}}</span>
</span></span><span class="line"><span class="cl">          <span class="na">placeholder</span><span class="o">=</span><span class="s">&#34;Search&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="na">value</span><span class="o">=</span><span class="p">{</span><span class="nx">query</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">SearchInputContainer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">SearchItems</span> <span class="na">query</span><span class="o">=</span><span class="p">{</span><span class="nx">query</span><span class="p">}</span> <span class="na">results</span><span class="o">=</span><span class="p">{</span><span class="nx">results</span><span class="p">}</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">SearchContainer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">SearchContainer</span> <span class="o">=</span> <span class="nx">tw</span><span class="p">.</span><span class="nx">div</span><span class="sb">`max-w-screen-md mx-auto pt-8`</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">SearchInputContainer</span> <span class="o">=</span> <span class="nx">tw</span><span class="p">.</span><span class="nx">div</span><span class="sb">`flex w-full text-left h-12 text-lg focus-within:shadow-outline my-8`</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">Search</span>
</span></span></code></pre></div><p>Let&rsquo;s break this down:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">index</span> <span class="o">=</span> <span class="nx">Index</span><span class="p">.</span><span class="nx">load</span><span class="p">(</span><span class="nx">searchIndex</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">[</span><span class="nx">query</span><span class="p">,</span> <span class="nx">setQuery</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">[</span><span class="nx">results</span><span class="p">,</span> <span class="nx">setResults</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">([]);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">searchInput</span> <span class="o">=</span> <span class="nx">React</span><span class="p">.</span><span class="nx">createRef</span><span class="p">();</span>
</span></span></code></pre></div><p>The first part will be used to store some variables we need later on. Like storing the current query the client has
typed into the search, the current search results and a reference to the search input so we can focus into it.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="nx">useEffect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">searchResults</span><span class="p">(</span><span class="s2">&#34;blog&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">searchInput</span><span class="p">.</span><span class="nx">current</span><span class="p">.</span><span class="nx">focus</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span> <span class="p">[]);</span>
</span></span></code></pre></div><p>Next, the <code>useEffect</code> hook is called as soon as the component mounts, so as soon as the component mounts we will focus
into the <code>searchInput</code> component <code>searchInput.current.focus()</code> and we pre-fill the search with any blog post with
<code>&quot;blog&quot;</code> in it&rsquo;s title/tags <code>searchResults(&quot;blog&quot;)</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">searchResults</span><span class="p">(</span><span class="nx">searchQuery</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">index</span><span class="p">.</span><span class="nx">search</span><span class="p">(</span><span class="nx">searchQuery</span><span class="p">,</span> <span class="p">{</span> <span class="nx">expand</span><span class="o">:</span> <span class="kc">true</span> <span class="p">}).</span><span class="nx">map</span><span class="p">(({</span> <span class="nx">ref</span> <span class="p">})</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nx">index</span><span class="p">.</span><span class="nx">documentStore</span><span class="p">.</span><span class="nx">getDoc</span><span class="p">(</span><span class="nx">ref</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="nx">setResults</span><span class="p">(</span><span class="nx">res</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This is the actual function which gets our search results. It makes the query with <code>elasticlunr</code> and
stores the results in out state hook variable <code>result</code> using the set function <code>setResults(res)</code>. The first part
of the function does most of the heavy lifting returning a list of possible results to show to the client.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">Input</span>
</span></span><span class="line"><span class="cl">  <span class="na">ref</span><span class="o">=</span><span class="p">{</span><span class="nx">searchInput</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="na">className</span><span class="o">=</span><span class="s">&#34;px-2&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="na">label</span><span class="o">=</span><span class="s">&#34;Search&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="na">onChange</span><span class="o">=</span><span class="p">{(</span><span class="nx">e</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">searchQuery</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">setQuery</span><span class="p">(</span><span class="nx">searchQuery</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">searchResults</span><span class="p">(</span><span class="nx">searchQuery</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}}</span>
</span></span><span class="line"><span class="cl">  <span class="na">placeholder</span><span class="o">=</span><span class="s">&#34;Search&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="na">value</span><span class="o">=</span><span class="p">{</span><span class="nx">query</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">/&gt;</span>
</span></span></code></pre></div><blockquote>
<p>Note: <code>e.target.query</code> is the current value in the text input.</p>
</blockquote>
<p>Finally when taking a look at the input you can see the <code>ref={searchInput}</code> we defined above
being assigned here, so we can focus on this component. Next on any change i.e. a keypress we call the <code>onChange</code>
function. Where we update the query with the new search query <code>setQuery(searchQuery)</code> again using a state hook.
Then we call the <code>searchResults(searchQuery)</code> function which will update the results.</p>
<p>This is then shown to the client using our SearchItems component defined above like so:
<code>&lt;SearchItems query={query} results={results} /&gt;</code>.</p>
<p>Finally, we have a &ldquo;<code>SearchBar.tsx</code>&rdquo;, this is the component we will use to tie everything together.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">styled</span> <span class="nx">from</span> <span class="s2">&#34;@emotion/styled&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">graphql</span><span class="p">,</span> <span class="nx">StaticQuery</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;gatsby&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">React</span><span class="p">,</span> <span class="p">{</span> <span class="nx">useState</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;react&#34;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">tw</span> <span class="nx">from</span> <span class="s2">&#34;twin.macro&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">Search</span> <span class="nx">from</span> <span class="s2">&#34;./Search&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">SearchBar</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="p">[</span><span class="nx">showSearch</span><span class="p">,</span> <span class="nx">setShowSearch</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="kc">false</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kd">function</span> <span class="nx">hideSearch</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">placeholder</span> <span class="o">!==</span> <span class="s2">&#34;Search&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">setShowSearch</span><span class="p">(</span><span class="kc">false</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">SearchComponent</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">h1</span>
</span></span><span class="line"><span class="cl">        <span class="na">className</span><span class="o">=</span><span class="s">&#34;hover:cursor-pointer text-orange-800 text-2xl my-10&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">onClick</span><span class="o">=</span><span class="p">{()</span> <span class="p">=&gt;</span> <span class="nx">setShowSearch</span><span class="p">(</span><span class="o">!</span><span class="nx">showSearch</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl">      <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">Search</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">SearchOverlay</span>
</span></span><span class="line"><span class="cl">        <span class="na">onClick</span><span class="o">=</span><span class="p">{</span><span class="nx">e</span> <span class="p">=&gt;</span> <span class="nx">hideSearch</span><span class="p">(</span><span class="nx">e</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl">        <span class="na">onKeyPress</span><span class="o">=</span><span class="p">{</span><span class="nx">e</span> <span class="p">=&gt;</span> <span class="nx">hideSearch</span><span class="p">(</span><span class="nx">e</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl">        <span class="na">role</span><span class="o">=</span><span class="s">&#34;presentation&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="na">showSearch</span><span class="o">=</span><span class="p">{</span><span class="nx">showSearch</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">StaticQuery</span>
</span></span><span class="line"><span class="cl">          <span class="na">query</span><span class="o">=</span><span class="p">{</span><span class="nx">graphql</span><span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">            query SearchIndexQuery {
</span></span></span><span class="line"><span class="cl"><span class="sb">              siteSearchIndex {
</span></span></span><span class="line"><span class="cl"><span class="sb">                index
</span></span></span><span class="line"><span class="cl"><span class="sb">              }
</span></span></span><span class="line"><span class="cl"><span class="sb">            }
</span></span></span><span class="line"><span class="cl"><span class="sb">          `</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">          <span class="na">render</span><span class="o">=</span><span class="p">{</span><span class="nx">data</span> <span class="p">=&gt;</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">SearchContainer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">{</span><span class="nx">showSearch</span> <span class="o">&amp;&amp;</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">                <span class="p">&lt;</span><span class="nt">Search</span> <span class="na">searchIndex</span><span class="o">=</span><span class="p">{</span><span class="nx">data</span><span class="p">.</span><span class="nx">siteSearchIndex</span><span class="p">.</span><span class="nx">index</span><span class="p">}</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">)}</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">SearchContainer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">)}</span>
</span></span><span class="line"><span class="cl">        <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">SearchOverlay</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">SearchComponent</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">SearchComponent</span> <span class="o">=</span> <span class="nx">tw</span><span class="p">.</span><span class="nx">div</span><span class="sb">`flex-grow flex`</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">SearchContainer</span> <span class="o">=</span> <span class="nx">tw</span><span class="p">.</span><span class="nx">div</span><span class="sb">`overflow-y-scroll h-screen w-full`</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">SearchOverlay</span> <span class="o">=</span> <span class="nx">styled</span><span class="p">.</span><span class="nx">div</span><span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">  opacity: </span><span class="si">${</span><span class="nx">props</span> <span class="p">=&gt;</span> <span class="p">(</span><span class="nx">props</span><span class="p">.</span><span class="nx">showSearch</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="mi">0</span><span class="p">)</span><span class="si">}</span><span class="sb">;
</span></span></span><span class="line"><span class="cl"><span class="sb">  display: </span><span class="si">${</span><span class="nx">props</span> <span class="p">=&gt;</span> <span class="p">(</span><span class="nx">props</span><span class="p">.</span><span class="nx">showSearch</span> <span class="o">?</span> <span class="s2">&#34;flex&#34;</span> <span class="o">:</span> <span class="s2">&#34;none&#34;</span><span class="p">)</span><span class="si">}</span><span class="sb">;
</span></span></span><span class="line"><span class="cl"><span class="sb">  transition: opacity 150ms linear 0s;
</span></span></span><span class="line"><span class="cl"><span class="sb">  background: rgba(255, 255, 255, 0.9);
</span></span></span><span class="line"><span class="cl"><span class="sb">  </span><span class="si">${</span><span class="nx">tw</span><span class="sb">`fixed inset-0 bg-opacity-50 z-50 m-0 items-center justify-center h-screen w-screen`</span><span class="si">}</span><span class="sb">;
</span></span></span><span class="line"><span class="cl"><span class="sb">`</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">SearchBar</span>
</span></span></code></pre></div><p>Normally I would use a search icon which when pressed would show the search overlay. However to keep things simple we
will just use the text &ldquo;Search&rdquo;, which when clicked on will show our search overlay to the client.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">h1</span>
</span></span><span class="line"><span class="cl">  <span class="na">className</span><span class="o">=</span><span class="s">&#34;hover:cursor-pointer text-orange-800 text-2xl my-10&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="na">onClick</span><span class="o">=</span><span class="p">{()</span> <span class="p">=&gt;</span> <span class="nx">setShowSearch</span><span class="p">(</span><span class="o">!</span><span class="nx">showSearch</span><span class="p">)}</span>
</span></span><span class="line"><span class="cl"><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Search</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>The main job of this component is to toggle the search on/off. To do this we use a state hook like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">[</span><span class="nx">showSearch</span><span class="p">,</span> <span class="nx">setShowSearch</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">hideSearch</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">placeholder</span> <span class="o">!==</span> <span class="s2">&#34;Search&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">setShowSearch</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Where we have a function to hide the search if the user clicks anything outside of the search. Hence the if statement
<code>event.target.placeholder</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-jsx" data-lang="jsx"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">StaticQuery</span>
</span></span><span class="line"><span class="cl">  <span class="na">query</span><span class="o">=</span><span class="p">{</span><span class="nx">graphql</span><span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">    query SearchIndexQuery {
</span></span></span><span class="line"><span class="cl"><span class="sb">      siteSearchIndex {
</span></span></span><span class="line"><span class="cl"><span class="sb">        index
</span></span></span><span class="line"><span class="cl"><span class="sb">      }
</span></span></span><span class="line"><span class="cl"><span class="sb">    }
</span></span></span><span class="line"><span class="cl"><span class="sb">  `</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="na">render</span><span class="o">=</span><span class="p">{(</span><span class="nx">data</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">SearchContainer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span><span class="nx">showSearch</span> <span class="o">&amp;&amp;</span> <span class="p">&lt;</span><span class="nt">Search</span> <span class="na">searchIndex</span><span class="o">=</span><span class="p">{</span><span class="nx">data</span><span class="p">.</span><span class="nx">siteSearchIndex</span><span class="p">.</span><span class="nx">index</span><span class="p">}</span> <span class="p">/&gt;}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">SearchContainer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">)}</span>
</span></span><span class="line"><span class="cl"><span class="p">/&gt;</span>
</span></span></code></pre></div><p>The next interesting part is the Graphql query to get the search index from <code>elasticlunr</code>. We this pass as <code>searchIndex</code>
prop to our <code>Search</code> component we created above. This is the same search index we search against the current user
query. We also use conditional rendering we only show the <code>Search</code> component when <code>showSearch</code> is true.</p>
<p>And that&rsquo;s it! We successfully added search to our <code>Gatsby</code> blog alongside search highlighting. Thanks for reading.</p>
<h2 id="appendix">Appendix</h2>
<ul>
<li><a href="https://gitlab.com/hmajid2301/blog/-/tree/main/content/posts/2020-08-20-how-to-add-offline-search-to-a-gatsby-blog/source_code">Example source code</a></li>
<li><a href="https://gitlab.com/hmajid2301/personal-site/-/blob/d5f413310d4404fc6a1761a592f5e10840fc30df/src/components/organisms/SearchBar/SearchBar.tsx">Example Project</a></li>
<li><a href="https://unsplash.com/@markuswinkler?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Cover Photo by Markus Winkler</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>How to use Storybooks with MDX</title>
      <link>https://haseebmajid.dev/posts/2020-07-20-how-to-use-storybooks-with-mdx/</link>
      <pubDate>Mon, 20 Jul 2020 00:00:00 +0000</pubDate>
      <author>Me</author>
      <guid>https://haseebmajid.dev/posts/2020-07-20-how-to-use-storybooks-with-mdx/</guid>
      <description>&lt;p&gt;This article (sort of) continues on from my previous article
&lt;a href=&#34;../../posts/2020-06-29-how-to-use-storybooks-gatsby-babel-tailwind-typescript-together/&#34;&gt;How to use Storybooks, Gatsby, Babel, Tailwind, Typescript together&lt;/a&gt;.
In this article, we will document our React components using Storybook with MDX.&lt;/p&gt;
&lt;p&gt;You can find an example project using this &lt;a href=&#34;https://gitlab.com/hmajid2301/personal-site/-/tree/e415420744b2a8f49eddaf2d3058b23c70f46638/.storybook&#34;&gt;here&lt;/a&gt;,
you can also find a &lt;a href=&#34;https://storybook.haseebmajid.dev/&#34;&gt;demo site&lt;/a&gt; for said project.&lt;/p&gt;
&lt;h2 id=&#34;prerequisite&#34;&gt;Prerequisite&lt;/h2&gt;
&lt;p&gt;Just to make sure everyone&amp;rsquo;s on the same page let&amp;rsquo;s follow the same steps to setup Storybook as we had in the last article.
We will use the latest versions of Storybook (v6) so we can access the latest features. We will go over how we can use
these features in the next article.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>This article (sort of) continues on from my previous article
<a href="/posts/2020-06-29-how-to-use-storybooks-gatsby-babel-tailwind-typescript-together/">How to use Storybooks, Gatsby, Babel, Tailwind, Typescript together</a>.
In this article, we will document our React components using Storybook with MDX.</p>
<p>You can find an example project using this <a href="https://gitlab.com/hmajid2301/personal-site/-/tree/e415420744b2a8f49eddaf2d3058b23c70f46638/.storybook">here</a>,
you can also find a <a href="https://storybook.haseebmajid.dev/">demo site</a> for said project.</p>
<h2 id="prerequisite">Prerequisite</h2>
<p>Just to make sure everyone&rsquo;s on the same page let&rsquo;s follow the same steps to setup Storybook as we had in the last article.
We will use the latest versions of Storybook (v6) so we can access the latest features. We will go over how we can use
these features in the next article.</p>
<p>First, remove any lines in your <code>package.json</code> that start with <code>@storybook</code>. In my case,
I removed <code>@storybook/addon-actions</code>, <code>@storybook/add-links</code>, <code>@storybook/addons</code> and
<code>@storybook/react</code>. The typescript docgen modules will be used to parse our components and retrieve the props information
for Storybook. We will see a bit later with the control addon.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add --dev @storybook/addon-docs@6.0.0-beta.20 @storybook/addon-essentials@6.0.0-beta.20 <span class="se">\
</span></span></span><span class="line"><span class="cl">@storybook/addon-storysource@6.0.0-beta.20  @storybook/preset-typescript@1.2.0 <span class="se">\
</span></span></span><span class="line"><span class="cl">@storybook/react@6.0.0-beta.20 core-js@2.6.5 react-docgen-typescript@1.16.5 <span class="se">\
</span></span></span><span class="line"><span class="cl">react-docgen-typescript-loader@3.6.0
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">npx -p @storybook/cli sb init -f
</span></span><span class="line"><span class="cl">vim .storybook/main.js
</span></span><span class="line"><span class="cl">vim .storybook/preview.js
</span></span><span class="line"><span class="cl">vim preview-head.html
</span></span><span class="line"><span class="cl">vim webpack.config.js
</span></span></code></pre></div><p>Next, we will update the <code>main.js</code> file. This will tell Storybook where to look for the stories, in this case in the <code>src</code> folder
any file called <code>x.stories.mdx</code> or <code>x.stories.tsx</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">stories</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;../src/**/*.stories.@(tsx|mdx)&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="nx">addons</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;@storybook/addon-essentials&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;@storybook/addon-docs&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;@storybook/preset-typescript&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>Next, let&rsquo;s update the preview file. Here you can define global parameters and decorators. Again
will see more of this in the next article.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">React</span> <span class="nx">from</span> <span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">withA11y</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;@storybook/addon-a11y&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">action</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;@storybook/addon-actions&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">configure</span><span class="p">,</span> <span class="nx">addDecorator</span><span class="p">,</span> <span class="nx">addParameters</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;@storybook/react&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">INITIAL_VIEWPORTS</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;@storybook/addon-viewport&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// We will address this later.
</span></span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s1">&#39;../src/styles/globals.css&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s1">&#39;./main.css&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// automatically import all files ending in *.stories.js
</span></span></span><span class="line"><span class="cl"><span class="nx">configure</span><span class="p">(</span><span class="nx">require</span><span class="p">.</span><span class="nx">context</span><span class="p">(</span><span class="s1">&#39;../src&#39;</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="sr">/\.stories\.mdx$/</span><span class="p">),</span> <span class="nx">module</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Gatsby Setup
</span></span></span><span class="line"><span class="cl"><span class="c1">// ============================================
</span></span></span><span class="line"><span class="cl"><span class="c1">// Gatsby&#39;s Link overrides:
</span></span></span><span class="line"><span class="cl"><span class="c1">// Gatsby defines a global called ___loader to prevent its method calls from creating console errors you override it here
</span></span></span><span class="line"><span class="cl"><span class="nx">global</span><span class="p">.</span><span class="nx">___loader</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">enqueue</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">hovering</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Gatsby internal mocking to prevent unnecessary errors in storybook testing environment
</span></span></span><span class="line"><span class="cl"><span class="nx">global</span><span class="p">.</span><span class="nx">__PATH_PREFIX__</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// This is to utilized to override the window.___navigate method Gatsby defines and uses to report what path a Link would be taking us to if it wasn&#39;t inside a storybook
</span></span></span><span class="line"><span class="cl"><span class="nb">window</span><span class="p">.</span><span class="nx">___navigate</span> <span class="o">=</span> <span class="p">(</span><span class="nx">pathname</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">action</span><span class="p">(</span><span class="s1">&#39;NavigateTo:&#39;</span><span class="p">)(</span><span class="nx">pathname</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Storybook Addons
</span></span></span><span class="line"><span class="cl"><span class="c1">// ============================================
</span></span></span><span class="line"><span class="cl"><span class="nx">addParameters</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">viewport</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">viewports</span><span class="o">:</span> <span class="nx">INITIAL_VIEWPORTS</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">defaultViewport</span><span class="o">:</span> <span class="s1">&#39;responsive&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">panelPosition</span><span class="o">:</span> <span class="s1">&#39;right&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">storySort</span><span class="o">:</span> <span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nx">a</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">kind</span> <span class="o">===</span> <span class="nx">b</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">kind</span>
</span></span><span class="line"><span class="cl">        <span class="o">?</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">        <span class="o">:</span> <span class="nx">a</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">id</span><span class="p">.</span><span class="nx">localeCompare</span><span class="p">(</span><span class="nx">b</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">id</span><span class="p">,</span> <span class="kc">undefined</span><span class="p">,</span> <span class="p">{</span> <span class="nx">numeric</span><span class="o">:</span> <span class="kc">true</span> <span class="p">}),</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">inline</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Storybook Decorators
</span></span></span><span class="line"><span class="cl"><span class="c1">// ============================================
</span></span></span><span class="line"><span class="cl"><span class="nx">addDecorator</span><span class="p">(</span><span class="nx">withA11y</span><span class="p">);</span>
</span></span></code></pre></div><p>If we want to use any custom fonts, such as google fonts or other styles within our Tailwind, we need to
define them here.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span>
</span></span><span class="line"><span class="cl">  <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://fonts.googleapis.com/css2?family=Inter:wght@600,900&amp;display=swap&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">/&gt;</span>
</span></span></code></pre></div><p>Storybook uses webpack, so if we want to add extra webpack options, we do that here. This allows us to use
things like Babel and PostCSS loader.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">({</span> <span class="nx">config</span> <span class="p">})</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">config</span><span class="p">.</span><span class="nx">module</span><span class="p">.</span><span class="nx">rules</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">use</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">loader</span> <span class="o">=</span> <span class="nx">require</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="s2">&#34;babel-loader&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">config</span><span class="p">.</span><span class="nx">module</span><span class="p">.</span><span class="nx">rules</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">use</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">options</span><span class="p">.</span><span class="nx">presets</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="nx">require</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="s2">&#34;@babel/preset-react&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nx">require</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="s2">&#34;@babel/preset-env&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">config</span><span class="p">.</span><span class="nx">module</span><span class="p">.</span><span class="nx">rules</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nx">test</span><span class="o">:</span> <span class="sr">/\.(ts|tsx)$/</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">loader</span><span class="o">:</span> <span class="nx">require</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="s2">&#34;babel-loader&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">presets</span><span class="o">:</span> <span class="p">[[</span><span class="s2">&#34;react-app&#34;</span><span class="p">,</span> <span class="p">{</span> <span class="nx">flow</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">typescript</span><span class="o">:</span> <span class="kc">true</span> <span class="p">}]],</span>
</span></span><span class="line"><span class="cl">      <span class="nx">plugins</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">config</span><span class="p">.</span><span class="nx">module</span><span class="p">.</span><span class="nx">rules</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nx">test</span><span class="o">:</span> <span class="sr">/\.css$/</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">use</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">loader</span><span class="o">:</span> <span class="s2">&#34;postcss-loader&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">sourceMap</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">config</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;./.storybook/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">config</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h2 id="add-ons">Add-ons</h2>
<p>Let&rsquo;s install the extra add-ons we will use with storybooks:</p>
<ul>
<li><a href="https://github.com/storybookjs/storybook/tree/next/addons/a11y">a11y</a>: Will list any accessibility issues with your component</li>
<li><a href="https://github.com/storybookjs/storybook/tree/next/addons/controls">controls</a>: Is the new version of the addon-knobs and will let you control the props you pass in</li>
<li><a href="https://github.com/storybookjs/storybook/tree/next/addons/viewport">viewport</a>: Allows you to test how your component looks, with different viewports like iPhone, 13&quot; etc</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add --dev @storybook/addon-a11y@6.0.0-beta.20 @storybook/addon-controls@6.0.0-beta.15 <span class="se">\
</span></span></span><span class="line"><span class="cl">                @storybook/addon-viewport@6.0.0-beta.20
</span></span></code></pre></div><p>Let&rsquo;s update the <code>.storybook/main.js</code> so that Storybook uses the new addons we&rsquo;ve just installed.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">stories</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;../src/**/*.stories.@(tsx|mdx)&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="nx">addons</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;@storybook/addon-a11y&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;@storybook/addon-controls&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;@storybook/addon-essentials&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;@storybook/preset-typescript&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;@storybook/addon-viewport&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>Next, let&rsquo;s update <code>.storybook/preview.js</code> so it looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">React</span> <span class="nx">from</span> <span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">withA11y</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;@storybook/addon-a11y&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">action</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;@storybook/addon-actions&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">configure</span><span class="p">,</span> <span class="nx">addDecorator</span><span class="p">,</span> <span class="nx">addParameters</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;@storybook/react&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">INITIAL_VIEWPORTS</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;@storybook/addon-viewport&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// We will address this later.
</span></span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s1">&#39;../src/styles/globals.css&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s1">&#39;./main.css&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// automatically import all files ending in *.stories.js
</span></span></span><span class="line"><span class="cl"><span class="nx">configure</span><span class="p">(</span><span class="nx">require</span><span class="p">.</span><span class="nx">context</span><span class="p">(</span><span class="s1">&#39;../src&#39;</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="sr">/\.stories\.mdx$/</span><span class="p">),</span> <span class="nx">module</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Gatsby Setup
</span></span></span><span class="line"><span class="cl"><span class="c1">// ============================================
</span></span></span><span class="line"><span class="cl"><span class="c1">// Gatsby&#39;s Link overrides:
</span></span></span><span class="line"><span class="cl"><span class="c1">// Gatsby defines a global called ___loader to prevent its method calls from creating console errors you override it here
</span></span></span><span class="line"><span class="cl"><span class="nx">global</span><span class="p">.</span><span class="nx">___loader</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">enqueue</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">hovering</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Gatsby internal mocking to prevent unnecessary errors in storybook testing environment
</span></span></span><span class="line"><span class="cl"><span class="nx">global</span><span class="p">.</span><span class="nx">__PATH_PREFIX__</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="c1">// This is to utilized to override the window.___navigate method Gatsby defines and uses to report what path a Link would be taking us to if it wasn&#39;t inside a storybook
</span></span></span><span class="line"><span class="cl"><span class="nb">window</span><span class="p">.</span><span class="nx">___navigate</span> <span class="o">=</span> <span class="p">(</span><span class="nx">pathname</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">action</span><span class="p">(</span><span class="s1">&#39;NavigateTo:&#39;</span><span class="p">)(</span><span class="nx">pathname</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Storybook Addons
</span></span></span><span class="line"><span class="cl"><span class="c1">// ============================================
</span></span></span><span class="line"><span class="cl"><span class="nx">addParameters</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">viewport</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">viewports</span><span class="o">:</span> <span class="nx">INITIAL_VIEWPORTS</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">defaultViewport</span><span class="o">:</span> <span class="s1">&#39;responsive&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">panelPosition</span><span class="o">:</span> <span class="s1">&#39;right&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">storySort</span><span class="o">:</span> <span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="nx">a</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">kind</span> <span class="o">===</span> <span class="nx">b</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">kind</span>
</span></span><span class="line"><span class="cl">        <span class="o">?</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">        <span class="o">:</span> <span class="nx">a</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">id</span><span class="p">.</span><span class="nx">localeCompare</span><span class="p">(</span><span class="nx">b</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">id</span><span class="p">,</span> <span class="kc">undefined</span><span class="p">,</span> <span class="p">{</span> <span class="nx">numeric</span><span class="o">:</span> <span class="kc">true</span> <span class="p">}),</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">inline</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Storybook Decorators
</span></span></span><span class="line"><span class="cl"><span class="c1">// ============================================
</span></span></span><span class="line"><span class="cl"><span class="nx">addDecorator</span><span class="p">(</span><span class="nx">withA11y</span><span class="p">);</span>
</span></span></code></pre></div><p>The following snippet allows Storybook to show the default list of viewports. You can customise this list by following
<a href="https://github.com/storybookjs/storybook/tree/next/addons/viewport">this README here</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">viewport</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">viewports</span><span class="o">:</span> <span class="nx">INITIAL_VIEWPORTS</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">defaultViewport</span><span class="o">:</span> <span class="s2">&#34;responsive&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This part of the <code>addParameters</code> sorts all of our storybook components in descending capital order (in the left panel).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">panelPosition</span><span class="o">:</span> <span class="s2">&#34;right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">storySort</span><span class="o">:</span> <span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">a</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">kind</span> <span class="o">===</span> <span class="nx">b</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">kind</span>
</span></span><span class="line"><span class="cl">      <span class="o">?</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">      <span class="o">:</span> <span class="nx">a</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">id</span><span class="p">.</span><span class="nx">localeCompare</span><span class="p">(</span><span class="nx">b</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">id</span><span class="p">,</span> <span class="kc">undefined</span><span class="p">,</span> <span class="p">{</span> <span class="nx">numeric</span><span class="o">:</span> <span class="kc">true</span> <span class="p">}),</span>
</span></span><span class="line"><span class="cl"><span class="p">},</span>
</span></span></code></pre></div><p>The final part will add a panel which will list accessibility (a11y) issues with our components, such as missing an
<code>alt</code> in an <code>&lt;img&gt;</code> tag.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">addDecorator</span><span class="p">(</span><span class="nx">withA11y</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="mdx">MDX</h2>
<p>MDX is markdown mixed with JSX, it lets us render &ldquo;React&rdquo; code within markdown files, whilst providing all the features
of markdown as well as headers and hyperlinks. So I feel it is a perfect way to document your Storybooks.</p>
<h3 id="introduction-mdx">Introduction MDX</h3>
<p>This file is an example of a story which will not render any of our components. It&rsquo;s simply there for documentation. This
file will list the colour palette of the app. It will get the colours from our <code>tailwind.config.js</code> (mixed with the default one).
So if you changed the values in the tailwind config file, it&rsquo;ll also change them in this story.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl">import {
</span></span><span class="line"><span class="cl">  Meta,
</span></span><span class="line"><span class="cl">  ColorPalette,
</span></span><span class="line"><span class="cl">  ColorItem,
</span></span><span class="line"><span class="cl">  Typeset,
</span></span><span class="line"><span class="cl">} from &#34;<span class="ni">@storybook/addon</span>-docs/blocks&#34;;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">import { Themes } from &#34;~/styles&#34;;
</span></span><span class="line"><span class="cl">import { theme } from &#34;~/utils/tailwindConfig&#34;;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;Meta title=&#34;Introduction&#34; /&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gh"># Introduction
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">This project uses Tailwindcss so a lot of colour props are passed in as CSS classes that
</span></span><span class="line"><span class="cl">Tailwind applies styling to i.e. <span class="sb">`text-blue-500`</span> to use the primary blue. You can find the
</span></span><span class="line"><span class="cl">full colour palette [<span class="nt">here</span>](<span class="na">https://tailwindcss.com/docs/customizing-colors/#default-color-palette</span>).
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">## Colours
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">### Light Theme
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;ColorPalette&gt;
</span></span><span class="line"><span class="cl">  &lt;ColorItem
</span></span><span class="line"><span class="cl">    title=&#34;theme.color.background&#34;
</span></span><span class="line"><span class="cl">    subtitle=&#34;White&#34;
</span></span><span class="line"><span class="cl">    colors={[theme.colors.white]}
</span></span><span class="line"><span class="cl">  /&gt;
</span></span><span class="line"><span class="cl">  &lt;ColorItem
</span></span><span class="line"><span class="cl">    title=&#34;theme.color.text&#34;
</span></span><span class="line"><span class="cl">    subtitle=&#34;Black&#34;
</span></span><span class="line"><span class="cl">    colors={[theme.colors.black]}
</span></span><span class="line"><span class="cl">  /&gt;
</span></span><span class="line"><span class="cl">  &lt;ColorItem
</span></span><span class="line"><span class="cl">    title=&#34;theme.color.header&#34;
</span></span><span class="line"><span class="cl">    subtitle=&#34;Grey&#34;
</span></span><span class="line"><span class="cl">    colors={[theme.colors.gray[700]]}
</span></span><span class="line"><span class="cl">  /&gt;
</span></span><span class="line"><span class="cl">&lt;/ColorPalette&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">### Dark Theme
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;ColorPalette&gt;
</span></span><span class="line"><span class="cl">  &lt;ColorItem
</span></span><span class="line"><span class="cl">    title=&#34;theme.color.background&#34;
</span></span><span class="line"><span class="cl">    subtitle=&#34;White&#34;
</span></span><span class="line"><span class="cl">    colors={[theme.colors.gray[900]]}
</span></span><span class="line"><span class="cl">  /&gt;
</span></span><span class="line"><span class="cl">  &lt;ColorItem
</span></span><span class="line"><span class="cl">    title=&#34;theme.color.text&#34;
</span></span><span class="line"><span class="cl">    subtitle=&#34;White&#34;
</span></span><span class="line"><span class="cl">    colors={[theme.colors.white]}
</span></span><span class="line"><span class="cl">  /&gt;
</span></span><span class="line"><span class="cl">  &lt;ColorItem
</span></span><span class="line"><span class="cl">    title=&#34;theme.color.header&#34;
</span></span><span class="line"><span class="cl">    subtitle=&#34;Grey&#34;
</span></span><span class="line"><span class="cl">    colors={[theme.colors.gray[200]]}
</span></span><span class="line"><span class="cl">  /&gt;
</span></span><span class="line"><span class="cl">&lt;/ColorPalette&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">### Shared
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;ColorPalette&gt;
</span></span><span class="line"><span class="cl">  &lt;ColorItem
</span></span><span class="line"><span class="cl">    title=&#34;theme.color.primary&#34;
</span></span><span class="line"><span class="cl">    subtitle=&#34;Blue&#34;
</span></span><span class="line"><span class="cl">    colors={[theme.colors.blue[500]]}
</span></span><span class="line"><span class="cl">  /&gt;
</span></span><span class="line"><span class="cl">  &lt;ColorItem
</span></span><span class="line"><span class="cl">    title=&#34;theme.color.secondary&#34;
</span></span><span class="line"><span class="cl">    subtitle=&#34;Orange&#34;
</span></span><span class="line"><span class="cl">    colors={[theme.colors.orange[500]]}
</span></span><span class="line"><span class="cl">  /&gt;
</span></span><span class="line"><span class="cl">  &lt;ColorItem
</span></span><span class="line"><span class="cl">    title=&#34;theme.color.tertiary&#34;
</span></span><span class="line"><span class="cl">    subtitle=&#34;Grey&#34;
</span></span><span class="line"><span class="cl">    colors={[theme.colors.gray[500]]}
</span></span><span class="line"><span class="cl">  /&gt;
</span></span><span class="line"><span class="cl">  &lt;ColorItem
</span></span><span class="line"><span class="cl">    title=&#34;Monochrome&#34;
</span></span><span class="line"><span class="cl">    colors={[
</span></span><span class="line"><span class="cl">      theme.colors.monochrome[900],
</span></span><span class="line"><span class="cl">      theme.colors.monochrome[800],
</span></span><span class="line"><span class="cl">      theme.colors.monochrome[700],
</span></span><span class="line"><span class="cl">      theme.colors.monochrome[600],
</span></span><span class="line"><span class="cl">      theme.colors.monochrome[500],
</span></span><span class="line"><span class="cl">      theme.colors.monochrome[400],
</span></span><span class="line"><span class="cl">      theme.colors.monochrome[300],
</span></span><span class="line"><span class="cl">      theme.colors.monochrome[200],
</span></span><span class="line"><span class="cl">      theme.colors.monochrome[100],
</span></span><span class="line"><span class="cl">    ]}
</span></span><span class="line"><span class="cl">  /&gt;
</span></span><span class="line"><span class="cl">&lt;/ColorPalette&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">## Typography
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">### Header
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gs">**Font**</span>: Inter
</span></span><span class="line"><span class="cl"><span class="gs">**Weight**</span>: 900
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;Typeset fontFamily=&#34;Inter&#34; fontSizes={[10, 20, 30, 40, 50]} fontWeight={900} /&gt;
</span></span></code></pre></div><p>The first part contains our imports so we can render the colours correctly. It also includes a meta tag where we list
the title of the page, each of our stories should have one of these meta tags.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl">import {
</span></span><span class="line"><span class="cl">Meta,
</span></span><span class="line"><span class="cl">ColorPalette,
</span></span><span class="line"><span class="cl">ColorItem,
</span></span><span class="line"><span class="cl">Typeset,
</span></span><span class="line"><span class="cl">} from &#34;<span class="ni">@storybook/addon</span>-docs/blocks&#34;;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">import { Themes } from &#34;~/styles&#34;;
</span></span><span class="line"><span class="cl">import { theme } from &#34;~/utils/tailwindConfig&#34;;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;Meta title=&#34;Introduction&#34; /&gt;
</span></span></code></pre></div><h4 id="utils">Utils</h4>
<p>We use this simple script to resolve the values of the tailwind config so we can use them like so
<code>theme.colors.orange[500]</code>. We pass the script our current config, it then combines it with the default config and
generates a final tailwind config. From this, we get the theme part as this is all we need to retrieve our colours.
So with a class name of say <code>text-blue-500</code> to get the colour, we would do <code>theme.colors.blue[500]</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">resolveConfig</span> <span class="nx">from</span> <span class="s1">&#39;tailwindcss/resolveConfig&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">tailwindConfig</span> <span class="nx">from</span> <span class="s1">&#39;../../tailwind.config&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">config</span> <span class="o">=</span> <span class="nx">resolveConfig</span><span class="p">(</span><span class="nx">tailwindConfig</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">{</span> <span class="nx">theme</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">config</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">config</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="p">{</span> <span class="nx">theme</span> <span class="p">};</span>
</span></span></code></pre></div><h2 id="component">Component</h2>
<p>Now let&rsquo;s take a look at how to create a Storybook story for one of our components.</p>
<h3 id="logo">Logo</h3>
<p>Say we have a component that looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-tsx" data-lang="tsx"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">React</span> <span class="kr">from</span> <span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">tw</span> <span class="kr">from</span> <span class="s1">&#39;twin.macro&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">interface</span> <span class="nx">Props</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="cm">/** The colour of the opening and closing tags. */</span>
</span></span><span class="line"><span class="cl">  <span class="nx">accent?</span>: <span class="kt">string</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="cm">/** The colour of main text. */</span>
</span></span><span class="line"><span class="cl">  <span class="nx">color?</span>: <span class="kt">string</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="cm">/** The colour when you hover over the logo. */</span>
</span></span><span class="line"><span class="cl">  <span class="nx">hoverColor?</span>: <span class="kt">string</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="cm">/** The main text of the logo for example, your name. */</span>
</span></span><span class="line"><span class="cl">  <span class="nx">text</span>: <span class="kt">string</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="cm">/** The size of the main text  */</span>
</span></span><span class="line"><span class="cl">  <span class="nx">size</span><span class="o">?:</span> <span class="s1">&#39;xs&#39;</span> <span class="o">|</span> <span class="s1">&#39;sm&#39;</span> <span class="o">|</span> <span class="s1">&#39;lg&#39;</span> <span class="o">|</span> <span class="s1">&#39;xl&#39;</span> <span class="o">|</span> <span class="s1">&#39;2xl&#39;</span> <span class="o">|</span> <span class="s1">&#39;3xl&#39;</span> <span class="o">|</span> <span class="s1">&#39;4xl&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">Logo</span> <span class="o">=</span> <span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">accent</span> <span class="o">=</span> <span class="s1">&#39;black&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">color</span> <span class="o">=</span> <span class="s1">&#39;black&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">hoverColor</span> <span class="o">=</span> <span class="s1">&#39;blue-500&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">text</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">size</span> <span class="o">=</span> <span class="s1">&#39;2xl&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="o">:</span> <span class="nx">Props</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">LogoContainer</span>
</span></span><span class="line"><span class="cl">    <span class="na">className</span><span class="o">=</span><span class="p">{</span><span class="sb">`hover:text-</span><span class="si">${</span><span class="nx">hoverColor</span><span class="si">}</span><span class="sb"> text-</span><span class="si">${</span><span class="nx">color</span><span class="si">}</span><span class="sb"> lg:text-</span><span class="si">${</span><span class="nx">size</span><span class="si">}</span><span class="sb">
</span></span></span><span class="line"><span class="cl"><span class="sb">    md:text-xl sm:text-md text-sm`</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">Tag</span> <span class="na">className</span><span class="o">=</span><span class="p">{</span><span class="sb">`text-</span><span class="si">${</span><span class="nx">accent</span><span class="si">}</span><span class="sb">`</span><span class="p">}</span> <span class="na">data-testid</span><span class="o">=</span><span class="s">&#34;OpeningTag&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="o">&amp;</span><span class="nx">lt</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">Tag</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="nx">text</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">Tag</span> <span class="na">className</span><span class="o">=</span><span class="p">{</span><span class="sb">`text-</span><span class="si">${</span><span class="nx">accent</span><span class="si">}</span><span class="sb">`</span><span class="p">}</span> <span class="na">data-testid</span><span class="o">=</span><span class="s">&#34;ClosingTag&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="o">/&amp;</span><span class="nx">gt</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">Tag</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">LogoContainer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">LogoContainer</span> <span class="o">=</span> <span class="nx">tw</span><span class="p">.</span><span class="nx">div</span><span class="sb">`cursor-pointer font-header font-black tracking-wide `</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">Tag</span> <span class="o">=</span> <span class="nx">tw</span><span class="p">.</span><span class="nx">span</span><span class="sb">``</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">Logo</span><span class="p">;</span>
</span></span></code></pre></div><p>Since we are using Typescript, we define our props as an interface. The comments above each item in the interface
will be parsed by docgen and shown in our story (we will see this a bit later). It will let the user know what
the prop is so they know how to adjust it. We will also see in a little bit.</p>
<p>The rest of the file is a normal React component, nothing special here. So how do we document this with Storybooks UI?</p>
<h3 id="logostoriesmdx">Logo.stories.mdx</h3>
<p>So now onto the real meat and potatoes of this article, let&rsquo;s document a React component. So I think one of the coolest
parts of the new Storybook is the controls add-on we installed earlier. So how we do this is to provide the props as
<code>args</code> object. Then we pass this onto the component give a name called <code>Basic</code> below.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl">import { Meta, Story, Preview, Props } from &#34;<span class="ni">@storybook/addon</span>-docs/blocks&#34;;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">import { theme } from &#34;~/styles&#34;;
</span></span><span class="line"><span class="cl">import Logo from &#34;./Logo&#34;;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;Meta title=&#34;Logo&#34; component={Logo} /&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gh"># Logo
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;Preview&gt;
</span></span><span class="line"><span class="cl">  &lt;Story
</span></span><span class="line"><span class="cl">    name=&#34;Basic&#34;
</span></span><span class="line"><span class="cl">    args={{
</span></span><span class="line"><span class="cl">      accent: &#34;gray-500&#34;,
</span></span><span class="line"><span class="cl">      color: &#34;black&#34;,
</span></span><span class="line"><span class="cl">      hoverColor: &#34;blue-500&#34;,
</span></span><span class="line"><span class="cl">      text: &#34;Haseeb&#34;,
</span></span><span class="line"><span class="cl">    }}
</span></span><span class="line"><span class="cl"><span class="k">  &gt;
</span></span></span><span class="line"><span class="cl"><span class="ge">    {(args) =&gt; &lt;Logo {...args} /&gt;}
</span></span></span><span class="line"><span class="cl">  &lt;/Story&gt;
</span></span><span class="line"><span class="cl">&lt;/Preview&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;Props story=&#34;Basic&#34; /&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">## Accent
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">You can adjust the accent (tags) color by passing the <span class="sb">`accent`</span> prop.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;Preview&gt;
</span></span><span class="line"><span class="cl">  &lt;Story name=&#34;Accent Colour&#34;&gt;
</span></span><span class="line"><span class="cl">    &lt;div&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo
</span></span><span class="line"><span class="cl">        accent=&#34;gray-500&#34;
</span></span><span class="line"><span class="cl">        color=&#34;blue-500&#34;
</span></span><span class="line"><span class="cl">        text=&#34;Haseeb&#34;
</span></span><span class="line"><span class="cl">        hoverColor=&#34;blue-500&#34;
</span></span><span class="line"><span class="cl">      /&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo
</span></span><span class="line"><span class="cl">        accent=&#34;gray-500&#34;
</span></span><span class="line"><span class="cl">        color=&#34;black&#34;
</span></span><span class="line"><span class="cl">        text=&#34;Haseeb&#34;
</span></span><span class="line"><span class="cl">        hoverColor=&#34;blue-500&#34;
</span></span><span class="line"><span class="cl">      /&gt;
</span></span><span class="line"><span class="cl">    &lt;/div&gt;
</span></span><span class="line"><span class="cl">  &lt;/Story&gt;
</span></span><span class="line"><span class="cl">&lt;/Preview&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">## Colour
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">You can change the default colour of the logo by passing the <span class="sb">`color`</span> prop.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;Preview&gt;
</span></span><span class="line"><span class="cl">  &lt;Story name=&#34;Main Text Colour&#34;&gt;
</span></span><span class="line"><span class="cl">    &lt;div&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo
</span></span><span class="line"><span class="cl">        accent=&#34;gray-500&#34;
</span></span><span class="line"><span class="cl">        color=&#34;gray-500&#34;
</span></span><span class="line"><span class="cl">        text=&#34;Haseeb&#34;
</span></span><span class="line"><span class="cl">        hoverColor=&#34;blue-500&#34;
</span></span><span class="line"><span class="cl">      /&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo
</span></span><span class="line"><span class="cl">        accent=&#34;gray-500&#34;
</span></span><span class="line"><span class="cl">        color=&#34;blue-500&#34;
</span></span><span class="line"><span class="cl">        text=&#34;Haseeb&#34;
</span></span><span class="line"><span class="cl">        hoverColor=&#34;blue-500&#34;
</span></span><span class="line"><span class="cl">      /&gt;
</span></span><span class="line"><span class="cl">    &lt;/div&gt;
</span></span><span class="line"><span class="cl">  &lt;/Story&gt;
</span></span><span class="line"><span class="cl">&lt;/Preview&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">## Hover Color
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">You can change the hover colour of the logo by passing the <span class="sb">`hoverColor`</span> prop.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;Preview&gt;
</span></span><span class="line"><span class="cl">  &lt;Story name=&#34;Hover Colour&#34;&gt;
</span></span><span class="line"><span class="cl">    &lt;div&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo
</span></span><span class="line"><span class="cl">        accent=&#34;gray-500&#34;
</span></span><span class="line"><span class="cl">        color=&#34;gray-500&#34;
</span></span><span class="line"><span class="cl">        text=&#34;Haseeb&#34;
</span></span><span class="line"><span class="cl">        hoverColor=&#34;blue-500&#34;
</span></span><span class="line"><span class="cl">      /&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo
</span></span><span class="line"><span class="cl">        accent=&#34;gray-500&#34;
</span></span><span class="line"><span class="cl">        color=&#34;gray-500&#34;
</span></span><span class="line"><span class="cl">        text=&#34;Haseeb&#34;
</span></span><span class="line"><span class="cl">        hoverColor=&#34;gray-500&#34;
</span></span><span class="line"><span class="cl">      /&gt;
</span></span><span class="line"><span class="cl">    &lt;/div&gt;
</span></span><span class="line"><span class="cl">  &lt;/Story&gt;
</span></span><span class="line"><span class="cl">&lt;/Preview&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">## Text
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">You must set the main text for the logo, such as a name using the <span class="sb">`text`</span> prop.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;Preview&gt;
</span></span><span class="line"><span class="cl">  &lt;Story name=&#34;Main Text&#34;&gt;
</span></span><span class="line"><span class="cl">    &lt;div&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo text=&#34;Haseeb&#34; /&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo text=&#34;John Smith&#34; /&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo text=&#34;xyz.io&#34; /&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo text=&#34;Random&#34; /&gt;
</span></span><span class="line"><span class="cl">    &lt;/div&gt;
</span></span><span class="line"><span class="cl">  &lt;/Story&gt;
</span></span><span class="line"><span class="cl">&lt;/Preview&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">## Size
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">You can also adjust the size of the logo by passing the <span class="sb">`size`</span> prop.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;Preview&gt;
</span></span><span class="line"><span class="cl">  &lt;Story name=&#34;Text Size&#34;&gt;
</span></span><span class="line"><span class="cl">    &lt;div&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo accent=&#34;gray-700&#34; size=&#34;xs&#34; text=&#34;Haseeb&#34; /&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo accent=&#34;gray-700&#34; size=&#34;sm&#34; text=&#34;Haseeb&#34; /&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo accent=&#34;gray-700&#34; size=&#34;lg&#34; text=&#34;Haseeb&#34; /&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo accent=&#34;gray-700&#34; size=&#34;xl&#34; text=&#34;Haseeb&#34; /&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo accent=&#34;gray-700&#34; size=&#34;2xl&#34; text=&#34;Haseeb&#34; /&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo accent=&#34;gray-700&#34; size=&#34;3xl&#34; text=&#34;Haseeb&#34; /&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo accent=&#34;gray-700&#34; size=&#34;4xl&#34; text=&#34;Haseeb&#34; /&gt;
</span></span><span class="line"><span class="cl">    &lt;/div&gt;
</span></span><span class="line"><span class="cl">  &lt;/Story&gt;
</span></span><span class="line"><span class="cl">&lt;/Preview&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">## Dark Mode
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Example dark mode component.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;Preview&gt;
</span></span><span class="line"><span class="cl">  &lt;Story name=&#34;Dark Mode&#34;&gt;
</span></span><span class="line"><span class="cl">    &lt;div class={<span class="sb">`bg-${theme.dark.background} p-5`</span>}&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo
</span></span><span class="line"><span class="cl">        accent=&#34;gray-500&#34;
</span></span><span class="line"><span class="cl">        color={theme.dark.text}
</span></span><span class="line"><span class="cl">        hoverColor={theme.colors.primary}
</span></span><span class="line"><span class="cl">        text=&#34;Haseeb&#34;
</span></span><span class="line"><span class="cl">      /&gt;
</span></span><span class="line"><span class="cl">    &lt;/div&gt;
</span></span><span class="line"><span class="cl">  &lt;/Story&gt;
</span></span><span class="line"><span class="cl">&lt;/Preview&gt;
</span></span></code></pre></div><p>In the diagram below is the <code>docs</code> tab in Storybook (default is the <code>canvas</code> tab). As you can see in the diagrams
below we have a table which is taken from the props in our Logo component. We also have the comments shown as
descriptions, any default values we assigned and the current value. Which we set above in the stories file
<code>Logo.stories.mdx</code>. We can then edit the values within the controls table, it will automatically re-render our
component on the fly. You can see this in the second image.</p>
<p><img
        loading="lazy"
        src="/posts/2020-07-20-how-to-use-storybooks-with-mdx/images/controls.png"
        type=""
        alt="Storybook Controls"
        
      /></p>
<p><img
        loading="lazy"
        src="/posts/2020-07-20-how-to-use-storybooks-with-mdx/images/controls2.png"
        type=""
        alt="Storybook Controls2"
        
      /></p>
<p>Then we can define our regular stories components like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl"><span class="gu">## Accent
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">You can adjust the accent (tags) color by passing the <span class="sb">`accent`</span> prop.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;Preview&gt;
</span></span><span class="line"><span class="cl">  &lt;Story name=&#34;Accent Colour&#34;&gt;
</span></span><span class="line"><span class="cl">    &lt;div&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo
</span></span><span class="line"><span class="cl">        accent=&#34;gray-500&#34;
</span></span><span class="line"><span class="cl">        color=&#34;blue-500&#34;
</span></span><span class="line"><span class="cl">        text=&#34;Haseeb&#34;
</span></span><span class="line"><span class="cl">        hoverColor=&#34;blue-500&#34;
</span></span><span class="line"><span class="cl">      /&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo
</span></span><span class="line"><span class="cl">        accent=&#34;gray-500&#34;
</span></span><span class="line"><span class="cl">        color=&#34;black&#34;
</span></span><span class="line"><span class="cl">        text=&#34;Haseeb&#34;
</span></span><span class="line"><span class="cl">        hoverColor=&#34;blue-500&#34;
</span></span><span class="line"><span class="cl">      /&gt;
</span></span><span class="line"><span class="cl">    &lt;/div&gt;
</span></span><span class="line"><span class="cl">  &lt;/Story&gt;
</span></span><span class="line"><span class="cl">&lt;/Preview&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">## Colour
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">You can change the default colour of the logo by passing the <span class="sb">`color`</span> prop.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;Preview&gt;
</span></span><span class="line"><span class="cl">  &lt;Story name=&#34;Main Text Colour&#34;&gt;
</span></span><span class="line"><span class="cl">    &lt;div&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo
</span></span><span class="line"><span class="cl">        accent=&#34;gray-500&#34;
</span></span><span class="line"><span class="cl">        color=&#34;gray-500&#34;
</span></span><span class="line"><span class="cl">        text=&#34;Haseeb&#34;
</span></span><span class="line"><span class="cl">        hoverColor=&#34;blue-500&#34;
</span></span><span class="line"><span class="cl">      /&gt;
</span></span><span class="line"><span class="cl">      &lt;Logo
</span></span><span class="line"><span class="cl">        accent=&#34;gray-500&#34;
</span></span><span class="line"><span class="cl">        color=&#34;blue-500&#34;
</span></span><span class="line"><span class="cl">        text=&#34;Haseeb&#34;
</span></span><span class="line"><span class="cl">        hoverColor=&#34;blue-500&#34;
</span></span><span class="line"><span class="cl">      /&gt;
</span></span><span class="line"><span class="cl">    &lt;/div&gt;
</span></span><span class="line"><span class="cl">  &lt;/Story&gt;
</span></span><span class="line"><span class="cl">&lt;/Preview&gt;
</span></span></code></pre></div><p>The example above will be rendered into something shown in the image below. You can see from this example we are now
mixing our React component with our normal markdown syntax. This provides a very flexible way to document our components.
It allows others to interact with and look at an example of its use.</p>
<p><img
        loading="lazy"
        src="/posts/2020-07-20-how-to-use-storybooks-with-mdx/images/components.png"
        type=""
        alt="Storybook Component"
        
      /></p>
<p>That&rsquo;s about it documentation wise! Taking a look at the other features we added you can see the a11y issues in a panel
(either on the bottom or on the right of when you are in the <code>canvas</code> tab). You can see this in the diagram below.</p>
<p><img
        loading="lazy"
        src="/posts/2020-07-20-how-to-use-storybooks-with-mdx/images/a11y.png"
        type=""
        alt="A11y Panel"
        
      /></p>
<p>Then at the top of the screen, we just adjust the viewport and see how our component would look at different resolutions.
The default list we have includes resolutions for devices like various iPhones, Google Pixel and Galaxy Sx. You can
see this in the diagram below.</p>
<p><img
        loading="lazy"
        src="/posts/2020-07-20-how-to-use-storybooks-with-mdx/images/viewport.png"
        type=""
        alt="Viewport"
        
      /></p>
<h2 id="run">Run</h2>
<p>We can run the Storybook UI from our source code like so:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clone https://gitlab.com/hmajid2301/articles.git
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> articles/30.<span class="se">\ </span>Storybooks,<span class="se">\ </span>Gatsby<span class="se">\ </span>and<span class="se">\ </span>MDX<span class="se">\ </span>II/source_code/
</span></span><span class="line"><span class="cl">yarn
</span></span><span class="line"><span class="cl">yarn storybook
</span></span></code></pre></div><p>With all of this setup now we can focus on a component first approach I like to use atomic design alongside Storybooks UI.
To create all of the components of my website before I start work on the pages themselves. This allows me to work out exactly
what each page will need, break them down into its core parts and work on them one at a time. Anyways that&rsquo;s it, thanks
for reading!</p>
<h2 id="appendix">Appendix</h2>
<ul>
<li><a href="https://gitlab.com/hmajid2301/personal-site/-/tree/e415420744b2a8f49eddaf2d3058b23c70f46638/.storybook">Example Project</a></li>
<li><a href="https://gitlab.com/hmajid2301/blog/-/tree/main/content/posts/2020-07-20-how-to-use-storybooks-with-mdx/source_code">Example source code</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>How to use Storybooks, Gatsby, Babel, Tailwind, Typescript together</title>
      <link>https://haseebmajid.dev/posts/2020-06-29-how-to-use-storybooks-gatsby-babel-tailwind-typescript-together/</link>
      <pubDate>Mon, 29 Jun 2020 00:00:00 +0000</pubDate>
      <author>Me</author>
      <guid>https://haseebmajid.dev/posts/2020-06-29-how-to-use-storybooks-gatsby-babel-tailwind-typescript-together/</guid>
      <description>&lt;p&gt;Recently I started to re-design my website, I decided to use this as an opportunity to learn some new technologies
such as Gatsby, Tailwind. I also decided to try using Storybook. For this said project I used MDX to create my
Storybook stories. In this article, I will show you how you can create Storybooks stories, for a Gatsby project
with TailwindCSS, Typescript using MDX.&lt;/p&gt;
&lt;p&gt;You can find an example project using this &lt;a href=&#34;https://gitlab.com/hmajid2301/personal-site/-/tree/e415420744b2a8f49eddaf2d3058b23c70f46638/.storybook&#34;&gt;here&lt;/a&gt;.
You can also find a &lt;a href=&#34;https://storybook.haseebmajid.dev/&#34;&gt;demo site&lt;/a&gt; for said project.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Recently I started to re-design my website, I decided to use this as an opportunity to learn some new technologies
such as Gatsby, Tailwind. I also decided to try using Storybook. For this said project I used MDX to create my
Storybook stories. In this article, I will show you how you can create Storybooks stories, for a Gatsby project
with TailwindCSS, Typescript using MDX.</p>
<p>You can find an example project using this <a href="https://gitlab.com/hmajid2301/personal-site/-/tree/e415420744b2a8f49eddaf2d3058b23c70f46638/.storybook">here</a>.
You can also find a <a href="https://storybook.haseebmajid.dev/">demo site</a> for said project.</p>
<blockquote>
<p>This article assumes you already familiar with Typescript, TailwindCSS and Gatsby.</p>
</blockquote>
<h2 id="storybook">Storybook</h2>
<blockquote>
<p>Storybook is an open source tool for developing UI components in isolation for React, Vue, and Angular. It makes building stunning UIs organized and efficient. - Storybook Website</p>
</blockquote>
<p>Storybook allows us to create and test (visually) components in isolation. It can be a great way to both document all
of your components but also speed up development as all you need to focus on is one component at a time. Storybook
also has a ton of extra plugins/addons which can help to customise storybooks to your liking. One such example being
checking for any accessibility issues your components may have.</p>
<h3 id="mdx">MDX</h3>
<p>MDX is a combination of markdown mixed with JSX. It allows us to &ldquo;execute&rdquo; and &ldquo;render&rdquo; JSX code from within an MDX
document. When used with Storybook it means we get all of the flexibility of markdown. So we can use normal markdown
syntax, to document our component. We also get access to MDX-flavored Component Story Format (CSF) which includes a collection
of components called &ldquo;Doc Blocks&rdquo;, that allow Storybook to translate MDX files into storybook stories.</p>
<h2 id="setup">Setup</h2>
<p>OK let&rsquo;s go over what we need to do, first let&rsquo;s create our gatsby site by using the <code>gatsby-cli</code> tool.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gatsby new gatsby-site
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> gatsby-site
</span></span></code></pre></div><h3 id="tailwindcss">TailwindCSS</h3>
<p>Now let&rsquo;s see how we add tailwindcss to this site:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add gatsby-plugin-typescript gatsby-plugin-postcss tailwindcss twin.macro postcss-preset-env
</span></span><span class="line"><span class="cl">vim gatsby-config.js
</span></span><span class="line"><span class="cl">vim postcss.config.js
</span></span><span class="line"><span class="cl">vim tailwind.config.js
</span></span><span class="line"><span class="cl">mkdir -p src/styles/
</span></span><span class="line"><span class="cl">vim src/styles/globals.css
</span></span><span class="line"><span class="cl">vim gatsby-browser.js
</span></span></code></pre></div><p>We need to update the <code>gatsby-config.js</code> file to add support for both typescript and PostCSS. Tailwind is written in PostCSS
so we need to include that in our gatsby file. You can either replace the default <code>gatsby-config.js</code> or update the plugins.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">plugins</span> <span class="o">=</span> <span class="p">[</span><span class="s2">&#34;gatsby-plugin-typescript&#34;</span><span class="p">,</span> <span class="s2">&#34;gatsby-plugin-postcss&#34;</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">plugins</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>Next we add a <code>postcss.config.js</code> file as per the Tailwind instructions found
<a href="https://tailwindcss.com/docs/installation#webpack-encore">here</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">tailwindcss</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;tailwindcss&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span><span class="nx">tailwindcss</span><span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>Finally, we create a <code>tailwind.config.js</code> file. Here we can add new colours, overwrite existing colours and extend the
configuration such as adding news fonts (<code>Inter</code>). This file will get merged with the default config by Tailwind.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">theme</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">themeVariants</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;dark&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">extend</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">colors</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">blue</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="mi">100</span><span class="o">:</span> <span class="s2">&#34;#EBF2FD&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="mi">200</span><span class="o">:</span> <span class="s2">&#34;#CDDFFA&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="mi">300</span><span class="o">:</span> <span class="s2">&#34;#AFCBF6&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="mi">400</span><span class="o">:</span> <span class="s2">&#34;#72A5F0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="mi">500</span><span class="o">:</span> <span class="s2">&#34;#367EE9&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="mi">600</span><span class="o">:</span> <span class="s2">&#34;#3171D2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="mi">700</span><span class="o">:</span> <span class="s2">&#34;#204C8C&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="mi">800</span><span class="o">:</span> <span class="s2">&#34;#183969&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="mi">900</span><span class="o">:</span> <span class="s2">&#34;#102646&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="nx">monochrome</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="mi">900</span><span class="o">:</span> <span class="s2">&#34;#333&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="mi">800</span><span class="o">:</span> <span class="s2">&#34;#444&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="mi">700</span><span class="o">:</span> <span class="s2">&#34;#666&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="mi">600</span><span class="o">:</span> <span class="s2">&#34;#999&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="mi">500</span><span class="o">:</span> <span class="s2">&#34;#ddd&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="mi">400</span><span class="o">:</span> <span class="s2">&#34;#eee&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="mi">300</span><span class="o">:</span> <span class="s2">&#34;#f3f3f3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="mi">200</span><span class="o">:</span> <span class="s2">&#34;#f8f8f8&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="mi">100</span><span class="o">:</span> <span class="s2">&#34;#fff&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">fontFamily</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">header</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;Inter&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">variants</span><span class="o">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>Next, to add the Tailwind styles or our app we need to create a CSS file, you can call this file whatever you want,
you just need to make sure it gets imported in such a place it can be used by any of your components.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">@</span><span class="k">tailwind</span> <span class="nt">base</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">tailwind</span> <span class="nt">components</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">@</span><span class="k">tailwind</span> <span class="nt">utilities</span><span class="p">;</span>
</span></span></code></pre></div><p>One place we can import this is in the <code>gatsby-brower.js</code> file. It should be empty, add the import shown below.
We will add babel later on in the app, which will allow us to use imports in the style we&rsquo;ve just described.
In this example, we will use the <code>~</code> to mean <code>src</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;~/styles/globals.css&#34;</span><span class="p">;</span>
</span></span></code></pre></div><h3 id="typescript">Typescript</h3>
<p>Now let&rsquo;s add typescript to our project:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add --dev react-docgen-typescript react-docgen-typescript-loader ts-loader typescript
</span></span><span class="line"><span class="cl">vim tsconfig.json
</span></span></code></pre></div><p>We will add some extra libraries that will be used by Storybooks to parse our Typescript components.
Like all Typescript projects, we need to include a <code>tsconfig.json</code> file. Note we add the <code>&quot;paths&quot;</code> so we can
have cleaner imports, this will be used alongside Babel.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;compileOnSave&#34;</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;compilerOptions&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;target&#34;</span><span class="p">:</span> <span class="s2">&#34;es5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;module&#34;</span><span class="p">:</span> <span class="s2">&#34;es6&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;types&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;node&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;moduleResolution&#34;</span><span class="p">:</span> <span class="s2">&#34;node&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;esModuleInterop&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;lib&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;dom&#34;</span><span class="p">,</span> <span class="s2">&#34;es2015&#34;</span><span class="p">,</span> <span class="s2">&#34;es2017&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;jsx&#34;</span><span class="p">:</span> <span class="s2">&#34;react&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;sourceMap&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;strict&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;resolveJsonModule&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;noUnusedLocals&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;noImplicitAny&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;noUnusedParameters&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;noFallthroughCasesInSwitch&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;allowSyntheticDefaultImports&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;downlevelIteration&#34;</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;baseUrl&#34;</span><span class="p">:</span> <span class="s2">&#34;./&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;paths&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;~/*&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;src/*&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;include&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;./src/**/*&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;exclude&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;node_modules&#34;</span><span class="p">,</span> <span class="s2">&#34;plugins&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="babel">Babel</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add --dev babel-plugin-module-resolver babel-preset-gatsby babel-preset-react-app @babel/compat-data <span class="se">\
</span></span></span><span class="line"><span class="cl">@babel/core @babel/preset-env babel-loader
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">vim .babelrc
</span></span></code></pre></div><p><code>Gatsby</code> automatically uses Babel, however, to customise babel we need to create our own <code>.babelrc</code> file. You can read
more about it <a href="https://www.gatsbyjs.org/docs/babel/">here</a>. The main reason we want to use it is to allow use to have cleaner
imports. So we can use <code>~</code> instead of <code>src</code> in imports. So we can do <code>import &quot;~/styles/globals.css&quot;;</code> instead of
<code>import &quot;../../../styles/globals.css&quot;'</code>.</p>
<blockquote>
<p><a href="/blog/better-imports-with-babel-tspath/">You can read more about it here, I wrote a previous article on this topic.</a></p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;env&#34;</span><span class="p">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;plugins&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;module-resolver&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;root&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;./src&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;alias&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&#34;~&#34;</span><span class="p">:</span> <span class="s2">&#34;./src&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;presets&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="s2">&#34;babel-preset-gatsby&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;targets&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nt">&#34;browsers&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;&gt;0.25%&#34;</span><span class="p">,</span> <span class="s2">&#34;not dead&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="cl">  <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="storybook-1">Storybook</h3>
<p>We will use the latest versions of Storybook (v6) so we can access the latest features. We will go over how we can use these features in the next article.</p>
<p>First remove any lines in your <code>package.json</code> that start with <code>@storybook</code>. In my case,
I removed <code>@storybook/addon-actions</code>, <code>@storybook/add-links</code>, <code>@storybook/addons</code> and
<code>@storybook/react</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn add --dev @storybook/addon-docs@6.0.0-beta.20 @storybook/addon-essentials@6.0.0-beta.20 <span class="se">\
</span></span></span><span class="line"><span class="cl">@storybook/addon-storysource@6.0.0-beta.20  @storybook/preset-typescript@1.2.0 <span class="se">\
</span></span></span><span class="line"><span class="cl">@storybook/react@6.0.0-beta.20 core-js@2.6.5
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">npx -p @storybook/cli sb init -f
</span></span><span class="line"><span class="cl">vim .storybook/main.js
</span></span><span class="line"><span class="cl">vim .storybook/preview.js
</span></span><span class="line"><span class="cl">vim preview-head.html
</span></span><span class="line"><span class="cl">vim webpack.config.js
</span></span></code></pre></div><p>Next, we will update the <code>main.js</code> file. This will tell Storybook where to look for the stories, in this case in the <code>src</code> folder
any file called <code>x.stories.mdx</code> or <code>x.stories.tsx</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">stories</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;../src/**/*.stories.@(tsx|mdx)&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="nx">addons</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;@storybook/addon-essentials&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;@storybook/addon-docs&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;@storybook/preset-typescript&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>Next, lets update the preview file. Here is typically you can define global parameters and decorators. Again
will see more of this in the next article.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">React</span> <span class="nx">from</span> <span class="s2">&#34;react&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">action</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;@storybook/addon-actions&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">configure</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;@storybook/react&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;../src/styles/globals.css&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;./main.css&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">configure</span><span class="p">(</span><span class="nx">require</span><span class="p">.</span><span class="nx">context</span><span class="p">(</span><span class="s2">&#34;../src&#34;</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="sr">/\.stories\.mdx$/</span><span class="p">),</span> <span class="nx">module</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">global</span><span class="p">.</span><span class="nx">___loader</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">enqueue</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="nx">hovering</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="nx">global</span><span class="p">.</span><span class="nx">__PATH_PREFIX__</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nb">window</span><span class="p">.</span><span class="nx">___navigate</span> <span class="o">=</span> <span class="p">(</span><span class="nx">pathname</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">action</span><span class="p">(</span><span class="s2">&#34;NavigateTo:&#34;</span><span class="p">)(</span><span class="nx">pathname</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>If we want to use any custom fonts, such as google fonts or other styles within our Tailwind, we need to
define them here.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span>
</span></span><span class="line"><span class="cl">  <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://fonts.googleapis.com/css2?family=Inter:wght@600,900&amp;display=swap&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">/&gt;</span>
</span></span></code></pre></div><p>Storybook uses webpack, so if we want to add extra webpack options, we do that here. This allows us to use
things like Babel and PostCSS loader.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-js" data-lang="js"><span class="line"><span class="cl"><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">({</span> <span class="nx">config</span> <span class="p">})</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">config</span><span class="p">.</span><span class="nx">module</span><span class="p">.</span><span class="nx">rules</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">use</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">loader</span> <span class="o">=</span> <span class="nx">require</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="s2">&#34;babel-loader&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nx">config</span><span class="p">.</span><span class="nx">module</span><span class="p">.</span><span class="nx">rules</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">use</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">options</span><span class="p">.</span><span class="nx">presets</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="nx">require</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="s2">&#34;@babel/preset-react&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nx">require</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="s2">&#34;@babel/preset-env&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">  <span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">config</span><span class="p">.</span><span class="nx">module</span><span class="p">.</span><span class="nx">rules</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nx">test</span><span class="o">:</span> <span class="sr">/\.(ts|tsx)$/</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">loader</span><span class="o">:</span> <span class="nx">require</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="s2">&#34;babel-loader&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">presets</span><span class="o">:</span> <span class="p">[[</span><span class="s2">&#34;react-app&#34;</span><span class="p">,</span> <span class="p">{</span> <span class="nx">flow</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">typescript</span><span class="o">:</span> <span class="kc">true</span> <span class="p">}]],</span>
</span></span><span class="line"><span class="cl">      <span class="nx">plugins</span><span class="o">:</span> <span class="p">[],</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">config</span><span class="p">.</span><span class="nx">module</span><span class="p">.</span><span class="nx">rules</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">    <span class="nx">test</span><span class="o">:</span> <span class="sr">/\.css$/</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">use</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">loader</span><span class="o">:</span> <span class="s2">&#34;postcss-loader&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">options</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">sourceMap</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">config</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">path</span><span class="o">:</span> <span class="s2">&#34;./.storybook/&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">config</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><h3 id="component">Component</h3>
<p>Finally, let&rsquo;s create a component that we will create a story for. First, create a new folder at <code>src/components/Logo</code>.
In that folder let&rsquo;s create the following files:</p>
<blockquote>
<p>Note the comments in the Props will be the comments shown in our story later, if you use the correct addons for Storybook. We will go over this in the next article.</p>
</blockquote>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-tsx" data-lang="tsx"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">React</span> <span class="kr">from</span> <span class="s1">&#39;react&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">tw</span> <span class="kr">from</span> <span class="s1">&#39;twin.macro&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="kr">interface</span> <span class="nx">Props</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="cm">/** The colour of the opening and closing tags. */</span>
</span></span><span class="line"><span class="cl">  <span class="nx">accent?</span>: <span class="kt">string</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="cm">/** The colour of main text. */</span>
</span></span><span class="line"><span class="cl">  <span class="nx">color?</span>: <span class="kt">string</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="cm">/** The colour when you hover over the logo. */</span>
</span></span><span class="line"><span class="cl">  <span class="nx">hoverColor?</span>: <span class="kt">string</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="cm">/** The main text of the logo for example, your name. */</span>
</span></span><span class="line"><span class="cl">  <span class="nx">text</span>: <span class="kt">string</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="cm">/** The size of the main text  */</span>
</span></span><span class="line"><span class="cl">  <span class="nx">size</span><span class="o">?:</span> <span class="s1">&#39;xs&#39;</span> <span class="o">|</span> <span class="s1">&#39;sm&#39;</span> <span class="o">|</span> <span class="s1">&#39;lg&#39;</span> <span class="o">|</span> <span class="s1">&#39;xl&#39;</span> <span class="o">|</span> <span class="s1">&#39;2xl&#39;</span> <span class="o">|</span> <span class="s1">&#39;3xl&#39;</span> <span class="o">|</span> <span class="s1">&#39;4xl&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">Logo</span> <span class="o">=</span> <span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="nx">accent</span> <span class="o">=</span> <span class="s1">&#39;black&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">color</span> <span class="o">=</span> <span class="s1">&#39;black&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">hoverColor</span> <span class="o">=</span> <span class="s1">&#39;blue-500&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">text</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">size</span> <span class="o">=</span> <span class="s1">&#39;2xl&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="o">:</span> <span class="nx">Props</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">LogoContainer</span>
</span></span><span class="line"><span class="cl">    <span class="na">className</span><span class="o">=</span><span class="p">{</span><span class="sb">`hover:text-</span><span class="si">${</span><span class="nx">hoverColor</span><span class="si">}</span><span class="sb"> text-</span><span class="si">${</span><span class="nx">color</span><span class="si">}</span><span class="sb"> lg:text-</span><span class="si">${</span><span class="nx">size</span><span class="si">}</span><span class="sb">
</span></span></span><span class="line"><span class="cl"><span class="sb">    md:text-xl sm:text-md text-sm`</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">Tag</span> <span class="na">className</span><span class="o">=</span><span class="p">{</span><span class="sb">`text-</span><span class="si">${</span><span class="nx">accent</span><span class="si">}</span><span class="sb">`</span><span class="p">}</span> <span class="na">data-testid</span><span class="o">=</span><span class="s">&#34;OpeningTag&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="o">&amp;</span><span class="nx">lt</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">Tag</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span><span class="nx">text</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">Tag</span> <span class="na">className</span><span class="o">=</span><span class="p">{</span><span class="sb">`text-</span><span class="si">${</span><span class="nx">accent</span><span class="si">}</span><span class="sb">`</span><span class="p">}</span> <span class="na">data-testid</span><span class="o">=</span><span class="s">&#34;ClosingTag&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="o">/&amp;</span><span class="nx">gt</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">Tag</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">LogoContainer</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">LogoContainer</span> <span class="o">=</span> <span class="nx">tw</span><span class="p">.</span><span class="nx">div</span><span class="sb">`cursor-pointer font-header font-black tracking-wide `</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">Tag</span> <span class="o">=</span> <span class="nx">tw</span><span class="p">.</span><span class="nx">span</span><span class="sb">``</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">Logo</span><span class="p">;</span>
</span></span></code></pre></div><p>This index file makes it easier to import the component from other files. As we don&rsquo;t have to do
<code>import {Logo} from &quot;src/components/Logo/Logo.ts</code> we can use <code>import {Logo} from &quot;src/components/Logo</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-tsx" data-lang="tsx"><span class="line"><span class="cl"><span class="kr">export</span> <span class="p">{</span> <span class="k">default</span> <span class="kr">as</span> <span class="nx">Logo</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;./Logo&#39;</span><span class="p">;</span>
</span></span></code></pre></div><h4 id="storybook-2">Storybook</h4>
<p>Now we have set everything up but do we create a story for our component. First, create a new file at <code>src/components/Logo/Logo.stories.mdx</code>.
You could keep this in another folder like storybooks/ or keep it in the same folder as your component, it&rsquo;s all personal preference.
Some people will also have all unit tests in the same folder <code>src/components/Logo/</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl">import { Meta, Story, Preview, Props } from &#34;<span class="ni">@storybook/addon</span>-docs/blocks&#34;;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">import Logo from &#34;./Logo&#34;;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;Meta title=&#34;Logo&#34; component={Logo} /&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gh"># Logo
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">## Accent
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">You can adjust the accent (tags) color by passing the <span class="sb">`accent`</span> prop.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">&lt;Preview&gt;
</span></span><span class="line"><span class="cl">  &lt;Story name=&#34;Accent Colour&#34;&gt;
</span></span><span class="line"><span class="cl">    &lt;Logo accent=&#34;gray-500&#34; color=&#34;blue-500&#34; text=&#34;Haseeb&#34; /&gt;
</span></span><span class="line"><span class="cl">    &lt;Logo accent=&#34;gray-500&#34; color=&#34;black&#34; text=&#34;Haseeb&#34; /&gt;
</span></span><span class="line"><span class="cl">  &lt;/Story&gt;
</span></span><span class="line"><span class="cl">&lt;/Preview&gt;
</span></span></code></pre></div><p>Add the following to your <code>package.json</code> to the &ldquo;scripts&rdquo; section. We need to pass it the <code>NODE_ENV=test</code>
environment variable, else the Gatsby Babel plugin will complain.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="s2">&#34;storybook&#34;</span><span class="err">:</span> <span class="s2">&#34;NODE_ENV=test start-storybook -p 6006&#34;</span><span class="err">,</span>
</span></span><span class="line"><span class="cl"><span class="s2">&#34;build-storybook&#34;</span><span class="err">:</span> <span class="s2">&#34;NODE_ENV=test build-storybook&#34;</span>
</span></span></code></pre></div><p>Now we can run our Storybook by running the following command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">yarn storybook
</span></span></code></pre></div><p>That&rsquo;s it! We managed to get Storybook to work with Gatsby. Where Gatsby is using Tailwind, Babel and Typescript.</p>
<h2 id="appendix">Appendix</h2>
<ul>
<li><a href="https://gitlab.com/hmajid2301/personal-site/-/tree/e415420744b2a8f49eddaf2d3058b23c70f46638/.storybook">Example Project</a></li>
<li><a href="https://storybook.haseebmajid.dev/">Example Storybook</a></li>
<li>Cover image from, <a href="https://worldvectorlogo.com/downloaded/storybook-1">World Vector Logo</a></li>
<li><a href="https://gitlab.com/hmajid2301/blog/-/tree/main/content/posts/2020-06-29-how-to-use-storybooks-gatsby-babel-tailwind-typescript-together/source_code">Example source code</a></li>
</ul>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
