From 411d3e3e684a83547fe0c09ded12edb90a4e89b9 Mon Sep 17 00:00:00 2001
From: Aanand Prasad <aanand.prasad@gmail.com>
Date: Wed, 31 Jan 2018 18:01:23 +0000
Subject: [PATCH] feat(ui): convert Menu to a styled component

---
 packages/ui/src/atoms/Menu.js                 | 185 ++++++++++++---
 packages/ui/src/atoms/Menu.local.scss         | 121 ----------
 .../ui/test/__snapshots__/Menu.test.js.snap   | 218 ++++++++++++++++--
 3 files changed, 354 insertions(+), 170 deletions(-)
 delete mode 100644 packages/ui/src/atoms/Menu.local.scss

diff --git a/packages/ui/src/atoms/Menu.js b/packages/ui/src/atoms/Menu.js
index ff22b84d5..95ce34eb5 100644
--- a/packages/ui/src/atoms/Menu.js
+++ b/packages/ui/src/atoms/Menu.js
@@ -1,10 +1,140 @@
 import React from 'react'
-import classnames from 'classnames'
-import classes from './Menu.local.scss'
+import styled from 'styled-components'
 
 // TODO: match the width of the container to the width of the widest option?
 // TODO: use a <select> element instead of divs?
 
+const Label = styled.span`
+  display: block;
+  font-size: 1em;
+  margin-bottom: 0.5em;
+`
+
+const OpenerContainer = styled.div``
+
+const Opener = styled.button.attrs({
+  type: 'button',
+})`
+  background: transparent;
+  border: none;
+  cursor: pointer;
+  font-family: inherit;
+  font-size: 1.2em;
+`
+
+const Placeholder = styled.span`
+  font-family: var(--font-interface);
+  font-style: italic;
+  font-weight: 400;
+  text-transform: normal;
+`
+
+const Arrow = styled.span`
+  display: inline-block;
+  font-size: 50%;
+  margin-left: 10px;
+  transition: transform 0.2s;
+`
+
+const Main = styled.div.attrs({
+  role: 'listbox',
+})`
+  position: relative;
+`
+
+const OptionsContainer = styled.div`
+  position: absolute;
+`
+
+const Options = styled.div`
+  background-color: white;
+  border-bottom: 2px solid var(--color-primary);
+  border-left: 2px solid var(--color-primary);
+  left: 0;
+  padding-bottom: 0.5em;
+  padding-top: 0.5em;
+  position: absolute;
+  top: 0;
+  transition: opacity 2s;
+  width: 0;
+  z-index: 10;
+`
+
+const Option = styled.div.attrs({
+  role: 'option',
+  tabIndex: '0',
+  'aria-selected': props => props.active,
+})`
+  color: ${props => (props.active ? 'black' : 'inherit')};
+  font-weight: ${props => (props.active ? '600' : 'inherit')};
+  cursor: pointer;
+  font-family: var(--font-author);
+  padding: 10px;
+  white-space: nowrap;
+
+  &:hover {
+    color: var(--color-primary);
+  }
+`
+
+const Root = styled.div`
+  font-size: 1em;
+
+  ${Opener} {
+    border-left: 2px solid
+      ${props => (props.open ? 'var(--color-primary)' : 'lightgrey')};
+    color: ${props => (props.open ? 'var(--color-primary)' : 'inherit')};
+
+    ${Placeholder} {
+      color: #aaa;
+    }
+  }
+
+  ${Opener}:hover {
+    border-left: 2px solid var(--color-primary);
+    color: var(--color-primary);
+
+    ${Placeholder} {
+      color: var(--color-primary);
+    }
+  }
+
+  ${Arrow} {
+    transform: scaleX(2.2) scaleY(${props => (props.open ? -1.2 : 1.2)});
+  }
+
+  ${Options} {
+    min-width: ${props => (props.open ? '10em' : '0')};
+    opacity: ${props => (props.open ? '1' : '0')};
+  }
+`
+
+/* Not used for now
+.inline {
+  align-items: flex-end;
+  display: flex;
+  flex-direction: row;
+  justify-content: flex-start;
+  margin-right: 0.5em;
+}
+
+.inline .label {
+  margin-right: 0.5em;
+}
+
+.inline .opener {
+  margin-bottom: -4px;
+}
+
+.root .inline {
+  flex-direction: columns;
+}
+
+.root.author {
+  font-family: var(--font-author);
+}
+*/
+
 class Menu extends React.Component {
   constructor(props) {
     super(props)
@@ -47,54 +177,41 @@ class Menu extends React.Component {
     const { open, selected } = this.state
 
     return (
-      <div
-        className={classnames(classes.root, {
-          [classes.open]: open,
-        })}
-      >
-        {label && <span className={classes.label}>{label}</span>}
-
-        <div className={classes.main} role="listbox">
-          <div className={classes.openerContainer}>
-            <button
-              className={classes.opener}
-              onClick={this.toggleMenu}
-              type="button"
-            >
+      <Root open={open}>
+        {label && <Label>{label}</Label>}
+
+        <Main>
+          <OpenerContainer>
+            <Opener onClick={this.toggleMenu}>
               {selected ? (
                 <span>{this.optionLabel(selected)}</span>
               ) : (
-                <span className={classes.placeholder}>{placeholder}</span>
+                <Placeholder>{placeholder}</Placeholder>
               )}
-              <span className={classes.arrow}>â–¼</span>
-            </button>
-          </div>
+              <Arrow>â–¼</Arrow>
+            </Opener>
+          </OpenerContainer>
 
-          <div className={classes.optionsContainer}>
+          <OptionsContainer>
             {open && (
-              <div className={classes.options}>
+              <Options>
                 {options.map(option => (
-                  <div
-                    aria-selected={option.value === selected}
-                    className={classnames(classes.option, {
-                      [classes.active]: option.value === selected,
-                    })}
+                  <Option
+                    active={option.value === selected}
                     key={option.value}
                     onClick={() => this.handleSelect(option.value)}
                     onKeyPress={event =>
                       this.handleKeyPress(event, option.value)
                     }
-                    role="option"
-                    tabIndex="0"
                   >
                     {option.label || option.value}
-                  </div>
+                  </Option>
                 ))}
-              </div>
+              </Options>
             )}
-          </div>
-        </div>
-      </div>
+          </OptionsContainer>
+        </Main>
+      </Root>
     )
   }
 }
diff --git a/packages/ui/src/atoms/Menu.local.scss b/packages/ui/src/atoms/Menu.local.scss
deleted file mode 100644
index edeb0f22e..000000000
--- a/packages/ui/src/atoms/Menu.local.scss
+++ /dev/null
@@ -1,121 +0,0 @@
-.root {
-  font-size: 1em;
-}
-
-.inline {
-  align-items: flex-end;
-  display: flex;
-  flex-direction: row;
-  justify-content: flex-start;
-  margin-right: 0.5em;
-}
-
-.inline .label {
-  margin-right: 0.5em;
-}
-
-.opener {
-  background: transparent;
-  border: none;
-  border-left: 2px solid lightgrey;
-  cursor: pointer;
-  font-family: inherit;
-  font-size: 1.2em;
-}
-
-.opener:hover {
-  border-left: 2px solid var(--color-primary);
-  color: var(--color-primary);
-}
-
-.inline .opener {
-  margin-bottom: -4px;
-}
-
-.root .inline {
-  flex-direction: columns;
-}
-
-.root:not(.inline) .label {
-  display: block;
-  font-size: 1em;
-  margin-bottom: 0.5em;
-}
-
-.main {
-  position: relative;
-}
-
-.optionsContainer {
-  position: absolute;
-}
-
-.open .opener {
-  border-left: 2px solid var(--color-primary);
-  color: var(--color-primary);
-}
-
-.placeholder {
-  color: #aaa;
-  font-family: var(--font-interface);
-  font-style: italic;
-  font-weight: 400;
-  text-transform: normal;
-}
-
-.opener:hover .placeholder {
-  color: var(--color-primary);
-}
-
-.arrow {
-  display: inline-block;
-  font-size: 50%;
-  margin-left: 10px;
-  transform: scaleY(1.2) scaleX(2.2);
-  transition: transform 0.2s;
-}
-
-.open .arrow {
-  transform: scaleX(2.2) scaleY(-1.2);
-}
-
-.options {
-  background-color: white;
-  border-bottom: 2px solid var(--color-primary);
-  border-left: 2px solid var(--color-primary);
-  left: 0;
-  min-width: 0;
-  opacity: 0;
-  padding-bottom: 0.5em;
-  padding-top: 0.5em;
-  position: absolute;
-  top: 0;
-  transition: opacity 2s;
-  width: 0;
-  z-index: 10;
-}
-
-.open .options {
-  min-width: 10em;
-  opacity: 1;
-}
-
-.option {
-  cursor: pointer;
-  font-family: var(--font-author);
-  padding: 10px;
-  white-space: nowrap;
-}
-
-.option:hover {
-  color: var(--color-primary);
-}
-
-.active {
-  color: black;
-  font-weight: 600; /* placeholder for the semibold */
-}
-
-.root.author {
-  font-family: var(--font-author);
-}
diff --git a/packages/ui/test/__snapshots__/Menu.test.js.snap b/packages/ui/test/__snapshots__/Menu.test.js.snap
index d5be802b3..c45f23fff 100644
--- a/packages/ui/test/__snapshots__/Menu.test.js.snap
+++ b/packages/ui/test/__snapshots__/Menu.test.js.snap
@@ -1,18 +1,77 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`Menu is rendered correctly 1`] = `
+.c3 {
+  background: transparent;
+  border: none;
+  cursor: pointer;
+  font-family: inherit;
+  font-size: 1.2em;
+}
+
+.c5 {
+  display: inline-block;
+  font-size: 50%;
+  margin-left: 10px;
+  -webkit-transition: -webkit-transform 0.2s;
+  -webkit-transition: transform 0.2s;
+  transition: transform 0.2s;
+}
+
+.c1 {
+  position: relative;
+}
+
+.c6 {
+  position: absolute;
+}
+
+.c0 {
+  font-size: 1em;
+}
+
+.c0 .c2 {
+  border-left: 2px solid lightgrey;
+  color: inherit;
+}
+
+.c0 .c2 .sc-bxivhb {
+  color: #aaa;
+}
+
+.c0 .c2:hover {
+  border-left: 2px solid var(--color-primary);
+  color: var(--color-primary);
+}
+
+.c0 .c2:hover .sc-bxivhb {
+  color: var(--color-primary);
+}
+
+.c0 .c4 {
+  -webkit-transform: scaleX(2.2) scaleY(1.2);
+  -ms-transform: scaleX(2.2) scaleY(1.2);
+  transform: scaleX(2.2) scaleY(1.2);
+}
+
+.c0 .sc-gzVnrw {
+  min-width: 0;
+  opacity: 0;
+}
+
 <div
-  className="root"
+  className="c0"
+  open={false}
 >
   <div
-    className="main"
+    className="c1"
     role="listbox"
   >
     <div
-      className="openerContainer"
+      className=""
     >
       <button
-        className="opener"
+        className="c2 c3"
         onClick={[Function]}
         type="button"
       >
@@ -20,32 +79,161 @@ exports[`Menu is rendered correctly 1`] = `
           Foo
         </span>
         <span
-          className="arrow"
+          className="c4 c5"
         >
           â–¼
         </span>
       </button>
     </div>
     <div
-      className="optionsContainer"
+      className="c6"
     />
   </div>
 </div>
 `;
 
 exports[`Menu is rendered correctly when open 1`] = `
+.c3 {
+  background: transparent;
+  border: none;
+  cursor: pointer;
+  font-family: inherit;
+  font-size: 1.2em;
+}
+
+.c5 {
+  display: inline-block;
+  font-size: 50%;
+  margin-left: 10px;
+  -webkit-transition: -webkit-transform 0.2s;
+  -webkit-transition: transform 0.2s;
+  transition: transform 0.2s;
+}
+
+.c1 {
+  position: relative;
+}
+
+.c6 {
+  position: absolute;
+}
+
+.c8 {
+  background-color: white;
+  border-bottom: 2px solid var(--color-primary);
+  border-left: 2px solid var(--color-primary);
+  left: 0;
+  padding-bottom: 0.5em;
+  padding-top: 0.5em;
+  position: absolute;
+  top: 0;
+  -webkit-transition: opacity 2s;
+  transition: opacity 2s;
+  width: 0;
+  z-index: 10;
+}
+
+.c9 {
+  color: black;
+  font-weight: 600;
+  cursor: pointer;
+  font-family: var(--font-author);
+  padding: 10px;
+  white-space: nowrap;
+}
+
+.c9:hover {
+  color: var(--color-primary);
+}
+
+.c10 {
+  color: inherit;
+  font-weight: inherit;
+  cursor: pointer;
+  font-family: var(--font-author);
+  padding: 10px;
+  white-space: nowrap;
+}
+
+.c10:hover {
+  color: var(--color-primary);
+}
+
+.gQZhbO .c2 {
+  border-left: 2px solid lightgrey;
+  color: inherit;
+}
+
+.gQZhbO .c2 .sc-bxivhb {
+  color: #aaa;
+}
+
+.gQZhbO .c2:hover {
+  border-left: 2px solid var(--color-primary);
+  color: var(--color-primary);
+}
+
+.gQZhbO .c2:hover .sc-bxivhb {
+  color: var(--color-primary);
+}
+
+.gQZhbO .c4 {
+  -webkit-transform: scaleX(2.2) scaleY(1.2);
+  -ms-transform: scaleX(2.2) scaleY(1.2);
+  transform: scaleX(2.2) scaleY(1.2);
+}
+
+.gQZhbO .c7 {
+  min-width: 0;
+  opacity: 0;
+}
+
+.c0 {
+  font-size: 1em;
+}
+
+.c0 .c2 {
+  border-left: 2px solid var(--color-primary);
+  color: var(--color-primary);
+}
+
+.c0 .c2 .sc-bxivhb {
+  color: #aaa;
+}
+
+.c0 .c2:hover {
+  border-left: 2px solid var(--color-primary);
+  color: var(--color-primary);
+}
+
+.c0 .c2:hover .sc-bxivhb {
+  color: var(--color-primary);
+}
+
+.c0 .c4 {
+  -webkit-transform: scaleX(2.2) scaleY(-1.2);
+  -ms-transform: scaleX(2.2) scaleY(-1.2);
+  transform: scaleX(2.2) scaleY(-1.2);
+}
+
+.c0 .c7 {
+  min-width: 10em;
+  opacity: 1;
+}
+
 <div
-  className="root open"
+  className="c0"
+  open={true}
 >
   <div
-    className="main"
+    className="c1"
     role="listbox"
   >
     <div
-      className="openerContainer"
+      className=""
     >
       <button
-        className="opener"
+        className="c2 c3"
         onClick={[Function]}
         type="button"
       >
@@ -53,21 +241,21 @@ exports[`Menu is rendered correctly when open 1`] = `
           Foo
         </span>
         <span
-          className="arrow"
+          className="c4 c5"
         >
           â–¼
         </span>
       </button>
     </div>
     <div
-      className="optionsContainer"
+      className="c6"
     >
       <div
-        className="options"
+        className="c7 c8"
       >
         <div
           aria-selected={true}
-          className="option active"
+          className="c9"
           onClick={[Function]}
           onKeyPress={[Function]}
           role="option"
@@ -77,7 +265,7 @@ exports[`Menu is rendered correctly when open 1`] = `
         </div>
         <div
           aria-selected={false}
-          className="option"
+          className="c10"
           onClick={[Function]}
           onKeyPress={[Function]}
           role="option"
-- 
GitLab