{"id":1625,"date":"2021-01-22T22:51:31","date_gmt":"2021-01-22T22:51:31","guid":{"rendered":"http:\/\/10.0.0.14\/?p=1625"},"modified":"2021-11-27T23:51:38","modified_gmt":"2021-11-27T23:51:38","slug":"tdd-with-gatsby-django-docker-part-2-chapter-14-mobile-menu","status":"publish","type":"post","link":"https:\/\/tutorials.leesonresearch.com\/tutorials\/2021\/01\/22\/tdd-with-gatsby-django-docker-part-2-chapter-14-mobile-menu\/","title":{"rendered":"TDD with Gatsby, Django &#038; Docker Part 2, Chapter 14 &#8212; Mobile Menu with Styled-Components"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">Build a Mobile Menu with Styled-Components<\/h1>\n\n\n\n<p>To take a break from the heavy lifting of the previous chapters lets address the need for a mobile friendly menu. For the sake of simplicity we will use our mobile menu for desktop as well as mobile. Onward!<\/p>\n\n\n\n<p>We will need touch the following files in order to optimize our menu more mobile friendly.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>components\/navbar.js<\/li><li>components\/header.js<\/li><li>components\/login.js<\/li><\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">Refactor navbar.js<\/h4>\n\n\n\n<p>We&#8217;ll start by adding a NavWrapper around the Nav component:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [7,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,45,46,47,54,55,56,57,58]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n\/\/ frontend\/src\/components\/navbar.js\n\nimport { Link } from &quot;gatsby&quot;\nimport React from &quot;react&quot;\nimport styled from &quot;styled-components&quot;\nimport { isLoggedIn, logout } from &quot;..\/services\/auth&quot;\nimport onClickOutside from &quot;react-onclickoutside&quot;;\n\nconst NavWrapper = styled.div`\n  background-color: transparent;\n  border-top: 5px solid #59338f;\n  border-left: 2px solid #59338f;\n  border-bottom: 5px solid #59338f;\n`\n\nconst Nav = styled.nav`\n  a {\n    color: #000000 !important;\n  }\n\n  ul {\n    li {\n      padding: 0.5rem 1.5rem 0.2rem 0.5rem;\n      border-bottom: 1px solid gray;\n      text-align: left;\n    }\n    li:last-child {\n      border-bottom: none;\n    }\n  }\n`\nexport const Navbar = (props) =&gt; {\n  const { closeMenu } = props;\n  Navbar.handleClickOutside = () =&gt; closeMenu();\n  const handleLogout = () =&gt; {\n    logout();\n    closeMenu();\n  };\n\n  return(\n    &amp;lt;NavWrapper&gt;\n      &amp;lt;Nav&gt;\n        &amp;lt;ul&gt;\n          &amp;lt;li&gt;&amp;lt;Link to=&quot;\/&quot;&gt;Home&amp;lt;\/Link&gt;&amp;lt;\/li&gt;\n          {isLoggedIn() ? &amp;lt;li&gt;&amp;lt;Link to=&quot;\/app\/profile&quot;&gt;Profile&amp;lt;\/Link&gt;&amp;lt;\/li&gt; : ''}\n          {!isLoggedIn() ? &amp;lt;li&gt;&amp;lt;Link to=&quot;\/app\/login&quot;&gt;Login&amp;lt;\/Link&gt;&amp;lt;\/li&gt; : ''}\n          {isLoggedIn() ? &amp;lt;li&gt;&amp;lt;Link to=&quot;#&quot; onClick={handleLogout}&gt;Logout&amp;lt;\/Link&gt;&amp;lt;\/li&gt; : ''}\n        &amp;lt;\/ul&gt;\n      &amp;lt;\/Nav&gt;\n    &amp;lt;\/NavWrapper&gt;\n  )\n}\n\nconst clickOutsideConfig = {\n  handleClickOutside: () =&gt; Navbar.handleClickOutside\n};\n\nexport default onClickOutside(Navbar, clickOutsideConfig);\n<\/pre><\/div>\n\n\n<p>The NavWrapper is going to allow us to slide the menu in and out and we&#8217;ve refactored the Nav component to style the menu&#8217;s unordered list.<\/p>\n\n\n\n<h5 class=\"wp-block-heading\">Walk through of navbar.js updates<\/h5>\n\n\n\n<p><strong>Line 7)<\/strong> We are importing a new node module called <code><a href=\"https:\/\/www.npmjs.com\/package\/react-onclickoutside\" target=\"_blank\" rel=\"noreferrer noopener\">react-onclickoutside<\/a><\/code>. Quoting from this modules docs:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>This is a React Higher Order Component (HOC) that you can use with your own React components if you want to have them listen for clicks that occur somewhere in the document, outside of the element itself (for instance, if you need to hide a menu when people click anywhere else on your page).<\/p><cite><a href=\"https:\/\/www.npmjs.com\/package\/react-onclickoutside\" target=\"_blank\" rel=\"noreferrer noopener\">react-onclickoutside<\/a><\/cite><\/blockquote>\n\n\n\n<p>But before we can import the <a href=\"https:\/\/www.npmjs.com\/package\/react-onclickoutside\" target=\"_blank\" rel=\"noreferrer noopener\">react-onclickoutside<\/a> module we need to install it.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nnpm install react-onclickoutside --save\n<\/pre><\/div>\n\n\n<p>Now when the user clicks anywhere outside the the navbar.js component the click event will be registered with the header.js component, (more on that later), and close the flyout menu.<\/p>\n\n\n\n<p><strong>Lines 9-30)<\/strong> Here we are restyling the menu as an unordered list.<\/p>\n\n\n\n<p><strong>Lines 32-38)<\/strong> <\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>We added the <strong>props<\/strong> argument for the <strong>Navbar.js<\/strong> component which will destructure a reference to the <strong>closeMenu<\/strong> handler, (in parent component <strong>header.js<\/strong>), listening for a click event outside <strong>Navbar.js<\/strong> which we will use to close the menu.<\/li><li><strong>handleLogout<\/strong> will handle logging out of the app as well as closing the menu on logout.<\/li><\/ul>\n\n\n\n<p><strong>Lines 45-47)<\/strong> Adding inline javascript logic testing for the isLoggedIn state which determines which link is displayed when. In line 44 we added an onClick event handler referencing the <strong>handleLogout<\/strong> function.<\/p>\n\n\n\n<p><strong>Lines 54-58)<\/strong> We configure and integrate the <strong>react-onclickoutside component<\/strong> with <strong>our navbar.js component<\/strong>.<\/p>\n\n\n\n<p>Now we can move on to refactoring the <strong>header.js<\/strong> component.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Refactor header.js<\/h4>\n\n\n\n<p>In the header.js file we will:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Import useState to allow us to track the current state of the slide menu<\/li><li>Import React Icons<\/li><li>Add a hamburger icon for hide\/showing the menu links with the component name MenuHandle<\/li><li>Create styled components:<ul><li>MenuHandle<\/li><li>MenuWrapper<\/li><li>Refactor HeaderWrapper<\/li><\/ul><\/li><\/ul>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; highlight: [5,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,61,64,65,66,70,71,73,74,75,76]; title: Double click to copy; notranslate\" title=\"Double click to copy\">\n\/\/ frontend\/src\/components\/header.js\n\nimport { Link } from &quot;gatsby&quot;\nimport PropTypes from &quot;prop-types&quot;\nimport React, { useState } from &quot;react&quot;\nimport { useStaticQuery, graphql } from &quot;gatsby&quot;\nimport styled from &quot;styled-components&quot;\nimport Navbar from &quot;.\/navbar&quot;\n\n\/\/ Import hamburger icon from react-icon library\nimport { MdMenu } from &quot;react-icons\/md&quot;\n\n\/\/ Component to wrap the hamburger icon for open\/closing menu\nconst MenuHandle = styled.div`\n  position: absolute;\n  right: 2.0rem;\n  top: 1.0rem;\n  z-index: 10;\n  margin-bottom: 1.0rem;\n  width: 30px;\n  height: 30px;\n  font-size: 3.0rem;\n  cursor: pointer;\n`\n\n\/\/ Component wrapping the menu\nconst MenuWrapper = styled.div`\n  background-color: white;\n  position: absolute;\n  z-index: 5;\n  top: 70px;\n\n  \/\/ Depending on the state of the menu, (open\/close), slide the menu in or out\n  right: ${(props) =&gt; (props.menuIsOpen ? '0' : '-14rem')};\n\n  \/\/ Define the transition between the open\/close states of the menu\n  transition: ${(props) =&gt; props.menuIsOpen ? 'all 1.00s ease-out' : 'all 0.6s ease-out'};\n`\n\n\/\/ Refactor the HeaderWrapper by removing display flex\nconst HeaderWrapper = styled.div`\n  position: relative;\n  padding: 1.0rem 2.0rem;\n  width: 100%;\n  background-color: #9c5ea9;\n  a {\n    color: #ffffff;\n    text-decoration: none;\n  }\n`\nconst SiteTitle = styled.div`\n  font-family: Arial, Helvetica, sans-serif;\n`\nconst HeaderH1 = styled.div`\n  font-size: 2.5rem;\n  line-height: 2.6rem;\n`\n\nexport const PureHeader = ({ data }) =&gt; {\n  \/\/ Use state to track open\/close status of the menu\n  const &#x5B;menuIsOpen, setMenuIsOpen] = useState(false);\n\n  \/\/ Handle click event to toggle state of the menu\n  let handleMenuClick = () =&gt; {\n    setMenuIsOpen(!menuIsOpen)\n  }\n  return (\n    &amp;lt;HeaderWrapper&gt;\n\n      {\/* Hamburger icon with event listener *\/}\n      &amp;lt;MenuHandle onClick={() =&gt; handleMenuClick()}&gt;&amp;lt;MdMenu \/&gt;&amp;lt;\/MenuHandle&gt;\n\n      {\/* Pass menu state into MenuWrapper component *\/}\n      &amp;lt;MenuWrapper menuIsOpen={menuIsOpen}&gt;\n        &amp;lt;Navbar \/&gt;\n      &amp;lt;\/MenuWrapper&gt;\n\n      &amp;lt;SiteTitle&gt;\n        &amp;lt;HeaderH1&gt;\n          &amp;lt;Link\n            to=&quot;\/&quot;\n            \n          &gt;\n            {data.site.siteMetadata.title}\n          &amp;lt;\/Link&gt;\n        &amp;lt;\/HeaderH1&gt;\n\n      &amp;lt;\/SiteTitle&gt;\n    &amp;lt;\/HeaderWrapper&gt;\n  )\n  \n}\n\nexport const Header = props =&gt; {\n  const data = useStaticQuery(graphql`\n    query {\n      site {\n        siteMetadata {\n          title\n        }\n      }\n    }\n  `)\n\n  return &amp;lt;PureHeader {...props} data={data}&gt;&amp;lt;\/PureHeader&gt;\n}\n\nHeader.propTypes = {\n  siteTitle: PropTypes.string,\n}\n\nHeader.defaultProps = {\n  siteTitle: ``,\n}\n\nexport default Header\n<\/pre><\/div>\n\n\n<h5 class=\"wp-block-heading\">Walk through refactoring of header.js<\/h5>\n\n\n\n<p>We are going to need graphic icons as we go along for the app including the hamburger icon which we can use now for control of the mobile menu. For icons we install the <a rel=\"noreferrer noopener\" href=\"https:\/\/react-icons.github.io\/react-icons\/\" target=\"_blank\">React Icons<\/a> library.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ncd frontend\nnpm install react-icons --save\n<\/pre><\/div>\n\n\n<p><strong>Line 5)<\/strong> We refactored our React import to include react&#8217;s <a rel=\"noreferrer noopener\" href=\"https:\/\/reactjs.org\/docs\/hooks-state.html\" target=\"_blank\">useState hook<\/a>. This hook allows us to track the current open\/close states of the mobile menu.<\/p>\n\n\n\n<p><strong>Line 11)<\/strong> Import the <strong>MdMenu<\/strong> from <strong>react-icons<\/strong>.<\/p>\n\n\n\n<p><strong>Lines 14-50)<\/strong> Using style-components to make the mobile menu look pretty.<\/p>\n\n\n\n<p><strong>Lines 34-38)<\/strong> Since we are using <strong>styled-components<\/strong> we can access the props argument listening for the the open\/close states of the mobile menu and define the style accordingly.<\/p>\n\n\n\n<p><strong>Line 61)<\/strong> Define and intialize the <strong>menuIsOpen<\/strong> state to false.<\/p>\n\n\n\n<p><strong>Lines 64-66)<\/strong> Handle the menu click event by toggling the <strong>menuIsOpen<\/strong> state with the not operator.<\/p>\n\n\n\n<p><strong>Line 71)<\/strong> We add the MenuHandle component with its onClick listener and handleMenuClick handler<\/p>\n\n\n\n<p>With the refactoring of <strong>navbar.js<\/strong> and <strong>header.js<\/strong> complete we need to rebuild the front end container:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose build frontend\n<\/pre><\/div>\n\n\n<p>Once the build is complete we can boot up the app and test the new mobile menu.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\nmake dev\n\n# or \ndocker-compose -f docker-compose.yml -f docker-compose-dev.yml up -d\n<\/pre><\/div>\n\n\n<p>Note of warning. Sometimes after a rebuild the Django server will try to connect to the postgres database before postgres is ready to accept connections causing the Django server to crash. A work-around to fix this issue is to boot postgres first, Django server second and finally the frontend.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: Double click to copy; notranslate\" title=\"Double click to copy\">\ndocker-compose up -d database\ndocker-compose up -d server\nmake dev\n<\/pre><\/div>\n\n\n<p>Now we should be able to test the new menu by hitting http:\/\/localhost:8000 where by clicking on the menu hamburger icon the menu opens and closes. Also on when a menu link is clicked the menu should close.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"862\" height=\"480\" src=\"http:\/\/10.0.0.14:5557\/wp-content\/uploads\/2021\/09\/chap_14_mobile_menu.jpg\" alt=\"\" class=\"wp-image-2570\" srcset=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/09\/chap_14_mobile_menu.jpg 862w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/09\/chap_14_mobile_menu-300x167.jpg 300w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/09\/chap_14_mobile_menu-768x428.jpg 768w, https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-content\/uploads\/2021\/09\/chap_14_mobile_menu-600x334.jpg 600w\" sizes=\"auto, (max-width: 862px) 100vw, 862px\" \/><\/figure>\n\n\n\n<p>What&#8217;s next? We need to add an additional testing utility called <a rel=\"noreferrer noopener\" href=\"https:\/\/enzymejs.github.io\/enzyme\/\" target=\"_blank\">Enzyme<\/a> that will allows us to use shallow testing on HOC components. You see, if we were to run our frontend unit tests now, the tests would fail since we implemented <a rel=\"noreferrer noopener\" href=\"https:\/\/www.npmjs.com\/package\/react-onclickoutside\" target=\"_blank\">react-onclickoutside<\/a> which is a Higher-Order Component and consequently our frontend tests fail now. But fear not, the <a rel=\"noreferrer noopener\" href=\"https:\/\/enzymejs.github.io\/enzyme\/\" target=\"_blank\">Enzyme<\/a> testing utility will allows us to work around this issue.<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link\" href=\"tdd-with-gatsby-django-docker-part-2-chapter-13-authentication\">&lt; Previous:  Chapter 13<\/a><\/div>\n\n\n\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link\" href=\"tdd-with-gatsby-django-docker-part-2-chapter-14-frontend-testing-with-enzyme\">Next: Chapter 15 &gt;<\/a><\/div>\n<\/div>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Build a Mobile Menu with Styled-Components To take a break from the heavy lifting of the previous chapters lets address the need for a mobile friendly menu. For the sake of simplicity we will use our mobile menu for desktop&#8230; <a class=\"more-link\" href=\"https:\/\/tutorials.leesonresearch.com\/tutorials\/2021\/01\/22\/tdd-with-gatsby-django-docker-part-2-chapter-14-mobile-menu\/\">Continue Reading &rarr;<\/a><\/p>\n","protected":false},"author":1,"featured_media":277,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[20,12],"tags":[10,28,26],"class_list":["post-1625","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-featured-tutorial","category-gatsby","tag-gatsby","tag-mobile","tag-styled-components"],"_links":{"self":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/1625","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/comments?post=1625"}],"version-history":[{"count":76,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/1625\/revisions"}],"predecessor-version":[{"id":2655,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/posts\/1625\/revisions\/2655"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/media\/277"}],"wp:attachment":[{"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/media?parent=1625"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/categories?post=1625"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tutorials.leesonresearch.com\/tutorials\/wp-json\/wp\/v2\/tags?post=1625"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}