<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rose</title>
    <description>The latest articles on DEV Community by Rose (@rose).</description>
    <link>https://dev.to/rose</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F241172%2Fd7cdd3d4-037e-4e49-afbe-3b185a2437e5.jpeg</url>
      <title>DEV Community: Rose</title>
      <link>https://dev.to/rose</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rose"/>
    <language>en</language>
    <item>
      <title>Tutorial: A Gatsby Photo Blog Powered by Flickr</title>
      <dc:creator>Rose</dc:creator>
      <pubDate>Tue, 16 Jun 2020 16:28:43 +0000</pubDate>
      <link>https://dev.to/rose/tutorial-a-gatsby-photo-blog-powered-by-flickr-40g0</link>
      <guid>https://dev.to/rose/tutorial-a-gatsby-photo-blog-powered-by-flickr-40g0</guid>
      <description>&lt;h3&gt;
  
  
  🤓 What this tutorial covers
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Setting up a paginated feed of photos in Gatsby, sourced from Flickr&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.rosey.dev/demos/basic-flickr-feed/"&gt;You can see the finished project here&lt;/a&gt; and &lt;a href="https://github.com/Rosey/flickr-gatsby-basic-example"&gt;You can see its source code here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🙅‍♀️ What this tutorial does NOT cover
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Making it look nice&lt;/li&gt;
&lt;li&gt;Handling rebuilds when new photos are added to Flickr&lt;/li&gt;
&lt;li&gt;Optimizations such as using environment variables for Flickr API key, "load-up" of images for better performance, or any other nice-to-have you can think of. This is bare-bones. The expectation is that you'd use this tutorial to get started, then tidy it up and make it exactly how you want it 💖&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🤯 Huh what who? Where am I?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://gatsbyjs.org"&gt;Gatsby&lt;/a&gt; is a very popular framework for building static websites. I chose it for this tutorial because of its popularity. Popular frameworks = more resources to learn from and available plugins to use. And this tutorial will provide more value if it's something many people use!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.flickr.com"&gt;Flickr&lt;/a&gt; is a photo hosting product. I chose it because I have nostalgic fondness for Flickr and they do allow you to embed their photos, as long as you &lt;a href="https://www.flickr.com/help/terms/api"&gt;follow their terms&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧐 Are you an expert?
&lt;/h3&gt;

&lt;p&gt;NO! I am a JavaScript developer and I do know React, but otherwise all the technologies in this tutorial are pretty new to me. &lt;/p&gt;

&lt;p&gt;I built out this project over a couple of hours. I've dabbled in Gatsby but don't really know it all that well. Same goes for GraphQL. I've never used the Flickr API before. So if I can fumble my way through this and then live to write a tutorial, so can you 😃&lt;/p&gt;

&lt;h3&gt;
  
  
  🥳 OK let's get started.
&lt;/h3&gt;

&lt;p&gt;You're going to need a few things right off the bat:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You'll need to &lt;a href="https://identity.flickr.com/sign-up"&gt;sign up for an account on Flickr&lt;/a&gt; (free plan available)&lt;/li&gt;
&lt;li&gt;You'll then need to &lt;a href="https://www.flickr.com/services/apps/create/"&gt;get your own Flickr API key&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;You'll want to make sure you have &lt;a href="https://www.gatsbyjs.org/docs/quick-start"&gt;the Gatsby CLI (command line interface)&lt;/a&gt; installed&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  👩‍💻 Start a new Gatsby Project
&lt;/h3&gt;

&lt;p&gt;Got all that? Ok let's get down to it.&lt;/p&gt;

&lt;p&gt;We're going to use the Gatsby CLI to create a new site. I ended up using their basic Hello World starter as my base template, because it really contains the bare minimum.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;gatsby new photo-blog https://github.com/gatsbyjs/gatsby-starter-hello-world
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We then want to navigate into this directory&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;photo-blog
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And now that we're there, we'll want to install &lt;a href="https://www.gatsbyjs.org/packages/gatsby-source-flickr/"&gt;gatsby-source-flickr&lt;/a&gt; which is what we'll be using to access the Flickr API. And this is the benefit of working with an established framework like Gatsby: Someone else has already done all the heavy lifting for us!&lt;/p&gt;

&lt;p&gt;Depending on if you're using yarn or npm you'll add this differently. Here it is if you're using npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save&lt;/span&gt; gatsby-source-flickr
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now all we're going to do is follow the setup documentation for &lt;code&gt;gatsby-source-flickr&lt;/code&gt;. We need to open &lt;code&gt;gatsby-config.js&lt;/code&gt; and configure our Flickr API key as well as the user ID on Flickr we want to get the photos from. &lt;code&gt;gatsby-config.js&lt;/code&gt; should look something like this now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gatsby-source-flickr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INSERT YOUR FLICKR API KEY HERE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INSERT YOUR FLICKR USEER ID HERE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;💁‍♀️ Hot tip: Once you have everything working as a proof-of-concept, if you want to use this in real life I'd advise looking into &lt;a href="https://www.gatsbyjs.org/docs/environment-variables/"&gt;environment variables&lt;/a&gt; for these values, instead of saving these values directly in the config file.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;❓&lt;strong&gt;Need help finding your API key?&lt;/strong&gt; You should have been given it after initial set up, but if you need to go back and find it, you can go to &lt;a href="https://www.flickr.com/services/"&gt;the app garden on Flickr&lt;/a&gt; and click the link that says "Apps by you" on the right hand side.&lt;/p&gt;

&lt;p&gt;❓ &lt;strong&gt;Need help finding your user ID?&lt;/strong&gt; If you navigate to your photo stream and look at the URL bar, the URL looks something like this: &lt;code&gt;https://www.flickr.com/photos/USERIDHERE&lt;/code&gt;. Grab that last value after &lt;code&gt;/photos/&lt;/code&gt;!&lt;/p&gt;




&lt;p&gt;Now we can run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;gatsby develop
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and then use our browser to navigate to &lt;a href="http://localhost:8000/___graphql"&gt;http://localhost:8000/___graphql&lt;/a&gt;. I &lt;em&gt;love&lt;/em&gt; this view because it gives you an overview of all the data available to you. Once Flickr is properly configured, it means we can play around and see what kind of information we can fetch from the Flickr API!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7E1IeoS6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xp2gg5rdzjrkhgxsmdn6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7E1IeoS6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xp2gg5rdzjrkhgxsmdn6.png" alt="screenshot of graphql query"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a feed of multiple photos, the interesting data is in &lt;code&gt;allFlickrPhoto&lt;/code&gt; You can click around to see what kind of data is available there and then decide what you need. Or you can just keep reading to see what I ended up using 🙃&lt;/p&gt;




&lt;h3&gt;
  
  
  ✍️ Let's set up some pages!
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.gatsbyjs.org/docs/adding-pagination/"&gt;Gatsby has a tutorial on adding pagination&lt;/a&gt; which is a good starting point. It's not Flickr-specific though, of course, so some tweaks need to be made. I also found &lt;a href="https://nickymeuleman.netlify.app/blog/gatsby-pagination"&gt;this tutorial&lt;/a&gt; useful for the previous/next links in pagination.&lt;/p&gt;

&lt;p&gt;Open up &lt;code&gt;gatsby-node.js&lt;/code&gt; This is where we'll define all our page paths.&lt;/p&gt;

&lt;p&gt;We need to know how many pages to generate. To do this we can query Flickr with our "number of photos per page" (aka 'limit') and then get the &lt;code&gt;pageInfo&lt;/code&gt; which contains a handy page count.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;allFlickrPhoto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;pageInfo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;pageCount&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;currentPage&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once we know how many pages there are, we can loop between 0 and totalPages and generate a path for each page. In the end, &lt;code&gt;gatsby-node.js&lt;/code&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;postsPerPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// change this value if you want a different number of posts per page.&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reporter&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createPage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`
    query($limit: Int!) {
      allFlickrPhoto(limit: $limit, filter: {media: {eq: "photo"}}) {
        pageInfo {
          pageCount
          currentPage
        }
      }
    }
    `&lt;/span&gt;
  &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;postsPerPage&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;reporter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;panicOnBuild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error while running GraphQL query.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;numPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allFlickrPhoto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;numPages&lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;createPage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`/`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/templates/flickr-photo-list-template.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;postsPerPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;postsPerPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;numPages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;currentPage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You'll noticed that I moved &lt;code&gt;limit&lt;/code&gt; into a variable to make it easier to change and have that change cascade nicely.&lt;/p&gt;

&lt;p&gt;I also added a "filter" param so that we're only getting photos, not videos and other media.&lt;/p&gt;




&lt;p&gt;Next up we need to set up &lt;code&gt;'./src/templates/flickr-photo-list-template.js'&lt;/code&gt;, which is referenced here but doesn't actually exist yet.&lt;/p&gt;

&lt;p&gt;🧙‍♀️ Create that file in the appropriate directory. &lt;/p&gt;

&lt;p&gt;🚮 You can also delete &lt;code&gt;pages/index.js&lt;/code&gt; if you're following this tutorial, as I set the route of &lt;code&gt;/&lt;/code&gt; to be page one of the Flickr photo list, so we don't need a default index page.&lt;/p&gt;




&lt;p&gt;On this &lt;code&gt;flickr-photo-list-template.js&lt;/code&gt; template we'll define the basic rendering of the data, as well as a GraphQL query for getting the information from Flickr we need in order to render that data.&lt;/p&gt;

&lt;p&gt;The query will look something like this. "Skip" is for pagination (If you are having 5 photos per page and you are on page 2, skip would be "5". Page 3, skip would be "10" etc. This value was passed in when we set up &lt;code&gt;gatsby-node.js&lt;/code&gt;, go check it out if you missed it.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$skip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;!,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;!)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;allFlickrPhoto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$skip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dateupload_date&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;media&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"photo"&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;edges&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;url_m&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;photo_id&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then we can use the data we receive to render each individual photo by looping through them via &lt;code&gt;map&lt;/code&gt; -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// get photos from response&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;photos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allFlickrPhoto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// call `renderPhoto` method for each photo via map&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;photos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;renderPhoto&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;renderPhoto&lt;/code&gt; looks at an individual object in the array of results and displays its title, image, and description. You can tweak this to your heart's desire, and if you need different attributes, you can update your GraphQL query and then reference it here -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;renderPhoto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;photo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;flickrPhotoURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://www.flickr.com/photos/[YOUR FLICKR USER ID HERE]/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;photo_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;flickrPhotoURL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url_m&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;flickrPhotoURL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;View&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;Flickr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;






&lt;p&gt;Add some pagination information and the entire file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gatsby&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;renderPhoto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;photo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;flickrPhotoURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://www.flickr.com/photos/[YOUR FLICKR USER ID HERE]/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;photo_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;flickrPhotoURL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url_m&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;photo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;flickrPhotoURL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;View&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;Flickr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;PhotoList&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;renderPagination&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;currentPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;numPages&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageContext&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isFirst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isLast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;numPages&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prevPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isFirst&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;prevPage&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="err"&gt;←&lt;/span&gt; &lt;span class="nx"&gt;Previous&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;)}&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isLast&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;nextPage&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nx"&gt;Next&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Link&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;photos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allFlickrPhoto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;photos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;renderPhoto&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
        &lt;span class="nx"&gt;Page&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentPage&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;numPages&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;renderPagination&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;photoListQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="s2"&gt;`
  query ($skip: Int!, $limit: Int!) {
    allFlickrPhoto(limit:$limit, skip: $skip, sort: { order: DESC, fields: [dateupload_date] }, filter: {media: {eq: "photo"}}) {
      edges {
        node {
          id
          title
          url_m
          description
          photo_id
        }
      }
    }
  }
`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you now navigate to &lt;a href="http://localhost:8000"&gt;http://localhost:8000&lt;/a&gt; you should see a feed of your photos. You have successfully set up a very basic Flickr powered photo blog! 🎊&lt;/p&gt;

&lt;p&gt;It's up to you where you take it from here 🙂 And thanks very much for sticking with me for this long! &lt;/p&gt;

&lt;p&gt;❗️I already mentioned this at the beginning but I think it is worth re-iterating: If you set this up and run &lt;code&gt;gatsby build&lt;/code&gt; to have this running in the wild... And then add photos to Flickr, it will not dynamically update whenever you upload a new photo. It is a &lt;em&gt;static site&lt;/em&gt;. You'll need some kind of re-build process to handle this. You could check Flickr's documentation to see if Flickr has any kind of 'new photo' webhook and use it to trigger a re-build, for example.&lt;/p&gt;

&lt;p&gt;🤓 Hopefully you found this useful and informative. Comment and let me know if there's any areas you think could be explained more thoroughly and I may tweak this post for future readers.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>javascript</category>
      <category>gatsby</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Fixing a bug on my open source project: From start to finish.</title>
      <dc:creator>Rose</dc:creator>
      <pubDate>Wed, 10 Jun 2020 18:49:29 +0000</pubDate>
      <link>https://dev.to/rose/fixing-a-bug-on-my-open-source-project-from-start-to-finish-1749</link>
      <guid>https://dev.to/rose/fixing-a-bug-on-my-open-source-project-from-start-to-finish-1749</guid>
      <description>&lt;p&gt;🥰 Hello! Long time no see! I am currently on maternity leave with my second child and writing code is basically &lt;em&gt;impossible&lt;/em&gt; to do right now, especially with the COVID situation preventing us from taking advantage of childcare.&lt;/p&gt;

&lt;p&gt;However, I did have a small amount of free time recently and I decided to spend it fixing an issue reported on an open source tool I maintain. And then, as a companion to my &lt;a href="https://dev.to/rose/debugging-in-javascript-for-beginners-how-i-approach-things-490j"&gt;Debugging in JavaScript for Beginners: How I approach things&lt;/a&gt; article I wrote, I thought I’d write this little real-life walkthrough of a bug I fixed.&lt;/p&gt;

&lt;p&gt;I wrote this one-handed while simultaneously  patting a baby's bottom rhythmically in an attempt to get her to sleep, so please forgive any quality issues 😳&lt;/p&gt;

&lt;h2&gt;
  
  
  The library
&lt;/h2&gt;

&lt;p&gt;I don’t do a ton of side-coding outside my job but I do have a small library that converts content from the Draft.js rich text editor to markdown and vice versa. &lt;/p&gt;

&lt;h2&gt;
  
  
  The issue
&lt;/h2&gt;

&lt;p&gt;Someone opened &lt;a href="https://github.com/Rosey/markdown-draft-js/issues/122"&gt;an issue on the GitHub repo&lt;/a&gt; reporting that a link at an end of a block of text was not being converted correctly.&lt;/p&gt;

&lt;p&gt;The issue was first reported in May -&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IrWJZu_x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/akr8ostatror1s6ea15y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IrWJZu_x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/akr8ostatror1s6ea15y.png" alt="Screenshot of bug report on GitHub"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When people put effort into reporting issues or opening pull requests I like to try to acknowledge them somehow, even if it might take me awhile to follow up in any concrete way -&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0v9DEuNA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ucshsh9qoxzem3yi1eno.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0v9DEuNA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ucshsh9qoxzem3yi1eno.png" alt="Screenshot of my reply to the GitHub issue"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The approach
&lt;/h2&gt;

&lt;p&gt;When I actually went to dig in to this issue, my first step was to try to re-create it. My repo has a &lt;code&gt;gh-pages&lt;/code&gt; branch that is just a demo of the code in master running (you can see it &lt;a href="https://rosey.github.io/markdown-draft-js/"&gt;here&lt;/a&gt;) &lt;/p&gt;

&lt;p&gt;My attempt to re-create failed. I tried a simple block of text with a link at the end and it converted correctly:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dJXahsd6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0sceq51kzqsk0gbacjgj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dJXahsd6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0sceq51kzqsk0gbacjgj.png" alt="Screenshot of attempting to re-create the bug"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;I tried a few different things (inside a blockquote perhaps?) but no luck re-creating. I returned to the issue and left a comment asking for more details, as well as sharing what I had tried.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RMQ93W-p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6f8qu36p0sfsn465eomk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RMQ93W-p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6f8qu36p0sfsn465eomk.png" alt="Screenshot of me requesting more details on GitHub"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;In case you are wondering: how did I manage to get the “raw drafts object” from the page I was testing on?&lt;/p&gt;

&lt;p&gt;I have defined a few tools on the &lt;code&gt;window&lt;/code&gt; so that I can quickly access them from the console. That, combined with the React Developer Tools extension:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en"&gt;Chrome&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://addons.mozilla.org/en-CA/firefox/addon/react-devtools/"&gt;Firefox&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Allowed me to grab the data using my browser's &lt;code&gt;console&lt;/code&gt; tool.&lt;/p&gt;

&lt;p&gt;Here's the code-snippet where I define some useful methods on window so I can quickly use them -&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lTRzWszg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rqz9u8ol6sgjth83qcze.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lTRzWszg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rqz9u8ol6sgjth83qcze.png" alt="Code snippet of defining methods on window"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Here's me making sure that the right component is selected so I can access it with &lt;code&gt;$r&lt;/code&gt; in the console:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZBvAmz81--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kclyqjs24lcy4gtf1tmd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZBvAmz81--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kclyqjs24lcy4gtf1tmd.png" alt="Screenshot of react dev tools"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;And here I am getting the editor state and using the &lt;code&gt;convertToRaw&lt;/code&gt; method so that I can copy and paste it into the GitHub issue:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7eH1T6uo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6dd8c9tg2abb0g7q3yzy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7eH1T6uo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6dd8c9tg2abb0g7q3yzy.png" alt="Screenshot of console"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;OK so that's all great but I still don't know how to re-create the bug. So now I just wait to see if I get a reply. If I had tons of time I could be more proactive and keep trying different things, but as noted: I have no time 🤭&lt;/p&gt;

&lt;p&gt;Luckily, a few days go by and I get this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cww6Ydr6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ev6j4szerdj8qk09c9sj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cww6Ydr6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ev6j4szerdj8qk09c9sj.png" alt="Reply with more details on how to re-create"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is where being a more experienced developer has some advantages, and I apologize to beginners that I don't have some super magical advice here except that, "Huh I have seen bugs like this before."&lt;/p&gt;

&lt;p&gt;I actually remember an old boss and mentor (hi Jason Webster) sending me &lt;a href="https://mathiasbynens.be/notes/javascript-unicode"&gt;JavaScript has a Unicode problem&lt;/a&gt; many years ago and it has been... a very useful article in my career. I guess I deal with a lot of strings at my work?  haha.&lt;/p&gt;

&lt;p&gt;It's a great article and I totally recommend you read the whole thing, but for our purposes: Basically the issue is that sometimes if you have a string in JavaScript that contains certain unicode characters, it doesn't "count" them the way you might expect. From the article:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt; '💩'.length // U+1F4A9 PILE OF POO
2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As the article explains, a way around this issue is to use &lt;code&gt;Array.from&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt; Array.from('💩').length // U+1F4A9 PILE OF POO
1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So somewhere in my code, I theorized, I was falling into this &lt;code&gt;.length&lt;/code&gt; trap.&lt;/p&gt;

&lt;p&gt;OK so now we're onto something! &lt;/p&gt;

&lt;h2&gt;
  
  
  Solving the bug
&lt;/h2&gt;

&lt;p&gt;My issue reporter handily included an example of a failing case, so I decide to write a new test with this failing case. That way, when I run the tests I will first see it fail, and once I have fixed it, I will see it pass. A really gratifying way of knowing that you have fixed the bug!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PKyLefNb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/91wms8slgyr24enlze1f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PKyLefNb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/91wms8slgyr24enlze1f.png" alt="Screenshot of the test I wrote"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;And here's the test failing:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5bhIpCbc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/hubk8hcowp4hwtchslx8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5bhIpCbc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/hubk8hcowp4hwtchslx8.png" alt="Screenshot of failing test case"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Next up: Opening &lt;code&gt;draft-to-markdown.js&lt;/code&gt;, which is where all the conversion code exists. I can tell by the appearance of the failed test that the &lt;em&gt;closing&lt;/em&gt; tag of the final link is what is failing. It just dies off after the opening tag and the link text.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Expected '🙋 [link](https://link.com) [link' to equal '🙋 [link](https://link.com) [link](https://link.com)'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So I look through the code to see where links are closed. I actually haven't worked on this project in ages so I kind of forget how it all fits together. I see this chunk of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;EntityItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LINK&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;](&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;"Hmm ok," I think, "So there's the snippet that handles link open/close."&lt;/p&gt;

&lt;p&gt;So I do a file search for &lt;code&gt;EntityItems[&lt;/code&gt; to see every spot where it is being used and see if I can find a place that looks like a likely culprit.&lt;/p&gt;

&lt;p&gt;Again, being an experienced developer probably helps here, but this stood out to me as I went through the search results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// Close any remaining entity tags&lt;/span&gt;
  &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entityRanges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rangeIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rawDraftObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entityMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customEntityItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;EntityItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;markdownString&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customEntityItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;EntityItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt; 

&lt;span class="c1"&gt;// Close any remaining inline tags (if an inline tag ends at the very last char, we won't catch it inside the loop)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The comment is helpful too:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Close any remaining entity tags&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and the one beneath, which is actually for a different bit of code but still provides context for the part I was working on:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;// Close any remaining inline tags (if an inline tag ends at the very last char, we won't catch it inside the loop)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;HMM this is code specifically for the last item, and it references &lt;code&gt;EntityItems&lt;/code&gt; and &lt;code&gt;.close&lt;/code&gt; AND I see a spot where &lt;code&gt;text.length&lt;/code&gt; is being used directly. Could this be it???&lt;/p&gt;

&lt;p&gt;I make a small change, can you spot it?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Close any remaining entity tags&lt;/span&gt;
  &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entityRanges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rangeIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rawDraftObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entityMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customEntityItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;EntityItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;markdownString&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;customEntityItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;EntityItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;OK let's run my test again:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BBLURz-d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pog3k84qihfj9ijvz60p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BBLURz-d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pog3k84qihfj9ijvz60p.png" alt="Screenshot of passing test"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;NAILED IT.&lt;/p&gt;




&lt;p&gt;I was lucky, this bug fix went so amazingly smoothly. Trust me, they are not all like that.&lt;/p&gt;

&lt;p&gt;My only final step was to write some nice commits explaining the cause of the bug and open a pull request to fix it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Rosey/markdown-draft-js/issues/123"&gt;You can see the pull request right here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;THAT IS IT!&lt;/p&gt;

&lt;p&gt;Thank you for reading! I have to run now, if you notice any major errors or typos or whatever, please comment and I'll try to fix. Like I said, I threw this together under a bit of pressure, so there's bound to be a few things 💕&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>draftjs</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Do you think ageism in tech is improving?</title>
      <dc:creator>Rose</dc:creator>
      <pubDate>Wed, 27 May 2020 20:58:47 +0000</pubDate>
      <link>https://dev.to/rose/do-you-think-ageism-in-tech-is-improving-i56</link>
      <guid>https://dev.to/rose/do-you-think-ageism-in-tech-is-improving-i56</guid>
      <description>&lt;p&gt;There's often been talk in tech communities about how there's a degree of ageism involved in hiring in software development that is worse than in a lot of other professional industries, i.e. young people get hired, old people do not. &lt;/p&gt;

&lt;p&gt;As the industry continues to grow I hear less about ageism in tech and I'm wondering if it's evolving and is less of an issue now than it was 10 years ago, and if it will continue to improve.&lt;/p&gt;

&lt;p&gt;Or am I way out of touch and it's still a rampant issue? &lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Debugging in JavaScript for Beginners: How I approach things.</title>
      <dc:creator>Rose</dc:creator>
      <pubDate>Mon, 16 Dec 2019 22:13:29 +0000</pubDate>
      <link>https://dev.to/rose/debugging-in-javascript-for-beginners-how-i-approach-things-490j</link>
      <guid>https://dev.to/rose/debugging-in-javascript-for-beginners-how-i-approach-things-490j</guid>
      <description>&lt;p&gt;📝 Note: Using Chrome, although most browsers are fairly similar so you can probably use this article and tweak slightly for your preferred browser.&lt;/p&gt;

&lt;p&gt;🤓 There’s lots of great in-depth articles out there about how to use Chrome to debug JavaScript, or opinionated articles telling you what NOT to do when it comes to debugging. This is neither of those things. Instead, it’s a quick overview of some of the early steps I take when looking into a problem.&lt;/p&gt;

&lt;p&gt;My hope is that it is a nice auxiliary to the more in-depth articles to give you a better feel for the “flow” of debugging in JS as a normal human being trying to figure out what the hell is happening with something breaks 🙂 &lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario: Clicking a button doesn’t have desired outcome
&lt;/h2&gt;

&lt;p&gt;I expect when I click  a button on a page that I receive a JavaScript alert. But when I click my button nothing happens 🙁 What do I try first?&lt;/p&gt;

&lt;h3&gt;
  
  
  Easy first thing to do: Check for errors in the console
&lt;/h3&gt;

&lt;p&gt;Sometimes this is all it takes.  I open the dev console (On Mac, Chrome: cmd-option-j. Safari: cmd-option-c. Firefox: cmd-option-k. Someone missed the memo on trying to keep shortcuts consistent 🤪.) &lt;/p&gt;

&lt;p&gt;I look to see if there’s any JS errors that pop up when I click the button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--91wEwXoC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/me7cz404uet3to4fo6nh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--91wEwXoC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/me7cz404uet3to4fo6nh.png" alt="aler is not defined screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hmm, &lt;code&gt;aler is not defined&lt;/code&gt;, eh? Oops! Looks like I spelled &lt;code&gt;alert&lt;/code&gt; wrong. Easy fix!&lt;/p&gt;

&lt;h3&gt;
  
  
  Try turning on “pause on caught exceptions”
&lt;/h3&gt;

&lt;p&gt;If there’s an error happening but it’s not showing up within the console, it might be because the error is happening within a try-catch block (or a promise with a &lt;code&gt;catch&lt;/code&gt; that doesn’t properly handle the exception) It can be helpful to go to the &lt;code&gt;sources&lt;/code&gt; tab and turn on “Pause on Exceptions” as well as checking off the “Pause on caught exceptions” check box. Then try clicking the button again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uMLOptou--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/0uhon7rx2o7awfgy2609.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uMLOptou--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/0uhon7rx2o7awfgy2609.png" alt="Turning on pause on exceptions screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--d3rfhemA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/16ngv90myg6n8znmwsc0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--d3rfhemA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/16ngv90myg6n8znmwsc0.png" alt="Rejected promise screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So in this case the promise is being rejected, but the alert only happens once the promise resolves (&lt;code&gt;promise.then&lt;/code&gt; a few lines down) &lt;/p&gt;

&lt;p&gt;Unfortunately, this promise is being rejected by a very realistic &lt;code&gt;reject(‘foo’)&lt;/code&gt; 😳 So the alert never happens. OK I can fix that! Turn that &lt;code&gt;reject&lt;/code&gt; to a &lt;code&gt;resolve&lt;/code&gt; and we’re fine (note: IRL it’s probably not &lt;em&gt;quite&lt;/em&gt; that simple 😛)&lt;/p&gt;

&lt;h3&gt;
  
  
  There’s another use for this sources tab too: Sometimes the error in the console might not give you enough information on its own, even if it is showing up.
&lt;/h3&gt;

&lt;p&gt;Maybe an error is saying that a variable is undefined but you can’t figure out why the heck not.&lt;/p&gt;

&lt;p&gt;In this case you can turn on “Pause on Exceptions” but you don’t need to turn on the “Pause on Caught Exceptions” bit, since you can SEE the error, you just don’t quite UNDERSTAND the error. Yet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0dbY2d7l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7lk4ujicpo96hym81ats.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0dbY2d7l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7lk4ujicpo96hym81ats.png" alt="undefined methodToUse screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oops, it looks like I accidentally overwrote the global &lt;code&gt;methodToUse&lt;/code&gt; within the scope of the event listener and that’s why it’s not defined here. I can see this by looking on the right hand side "scope" section which shows you all the available variables in the scope of that method, and what they are. On the left I see the actual code, and luckily the line setting the variable to undefined is glaring in my face, so it's an easy thing to find and fix.&lt;/p&gt;

&lt;h3&gt;
  
  
  But sometimes there’s no error at all and none of this is helping so far!
&lt;/h3&gt;

&lt;p&gt;Here you have a few options. My fav approach is to identify each moving part of a button click, and adding a &lt;code&gt;console.log&lt;/code&gt;  to every part to see how far along we get before something isn’t called. This helps me to narrow down WHERE the point of failure is.&lt;/p&gt;

&lt;p&gt;Example of my console.log set up:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FnWhM0LG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/kr2c1hb7whrjs3zchjni.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FnWhM0LG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/kr2c1hb7whrjs3zchjni.png" alt="Screenshot of console.logs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What I see in the console when I click the button:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mMoDYiiV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/oooyjxbw90grkfb9fco0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mMoDYiiV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/oooyjxbw90grkfb9fco0.png" alt="Console output screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;HM, ok so the first two methods get called but the final one doesn’t. Why not?&lt;/p&gt;

&lt;p&gt;If I can’t figure it out just by looking at the code I may throw in a &lt;code&gt;debugger&lt;/code&gt; and try again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ilZx0Z5T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rhw7tknatjw13qkrkze8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ilZx0Z5T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rhw7tknatjw13qkrkze8.png" alt="Debugger line in code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the &lt;code&gt;debugger&lt;/code&gt; line is hit, everything will “pause” and I can go to the console and play around in the current scope of the function, so I can access all the same variables and functions that it can with ease.&lt;/p&gt;

&lt;p&gt;Maybe I’ll try calling &lt;code&gt;showAlert&lt;/code&gt; myself here from the console and see what it returns.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---p7DshcQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/kt1w0gbv0y4f7pzsuu5h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---p7DshcQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/kt1w0gbv0y4f7pzsuu5h.png" alt="Screenshot of calling showAlert"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OHHHH it’s returning a &lt;em&gt;function&lt;/em&gt; and not calling alert right away. What a dummy I am. I need to call it like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;showAlert()()&lt;/code&gt; which seems utterly absurd but this is just my weird fake example everyone, calm down. Let’s try it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i5FyDsx8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/q0i93alx2xt9f434907o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i5FyDsx8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/q0i93alx2xt9f434907o.png" alt="Screenshot of calling showAlert()()"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;… 🎉 It worked!!&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks for reading
&lt;/h2&gt;

&lt;p&gt;A short read with some absurd bugs but hopefully for those of you who have read the literature on JS debugging tools but aren't yet adept at actually using them, this gives you an idea of how/when to try things.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recommended reading for the more in-depth documentation
&lt;/h3&gt;

&lt;p&gt;This post wasn't a complete overview of everything you can do from your browser to debug, so if you really want to sink your head into some of the resources available to you, check out these articles 🙂 &lt;/p&gt;

&lt;p&gt;If you know of any other good articles about JS debugging on dev.to, link them in the comments! 💖&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/web/tools/chrome-devtools/javascript/breakpoints"&gt;Pause your code with breakpoints&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/web/tools/chrome-devtools/javascript/reference"&gt;JS Debugging reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/web/tools/chrome-devtools/console"&gt;Console Overview&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>If you work as a dev for a product team, what's it like?</title>
      <dc:creator>Rose</dc:creator>
      <pubDate>Wed, 11 Dec 2019 19:24:35 +0000</pubDate>
      <link>https://dev.to/rose/if-you-work-as-a-dev-for-a-product-team-what-s-it-like-3fbf</link>
      <guid>https://dev.to/rose/if-you-work-as-a-dev-for-a-product-team-what-s-it-like-3fbf</guid>
      <description>&lt;p&gt;I feel so sheltered! I've worked for the same SaaS product for about 8 years, and I don't live in a place like SF where it seems like everyone is "tech tech tech tech" all the time, so I really only have experience in this one place. I'm curious about culture, how legacy code is handled, team size, management style... Just anything/everything, really 🙂 &lt;/p&gt;

&lt;p&gt;I always find it a bit tricky to talk about my work TOO much because I always wonder where the line is when it comes to over-sharing the details of your employer? Are there unspoken rules about that kind of thing? I don't even know 😳 But here's a little snapshot of my work situation to get the conversation started.&lt;/p&gt;

&lt;p&gt;My employer is remote-first at this point, although when I was first hired most people were local and worked out of the office. Over time things have changed, people have moved away but continued to work for us, and remote people have been hired. I quite like the remote lifestyle and I don't think I could go back to a regular 9-5 in office anymore!&lt;/p&gt;

&lt;p&gt;We don't have set working hours but people tend to work anywhere between 8am and 5pm in their respective time zones. Sometimes people work weirder hours if it makes sense for them (a co-worker was in Japan for a month and decided to start their workday at 6am Japan time, for example) We chat online and use conference calls for when something face-to-face feels like a better fit. Sometimes we fly people into town for IRL team bonding.&lt;/p&gt;

&lt;p&gt;The devs are really dedicated to building things properly and thinking a feature through holistically before writing any code, but it can sometimes be a struggle between development and product managers, trying to find the balance between doing something carefully/properly and shipping something by X date. I imagine this is fairly common in the industry. We have crunch times where things get hairy and we have downtimes where things feel a little smoother for the devs.&lt;/p&gt;

&lt;p&gt;We are a small team, so during crunch times, juggling bug fixes, support requests, "can you quickly squeeze in this small quick feature" (lol) as well as working on whatever our current "big long-running feature" is, can sometimes be very daunting, and we're always trying to find ways to manage this workload better, but I don't think we've found the magic bullet yet. Has anyone???&lt;/p&gt;

&lt;p&gt;Our dev process is also quite loose: We have a weekly planning meeting where we go around and each dev talks about their progress, how the week went, and what is up for the next week. We take notes each meeting and upload them to a "meeting minutes" repository on github. We also use a task manager to track assigned work. Sometimes I wonder how/if this could be improved. I like how simple it is but I don't think it works perfectly either. Does anything work perfectly??&lt;/p&gt;

&lt;p&gt;Dev team is split by platform &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API (back end)/devops: Rails, Elixir, any devops-y infrastructure work needed. &lt;/li&gt;
&lt;li&gt;Front end: Single page web app so a lot of JS needed. Plus CSS of course. Front end team also manages an electron app. &lt;/li&gt;
&lt;li&gt;Mobile: iOS/Android devs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rarely do devs contribute "full stack" although it's not entirely unheard of. Recently we had a front end dev move to our API team because they wanted to grow their skillset, which is awesome and I'm so glad we were able to accommodate that.&lt;/p&gt;

&lt;p&gt;I used to grow a lot technically due to the the people above me being excellent mentors, but more recently as I've become more senior I've found this to be harder. Now I find I need to go outside of work and read articles/try to do side projects to explore new technologies. I don't feel like I'm super great at this and I wish I was better! Since I am managing people I think it's partly my responsibility to research and bring things back to the team that I think could be good for us. An area for me to improve for sure.&lt;/p&gt;

&lt;p&gt;I find the culture here to be very good. Management has historically cared deeply about employees and their lives and making sure people are supported, which is really nice. &lt;/p&gt;

&lt;p&gt;Ok your turn! How is your job different, or the same??&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>startup</category>
      <category>career</category>
    </item>
    <item>
      <title>Women in tech: Being a developer and a mom 🤪</title>
      <dc:creator>Rose</dc:creator>
      <pubDate>Thu, 07 Nov 2019 00:29:10 +0000</pubDate>
      <link>https://dev.to/rose/women-in-tech-being-a-developer-and-a-mom-gcf</link>
      <guid>https://dev.to/rose/women-in-tech-being-a-developer-and-a-mom-gcf</guid>
      <description>&lt;p&gt;&lt;em&gt;Warning: I wanted to write about being a mom in tech but I didn’t really know exactly how I wanted to approach it so I thought I’d just start writing and see what happened. This is what happened. It’s a thought dump.  I feel like I’ll have to go back and revise it one day to be less of a literary disaster. But I am a mom so I have no time so I'm just going to go ahead and post as-is. Have fun. 😈&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;🙋‍♀️ Hello! I am a professional developer - I have been writing software for a living since 2011 and for many years before that I was a hobbiest web developer 🙂 I am very passionate about my work and loooove what I do ❤️ &lt;/p&gt;

&lt;p&gt;🤰I am also a mom! I haven’t been a mom for my whole career of course, but I became a mom in early 2018 and I am expecting baby number 2 in a couple of weeks.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I have never worked with another mom.&lt;/strong&gt; I have worked with a handful of other female devs, but I guess due to the mixture of “not many women in tech” + “lots of people in tech are relatively young” … no other moms.&lt;/p&gt;

&lt;p&gt;SO IF YOU ARE A MOM AND YOU ARE A DEV AND YOU ARE READING THIS: HI 😊😊😊😊&lt;/p&gt;

&lt;p&gt;For any working mother, no matter the industry, I think there are a lot of challenges that come with the territory. Things that I was sort-of aware of before having kids, but have become more and more cognizant of now that I am living it. &lt;/p&gt;

&lt;p&gt;I know that there’s a big push for gender equality in parenting but we have a very, VERY long way to go. A simple example: My husband is a great partner and dad, but when I ask him to take time off work because our kid is sick or has a doctor appointment he gets super stressed out and claims he can’t possibly request time off from work for something related to parenting. So it often falls to me. Because I am the mom, and someone has to do it, and it’s the mom’s job, right? &lt;/p&gt;

&lt;p&gt;Luckily my work is amazing and no one has ever given me a hard time for needing to step away for this kind of stuff, but it’s still still stressful and frustrating knowing that there’s this expectation that I have to be the one to make sacrifices.  And it’s not just my husband’s “fault”  …it’s society’s fault. He’s probably &lt;em&gt;right&lt;/em&gt; that his work would raise a lot more eyebrows at him than they would at me. (And to his credit he’s been a lot better about this recently after switching to a new employer that is more family friendly)&lt;/p&gt;

&lt;p&gt;Another example: A co-worker dad I know talks about staying up late to get extra work done. OK obviously in this industry we have a general problem with people working themselves to death, so let’s just ignore THAT part of his comment for now and think about this: I cannot. CANNOT. Work long hours. ever. I barely get enough sleep to survive, I’m up with my toddler at all kinds of crazy hours. There is no way in hell I can stay up late to do extra work, I’d probably die from exhaustion.  I GO TO BED AT 8:30PM EVERY NIGHT JUST SO THAT I CAN SURVIVE THE NEXT DAY.&lt;/p&gt;

&lt;p&gt;I am fortunate again that my employer is great and this hasn’t posed a problem for me, but it still haunts me knowing that if I were to ever need to apply for a new job, I’d have to say very firmly “I can’t do overtime. I have to pick up my daughter from daycare at 4pm on the dot. I am not flexible. Please hire me even though that eager 20-something I’m competing with has no family and is willing to work whenever and wherever.”&lt;/p&gt;

&lt;p&gt;As baby number 2 approaches, I also have a lot of fears for the future.  I feel like having 1 kid is… tough, but 2 or 3 is when your career can really start to feel the impact of all that maternity leave. And in an industry where things change quickly technology wise, it is scary being away and “missing out”/“falling behind”. And while I know some people might scoff and tell me that if I’m so worried, I should take less maternity leave, that is not something I am willing to do. Giving birth sucks. Babies suck. That first year can be absolutely hellish and I don’t even know how some women manage to go back to work after only a few weeks. I was a mess for MONTHS. Plus as much as I love my work, I don’t want to feel obligated to sacrifice my personal life and family. I WANT to enjoy those early months and spend that time with my new baby.&lt;/p&gt;

&lt;p&gt;So that’s all my “working in tech as a mom is scary and here's why." thoughts.&lt;/p&gt;

&lt;p&gt;Here’s a couple of perks with my current employer that have saved me and I would look for in any other job, so if you are an employer and want to be inclusive of mothers, consider this stuff!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We are a remote-first company. So I work from home! A lot! And that means on my lunch break I can throw in a load of laundry or quickly shower or whatever. Get stuff done that I never seem to have time for otherwise. My husband is out of town this week so my lunch break is my ONLY time to get anything done where I am not also watching our toddler. Having that flexibility is so so so wonderful.&lt;/li&gt;
&lt;li&gt;We also don’t micromanage people’s time. We trust that people work hard and do their best. If I tell my boss I need to leave at 3:30pm to get my daughter for 4pm because that’s when daycare closes, he’s like “that’s great, family comes first.” And that’s it. No questions asked. No secret doubt about my work ethic. Family comes first.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I also think it’s important to be aware and supportive of what a difficult role motherhood can be, and how hard it can be to juggle both work and kids.  I’m sure some people reading this will argue with me, but in my experience with every mom I know: We take on more parenting responsibility than dads. We bust our butts. And when you’re also working full time, it’s a LOT.  So be kind to us 😛&lt;/p&gt;

</description>
      <category>womenintech</category>
      <category>watercooler</category>
      <category>career</category>
    </item>
    <item>
      <title>More experiments with form animations ✅</title>
      <dc:creator>Rose</dc:creator>
      <pubDate>Sun, 03 Nov 2019 00:45:47 +0000</pubDate>
      <link>https://dev.to/rose/more-experiments-with-form-animations-114e</link>
      <guid>https://dev.to/rose/more-experiments-with-form-animations-114e</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/rose/css-variables-input-type-color-and-form-animations-4g1n"&gt;A sort of follow-up to this post&lt;/a&gt; .... a few more form animations.&lt;/p&gt;

&lt;p&gt;Not totally happy with the execution in some cases (eg: Why does Firefox require that I use a setTimeout for the transition in example 1 to work correctly? I thought just using something like requestAnimationFrame would suffice but it does not.). , nor with my artistic direction (I feel like someone else could really figure out how to make these "pop" in a super-cool way.)&lt;/p&gt;

&lt;p&gt;But they're kind of fun. Enough to show off I guess 🙂 &lt;/p&gt;

&lt;p&gt;For the first: Using transitions on &lt;code&gt;transform&lt;/code&gt; and utilizing &lt;code&gt;getBoundingClientRect&lt;/code&gt; to calculate where the dot needs to move to. Works well for this simple case but if you wanted to use it IRL with a bunch of relatively positioned elements etc it would probably need some better positioning logic.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/roseyrobertson/embed/ExxomQx?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;For the second: a SVG with a bunch of little circles that scales in size and fades out, so it looks like a little burst of dots. This one was inspired by something on dribbble (&lt;a href="https://dribbble.com/shots/6440021-Card-Theme-Switch"&gt;https://dribbble.com/shots/6440021-Card-Theme-Switch&lt;/a&gt;) that is way cooler than my attempt to "do something similar but not totally copy"&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/roseyrobertson/embed/WNNdgLj?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>css</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Draft.js: Simple Content Manipulation</title>
      <dc:creator>Rose</dc:creator>
      <pubDate>Sat, 02 Nov 2019 00:52:36 +0000</pubDate>
      <link>https://dev.to/rose/draft-js-simple-content-manipulation-b7a</link>
      <guid>https://dev.to/rose/draft-js-simple-content-manipulation-b7a</guid>
      <description>&lt;p&gt;Hey it’s part 5 of the series! I hope you are enjoying playing with Draft.js so far.&lt;/p&gt;

&lt;p&gt;Today, I want to talk a bit about inserting content into the editor.&lt;/p&gt;

&lt;p&gt;Some examples of when you may want to do this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(simple) emoji picker: Maybe you want to let people click an emoji icon and have it automatically insert that emoji character&lt;/li&gt;
&lt;li&gt;(simple) mentioning: Maybe you want to have an icon you click that inserts the &lt;code&gt;@&lt;/code&gt; mention trigger character&lt;/li&gt;
&lt;li&gt;(more complicated) markdown: Maybe you want to be able to insert markdown characters  under certain circumstances, or wrap text in markdown characters (eg maybe highlighting a word and hitting cmd/ctrl b wraps it in &lt;code&gt;**&lt;/code&gt; instead of actually bolding)&lt;/li&gt;
&lt;li&gt;(more complicated) find-and-replace functionality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s look at a simple example first 🙂 &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.rosey.dev/demos/draft-article-5/"&gt;Here is what the finished product will look like&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Simple example: Emoji Insertion
&lt;/h2&gt;

&lt;p&gt;⁉️ For this example we’ll just be inserting the emoji characters into our editor when you click on them. If you are doing some real-life emoji work with your editor,  you may want to look at something like &lt;a href="https://www.draft-js-plugins.com/plugin/emoji"&gt;This Draft.Js emoji plugin&lt;/a&gt; which converts emoji into their own custom &lt;a href="https://draftjs.org/docs/advanced-topics-entities"&gt;Entity&lt;/a&gt; and provides a lot more flexibility for styling in-editor, as well as swapping out emoji assets for non-native assets if you want to.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Modifier Module
&lt;/h3&gt;

&lt;p&gt;We’re going to be using &lt;a href="https://draftjs.org/docs/api-reference-modifier"&gt;Draft.js’s Modifier&lt;/a&gt; to insert content into our editor.&lt;/p&gt;

&lt;p&gt;What we want to do is insert the emoji at whatever location the user’s caret is currently at (their current “selection”) . We also want to replace content, if they have a range of text selected.&lt;/p&gt;

&lt;p&gt;For this we’ll use &lt;code&gt;Modifier&lt;/code&gt;’s method &lt;code&gt;replaceText&lt;/code&gt; which takes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The editor’s current &lt;code&gt;ContentState&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The text to be replaced, as indicated by &lt;code&gt;SelectionState&lt;/code&gt; (this provides a range so the editor can say “ok I need to replace the content that currently exists between location X and location Y”. You could provide any range you like, provided content exists in that range, but for this simple case we just want to insert text where the selection already is, so no need to specify anything custom)&lt;/li&gt;
&lt;li&gt;The text to be inserted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What it &lt;em&gt;returns&lt;/em&gt; is a new instance of &lt;code&gt;ContentState&lt;/code&gt; that contains this change.&lt;/p&gt;

&lt;p&gt;However, we still need to get this change into our &lt;code&gt;EditorState&lt;/code&gt; and we can do this by using &lt;code&gt;EditorState.push&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;EditorState.push&lt;/code&gt; takes&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The editor state to apply changes to&lt;/li&gt;
&lt;li&gt;The content state to apply (so we want the result of &lt;code&gt;Modifier.replaceText&lt;/code&gt; here)&lt;/li&gt;
&lt;li&gt;What kind of change you’re making, also known as an &lt;a href="https://draftjs.org/docs/api-reference-editor-change-type"&gt;EditorChangeType&lt;/a&gt;. We’re doing &lt;code&gt;insert-characters&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;EditorState.push&lt;/code&gt; returns a new instance of &lt;code&gt;EditorState&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So with this in mind I wrote this small &lt;code&gt;insertCharacter&lt;/code&gt; helper function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;insertCharacter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;characterToInsert&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;editorState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;editorState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getCurrentContent&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nx"&gt;currentSelection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;editorState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getSelection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replaceText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;currentContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;currentSelection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;characterToInsert&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newEditorState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;EditorState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;editorState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;insert-characters&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt;  &lt;span class="nx"&gt;newEditorState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It takes the character you want to insert, as well as the current &lt;code&gt;editorState&lt;/code&gt;. It applies the modifications needed, then returns the new &lt;code&gt;editorState&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We’d then need to apply &lt;code&gt;this.setState&lt;/code&gt; with the new editorState to ensure we are actually &lt;em&gt;using&lt;/em&gt; it, so the code that calls it would be something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newEditorState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;insertCharacter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;💖&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;editorState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;editorState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newEditorState&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;For my example I wanted to make some little emoji buttons, so I added this to my render method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"emoji-picker"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"toolbar-title"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Insert Emoji:&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
      &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"emoji"&lt;/span&gt;
      &lt;span class="na"&gt;onMouseDown&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onEmojiClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"🎊"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"img"&lt;/span&gt; &lt;span class="na"&gt;aria&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"confetti"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;🎊&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
      &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"emoji"&lt;/span&gt;
      &lt;span class="na"&gt;onMouseDown&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onEmojiClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"💖"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"img"&lt;/span&gt; &lt;span class="na"&gt;aria&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"sparkle heart"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;💖&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
      &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"emoji"&lt;/span&gt;
      &lt;span class="na"&gt;onMouseDown&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onEmojiClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"🌼"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"img"&lt;/span&gt; &lt;span class="na"&gt;aria&lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"yellow flower"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;🌼&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then I defined &lt;code&gt;onEmojiClick&lt;/code&gt; like so -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;onEmojiClick&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;emoji&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-emoji&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;editorState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;insertCharacter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;editorState&lt;/span&gt;&lt;span class="p"&gt;)});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In other words, exactly what we talked about 🙂 &lt;/p&gt;

&lt;p&gt;There’s one other little gotcha here. You may notice that if you click the button when the editor doesn’t have focus, an emoji is inserted but the editor remains unfocused, which is likely not your desired behaviour.&lt;/p&gt;

&lt;p&gt;If you like,  you can ensure focus by using &lt;code&gt;EditorState.forceSelection&lt;/code&gt; and update your &lt;code&gt;insertCharacter&lt;/code&gt; method slightly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;insertCharacter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;characterToInsert&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;editorState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;editorState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getCurrentContent&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nx"&gt;currentSelection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;editorState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getSelection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replaceText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;currentContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;currentSelection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;characterToInsert&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newEditorState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;EditorState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;editorState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;insert-characters&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt;  &lt;span class="nx"&gt;EditorState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forceSelection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newEditorState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newContent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getSelectionAfter&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will make sure that after you insert the character, the selected state will be immediately after that character. (&lt;a href="https://draftjs.org/docs/api-reference-editor-state#forceselection"&gt;Official forceSelection documentation here&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;So there’s our little intro into making modifications to your editor’s content. I will follow up in my next post with some trickier content replacement 🙂 (I would have done it all in one but I think shorter articles are a bit easier for people to consume, plus it makes it easier for me to publish updates more frequently 🙃)&lt;/p&gt;

&lt;p&gt;Thanks (as always) for reading 💕&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>javascript</category>
      <category>react</category>
      <category>draftjs</category>
    </item>
    <item>
      <title>On being a dev team manager: Lessons learned from different managers I have had</title>
      <dc:creator>Rose</dc:creator>
      <pubDate>Wed, 30 Oct 2019 03:08:34 +0000</pubDate>
      <link>https://dev.to/rose/on-being-a-dev-team-manager-lessons-learned-from-different-managers-i-have-had-2mgp</link>
      <guid>https://dev.to/rose/on-being-a-dev-team-manager-lessons-learned-from-different-managers-i-have-had-2mgp</guid>
      <description>&lt;p&gt;I’m not huge on clickbait lists of “Top five habits of good managers” and this post is a little bit… like that….  so I am sorry 😬. But I’m writing it because I am a manager of people and I have also &lt;em&gt;been&lt;/em&gt; managed by a variety of people and I often think about what I learned from each of them and how it has shaped me as a leader, or just changed how I think about leadership in general.&lt;/p&gt;

&lt;p&gt;I also imagine that different people with different experiences would have a completely different list! So I’m curious, after reading this, if you agree or disagree or think I missed out anything super important. And tell me in the comments!&lt;/p&gt;

&lt;h3&gt;
  
  
  On coaching new developers and setting them up for success with their first projects.
&lt;/h3&gt;

&lt;p&gt;I’ll never forget when I started a new job as a fairly inexperienced developer and for my first task, my boss carefully laid out for me &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what I needed to build&lt;/li&gt;
&lt;li&gt;exactly how it needed to behave&lt;/li&gt;
&lt;li&gt;what files I should take a look at before I got started&lt;/li&gt;
&lt;li&gt;a general overview of how the app worked and what the hierarchy was, file-wise&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It was the perfect mix of hand-holding without micromanaging. There was enough room for me to solve the problem on my own, but enough structure that I wasn’t floundering. &lt;/p&gt;

&lt;p&gt;As I grew over time, their approach with me changed, and I slowly got less and less direction and more and more autonomy to make my own decisions on how to architect solutions.&lt;/p&gt;

&lt;p&gt;I think their approach was fantastic and it’s something I try to emulate when we hire junior developers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Forget about blame, focus on solutions
&lt;/h3&gt;

&lt;p&gt;I once caused a major bug on production. I was horrified. My boss looked chagrined and said, “Yes it sucks but I’m even more to blame than you, because I didn’t catch it when I reviewed your work, and I’m supposed to be the experienced one.”&lt;/p&gt;

&lt;p&gt;I definitely took this to heart and feel the same way with any of the developers that I manage. We all are responsible for making sure production is stable, and no mistake lays on one person’s shoulders.&lt;/p&gt;

&lt;p&gt;When something goes wrong, in my opinion it’s best to completely avoid mentioning &lt;em&gt;who&lt;/em&gt; you feel like should be blamed (and yes, we are all human, it’s easy to roll your eyes and want to blame the person who wrote the code. RESIST THE URGE.  In addition to not being solely responsible, they are also already beating themselves up internally.), and instead just focus on the problem itself and how to fix it/make sure it doesn’t happen again. &lt;/p&gt;

&lt;h3&gt;
  
  
  Celebrate successes by name
&lt;/h3&gt;

&lt;p&gt;The opposite of blame! My absolute favourite thing to do is to post in our company chat whenever something ships, and &lt;strong&gt;make sure I include the names of the people involved in building it&lt;/strong&gt;.  If you are a manager and you were also involved, in my opinion you should downplay your own role. Celebrate your developers, there’s nothing to be gained by trying to upstage them.&lt;/p&gt;

&lt;h3&gt;
  
  
  It’s ok to redo work. It’s good for improved quality, and it’s good for learning.
&lt;/h3&gt;

&lt;p&gt;I was once asked by my boss to re-write a feature 3 different times using 3 different approaches.&lt;/p&gt;

&lt;p&gt;It was a complicated feature that could be solved in many ways. The first attempt was my own solution, the second was a solution that we came up with together, and the third was when my boss looked at how I had interpreted our “solution” and was like, “um that’s not exactly what I had in mind when we discussed this.” And so I had to then re-do the third time.&lt;/p&gt;

&lt;p&gt;It was frustrating having to scrap and start over, but I was extremely proud of the finished product in the end, and it still works quite well to this day, many years later!&lt;/p&gt;

&lt;h3&gt;
  
  
  Saying no and saying yes
&lt;/h3&gt;

&lt;p&gt;I once had an engineering manager who was very scrappy and more than happy to push back on anything that they felt was a bad decision from a performance/technical architecture point of view. I liked it because it made me feel very protected, knowing that my boss was keeping the designers and product managers from forcing us to build “bad” things.&lt;/p&gt;

&lt;p&gt;Then I had a manager who was a bit more pragmatic. “One thing some devs do,” they told me, "is to say no too often. Almost anything is possible, you just need to think about it carefully and find the right solution.”&lt;/p&gt;

&lt;p&gt;I think about this a lot. Whenever I’m asked if we can build something that intimidates me, I don’t generally say “no” up front. Instead I say something like, “Interesting, let me think about how we might build that and get back to you.”&lt;/p&gt;

&lt;p&gt;Sometimes, in the end, even if it’s possible, it’s not advisable given time (or other) constraints.  But it’s always worth considering and thinking through. I’m actually surprised by how often something that seems scary at first ends up being 100% doable, given more serious consideration. And often even if the original proposed feature doesn’t make sense, by thinking it over you can usually come up with reasonable alternatives that make everybody happy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kindness is so important
&lt;/h3&gt;

&lt;p&gt;I have had a lot of terse and intimidating dev bosses. I have had bosses who were generally very happy with me and my work, but occasionally left cutting code reviews that would be so harsh I’d go home and cry that night. Don’t get me wrong: I liked these bosses. They were smart and organized and capable, and generally we got along very well! We’d go out for beers after work and had genuine friendships. But they were not warm and fuzzy.&lt;/p&gt;

&lt;p&gt;Then one day I had a boss who was the total opposite. Compassionate and thoughtful. They instituted a culture of one-on-one chats in which we focused on how I was doing, how I was feeling, what I needed, what my concerns were.&lt;/p&gt;

&lt;p&gt;It was a total 180 from what I was used to, and it taught me a lot about the value of kindness in a manager.&lt;/p&gt;

&lt;p&gt;You can provide constructive criticism and be kind at the same time.&lt;/p&gt;

&lt;h3&gt;
  
  
  My favourite bosses are the ones who seem like they are in control
&lt;/h3&gt;

&lt;p&gt;I want to trust that I can rely on my boss and not worry about them dropping the ball. I want to feel like they know what they’re doing, they know what the team is doing, they understand the big picture, and they aren’t asking anything of the team that is beyond what is possible for that team.  I like bosses that seem calm and even-keel. If there’s a huge deadline looming, I feel a lot better if my boss seems   zen about the situation. Zen doesn’t mean apathetic or disengaged. Just capable of keeping calm and focused and not causing the team needless extra stress during a stressful time. &lt;/p&gt;

&lt;h3&gt;
  
  
  You don’t have to be a closed book
&lt;/h3&gt;

&lt;p&gt;I am a big believer in being candid and open with my team. The only thing that I think is off-limits is speaking poorly of others on the team/at the company. And of course not talking about anything that is confidential 😛&lt;/p&gt;

&lt;h3&gt;
  
  
  You can make mistakes, just own them
&lt;/h3&gt;

&lt;p&gt;Apologize to your team when you mess up. Then move on and do better next time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Be an advocate for your team
&lt;/h3&gt;

&lt;p&gt;The people on your team are your responsibility, and should be your number one priority. Be loyal to them.&lt;/p&gt;




&lt;p&gt;Fin. For now. Until someone comments with another great point that I forgot 😁&lt;/p&gt;

&lt;p&gt;Reading this over it actually makes me think: These are all the qualities I try to embody as a manager, but I know there are areas where I fall short sometimes.  Sometimes I am a better manager than other times.  I find when I’m feeling overwhelmed I begin to slip and not be the kind of manager I want to be. &lt;/p&gt;

&lt;p&gt;A better manager than I can probably compartmentalize this and continue to be excellent. Maybe that’s something I can strive towards in the future.&lt;/p&gt;

&lt;p&gt;But I also think it’s ok to show a little self-compassion. We are all human.  We are not perfect. Be the best you can be, and take care of yourself too.&lt;/p&gt;

</description>
      <category>management</category>
      <category>career</category>
    </item>
    <item>
      <title>CSS variables, input[type="color"] and form animations</title>
      <dc:creator>Rose</dc:creator>
      <pubDate>Sun, 27 Oct 2019 20:52:47 +0000</pubDate>
      <link>https://dev.to/rose/css-variables-input-type-color-and-form-animations-4g1n</link>
      <guid>https://dev.to/rose/css-variables-input-type-color-and-form-animations-4g1n</guid>
      <description>&lt;p&gt;I was contemplating not sharing this because I was like "Meh it's pretty simple, anyone can do it" but then I reminded myself that the word "simple" is very relative, and there was a time in my life when this would have made me 🤯&lt;/p&gt;

&lt;p&gt;(this is a good thing to think about for anyone who hesitates on writing blog posts! Just because you think "everybody knows this stuff" doesn't mean it's true. Lots of people do NOT know this stuff.)&lt;/p&gt;

&lt;p&gt;Anyway, I was just in the mood to play around with checkbox/radio button animations and as I went along got carried away and added customization. Here we are:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/roseyrobertson/embed/vYYZjOX?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick stuff to know about
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Checkbox "x" animations use CSS gradients for the progressive "growing" of the x/"filling" of the box&lt;/li&gt;
&lt;li&gt;The x elements themselves are &lt;code&gt;::before&lt;/code&gt; and &lt;code&gt;::after&lt;/code&gt; pseudo elements rotated on an angle so that they form diagonal lines, crossed over one another.&lt;/li&gt;
&lt;li&gt;Radio buttons use "scale" transition animation for the circle to grow-in/shrink-in&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;input type="color"&amp;gt;&lt;/code&gt; is how you get that color picker input&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties"&gt;CSS Custom Properties&lt;/a&gt; (variables) are how you easily have the user pick a color.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/bgrins/TinyColor"&gt;I found this little colour manipulation library&lt;/a&gt; so that after someone picks a colour, I can find lighter/darker versions.&lt;/li&gt;
&lt;li&gt;🐛 Safari is annoying in that when you change the accent colour, it doesn't seem to properly update already-checked items that used a gradient animation, so you have to uncheck and recheck. Chrome/Firefox seem to update correctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can of course view the source to see all the nitty gritty 🙂 &lt;/p&gt;

</description>
      <category>showdev</category>
      <category>css</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Draft.js introduction: Custom styles (highlighted text!) and have formatting buttons show whether they are “on” or “off”</title>
      <dc:creator>Rose</dc:creator>
      <pubDate>Sat, 26 Oct 2019 18:23:09 +0000</pubDate>
      <link>https://dev.to/rose/draft-js-introduction-custom-styles-highlighted-text-and-have-formatting-buttons-show-whether-they-are-on-or-off-4f9p</link>
      <guid>https://dev.to/rose/draft-js-introduction-custom-styles-highlighted-text-and-have-formatting-buttons-show-whether-they-are-on-or-off-4f9p</guid>
      <description>&lt;p&gt;Hello!&lt;/p&gt;

&lt;p&gt;I can’t believe we’re already on #4 of this series 😱&lt;/p&gt;

&lt;p&gt;Today I wanted to cover 2 fun topics that I think are useful to know about:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Defining custom styles (in my example we’ll be adding a “highlighter” option to the editor)&lt;/li&gt;
&lt;li&gt;Detecting what styles/blocks are currently active based on the selected text or cursor position. This can be useful for things like showing an “on” and “off” in state in the formatting buttons.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;🚀 &lt;a href="https://www.rosey.dev/demos/draft-article-4/"&gt;This is how the finished product will look at the end of the post&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Defining custom styles
&lt;/h2&gt;

&lt;p&gt;Draft.js makes this very simple 🙂 We need to define a "style object" and pass it in to the editor.&lt;/p&gt;

&lt;p&gt;I liked the idea of letting users highlight certain blocks of text, so that’s the style I thought we could add for this demo.&lt;/p&gt;

&lt;p&gt;We create an object that we’ll call &lt;code&gt;styleMap&lt;/code&gt; that has the &lt;code&gt;key&lt;/code&gt; for the style’s name, and the &lt;code&gt;value&lt;/code&gt; is another object that defines the CSS we want to use to style that item. So if bold didn’t already exist the &lt;code&gt;key&lt;/code&gt; would be &lt;code&gt;BOLD&lt;/code&gt; and the value would be &lt;code&gt;{'fontWeight': 'bold'}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;But bold does exist of course, so here’s our little custom style for highlight:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;styleMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HIGHLIGHT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;backgroundColor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#faed27&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;(🤓 Newbie hint: If you want to define multiple custom styles, they can be comma-separated here, so after the closing &lt;code&gt;}&lt;/code&gt; for &lt;code&gt;HIGHLIGHT&lt;/code&gt; you could add another style definition).&lt;/p&gt;

&lt;p&gt;Then we need to pass in our &lt;code&gt;styleMap&lt;/code&gt; to the editor component itself, as &lt;code&gt;customStyleMap&lt;/code&gt; -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Editor&lt;/span&gt;
  &lt;span class="nx"&gt;customStyleMap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styleMap&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Start typing!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;editorState&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;editorState&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;handleKeyCommand&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleKeyCommand&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;keyBindingFn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;keyBindingFunction&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;🌼 So now the editor knows about the highlight style and knows what to do with it, but that’s not much use if we don’t expose anywhere for the user to toggle it.&lt;/p&gt;

&lt;p&gt;As taught in &lt;a href="https://dev.to/rose/rich-text-editing-on-the-web-formatting-text-and-keyboard-shortcuts-in-draft-js-4g9f"&gt;this previous series post&lt;/a&gt; I’ll add a keyboard shortcut (cmd [or ctrl] + shift + h) for highlighting text, and I’ll also add a button to my inline style buttons.&lt;/p&gt;

&lt;p&gt;I’d go back and read that post if you need the full block of code to jog your memory, but for the keyboard shortcut, I’ll be adding to my &lt;code&gt;keyBindingFunction&lt;/code&gt; the following &lt;code&gt;if&lt;/code&gt; statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;KeyBindingUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasCommandModifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shiftKey&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;highlight&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And then in the &lt;code&gt;handleKeyCommand&lt;/code&gt; function I’ll add this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;editorState&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;highlight&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;editorState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;RichUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toggleInlineStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;editorState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HIGHLIGHT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And now the keyboard shortcut to highlight text should be functioning! &lt;/p&gt;

&lt;p&gt;Adding the button should also be fairly straightforward. Since I have an array defining all my inline styles which then uses &lt;code&gt;.map&lt;/code&gt; to render the buttons, I just add a new item to that array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inlineStyleButtons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bold&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BOLD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Italic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ITALIC&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Underline&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UNDERLINE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Strikethrough&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;STRIKETHROUGH&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

   &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CODE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Highlight&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HIGHLIGHT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And we’re done. Highlighting functionality complete ✅&lt;/p&gt;

&lt;h2&gt;
  
  
  Detecting active styles and blocks
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;editorState&lt;/code&gt; instance contains everything there is to know about your Draft editor at any given moment, and this includes knowing where your cursor (selection state) is.&lt;/p&gt;

&lt;p&gt;Draft.js also provides a couple of handy helper functions to use this knowledge to tell you exactly what styles are active, and what block element is selected.&lt;/p&gt;

&lt;p&gt;To get the &lt;em&gt;current inline style&lt;/em&gt; - in other words, bold, italic, code, highlight, etc, you can call &lt;code&gt;this.state.editorState.getCurrentInlineStyle()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This returns an &lt;a href="https://immutable-js.github.io/immutable-js/docs/#/OrderedSet"&gt;Ordered Set&lt;/a&gt; of the currently active styles. If you aren’t familiar with immutable.js’s Ordered Set you can check out that link if you like, but the thing we care about right now is that it has a method called &lt;code&gt;has&lt;/code&gt; to check for the existence of a key.&lt;/p&gt;

&lt;p&gt;We can use that to search for things like &lt;code&gt;.has('BOLD')&lt;/code&gt; to get a true-or-false response.&lt;/p&gt;

&lt;p&gt;Here’s my updated &lt;code&gt;renderInlineStyleButton&lt;/code&gt; method that uses this check and then conditionally sets an &lt;code&gt;active&lt;/code&gt; class on the button if &lt;code&gt;.has&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt; for that style type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;renderInlineStyleButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentInlineStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;editorState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getCurrentInlineStyle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentInlineStyle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toggleInlineStyle&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;onMouseDown&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We’re going to do something similar with our &lt;code&gt;renderBlockButton&lt;/code&gt; but instead of using &lt;code&gt;editorState.getCurrentInlineStyle&lt;/code&gt; we’re going to use a helper method on &lt;code&gt;RichUtils&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;⁉️ I have no idea why two fairly similar functionalities are found in two different parts of Draft.js 😬&lt;/p&gt;

&lt;p&gt;Draft only allows you to have one block type at a time, so instead of getting a set of values, we’ll just be getting a single block type.&lt;/p&gt;

&lt;p&gt;The method we want to use is &lt;code&gt;RichUtils.getCurrentBlockType&lt;/code&gt; which takes &lt;code&gt;editorState&lt;/code&gt; as an argument and returns a &lt;code&gt;string&lt;/code&gt; of a block type as a response. So if  we were checking for a &lt;code&gt;blockquote&lt;/code&gt; we could do something like &lt;code&gt;RichUtils.getCurrentBlockType(this.state.editorState) === 'blockquote'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here’s my block button render method with the additional conditional &lt;code&gt;active&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;renderBlockButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentBlockType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;RichUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getCurrentBlockType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;editorState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentBlockType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toggleBlockType&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;onMouseDown&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And then you’re basically done. The only thing left is to add some CSS so that toggling the &lt;code&gt;active&lt;/code&gt; class on and off actually has some visual effect.&lt;/p&gt;

&lt;p&gt;Here’s the simple CSS I used if you want something to get started:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.toolbar&lt;/span&gt; &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"button"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#eee&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.toolbar&lt;/span&gt; &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"button"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;:active&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;.95&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.toolbar&lt;/span&gt; &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"button"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.active&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#eee&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.toolbar&lt;/span&gt; &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"button"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nc"&gt;.active&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;turquoise&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;🎊 Another tutorial complete! Again,  &lt;a href="https://www.rosey.dev/demos/draft-article-4/"&gt;you can check out the finished product of this tutorial here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’m hoping next time to step away from some of the basics and jump into a post about an interesting problem I had to solve recently that required some more complex manipulation of content. So stay tuned for that if you’re finding these posts a little too easy for your skill level 🙂 But also, don’t worry! If you like the simplicity of these posts, I have some more beginner-friendly ideas up my sleeve too. 💕&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>react</category>
      <category>javascript</category>
      <category>draftjs</category>
    </item>
    <item>
      <title>Draft.js: Common Questions and Answers</title>
      <dc:creator>Rose</dc:creator>
      <pubDate>Sat, 26 Oct 2019 01:57:35 +0000</pubDate>
      <link>https://dev.to/rose/draft-js-common-questions-and-answers-202</link>
      <guid>https://dev.to/rose/draft-js-common-questions-and-answers-202</guid>
      <description>&lt;p&gt;☀️ Welcome back to my series on Draft.js&lt;/p&gt;

&lt;p&gt;Before continuing with more code-stuff with this series, now seems like a good time to hit pause and answer a few questions that I have seen come up fairly often on the &lt;a href="https://draftjs.herokuapp.com"&gt;Draft.js Slack Group&lt;/a&gt; (Disclaimer: I don’t actually spend a ton of time lurking there, I am not some super contributor who is always chatting. But I am a member and sometimes I read stuff.)&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the best way to save the contents of a form to my database after someone submits the form?
&lt;/h3&gt;

&lt;p&gt;You can do this in different ways, and there are pros and cons to each way 🙂 Below I’ve listed 3 popular options with pros and cons. It’s really up to you to decide what makes the most sense for your situation.&lt;/p&gt;

&lt;p&gt;💻 &lt;strong&gt;You could convert to HTML and save as HTML&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There are several libraries out there that will do this for you. The most popular ones are probably

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/sstur/draft-js-utils/tree/master/packages/draft-js-export-html"&gt;draft-js-export-html&lt;/a&gt; and&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sstur/draft-js-utils/tree/master/packages/draft-js-import-html"&gt;draft-js-import-html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;👍 &lt;strong&gt;Pro:&lt;/strong&gt; Easy to later render elsewhere outside of the editor&lt;/li&gt;
&lt;li&gt;👍 &lt;strong&gt;Pro:&lt;/strong&gt; Not locked in to any proprietary Draft.js conventions if you later switch to a new editor.&lt;/li&gt;
&lt;li&gt;👎 &lt;strong&gt;Con:&lt;/strong&gt; Have to convert back and forth from HTML to a Draft.js format if you want to edit it later. Not a big deal but there does run the risk of conversion bugs where things don’t convert perfectly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🖋 You could save as a raw draftjs object&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Draft.js provides a couple of &lt;a href="https://draftjs.org/docs/api-reference-data-conversion"&gt;utility functions&lt;/a&gt;: &lt;code&gt;convertToRaw&lt;/code&gt; and &lt;code&gt;convertFromRaw&lt;/code&gt; which take your Editor’s &lt;code&gt;ContentState&lt;/code&gt; and makes it an object that you can easily save on your database.  You can also easily re-convert to a &lt;code&gt;ContentState&lt;/code&gt; back from the object.&lt;/li&gt;
&lt;li&gt;👍 &lt;strong&gt;Pro:&lt;/strong&gt; No risk of conversion bugs because you’re saving Draft.js’s exact state&lt;/li&gt;
&lt;li&gt;👎 &lt;strong&gt;Con:&lt;/strong&gt; You will need to find some way to render that content outside of the editor, if that’s a functionality you need.&lt;/li&gt;
&lt;li&gt;👎 &lt;strong&gt;Con:&lt;/strong&gt; If you ever decide to switch to a different rich text editor, you need to write something to convert all your existing saved data to a new format supported by the new editor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🖍 You could save in another format, like markdown&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Similar to HTML, libraries already exist for this.

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/sstur/draft-js-utils/blob/master/packages/draft-js-export-markdown"&gt;draft-js-export-markdown&lt;/a&gt; and&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sstur/draft-js-utils/tree/master/packages/draft-js-import-markdown"&gt;draft-js-import-markdown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Rosey/markdown-draft-js"&gt;markdown-draft-js&lt;/a&gt; (shameless, as I maintain this one)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;👍 &lt;strong&gt;Pro:&lt;/strong&gt; Easy to later render elsewhere outside of the editor&lt;/li&gt;
&lt;li&gt;👍 &lt;strong&gt;Pro:&lt;/strong&gt; Not locked in to any proprietary Draft.js conventions if you later switch to a new editor.&lt;/li&gt;
&lt;li&gt;👍 &lt;strong&gt;Pro:&lt;/strong&gt; If you want to let people toggle between markdown and non-markdown mode seamlessly when editing text, this could be a good use case for saving as markdown.&lt;/li&gt;
&lt;li&gt;👎 &lt;strong&gt;Con:&lt;/strong&gt; If you plan on rendering the content in HTML or some other format outside of the editor, you have to take the additional step of converting the markdown.&lt;/li&gt;
&lt;li&gt;👎 &lt;strong&gt;Con:&lt;/strong&gt; Markdown behaves a bit differently from “normal” plain text, in that it strips multiple newlines. This may cause problems - if someone has ten empty lines between paragraphs, markdown by default will strip them so people won’t see EXACTLY what they saved. There are work-arounds but 🤷‍♀️&lt;/li&gt;
&lt;li&gt;👎 &lt;strong&gt;Con:&lt;/strong&gt; Same as HTML: Have to convert back and forth from markdown to a Draft.js format if you want to edit it later.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What is the best way to render the rich text (eg in a comment feed, if the form is for writing a comment) after someone submits a form?
&lt;/h3&gt;

&lt;p&gt;If you are saving the raw object and need a way to then convert it to HTML for rendering purposes, this library exists: &lt;a href="https://github.com/jpuri/draftjs-to-html"&gt;draftjs-to-html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There’s also a library for rendering in react native here:  &lt;a href="https://github.com/globocom/react-native-draftjs-render"&gt;react-native-draftjs-render&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I get the placeholder to work like a normal placeholder in an input?
&lt;/h3&gt;

&lt;p&gt;You can pass in &lt;code&gt;placeholder=“Your placeholder text here"&lt;/code&gt; to the Editor component just like you can with a regular input or textarea, but as you may have noticed, by default it doesn’t really behave the way you’d probably like it to.&lt;/p&gt;

&lt;p&gt;My go-to for this is to use a bit of CSS.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set to &lt;code&gt;position: absolute&lt;/code&gt; so it doesn’t take up any space&lt;/li&gt;
&lt;li&gt;Set to &lt;code&gt;pointer-events: none&lt;/code&gt; so that it doesn’t block you from click-to-focus on the editor&lt;/li&gt;
&lt;li&gt;Change its colour to something a bit more placeholder-y.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here that is in CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.public-DraftEditorPlaceholder-inner&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ccc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;pointer-events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There's another little problem though with the placeholder: If you insert a blockquote or an ordered list, the placeholder hangs around 😧&lt;/p&gt;

&lt;p&gt;My solution to this problem is to toggle a class on the parent element to detect if there's some block-level style in place, and add a &lt;code&gt;hide-placeholder&lt;/code&gt; class if so.&lt;/p&gt;

&lt;p&gt;The CSS -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.hide-placeholder&lt;/span&gt; &lt;span class="nc"&gt;.public-DraftEditorPlaceholder-inner&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The JS -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contentState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;editorState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getCurrentContent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;showPlaceholder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;contentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasText&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getBlockMap&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;getType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unstyled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;showPlaceholder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then further down, when rendering, you can do something to dynamically update the className, either using a tool like &lt;a href="https://www.npmjs.com/package/classnames"&gt;classnames&lt;/a&gt; or just hardcoding the logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`my-little-app &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;showPlaceholder&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hide-placeholder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Are there any places where I can find a list of useful tools and extensions for Draft?
&lt;/h3&gt;

&lt;p&gt;There are &lt;em&gt;tons&lt;/em&gt; of useful tools that people have written. &lt;a href="https://github.com/nikgraf/awesome-draft-js"&gt;nikgraf/awesome-draft-js&lt;/a&gt; is a good resource for finding many of them 🙂 &lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>draftjs</category>
    </item>
  </channel>
</rss>
