Browse Source

Merge branch 'init_atlas' into SVGs

Francesco 4 years ago
parent
commit
08d14a701a

+ 104 - 0
packages/components/src/components/Dropdown/Dropdown.style.ts

@@ -0,0 +1,104 @@
+import { css } from "@emotion/core"
+import { typography, colors, spacing } from "./../../theme"
+
+export type DropdownStyleProps = {
+  disabled?: boolean
+  focus?: boolean
+  error?: boolean
+  isActive?: boolean
+}
+
+export let makeStyles = ({
+  disabled = false,
+  focus = false,
+  error = false,
+  isActive = false,
+}: DropdownStyleProps) => {
+
+  const fieldWidth = "250px"
+
+  const borderColor = disabled ? colors.gray[200] :
+    error ? colors.error :
+    focus ? colors.blue[500] :
+    isActive ? colors.gray[200] : colors.gray[400]
+
+  return {
+    wrapper: css`
+      display: block;
+      max-width: ${fieldWidth};
+      font-family: ${typography.fonts.base};
+    `,
+    container: css`
+      position: relative;
+      width: 100%;
+      height: 48px;
+      display: inline-flex;
+      cursor: ${disabled ? "not-allowed" : "default"};
+    `,
+    border: css`
+      position: absolute;
+      top: 0;
+      left: 0;
+      right: 0;
+      bottom: 0;
+      border: 1px solid ${borderColor};
+      display: flex;
+      align-items: center;
+      justify-content: left;
+    `,
+    label: css`
+      color: ${error ? colors.error : colors.gray[400]};
+      padding: 0 ${spacing.xxxxl} 0 ${spacing.s};
+      background-color: ${colors.black};
+      font-size: ${typography.sizes.body2};
+      &::selection {
+        background-color: transparent;
+      }
+    `,
+    input: css`
+      display: none;
+      width: 100%;
+      margin: 0 ${spacing.xxxxl} 0 ${spacing.s};
+      background: none;
+      border: none;
+      color: ${colors.white};
+      outline: none;
+      font-size: ${typography.sizes.body2};
+      padding: 5px 0;
+    `,
+    iconOpen: css`
+      color: ${colors.gray[300]};
+      font-size: ${typography.sizes.icon.medium};
+      position: absolute;
+      top: ${spacing.m};
+      right: ${spacing.s};
+    `,
+    iconClose: css`
+      color: ${colors.blue[500]};
+      font-size: ${typography.sizes.icon.medium};
+      position: absolute;
+      top: ${spacing.m};
+      right: ${spacing.s};
+    `,
+    helper: css`
+      color: ${error ? colors.error : colors.gray[400]};
+      font-size: ${typography.sizes.caption};
+      margin: ${spacing.xxs} ${spacing.xs};
+    `,
+    options: css`
+      background-color: ${colors.gray[700]};
+      color: ${colors.white};
+      display: block;
+      width: 100%;
+      position: absolute;
+      top: 50px;
+    `,
+    option: css`
+      padding: ${spacing.s};
+      font-size: ${typography.sizes.body2};
+      &:hover {
+        background-color: ${colors.gray[600]}
+      }
+    `
+  }
+}

+ 96 - 0
packages/components/src/components/Dropdown/Dropdown.tsx

@@ -0,0 +1,96 @@
+import React, { useState, useRef, useEffect } from "react"
+import { makeStyles, DropdownStyleProps } from "./Dropdown.style"
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
+import { faChevronDown, faChevronUp } from "@fortawesome/free-solid-svg-icons"
+import { spacing } from "./../../theme"
+
+type DropdownOption = {
+  text: string
+  value: string
+}
+
+type DropdownProps = {
+  label: string
+  helper?: string
+  value?: string
+  options: Array<DropdownOption>
+  onChange?: (option: DropdownOption) => void
+} & DropdownStyleProps
+
+export default function Dropdown({
+  label,
+  helper = "",
+  value = "",
+  options,
+  disabled = false,
+  onChange = () => {},
+  ...styleProps
+}: DropdownProps) {
+
+  const inputRef = useRef(null)
+  const [isActive, setIsActive] = useState(!!value)
+  const [inputTextValue, setInputTextValue] = useState(!!value ? options.find(option => option.value === value).text : "")
+  const [showOptions, setShowOptions] = useState(false)
+  const styles = makeStyles({ isActive, disabled, ...styleProps })
+
+  function onToggleDropdown(): void {
+    if (!disabled) {
+      setShowOptions(!showOptions)
+    }
+  }
+
+  function onOptionSelected(option: DropdownOption): void {
+    setIsActive(false)
+    setInputTextValue(option.text)
+    onChange(option)
+  } 
+  
+  return (
+    <div css={styles.wrapper}>
+      <div css={styles.container} onClick={onToggleDropdown}>
+        <div css={styles.border}>
+          <div
+            css={styles.label}
+            style={!inputTextValue && !isActive ? {} : {
+              position: "absolute",
+              top: "-8px",
+              left: "5px",
+              fontSize: "0.7rem",
+              padding: `0 ${spacing.xs}`
+            }}>
+              {label}
+          </div>
+          <input
+            css={styles.input}
+            style={{ display: !!inputTextValue || isActive ? "block" : "none"}}
+            ref={inputRef}
+            type="text"
+            disabled={true}
+            value={inputTextValue}
+          />
+          {!showOptions && 
+            <FontAwesomeIcon icon={faChevronDown} css={styles.iconOpen} />
+          }
+          {!!showOptions && 
+            <FontAwesomeIcon icon={faChevronUp} css={styles.iconClose} />
+          }
+        </div>
+        {showOptions &&
+          <div css={styles.options}>
+            {options.map((option, index) =>
+              <div
+                key={`${label}-${index}`}
+                css={styles.option}
+                defaultValue={option.value}
+                onClick={() => onOptionSelected(option)}>
+                  {option.text}
+              </div>)}
+          </div>
+        }
+      </div>
+      {!!helper &&
+        <p css={styles.helper}>{helper}</p>
+      }
+    </div>
+  )
+}

+ 3 - 0
packages/components/src/components/Dropdown/index.ts

@@ -0,0 +1,3 @@
+import { memo } from "react"
+import Dropdown from "./Dropdown"
+export default memo(Dropdown)

+ 47 - 9
packages/components/src/components/TextField/TextField.style.ts

@@ -1,21 +1,42 @@
 import { css } from "@emotion/core"
 import { css } from "@emotion/core"
-import { typography, colors } from "./../../theme"
+import { typography, colors, spacing } from "./../../theme"
+import { icon } from "@fortawesome/fontawesome-svg-core"
 
 
 export type TextFieldStyleProps = {
 export type TextFieldStyleProps = {
   disabled?: boolean
   disabled?: boolean
+  focus?: boolean
+  error?: boolean
+  isActive?: boolean
+  iconPosition?: "right" | "left"
 }
 }
 
 
 export let makeStyles = ({
 export let makeStyles = ({
-  disabled = false
+  disabled = false,
+  focus = false,
+  error = false,
+  isActive = false,
+  iconPosition = "right"
 }: TextFieldStyleProps) => {
 }: TextFieldStyleProps) => {
 
 
+  const fieldWidth = "250px"
+
+  const borderColor = disabled ? colors.gray[200] :
+    error ? colors.error :
+    focus ? colors.blue[500] :
+    isActive ? colors.gray[200] : colors.gray[400]
+
   return {
   return {
+    wrapper: css`
+      display: block;
+      max-width: ${fieldWidth};
+      font-family: ${typography.fonts.base};
+    `,
     container: css`
     container: css`
       position: relative;
       position: relative;
-      min-width: 250px;
-      height: 50px;
-      font-family: ${typography.fonts.base};
+      width: 100%;
+      height: 48px;
       display: inline-flex;
       display: inline-flex;
+      cursor: ${disabled ? "not-allowed" : "default"};
     `,
     `,
     border: css`
     border: css`
       position: absolute;
       position: absolute;
@@ -23,25 +44,42 @@ export let makeStyles = ({
       left: 0;
       left: 0;
       right: 0;
       right: 0;
       bottom: 0;
       bottom: 0;
-      border: 1px solid ${colors.gray[400]};
+      border: 1px solid ${borderColor};
       display: flex;
       display: flex;
       align-items: center;
       align-items: center;
       justify-content: left;
       justify-content: left;
     `,
     `,
     label: css`
     label: css`
-      color: ${colors.gray[400]};
-      padding: 0 10px;
+      color: ${error ? colors.error : colors.gray[400]};
+      padding: 0 ${spacing.s};
+      ${icon ? `padding-${iconPosition}: ${spacing.xxxxl};` : ""}
       background-color: ${colors.black};
       background-color: ${colors.black};
+      font-size: ${typography.sizes.body2};
       transition: all 0.1s linear;
       transition: all 0.1s linear;
     `,
     `,
     input: css`
     input: css`
       display: none;
       display: none;
       width: 100%;
       width: 100%;
-      margin: 0 10px;
+      margin: 0 ${spacing.s};
+      ${icon ? `margin-${iconPosition}: ${spacing.xxxxl};` : ""}
       background: none;
       background: none;
       border: none;
       border: none;
       color: ${colors.white};
       color: ${colors.white};
       outline: none;
       outline: none;
+      font-size: ${typography.sizes.body2};
+      padding: 5px 0;
+    `,
+    icon: css`
+      color: ${colors.gray[300]};
+      font-size: ${typography.sizes.icon.xlarge};
+      position: absolute;
+      top: ${spacing.s};
+      ${iconPosition}: ${spacing.s};
+    `,
+    helper: css`
+      color: ${error ? colors.error : colors.gray[400]};
+      font-size: ${typography.sizes.caption};
+      margin: ${spacing.xxs} ${spacing.xs};
     `
     `
   }
   }
 }
 }

+ 38 - 37
packages/components/src/components/TextField/TextField.tsx

@@ -1,26 +1,23 @@
 import React, { useState, useRef, useEffect } from "react"
 import React, { useState, useRef, useEffect } from "react"
 import { makeStyles, TextFieldStyleProps } from "./TextField.style"
 import { makeStyles, TextFieldStyleProps } from "./TextField.style"
-import { colors } from "./../../theme"
 import { IconProp } from "@fortawesome/fontawesome-svg-core"
 import { IconProp } from "@fortawesome/fontawesome-svg-core"
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
+import { spacing } from "./../../theme"
 
 
 type TextFieldProps = {
 type TextFieldProps = {
   label: string
   label: string
-  value?: string,
+  helper?: string
+  value?: string
   icon?: IconProp
   icon?: IconProp
-  iconPosition?: "right" | "left"
-  disabled?: boolean
-  onClick?: (e: React.MouseEvent<HTMLDivElement>) => void
   onChange?: (e: React.ChangeEvent) => void
   onChange?: (e: React.ChangeEvent) => void
 } & TextFieldStyleProps
 } & TextFieldStyleProps
 
 
 export default function TextField({
 export default function TextField({
   label,
   label,
+  helper = "",
   value = "",
   value = "",
   icon = null,
   icon = null,
-  iconPosition = "right",
   disabled = false,
   disabled = false,
-  onClick,
   onChange,
   onChange,
   ...styleProps
   ...styleProps
 }: TextFieldProps) {
 }: TextFieldProps) {
@@ -28,7 +25,7 @@ export default function TextField({
   const inputRef = useRef(null)
   const inputRef = useRef(null)
   const [isActive, setIsActive] = useState(!!value)
   const [isActive, setIsActive] = useState(!!value)
   const [inputTextValue, setInputTextValue] = useState(value)
   const [inputTextValue, setInputTextValue] = useState(value)
-  const styles = makeStyles(styleProps)
+  const styles = makeStyles({ isActive, disabled, ...styleProps })
 
 
   useEffect(() => {
   useEffect(() => {
     if (isActive) {
     if (isActive) {
@@ -40,45 +37,49 @@ export default function TextField({
   }, [isActive, inputRef]);
   }, [isActive, inputRef]);
 
 
   function onTextFieldClick() {
   function onTextFieldClick() {
-    setIsActive(true)
+    if (!disabled) setIsActive(true)
   }
   }
 
 
   function onInputTextBlur() {
   function onInputTextBlur() {
     setIsActive(false)
     setIsActive(false)
   }
   }
 
 
-  function onInputTextChange(event: React.FormEvent<HTMLInputElement>): React.ChangeEventHandler {
-    if (disabled) return
-    setInputTextValue(event.currentTarget.value)
-  }
+  function onInputTextChange(event: React.FormEvent<HTMLInputElement>): void {
+    if (!disabled) setInputTextValue(event.currentTarget.value)
+  } 
   
   
   return (
   return (
-    <div css={styles.container} onClick={onTextFieldClick}>
-      <div
-        css={styles.border}
-        style={!inputTextValue && !isActive ? {} : {
-          border: `1px solid ${colors.gray[200]}`
-        }}>
-        <div
-          css={styles.label}
-          style={!inputTextValue && !isActive ? {} : {
-            position: "absolute",
-            top: "-8px",
-            left: "5px",
-            fontSize: "0.7rem"
-          }}>
-            {label}
+    <div css={styles.wrapper}>
+      <div css={styles.container} onClick={onTextFieldClick}>
+        <div css={styles.border}>
+          <div
+            css={styles.label}
+            style={!inputTextValue && !isActive ? {} : {
+              position: "absolute",
+              top: "-8px",
+              left: "5px",
+              fontSize: "0.7rem",
+              padding: `0 ${spacing.xs}`
+            }}>
+              {label}
+          </div>
+          <input
+            css={styles.input}
+            style={{ display: !!inputTextValue || isActive ? "block" : "none"}}
+            ref={inputRef}
+            type="text"
+            value={inputTextValue}
+            onChange={disabled ? null : onInputTextChange}
+            onBlur={onInputTextBlur}
+          />
+          {!!icon &&
+            <FontAwesomeIcon icon={icon} css={styles.icon} />
+          }
         </div>
         </div>
-        <input
-          css={styles.input}
-          style={{ display: !!inputTextValue || isActive ? "block" : "none"}}
-          ref={inputRef}
-          type="text"
-          value={inputTextValue}
-          onChange={disabled ? null : onInputTextChange}
-          onBlur={onInputTextBlur}
-        />
       </div>
       </div>
+      {!!helper &&
+        <p css={styles.helper}>{helper}</p>
+      }
     </div>
     </div>
   )
   )
 }
 }

+ 3 - 2
packages/components/src/index.ts

@@ -1,8 +1,9 @@
 export { default as Button } from "./components/Button"
 export { default as Button } from "./components/Button"
+export { default as Dropdown } from "./components/Dropdown"
+export { default as Grid } from "./components/Grid"
+export { default as Header } from "./components/Header"
 export { default as NavButton } from "./components/NavButton"
 export { default as NavButton } from "./components/NavButton"
 export { default as TagButton } from "./components/TagButton"
 export { default as TagButton } from "./components/TagButton"
-export { default as Header } from "./components/Header"
 export { default as Tag } from "./components/Tag"
 export { default as Tag } from "./components/Tag"
 export { default as TextField } from "./components/TextField"
 export { default as TextField } from "./components/TextField"
 export { default as Typography } from "./components/Typography"
 export { default as Typography } from "./components/Typography"
-export { default as Grid } from "./components/Grid"

+ 26 - 26
packages/components/src/theme/colors.ts

@@ -1,28 +1,28 @@
 export default {
 export default {
-	white: "#FFFFFF",
-	black: "#000000",
-	gray: {
-		900: "#0B0C0F",
-		800: "#181C20",
-		700: "#272D33",
-		600: "#343D44",
-		500: "#424E57",
-		400: "#52616B",
-		300: "#7B8A95",
-		200: "#B5C1C9",
-		100: "#DAE2EB",
-		50: "#F4F6F8"
-	},
-	blue: {
-		900: "#030041",
-		700: "#261EE4",
-		600: "#2F2FF4",
-		500: "#4038FF",
-		400: "#5252FF",
-		300: "#6C6CFF",
-		100: "#B4BBFF",
-		50: "#E0E1FF"
-	},
-	success: "#00DBB0",
-	error: "#FF3861"
+  white: "#FFFFFF",
+  black: "#000000",
+  gray: {
+    900: "#0B0C0F",
+    800: "#181C20",
+    700: "#272D33",
+    600: "#343D44",
+    500: "#424E57",
+    400: "#52616B",
+    300: "#7B8A95",
+    200: "#B5C1C9",
+    100: "#DAE2EB",
+    50: "#F4F6F8"
+  },
+  blue: {
+    900: "#030041",
+    700: "#261EE4",
+    600: "#2F2FF4",
+    500: "#4038FF",
+    400: "#5252FF",
+    300: "#6C6CFF",
+    100: "#B4BBFF",
+    50: "#E0E1FF"
+  },
+  success: "#1CCB00",
+  error: "#B00020"
 }
 }

+ 5 - 5
packages/components/src/theme/spacing.ts

@@ -1,12 +1,12 @@
 export default {
 export default {
-  xxs: "2px",
-  xs: "4px",
-  s: "8px",
+  xxs: "4px",
+  xs: "8px",
+  s: "12px",
   m: "16px",
   m: "16px",
   l: "20px",
   l: "20px",
   xl: "24px",
   xl: "24px",
   xxl: "32px",
   xxl: "32px",
   xxxl: "40px",
   xxxl: "40px",
-  "4l": "48px",
-  "5l": "64px"
+  xxxxl: "48px",
+  xxxxxl: "64px"
 }
 }

+ 55 - 1
packages/components/stories/06-TextField.stories.tsx

@@ -1,5 +1,5 @@
 import React from "react"
 import React from "react"
-import { TextField } from "../src"
+import { TextField } from "./../src"
 import { faBan } from "@fortawesome/free-solid-svg-icons"
 import { faBan } from "@fortawesome/free-solid-svg-icons"
 
 
 export default {
 export default {
@@ -12,3 +12,57 @@ export const Primary = () => (
     <TextField label="Label" />
     <TextField label="Label" />
   </div>
   </div>
 )
 )
+
+export const Focus = () => (
+  <div style={{ backgroundColor: "black", padding: "50px 20px" }}>
+    <TextField label="Label" focus={true} />
+  </div>
+)
+
+export const Error = () => (
+  <div style={{ backgroundColor: "black", padding: "50px 20px" }}>
+    <TextField label="Label" error={true} />
+  </div>
+)
+
+export const Disabled = () => (
+  <div style={{ backgroundColor: "black", padding: "50px 20px" }}>
+    <TextField label="Label" disabled={true} />
+  </div>
+)
+
+export const DisabledWithValue = () => (
+  <div style={{ backgroundColor: "black", padding: "50px 20px" }}>
+    <TextField label="Label" value="Disabled" disabled={true} />
+  </div>
+)
+
+export const PrimaryWithIconRight = () => (
+  <div style={{ backgroundColor: "black", padding: "50px 20px" }}>
+    <TextField label="Label" icon={faBan} iconPosition="right" />
+  </div>
+)
+
+export const PrimaryWithIconLeft = () => (
+  <div style={{ backgroundColor: "black", padding: "50px 20px" }}>
+    <TextField label="Label" icon={faBan} iconPosition="left" />
+  </div>
+)
+
+export const PrimaryWithHelperText = () => (
+  <div style={{ backgroundColor: "black", padding: "50px 20px" }}>
+    <TextField label="Label" helper="Helper text" />
+  </div>
+)
+
+export const FocusWithHelperText = () => (
+  <div style={{ backgroundColor: "black", padding: "50px 20px" }}>
+    <TextField label="Label" focus={true} helper="Helper text" />
+  </div>
+)
+
+export const ErrorWithHelperText = () => (
+  <div style={{ backgroundColor: "black", padding: "50px 20px" }}>
+    <TextField label="Label" error={true} helper="Helper text" />
+  </div>
+)

+ 46 - 0
packages/components/stories/07-Dropdown.stories.tsx

@@ -0,0 +1,46 @@
+import React from "react"
+import { Dropdown } from "./../src"
+
+export default {
+  title: "Dropdown",
+  component: Dropdown,
+}
+
+const options = [
+  {
+    text: "Option 1",
+    value: "1"
+  },
+  {
+    text: "Option 2",
+    value: "2"
+  },
+  {
+    text: "Option 3",
+    value: "3"
+  }
+]
+
+export const Primary = () => (
+  <div style={{ backgroundColor: "black", padding: "50px 20px" }}>
+    <Dropdown label="Label" options={options} />
+  </div>
+)
+
+export const PrimaryWithValue = () => (
+  <div style={{ backgroundColor: "black", padding: "50px 20px" }}>
+    <Dropdown label="Label" options={options} value={options[1].value} />
+  </div>
+)
+
+export const PrimaryFocus = () => (
+  <div style={{ backgroundColor: "black", padding: "50px 20px" }}>
+    <Dropdown label="Label" options={options} focus={true} />
+  </div>
+)
+
+export const PrimaryError = () => (
+  <div style={{ backgroundColor: "black", padding: "50px 20px" }}>
+    <Dropdown label="Label" options={options} error={true} />
+  </div>
+)