diff --git a/.babelrc b/.babelrc
index 8b838d2..7336e76 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,11 +1,8 @@
-{
- "presets": [
- "@babel/preset-env",
- "@babel/preset-react",
- "@babel/preset-flow"
- ],
- "plugins": [
- "@babel/plugin-proposal-class-properties",
- "@babel/plugin-proposal-object-rest-spread"
- ]
-}
\ No newline at end of file
+{
+ "presets": ["@babel/preset-env", "@babel/preset-react", "@babel/preset-flow"],
+ "plugins": [
+ "@babel/plugin-transform-regenerator",
+ "@babel/plugin-proposal-class-properties",
+ "@babel/plugin-proposal-object-rest-spread"
+ ]
+}
diff --git a/.eslintrc b/.eslintrc
index 7268b93..390923c 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,47 +1,49 @@
-{
- "parser": "babel-eslint",
- "extends": ["airbnb", "plugin:flowtype/recommended"],
- "env": {
- "browser": true,
- "node": true,
- "mocha": true,
- "jest/globals": true
- },
- "plugins": ["flowtype", "jest"],
- "settings": {
- "flowtype": {
- "onlyFilesWithFlowAnnotation": true
- }
- },
- "rules": {
- "jsx-quotes": ["error", "prefer-single"],
- "import/prefer-default-export": ["off"],
- "react/jsx-filename-extension": [1, { "extensions": [".js"] }],
- "jsx-a11y/anchor-is-valid": [
- "error",
- {
- "components": ["Link"],
- "specialLink": ["to", "hrefLeft", "hrefRight"],
- "aspects": ["noHref", "invalidHref", "preferButton"]
- }
- ],
- "jsx-a11y/no-autofocus": [
- 0,
- {
- "ignoreNonDOM": true
- }
- ],
- "max-len": [
- "error",
- {
- "code": 120,
- "tabWidth": 2,
- "ignoreUrls": true,
- "ignoreComments": true,
- "ignoreStrings": true,
- "ignorePattern": "
]*>.*?
",
- "ignoreTrailingComments": true
- }
- ]
- }
-}
+{
+ "parser": "babel-eslint",
+ "extends": ["airbnb", "plugin:flowtype/recommended"],
+ "env": {
+ "browser": true,
+ "node": true,
+ "mocha": true,
+ "jest/globals": true
+ },
+ "plugins": ["flowtype", "jest"],
+ "settings": {
+ "flowtype": {
+ "onlyFilesWithFlowAnnotation": true
+ }
+ },
+ "rules": {
+ "jsx-quotes": ["error", "prefer-single"],
+ "import/prefer-default-export": ["off"],
+ "react/jsx-filename-extension": [1, { "extensions": [".js"] }],
+ "jsx-a11y/anchor-is-valid": [
+ "error",
+ {
+ "components": ["Link"],
+ "specialLink": ["to", "hrefLeft", "hrefRight"],
+ "aspects": ["noHref", "invalidHref", "preferButton"]
+ }
+ ],
+ "jsx-a11y/no-autofocus": [
+ 0,
+ {
+ "ignoreNonDOM": true
+ }
+ ],
+ "max-len": [
+ "error",
+ {
+ "code": 120,
+ "tabWidth": 2,
+ "ignoreUrls": true,
+ "ignoreComments": true,
+ "ignoreStrings": true,
+ "ignorePattern": "]*>.*?
",
+ "ignoreTrailingComments": true
+ }
+ ],
+ "consistent-return": 0,
+ "react/destructuring-assignment": 0
+ }
+}
diff --git a/__tests__/components/Sidebar.test.js b/__tests__/components/Sidebar.test.js
index c42f932..d8636ae 100644
--- a/__tests__/components/Sidebar.test.js
+++ b/__tests__/components/Sidebar.test.js
@@ -10,6 +10,7 @@ import { SidebarComponent } from '../../app/components/sidebar';
describe('', () => {
describe('render()', () => {
test('should render correctly', () => {
+ // $FlowFixMe
const { asFragment } = render(
diff --git a/app/components/Button.mdx b/app/components/Button.mdx
new file mode 100644
index 0000000..c26bb96
--- /dev/null
+++ b/app/components/Button.mdx
@@ -0,0 +1,54 @@
+---
+name: Button
+---
+
+import { Playground, PropsTable } from 'docz'
+
+import { Button } from './button.js'
+import { DoczWrapper } from '../theme.js'
+
+# Button
+
+
+
+## Primary
+
+
+
+ {() => }
+
+
+
+## Secondary
+
+
+
+ {() =>
+
+
+
+## Primary Disabled
+
+
+
+ {() =>
+
+
+
+## Secondary Disabled
+
+
+
+
+ {() =>
+
+
+
+## Link Button
+
+
+
+ {() => }
+
+
diff --git a/app/components/Input.mdx b/app/components/Input.mdx
new file mode 100644
index 0000000..5111f62
--- /dev/null
+++ b/app/components/Input.mdx
@@ -0,0 +1,26 @@
+---
+name: Input
+---
+
+import { Playground, PropsTable } from 'docz'
+
+import { InputComponent } from './input.js'
+import { DoczWrapper } from '../theme.js'
+
+# Input
+
+
+
+## Text Input
+
+
+ {() => }
+
+
+## Textarea
+
+
+
+ {() => }
+
+
diff --git a/app/components/QRCode.mdx b/app/components/QRCode.mdx
new file mode 100644
index 0000000..d424423
--- /dev/null
+++ b/app/components/QRCode.mdx
@@ -0,0 +1,23 @@
+---
+name: QRCode
+---
+
+import { Playground, PropsTable } from 'docz'
+
+import { QRCode } from './qrcode.js'
+
+# QRCode
+
+
+
+## Basic usage
+
+
+
+
+
+## Custom size
+
+
+
+
diff --git a/app/components/button.js b/app/components/button.js
new file mode 100644
index 0000000..650bc7b
--- /dev/null
+++ b/app/components/button.js
@@ -0,0 +1,94 @@
+// @flow
+
+import React from 'react';
+import styled from 'styled-components';
+import { Link } from 'react-router-dom';
+/* eslint-disable import/no-extraneous-dependencies */
+// $FlowFixMe
+import { darken } from 'polished';
+
+const defaultStyles = `
+ padding: 10px 30px;
+ font-family: ${
+ // $FlowFixMe
+ props => props.theme.fontFamily
+};
+ font-weight: bold;
+ font-size: 0.9em;
+ cursor: pointer;
+ outline: none;
+ min-width: 100px;
+ border-radius: 100px;
+ transition: background-color 0.1s ease-in-out;
+`;
+
+const Primary = styled.button`
+ ${defaultStyles};
+ background-color: ${props => props.theme.colors.primary};
+ color: ${props => props.theme.colors.secondary};
+ border: none;
+
+ &:hover {
+ background-color: ${props => darken(0.1, props.theme.colors.primary(props))};
+ }
+
+ &:disabled {
+ background-color: #3e3c42;
+ cursor: not-allowed;
+ opacity: 0.8;
+ }
+`;
+
+const Secondary = styled.button`
+ ${defaultStyles};
+ background-color: Transparent;
+ color: ${props => props.theme.colors.secondary};
+ border: 2px solid #3e3c42;
+
+ &:hover {
+ border-color: ${props => props.theme.colors.primary};
+ }
+
+ &:disabled {
+ background-color: Transparent;
+ cursor: not-allowed;
+ color: #3e3c42;
+
+ &:hover {
+ border-color: #3e3c42;
+ }
+ }
+`;
+
+type Props = {
+ label: string,
+ onClick?: () => void,
+ to?: string,
+ variant?: 'primary' | 'secondary',
+ disabled?: boolean,
+};
+
+export const Button = ({
+ onClick, label, to, variant, disabled,
+}: Props) => {
+ if (to && onClick) throw new Error('Should define either "to" or "onClick"');
+
+ const component = variant === 'primary' ? (
+
+ {label}
+
+ ) : (
+
+ {label}
+
+ );
+
+ return to ? {component} : component;
+};
+
+Button.defaultProps = {
+ to: null,
+ variant: 'primary',
+ onClick: null,
+ disabled: false,
+};
diff --git a/app/components/input.js b/app/components/input.js
new file mode 100644
index 0000000..459ae30
--- /dev/null
+++ b/app/components/input.js
@@ -0,0 +1,53 @@
+// @flow
+import React from 'react';
+
+import styled from 'styled-components';
+
+// TODO: Missing styles
+
+const defaultStyles = `
+ padding: 10px;
+ width: 100%;
+ outline: none;
+ font-family: ${props => props.theme.fontFamily}
+`;
+
+const Input = styled.input.attrs({
+ type: 'text',
+})`
+ ${defaultStyles};
+`;
+
+const Textarea = styled.textarea`
+ ${defaultStyles};
+`;
+
+type Props = {
+ inputType?: 'input' | 'textarea' | 'dropdown',
+ value: string,
+ onChange: string => void,
+ rows?: number,
+ disabled?: boolean,
+ type?: string,
+};
+
+export const InputComponent = ({ inputType, onChange, ...props }: Props) => {
+ const inputTypes = {
+ input: () => onChange(evt.target.value)} {...props} />,
+ textarea: () =>