Browse Source

Refactor TextInput

Francesco Baccetti 4 years ago
parent
commit
49f90459db

+ 1 - 0
package.json

@@ -15,6 +15,7 @@
   },
   "devDependencies": {
     "husky": "^4.2.5",
+    "jest": "^26.0.1",
     "lerna": "^3.20.2"
   }
 }

+ 39 - 39
packages/app/package.json

@@ -1,41 +1,41 @@
 {
-  "name": "app",
-  "version": "1.0.0",
-  "description": "A user governed video platform",
-  "homepage": "https://github.com/Joystream/atlas#readme",
-  "license": "ISC",
-  "main": "src/app.js",
-  "directories": {
-    "src": "src",
-    "test": "__tests__"
-  },
-  "files": [
-    "src"
-  ],
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/Joystream/atlas.git"
-  },
-  "scripts": {
-    "start": "parcel public/index.html",
-    "dev": "parcel public/index.html",
-    "test": "echo \"Error: run tests from root\" && exit 1"
-  },
-  "bugs": {
-    "url": "https://github.com/Joystream/atlas/issues"
-  },
-  "dependencies": {
-    "@reach/router": "^1.3.3",
-    "normalize.css": "^8.0.1",
-    "packages": "^0.0.8",
-    "react": "^16.13.0",
-    "react-dom": "^16.13.0",
-    "react-redux": "^7.2.0",
-    "redux": "^4.0.5"
-  },
-  "devDependencies": {
-    "@types/reach__router": "^1.3.1",
-    "@types/react-redux": "^7.1.7",
-    "babel-core": "^6.26.3"
-  }
+	"name": "app",
+	"version": "1.0.0",
+	"description": "A user governed video platform",
+	"homepage": "https://github.com/Joystream/atlas#readme",
+	"license": "ISC",
+	"main": "src/app.js",
+	"directories": {
+		"src": "src",
+		"test": "__tests__"
+	},
+	"files": [
+		"src"
+	],
+	"repository": {
+		"type": "git",
+		"url": "https://github.com/Joystream/atlas.git"
+	},
+	"scripts": {
+		"start": "parcel public/index.html",
+		"dev": "parcel public/index.html",
+		"test": "echo \"Error: run tests from root\" && exit 1"
+	},
+	"bugs": {
+		"url": "https://github.com/Joystream/atlas/issues"
+	},
+	"dependencies": {
+		"@reach/router": "^1.3.3",
+		"normalize.css": "^8.0.1",
+		"packages": "^0.0.8",
+		"react": "^16.13.0",
+		"react-dom": "^16.13.0",
+		"react-redux": "^7.2.0",
+		"redux": "^4.0.5"
+	},
+	"devDependencies": {
+		"@types/reach__router": "^1.3.1",
+		"@types/react-redux": "^7.1.7",
+		"babel-core": "^6.26.3"
+	}
 }

+ 39 - 36
packages/components/src/components/NavButton/NavButton.style.ts

@@ -1,43 +1,46 @@
-import { css } from "@emotion/core"
 import { typography, colors } from "../../theme"
+import { StyleFn, makeStyles } from "../../utils"
 
 export type NavButtonStyleProps = {
-  type?: "primary" | "secondary"
+	type?: "primary" | "secondary"
 }
 
-export let makeStyles = ({
-  type = "primary"
-}: NavButtonStyleProps) => {
-  return css`
-    border: 0;
-    color: ${colors.white};
-    background-color: ${type === "primary" ? colors.blue[500] : colors.black};
-    text-align: center;
-    display: inline-block;
-    cursor: default;
-    font-family: ${typography.fonts.base};
-    font-weight: ${typography.weights.medium};
-    font-size: ${typography.sizes.subtitle1};
-    margin: 1px;
-    padding: 0;
-    width: 50px;
-    height: 50px;
-    line-height: 50px;
+const baseStyles: StyleFn = () => ({
+	border: 0,
+	color: colors.white,
+	textAlign: "center",
+	display: "inline-block",
+	cursor: "default",
+	fontFamily: typography.fonts.base,
+	fontWeight: typography.weights.medium,
+	fontSize: typography.sizes.subtitle1,
+	margin: "1px",
+	padding: 0,
+	width: "50px",
+	height: "50px",
+	lineHeight: "50px",
+	"&:hover": {
+		borderColor: colors.blue[700]
+	},
+	"&:active": {
+		borderColor: colors.blue[900]
+	},
+	"&::selection": {
+		background: "transparent"
+	}
+})
 
-    &:hover {
-      background-color: ${type === "primary" ? colors.blue[700] : colors.black};
-      border-color: ${colors.blue[700]};
-      color: ${type === "primary" ? colors.white : colors.blue[300]};
-    }
+const colorFromType: StyleFn = (styles, { type = "primary" }) => ({
+	...styles,
+	backgroundColor: type === "primary" ? colors.blue[700] : colors.black,
+	"&:hover": {
+		backgroundColor: type === "primary" ? colors.blue[700] : colors.black,
+		color: type === "primary" ? colors.white : colors.blue[300]
+	},
+	"&:active": {
+		backgroundColor: type === "primary" ? colors.blue[900] : colors.black,
+		color: type === "primary" ? colors.white : colors.blue[700]
+	}
+})
 
-    &:active {
-      background-color: ${type === "primary" ? colors.blue[900] : colors.black};
-      border-color: ${colors.blue[900]};
-      color: ${type === "primary" ? colors.white : colors.blue[700]};
-    }
-
-    &::selection {
-      background: transparent;
-    }
-  `
-}
+export const useCSS = (props: NavButtonStyleProps) => makeStyles([baseStyles, colorFromType])(props)

+ 16 - 18
packages/components/src/components/NavButton/NavButton.tsx

@@ -1,22 +1,20 @@
-import React from "react"
-import { makeStyles, NavButtonStyleProps } from "./NavButton.style"
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
-import { faChevronLeft, faChevronRight } from "@fortawesome/free-solid-svg-icons"
+import React from "react";
+import { useCSS, NavButtonStyleProps } from "./NavButton.style";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import ChevronRightIcon from "../../../assets/chevron-right-big.svg";
+import ChevronLeftIcon from "../../../assets/chevron-left-big.svg";
+import { faChevronLeft, faChevronRight } from "@fortawesome/free-solid-svg-icons";
 
 type NavButtonProps = {
-  direction?: "right" | "left",
-  onClick?: (e: React.MouseEvent<HTMLDivElement>) => void
-} & NavButtonStyleProps
+	direction?: "right" | "left";
+	onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
+} & NavButtonStyleProps;
 
-export default function NavButton({
-  direction = "right",
-  onClick,
-  ...styleProps
-}: NavButtonProps) {
-  let styles = makeStyles(styleProps)
-  return (
-    <div css={styles} onClick={onClick}>
-      {direction === "right" ? <FontAwesomeIcon icon={faChevronRight} /> : <FontAwesomeIcon icon={faChevronLeft} />}
-    </div>
-  )
+export default function NavButton({ direction = "right", onClick, ...styleProps }: NavButtonProps) {
+	let styles = useCSS(styleProps);
+	return (
+		<div css={styles} onClick={onClick}>
+			{direction === "right" ? <ChevronRightIcon /> : <ChevronLeftIcon />}
+		</div>
+	);
 }

+ 8 - 15
packages/components/src/components/Tabs/Tab.tsx

@@ -1,20 +1,13 @@
-import React, { ReactNode } from "react"
+import React, { ReactNode } from "react";
 
 type TabProps = {
-  label: string
-  children: ReactNode
-}
-
-export default function Tab({
-  label,
-  children
-}: TabProps) {
+	label: string;
+	children: ReactNode;
+};
 
-  // let styles = makeStyles(styleProps)
+//FIXME: Actually add markup for the tab
+export default function Tab({ label, children }: TabProps) {
+	// let styles = makeStyles(styleProps)
 
-  return (
-    <div>
-      {children}
-    </div>
-  )
+	return <div>{children}</div>;
 }

+ 32 - 34
packages/components/src/components/Tabs/Tabs.style.ts

@@ -1,37 +1,35 @@
-import { css } from "@emotion/core"
 import { typography, colors, spacing } from "../../theme"
+import { StyleFn, makeStyles } from "../../utils"
 
-export type TabsStyleProps = {
-}
+export type TabsStyleProps = {}
 
-export let makeStyles = ({
-}: TabsStyleProps) => {
-  return {
-    container: css`
-      font-family: ${typography.fonts.base};
-      color: ${colors.white};
-    `,
-    tabs: css`
-      display: flex;
-    `,
-    tab: css`
-      flex-basis: content;
-      padding: ${spacing.m} ${spacing.l};
-      cursor: pointer;
-      border-bottom: 3px solid ${colors.gray[900]};
-      min-width: 100px;
-      color: ${colors.gray[200]};
-      text-align: center;
-    `,
-    activeTab: css`
-      flex-basis: content;
-      padding: ${spacing.m} ${spacing.l};
-      cursor: pointer;
-      color: ${colors.white};
-      background-color: transparent;
-      border-bottom: 3px solid ${colors.blue[500]};
-      min-width: 100px;
-      text-align: center;
-    `
-  }
-}
+const container: StyleFn = () => ({
+	fontFamily: typography.fonts.base,
+	color: colors.white
+})
+
+const tabs: StyleFn = () => ({
+	display: "flex"
+})
+const tab: StyleFn = () => ({
+	flexBasis: "content",
+	padding: `${spacing.m} ${spacing.l}`,
+	cursor: "pointer",
+	borderBottom: `3px solid ${colors.gray[900]}`,
+	minWidth: "100px",
+	colors: colors.gray[200],
+	textAlign: "center"
+})
+
+const activeTab: StyleFn = () => ({
+	...tab(),
+	color: colors.white,
+	backgroundColor: "transparent",
+	borderBottom: `3px solid ${colors.blue[500]}`
+})
+export const useCSS = (props: TabsStyleProps) => ({
+	container: makeStyles([container])(props),
+	tabs: makeStyles([tabs])(props),
+	tab: makeStyles([tab])(props),
+	activeTab: makeStyles([activeTab])(props)
+})

+ 94 - 76
packages/components/src/components/TextField/TextField.style.ts

@@ -1,85 +1,103 @@
-import { css } from "@emotion/core"
+import { StyleFn, makeStyles } from "../../utils"
 import { typography, colors, spacing } from "./../../theme"
-import { icon } from "@fortawesome/fontawesome-svg-core"
 
 export type TextFieldStyleProps = {
-  disabled?: boolean
-  focus?: boolean
-  error?: boolean
-  isActive?: boolean
-  iconPosition?: "right" | "left"
+	disabled?: boolean
+	focus?: boolean
+	error?: boolean
+	isActive?: boolean
+	iconPosition?: "right" | "left"
 }
 
-export let makeStyles = ({
-  disabled = false,
-  focus = false,
-  error = false,
-  isActive = false,
-  iconPosition = "right"
-}: TextFieldStyleProps) => {
+const FIELD_WIDTH = "250px"
+const wrapper: StyleFn = () => ({
+	display: "block",
+	maxWidth: FIELD_WIDTH,
+	fontFamily: typography.fonts.base
+})
+
+const container: StyleFn = (_, { disabled }) => ({
+	position: "relative",
+	width: "100%",
+	height: "48px",
+	display: "inline-flex",
+	cursor: disabled ? "not-allowed" : "default"
+})
 
-  const fieldWidth = "250px"
+const border: StyleFn = () => ({
+	position: "absolute",
+	top: 0,
+	left: 0,
+	right: 0,
+	bottom: 0,
+	borderWidth: "1px",
+	borderStyle: "solid",
+	display: "flex",
+	alignItems: "center",
+	justifyContent: "left"
+})
+const borderColor: StyleFn = (styles, { disabled, error, isActive, focus }) => {
+	const borderColor = disabled
+		? colors.gray[200]
+		: error
+		? colors.error
+		: focus
+		? colors.blue[500]
+		: isActive
+		? colors.gray[200]
+		: colors.gray[400]
 
-  const borderColor = disabled ? colors.gray[200] :
-    error ? colors.error :
-    focus ? colors.blue[500] :
-    isActive ? colors.gray[200] : colors.gray[400]
+	return {
+		...styles,
+		borderColor
+	}
+}
+const label: StyleFn = (_, { error, iconPosition }) => ({
+	color: error ? colors.error : colors.gray[400],
+	padding: `0 ${spacing.s}`,
+	backgroundColor: colors.black,
+	[`padding-${iconPosition}`]: spacing.xxxxxl,
+	fontSize: typography.sizes.body2,
+	transition: `all 0.1s linear`
+})
+const input: StyleFn = (_, { iconPosition }) => ({
+	display: "none",
+	width: "100%",
+	margin: `0 ${spacing.s}`,
+	[`margin-${iconPosition}`]: spacing.xxxxxl,
+	background: "none",
+	border: "none",
+	outline: "none",
+	color: colors.white,
+	fontSize: typography.sizes.body2,
+	padding: `5px 0`
+})
+const icon: StyleFn = (_, { iconPosition }) => ({
+	color: colors.gray[300],
+	fontSize: typography.sizes.icon.xlarge,
+	position: "absolute",
+	top: spacing.s,
+	[`${iconPosition}`]: spacing.s
+})
+const helper: StyleFn = (_, { error }) => ({
+	color: error ? colors.error : 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.s};
-      ${icon ? `padding-${iconPosition}: ${spacing.xxxxl};` : ""}
-      background-color: ${colors.black};
-      font-size: ${typography.sizes.body2};
-      transition: all 0.1s linear;
-    `,
-    input: css`
-      display: none;
-      width: 100%;
-      margin: 0 ${spacing.s};
-      ${icon ? `margin-${iconPosition}: ${spacing.xxxxl};` : ""}
-      background: none;
-      border: none;
-      color: ${colors.white};
-      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};
-    `
-  }
+export const useCSS = ({
+	disabled = false,
+	focus = false,
+	error = false,
+	isActive = false,
+	iconPosition = "right"
+}: TextFieldStyleProps) => {
+	const props = { disabled, focus, error, isActive, iconPosition }
+	return {
+		wrapper: makeStyles([wrapper])(props),
+		container: makeStyles([container])(props),
+		border: makeStyles([border, borderColor])(props),
+		label: makeStyles([label])(props),
+		input: makeStyles([input])(props),
+		icon: makeStyles([icon])(props),
+		helper: makeStyles([helper])(props)
+	}
 }

+ 73 - 74
packages/components/src/components/TextField/TextField.tsx

@@ -1,85 +1,84 @@
-import React, { useState, useRef, useEffect } from "react"
-import { makeStyles, TextFieldStyleProps } from "./TextField.style"
-import { IconProp } from "@fortawesome/fontawesome-svg-core"
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
-import { spacing } from "./../../theme"
+import React, { useState, useRef, useEffect } from "react";
+import { useCSS, TextFieldStyleProps } from "./TextField.style";
+import { IconProp } from "@fortawesome/fontawesome-svg-core";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { spacing } from "./../../theme";
 
 type TextFieldProps = {
-  label: string
-  helper?: string
-  value?: string
-  icon?: IconProp
-  onChange?: (e: React.ChangeEvent) => void
-} & TextFieldStyleProps
+	label: string;
+	helper?: string;
+	value?: string;
+	icon?: IconProp;
+	onChange?: (e: React.ChangeEvent) => void;
+} & TextFieldStyleProps;
 
 export default function TextField({
-  label,
-  helper = "",
-  value = "",
-  icon = null,
-  disabled = false,
-  onChange,
-  ...styleProps
+	label,
+	helper = "",
+	value = "",
+	icon = null,
+	disabled = false,
+	onChange,
+	...styleProps
 }: TextFieldProps) {
+	const inputRef = useRef(null);
+	const [isActive, setIsActive] = useState(!!value);
+	const [inputTextValue, setInputTextValue] = useState(value);
+	const styles = useCSS({ isActive, disabled, ...styleProps });
 
-  const inputRef = useRef(null)
-  const [isActive, setIsActive] = useState(!!value)
-  const [inputTextValue, setInputTextValue] = useState(value)
-  const styles = makeStyles({ isActive, disabled, ...styleProps })
+	useEffect(() => {
+		if (isActive) {
+			inputRef.current.focus();
+		} else {
+			inputRef.current.blur();
+		}
+	}, [isActive, inputRef]);
 
-  useEffect(() => {
-    if (isActive) {
-      inputRef.current.focus()
-    }
-    else {
-      inputRef.current.blur()
-    }
-  }, [isActive, inputRef]);
+	function onTextFieldClick() {
+		if (!disabled) setIsActive(true);
+	}
 
-  function onTextFieldClick() {
-    if (!disabled) setIsActive(true)
-  }
+	function onInputTextBlur() {
+		setIsActive(false);
+	}
 
-  function onInputTextBlur() {
-    setIsActive(false)
-  }
+	function onInputTextChange(event: React.FormEvent<HTMLInputElement>): void {
+		if (!disabled) setInputTextValue(event.currentTarget.value);
+	}
 
-  function onInputTextChange(event: React.FormEvent<HTMLInputElement>): void {
-    if (!disabled) setInputTextValue(event.currentTarget.value)
-  } 
-  
-  return (
-    <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>
-      {!!helper &&
-        <p css={styles.helper}>{helper}</p>
-      }
-    </div>
-  )
+	return (
+		<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>
+			{!!helper && <p css={styles.helper}>{helper}</p>}
+		</div>
+	);
 }

+ 17 - 17
packages/components/src/utils/style-reducer.ts

@@ -1,40 +1,40 @@
-import * as CSS from "csstype";
-import { css } from "@emotion/core";
+import * as CSS from "csstype"
+import { css } from "@emotion/core"
 
 //FIXME: This is not correctly typed, since it doesn't
-export type StyleObj = { [k in keyof CSS.Properties]: number | string | StyleObj };
-export type StyleFn = (style: StyleObj, x: any) => StyleObj;
+export type StyleObj = { [k in keyof CSS.Properties]: number | string | StyleObj }
+export type StyleFn = (style?: StyleObj, x?: any) => StyleObj
 
-export type Modifiers = { [k: string]: StyleFn };
+export type Modifiers = { [k: string]: StyleFn }
 
 // TODO: Properly type this
 type StyleMonad = (
 	run: StyleFn
 ) => {
-	run: StyleFn;
-	map: (f: (args: any) => any) => StyleMonad;
-	contramap: (f: (args: any) => any) => StyleMonad;
-	concat: (other: StyleMonad) => StyleMonad;
-};
+	run: StyleFn
+	map: (f: (args: any) => any) => StyleMonad
+	contramap: (f: (args: any) => any) => StyleMonad
+	concat: (other: StyleMonad) => StyleMonad
+}
 
 export const Reducer = (run: StyleFn) => ({
 	run,
 	concat: (other: any) => Reducer((styles: StyleObj, props: any) => other.run(run(styles, props), props)),
 	map: (f: (x: any) => any) => Reducer((styles: StyleObj, props: any) => f(run(styles, props))),
-	contramap: (f: (x: any) => any) => Reducer((styles: StyleObj, props: any) => run(styles, f(props))),
-});
+	contramap: (f: (x: any) => any) => Reducer((styles: StyleObj, props: any) => run(styles, f(props)))
+})
 
 export function combineReducers(...reducers: StyleFn[]) {
 	return reducers.reduce(
 		(acc, reducer) => acc.concat(Reducer(reducer)),
 		Reducer(() => ({}))
-	);
+	)
 }
 
 export function makeStyles(reducers: StyleFn[]) {
-	const reducer = combineReducers(...reducers);
+	const reducer = combineReducers(...reducers)
 	return function(props: any) {
-		const styles: any = reducer.run({}, props);
-		return css(styles);
-	};
+		const styles: any = reducer.run({}, props)
+		return css(styles)
+	}
 }

File diff suppressed because it is too large
+ 420 - 408
yarn.lock


Some files were not shown because too many files changed in this diff