Browse Source

Refactor Dropdown Component

Francesco Baccetti 4 years ago
parent
commit
dc48e168bc

+ 1 - 1
packages/components/assets/bars-play.svg

@@ -1 +1 @@
-<svg width="20" height="14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M17 4H0v2h17V4zm0-4H0v2h17V0zM0 10h13V8H0v2zm15-2v6l5-3-5-3z" fill="#7B8A95"/></svg>
+<svg width="20" height="14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M17 4H0v2h17V4zm0-4H0v2h17V0zM0 10h13V8H0v2zm15-2v6l5-3-5-3z" fill="currentColor"/></svg>

+ 1 - 1
packages/components/assets/bars-plus.svg

@@ -1 +1 @@
-<svg width="20" height="14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 4H0v2h12V4zm0-4H0v2h12V0zm4 8V4h-2v4h-4v2h4v4h2v-4h4V8h-4zM0 10h8V8H0v2z" fill="#7B8A95"/></svg>
+<svg width="20" height="14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 4H0v2h12V4zm0-4H0v2h12V0zm4 8V4h-2v4h-4v2h4v4h2v-4h4V8h-4zM0 10h8V8H0v2z" fill="currentColor"/></svg>

+ 1 - 1
packages/components/assets/bars.svg

@@ -1 +1 @@
-<svg width="18" height="12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M0 12h18v-2H0v2zm0-5h18V5H0v2zm0-7v2h18V0H0z" fill="#7B8A95"/></svg>
+<svg width="18" height="12" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M0 12h18v-2H0v2zm0-5h18V5H0v2zm0-7v2h18V0H0z" fill="currentColor"/></svg>

+ 1 - 1
packages/components/assets/block-large.svg

@@ -1 +1 @@
-<svg width="20" height="20" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10 0C4.48 0 0 4.48 0 10s4.48 10 10 10 10-4.48 10-10S15.52 0 10 0zm0 18c-4.42 0-8-3.58-8-8 0-1.85.63-3.55 1.69-4.9L14.9 16.31A7.902 7.902 0 0110 18zm6.31-3.1L5.1 3.69A7.902 7.902 0 0110 2c4.42 0 8 3.58 8 8 0 1.85-.63 3.55-1.69 4.9z" fill="#7B8A95"/></svg>
+<svg width="20" height="20" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10 0C4.48 0 0 4.48 0 10s4.48 10 10 10 10-4.48 10-10S15.52 0 10 0zm0 18c-4.42 0-8-3.58-8-8 0-1.85.63-3.55 1.69-4.9L14.9 16.31A7.902 7.902 0 0110 18zm6.31-3.1L5.1 3.69A7.902 7.902 0 0110 2c4.42 0 8 3.58 8 8 0 1.85-.63 3.55-1.69 4.9z" fill="currentColor"/></svg>

+ 1 - 1
packages/components/assets/block.svg

@@ -1 +1 @@
-<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" r="6" stroke="#7B8A95" stroke-width="2" stroke-linecap="round"/><path d="M4 4l8 8" stroke="#7B8A95" stroke-width="2" stroke-linecap="round"/></svg>
+<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="8" r="6" stroke="#7B8A95" stroke-width="2" stroke-linecap="round"/><path d="M4 4l8 8" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>

+ 1 - 1
packages/components/assets/check.svg

@@ -1 +1 @@
-<svg width="12" height="11" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 6.5L3.5 9l7-8" stroke="#7B8A95" stroke-width="2" stroke-linecap="round"/></svg>
+<svg width="12" height="11" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 6.5L3.5 9l7-8" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>

+ 1 - 1
packages/components/assets/chevron-down-big.svg

@@ -1 +1 @@
-<svg width="12" height="8" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1l5 5 5-5" stroke="#7B8A95" stroke-width="2" stroke-linecap="round"/></svg>
+<svg width="12" height="8" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1l5 5 5-5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>

+ 1 - 1
packages/components/assets/chevron-left-big.svg

@@ -1 +1 @@
-<svg width="8" height="13" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.5 11.5l-5-5 5-5" stroke="#7B8A95" stroke-width="2" stroke-linecap="round"/></svg>
+<svg width="8" height="13" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M6.5 11.5l-5-5 5-5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>

+ 1 - 1
packages/components/assets/chevron-right-big.svg

@@ -1 +1 @@
-<svg width="8" height="13" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1.5 11.5l5-5-5-5" stroke="#7B8A95" stroke-width="2" stroke-linecap="round"/></svg>
+<svg width="8" height="13" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1.5 11.5l5-5-5-5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>

+ 1 - 1
packages/components/assets/chevron-up-big.svg

@@ -1 +1 @@
-<svg width="12" height="8" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 7l5-5 5 5" stroke="#7B8A95" stroke-width="2" stroke-linecap="round"/></svg>
+<svg width="12" height="8" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 7l5-5 5 5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>

+ 1 - 1
packages/components/assets/chevron.svg

@@ -1 +1 @@
-<svg width="8" height="6" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1l3 3 3-3" stroke="#7B8A95" stroke-width="2" stroke-linecap="round"/></svg>
+<svg width="8" height="6" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1l3 3 3-3" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>

+ 1 - 1
packages/components/assets/dash.svg

@@ -1 +1 @@
-<svg width="12" height="2" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1h10" stroke="#7B8A95" stroke-width="2" stroke-linecap="round"/></svg>
+<svg width="12" height="2" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1h10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>

+ 1 - 1
packages/components/assets/eyes.svg

@@ -1 +1 @@
-<svg width="22" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11 2.5A9.77 9.77 0 0119.82 8 9.76 9.76 0 0111 13.5 9.76 9.76 0 012.18 8 9.77 9.77 0 0111 2.5zm0-2C6 .5 1.73 3.61 0 8c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5C20.27 3.61 16 .5 11 .5zm0 5a2.5 2.5 0 010 5 2.5 2.5 0 010-5zm0-2C8.52 3.5 6.5 5.52 6.5 8s2.02 4.5 4.5 4.5 4.5-2.02 4.5-4.5-2.02-4.5-4.5-4.5z" fill="#7B8A95"/></svg>
+<svg width="22" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path  fill="currentColor" d="M11 2.5A9.77 9.77 0 0119.82 8 9.76 9.76 0 0111 13.5 9.76 9.76 0 012.18 8 9.77 9.77 0 0111 2.5zm0-2C6 .5 1.73 3.61 0 8c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5C20.27 3.61 16 .5 11 .5zm0 5a2.5 2.5 0 010 5 2.5 2.5 0 010-5zm0-2C8.52 3.5 6.5 5.52 6.5 8s2.02 4.5 4.5 4.5 4.5-2.02 4.5-4.5-2.02-4.5-4.5-4.5z" fill="#7B8A95"/></svg>

+ 0 - 2
packages/components/assets/index.ts

@@ -1,2 +0,0 @@
-export * from "./bars-play.svg";
-export * from "./bars-plus.svg";

+ 1 - 1
packages/components/assets/plus.svg

@@ -1 +1 @@
-<svg width="14" height="14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M14 8H8v6H6V8H0V6h6V0h2v6h6v2z" fill="#7B8A95"/></svg>
+<svg width="14" height="14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M14 8H8v6H6V8H0V6h6V0h2v6h6v2z" fill="currentColor"/></svg>

+ 125 - 101
packages/components/src/components/Dropdown/Dropdown.style.ts

@@ -1,107 +1,131 @@
-import { css } from "@emotion/core"
-import { typography, colors, spacing } from "./../../theme"
+import { StyleFn, makeStyles } from "./../../utils";
+import { css } from "@emotion/core";
+import { typography, colors, spacing } from "./../../theme";
 
 export type DropdownStyleProps = {
-  disabled?: boolean
-  focus?: boolean
-  error?: boolean
-  isActive?: boolean
-}
+	disabled?: boolean;
+	focus?: boolean;
+	error?: boolean;
+	isActive?: boolean;
+};
 
-export let makeStyles = ({
-  disabled = false,
-  focus = false,
-  error = false,
-  isActive = false,
-}: DropdownStyleProps) => {
+const wrapper: StyleFn = () => ({
+	display: "block",
+	maxWidth: "250px",
+	fontFamily: typography.fonts.base,
+});
 
-  const fieldWidth = "250px"
+const container: StyleFn = (_, { disabled }) => ({
+	position: "relative",
+	width: "100%",
+	height: "48px",
+	display: "inline-flex",
+	cursor: disabled ? "not-allowed" : "default",
+});
 
-  const borderColor = disabled ? colors.gray[200] :
-    error ? colors.error :
-    focus ? colors.blue[500] :
-    isActive ? colors.gray[200] : colors.gray[400]
+const borderBase: StyleFn = () => ({
+	position: "absolute",
+	top: 0,
+	left: 0,
+	right: 0,
+	bottom: 0,
+	borderWidth: "1px",
+	borderStyle: "solid",
+	display: "flex",
+	alignItems: "center",
+	justifyContent: "left",
+});
 
-  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;
-      max-height: 145px;
-      overflow-x: none;
-      overflow-y: auto;
-    `,
-    option: css`
-      padding: ${spacing.s};
-      font-size: ${typography.sizes.body2};
-      &:hover {
-        background-color: ${colors.gray[600]}
-      }
-    `
-  }
-}
+const label: StyleFn = (_, { error }) => ({
+	color: error ? colors.error : colors.gray[400],
+	padding: `0 ${spacing.xxxxl} 0 ${spacing.s}`,
+	backgroundColor: colors.black,
+	fontSize: typography.sizes.body2,
+	"&::selection": {
+		backgroundColor: "transparent",
+	},
+});
+
+const input: StyleFn = () => ({
+	display: "none",
+	widht: "100%",
+	margin: `0 ${spacing.xxxxl} 0 ${spacing.s}`,
+	background: "none",
+	border: "none",
+	color: colors.white,
+	outline: "none",
+	fontSize: typography.sizes.body2,
+	padding: "5px 0",
+});
+
+const iconOpen: StyleFn = () => ({
+	color: colors.gray[300],
+	fontSize: typography.sizes.icon.medium,
+	position: "absolute",
+	top: spacing.m,
+	right: spacing.s,
+});
+
+const iconClose: StyleFn = (_, { error }) => ({
+	color: error ? colors.error : colors.blue[500],
+	fontSize: typography.sizes.icon.medium,
+	position: "absolute",
+	top: spacing.m,
+	right: spacing.s,
+});
+
+const helper: StyleFn = (_, { error }) => ({
+	color: error ? colors.error : colors.gray[400],
+	fontSize: typography.sizes.caption,
+	margin: `${spacing.xxs} ${spacing.xs}`,
+});
+
+const options: StyleFn = () => ({
+	backgroundColor: colors.gray[700],
+	color: colors.white,
+	display: "block",
+	width: "100%",
+	position: "absolute",
+	top: "50px",
+	maxHeight: "145px",
+	overflowX: "none",
+	overflowY: "auto",
+});
+
+const option: StyleFn = () => ({
+	padding: spacing.s,
+	fontSize: typography.sizes.body2,
+	"&:hover": {
+		backgroundColor: colors.gray[600],
+	},
+});
+
+const borderColorFromProps: StyleFn = (styles, { error, disabled, isActive, focus }) => {
+	const borderColor = disabled
+		? colors.gray[200]
+		: error
+		? colors.error
+		: focus
+		? colors.blue[500]
+		: isActive
+		? colors.gray[200]
+		: colors.gray[400];
+
+	return {
+		...styles,
+		borderColor,
+	};
+};
+
+export const useCSS = (props: DropdownStyleProps) => ({
+	wrapper: makeStyles([wrapper])(props),
+	container: makeStyles([container])(props),
+	border: makeStyles([borderBase, borderColorFromProps])(props),
+	label: makeStyles([label])(props),
+	input: makeStyles([input])(props),
+	iconOpen: makeStyles([iconOpen])(props),
+	iconClose: makeStyles([iconClose])(props),
+	helper: makeStyles([helper])(props),
+	options: makeStyles([options])(props),
+	option: makeStyles([option])(props),
+});

+ 89 - 85
packages/components/src/components/Dropdown/Dropdown.tsx

@@ -1,96 +1,100 @@
-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"
+import React, { useState, useRef, useEffect } from "react";
+import { DropdownStyleProps, useCSS } from "./Dropdown.style";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faChevronDown, faChevronUp } from "@fortawesome/free-solid-svg-icons";
+import ChevronIconUp from "../../../assets/chevron-down-big.svg";
+import ChevronIconDown from "../../../assets/chevron-up-big.svg";
+import { spacing } from "./../../theme";
 
 type DropdownOption = {
-  text: string
-  value: string
-}
+	text: string;
+	value: string;
+};
 
 type DropdownProps = {
-  label: string
-  helper?: string
-  value?: string
-  options: Array<DropdownOption>
-  onChange?: (option: DropdownOption) => void
-} & DropdownStyleProps
+	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
+	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 = useCSS({ isActive, disabled, ...styleProps });
 
-  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 onToggleDropdown(): void {
-    if (!disabled) {
-      setShowOptions(!showOptions)
-    }
-  }
+	function onOptionSelected(option: DropdownOption): void {
+		setIsActive(false);
+		setInputTextValue(option.text);
+		onChange(option);
+	}
 
-  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>
-  )
+	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 && <ChevronIconUp css={styles.iconOpen} />}
+					{!!showOptions && <ChevronIconDown 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>
+	);
 }