diff --git a/jest.config.js b/jest.config.js index 6d240be..a6f1137 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,9 +1,14 @@ module.exports = { - testMatch: ['**/*.test.js'], + preset: "ts-jest", verbose: true, bail: true, + jsx: true, + testEnvironment: 'node', setupFilesAfterEnv: ['/setupTests.js'], snapshotSerializers: [ 'enzyme-to-json/serializer' - ] + ], + transform: { + ".(ts|tsx)": "/node_modules/ts-jest/preprocessor.js" + }, } diff --git a/package.json b/package.json index 9bef20f..f1df003 100644 --- a/package.json +++ b/package.json @@ -13,11 +13,11 @@ "flow": "flow", "remotedebug": "remotedebug_ios_webkit_adapter --port=9000 > /dev/null", "test": "jest --color --coverage", - "lint": "run-p lint:*", - "lint:css": "stylelint 'packages/roleypoly-ui/**/*.js'", - "lint:js": "standard", + "lint": "lerna run lint-staged", "rpcrepl": "babel-node packages/roleypoly-server/util/rpcrepl.js", - "dev:up": "docker-compose up -d" + "dev:up": "docker-compose up -d", + "storybook": "start-storybook -p 6006", + "build-storybook": "build-storybook" }, "private": true, "workspaces": [ @@ -25,63 +25,67 @@ ], "husky": { "hooks": { - "pre-commit": "yarn lint" + "pre-commit": "lerna run --concurrency 1 --stream precommit" } }, "devDependencies": { - "@babel/cli": "^7.4.3", - "@babel/core": "^7.4.3", + "@babel/cli": "^7.4.4", + "@babel/core": "^7.4.4", "@babel/node": "^7.2.2", - "@babel/plugin-proposal-class-properties": "^7.4.0", + "@babel/plugin-proposal-class-properties": "^7.4.4", "@babel/plugin-proposal-optional-chaining": "^7.2.0", "@babel/plugin-syntax-dynamic-import": "^7.2.0", - "@babel/plugin-transform-runtime": "^7.4.3", - "@babel/preset-env": "^7.4.3", + "@babel/plugin-transform-runtime": "^7.4.4", + "@babel/preset-env": "^7.4.4", "@babel/preset-flow": "^7.0.0", "@babel/preset-react": "^7.0.0", + "@types/enzyme": "^3.9.2", + "@types/sinon": "^7.0.11", + "@types/webpack-env": "^1.13.9", "await-outside": "^2.1.2", "babel-eslint": "^10.0.1", - "babel-jest": "^24.7.1", + "babel-jest": "^24.8.0", + "babel-loader": "^8.0.6", + "babel-plugin-macros": "^2.5.1", "babel-plugin-styled-components": "^1.10.0", - "babel-plugin-transform-flow-strip-types": "^6.22.0", "chokidar": "^2.1.5", "enzyme": "^3.9.0", - "enzyme-adapter-react-16": "^1.12.1", + "enzyme-adapter-react-16": "^1.13.1", "enzyme-to-json": "^3.3.5", - "eslint": "^5.16.0", - "eslint-plugin-flowtype": "^3.6.1", - "flow-bin": "^0.97.0", - "flow-mono-cli": "^1.5.0", - "flow-typed": "^2.5.1", "husky": "^1.3.1", - "jest": "^24.7.1", + "jest": "^24.8.0", "jest-styled-components": "^6.3.1", - "lerna": "^3.13.2", - "next": "^8.0.4", + "lerna": "^3.14.1", + "lint-staged": "^8.1.7", + "next": "^8.1.0", "npm-run-all": "^4.1.5", "react": "^16.8.6", "react-dom": "^16.8.6", "react-test-renderer": "^16.8.6", + "require-context.macro": "^1.0.4", "standard": "12.0.1", "styled-components": "^4.2.0", "stylelint": "^10.0.1", "stylelint-config-standard": "^18.3.0", "stylelint-config-styled-components": "^0.1.1", - "stylelint-processor-styled-components": "^1.6.0" + "stylelint-processor-styled-components": "^1.7.0", + "ts-jest": "^24.0.2", + "tslint": "^5.16.0", + "typescript": "^3.4.5", + "typescript-tslint-plugin": "^0.3.1" }, - "standard": { - "parser": "babel-eslint", - "plugins": [ - "flowtype" + "lint-staged": { + "*.{ts,tsx}": [ + "stylelint --fix", + "tslint --fix", + "jest", + "git add" ], - "globals": [ - "$Shape", - "$Call" - ], - "ignore": [ - "flow-typed/*", - "packages/*/lib/*", - "packages/*/dist/*" + "*.{js,jsx}": [ + "standard --fix", + "jest --bail --findRelatedTests", + "git add" ] - } + }, + "dependencies": {} } diff --git a/packages/roleypoly-design/.babelrc b/packages/roleypoly-design/.babelrc new file mode 100644 index 0000000..74fc32e --- /dev/null +++ b/packages/roleypoly-design/.babelrc @@ -0,0 +1,42 @@ +{ + "presets": [ + "next/babel", + "@babel/preset-typescript", + ], + "plugins": [ + "@babel/plugin-syntax-dynamic-import", + "@babel/plugin-proposal-class-properties", + [ + "@babel/plugin-transform-runtime", + { + "helpers": false + } + ], + [ + "styled-components", + { + "ssr": true + } + ], + "@babel/plugin-proposal-optional-chaining", + "macros" + ], + "env": { + "test": { + "presets": [ + [ + "next/babel", + { + "preset-env": { + "modules": "commonjs" + } + } + ] + ], + "plugins": [ + "require-context-hook", + "macros" + ] + } + } +} \ No newline at end of file diff --git a/packages/roleypoly-design/.gitignore b/packages/roleypoly-design/.gitignore index 1eaff4a..ab05030 100644 --- a/packages/roleypoly-design/.gitignore +++ b/packages/roleypoly-design/.gitignore @@ -1,3 +1,2 @@ -lib node_modules *.log \ No newline at end of file diff --git a/packages/roleypoly-design/.lintstagedrc.yml b/packages/roleypoly-design/.lintstagedrc.yml new file mode 100644 index 0000000..39e386b --- /dev/null +++ b/packages/roleypoly-design/.lintstagedrc.yml @@ -0,0 +1,11 @@ +linters: + lib/*.{js,jsx}: + - standard --fix + - git add + lib/*.d.ts: + - tslint --fix + src/*.{ts,tsx}: + - tslint --fix + - stylelint --fix + - jest --bail --findRelatedTests + - git add \ No newline at end of file diff --git a/packages/roleypoly-design/.storybook/addons.js b/packages/roleypoly-design/.storybook/addons.js deleted file mode 100644 index 6aed412..0000000 --- a/packages/roleypoly-design/.storybook/addons.js +++ /dev/null @@ -1,2 +0,0 @@ -import '@storybook/addon-actions/register'; -import '@storybook/addon-links/register'; diff --git a/packages/roleypoly-design/.storybook/addons.ts b/packages/roleypoly-design/.storybook/addons.ts new file mode 100644 index 0000000..a77bd59 --- /dev/null +++ b/packages/roleypoly-design/.storybook/addons.ts @@ -0,0 +1,3 @@ +import '@storybook/addon-actions/register' +import '@storybook/addon-links/register' +import '@storybook/addon-knobs/register' diff --git a/packages/roleypoly-design/.storybook/config.js b/packages/roleypoly-design/.storybook/config.js deleted file mode 100644 index 1f94f9f..0000000 --- a/packages/roleypoly-design/.storybook/config.js +++ /dev/null @@ -1,8 +0,0 @@ -import { configure } from '@storybook/react' -const req = require.context('../src', true, /\.stor\bies|y\b\.[tj]sx?$/) - -function loadStories() { - req.keys().forEach(req) -} - -configure(loadStories, module) diff --git a/packages/roleypoly-design/.storybook/config.ts b/packages/roleypoly-design/.storybook/config.ts new file mode 100644 index 0000000..4de6e16 --- /dev/null +++ b/packages/roleypoly-design/.storybook/config.ts @@ -0,0 +1,51 @@ +const { configure } = require('@storybook/react') + +// polyfill for require.context +try { + if (require.context === undefined) { + const fs = require('fs') + const path = require('path') + // @ts-ignore + require.context = (base: string = '.', scanSubDirectories: boolean = false, regularExpression: RegExp = /\.js$/) => { + const files = {} + + function readDirectory (directory: string) { + fs.readdirSync(directory).forEach((file) => { + const fullPath = path.resolve(directory, file) + + if (fs.statSync(fullPath).isDirectory()) { + if (scanSubDirectories) readDirectory(fullPath) + + return + } + + if (!regularExpression.test(fullPath)) return + + files[fullPath] = true + }) + } + + readDirectory(path.resolve(__dirname, base)) + + function Module (file: string) { + return require(file) + } + + Module.keys = () => Object.keys(files) + + return Module + } + } +} catch (e) { + if (e) { + console.log(e) + } +} + +const req = require.context('../src', true, /\.stor\bies|y\b\.[tj]sx?$/) + +function loadStories () { + req.keys().forEach(req) +} + +configure(loadStories, module) diff --git a/packages/roleypoly-design/package.json b/packages/roleypoly-design/package.json index 29fbe5a..6e9c567 100644 --- a/packages/roleypoly-design/package.json +++ b/packages/roleypoly-design/package.json @@ -12,24 +12,34 @@ ], "devDependencies": { "@babel/core": "^7.4.4", + "@babel/preset-typescript": "^7.3.3", "@storybook/addon-actions": "^5.0.11", "@storybook/addon-info": "^5.0.11", "@storybook/addon-knobs": "^5.0.11", "@storybook/addon-links": "^5.0.11", + "@storybook/addon-storyshots": "^5.0.11", "@storybook/addons": "^5.0.11", "@storybook/react": "^5.0.11", + "@types/enzyme-adapter-react-16": "^1.0.5", "@types/jest": "^24.0.13", "@types/node": "^12.0.2", "@types/react": "^16.8.17", "@types/react-dom": "^16.8.4", + "@types/react-test-renderer": "^16.8.1", "@types/storybook__addon-actions": "^3.4.2", "@types/storybook__addon-knobs": "^5.0.0", "@types/storybook__addon-links": "^3.3.4", + "@types/storybook__addon-storyshots": "^4.0.0", "@types/storybook__react": "^4.0.1", "@types/styled-components": "4.1.8", "awesome-typescript-loader": "^5.2.1", "babel-loader": "^8.0.6", + "babel-plugin-macros": "^2.5.1", + "babel-plugin-require-context-hook": "^1.0.0", "react-docgen-typescript-loader": "^3.1.0", + "react-test-renderer": "^16.8.6", + "require-context.macro": "^1.0.4", + "sinon": "^7.3.2", "stylelint": "^10.0.1", "tslint": "^5.16.0", "tslint-config-standard": "^8.0.1", @@ -37,6 +47,9 @@ }, "scripts": { "storybook": "start-storybook -p 6006", - "build-storybook": "build-storybook" + "build-storybook": "build-storybook", + "precommit": "lint-staged", + "build": "tsc", + "tsc": "tsc" } -} \ No newline at end of file +} diff --git a/packages/roleypoly-design/src/__test__/__snapshots__/storyshots.test.ts.snap b/packages/roleypoly-design/src/__test__/__snapshots__/storyshots.test.ts.snap new file mode 100644 index 0000000..c52a7ab --- /dev/null +++ b/packages/roleypoly-design/src/__test__/__snapshots__/storyshots.test.ts.snap @@ -0,0 +1,51 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Storyshots Button Default 1`] = ` + +`; + +exports[`Storyshots Button Disabled 1`] = ` + +`; + +exports[`Storyshots Button Loading 1`] = ` + +`; + +exports[`Storyshots Button Primary 1`] = ` + +`; + +exports[`Storyshots Button Secondary 1`] = ` + +`; diff --git a/packages/roleypoly-design/src/__test__/storyshots.test.ts b/packages/roleypoly-design/src/__test__/storyshots.test.ts new file mode 100644 index 0000000..c56bd56 --- /dev/null +++ b/packages/roleypoly-design/src/__test__/storyshots.test.ts @@ -0,0 +1,9 @@ +import * as path from 'path' +import initStoryshots from '@storybook/addon-storyshots' +// import Adapter from 'enzyme-adapter-react-16' + +// enzyme.configure({ adapter: new Adapter() }) + +initStoryshots({ + configPath: path.resolve(__dirname, '../../.storybook') +}) \ No newline at end of file diff --git a/packages/roleypoly-design/src/button/Button.story.tsx b/packages/roleypoly-design/src/button/Button.story.tsx index eb9bd8d..809d716 100644 --- a/packages/roleypoly-design/src/button/Button.story.tsx +++ b/packages/roleypoly-design/src/button/Button.story.tsx @@ -1,12 +1,15 @@ import * as React from 'react' import { storiesOf } from '@storybook/react' +import { action } from '@storybook/addon-actions' -import Button from './Button' +import Button, { PrimaryButton, SecondaryButton } from './Button' const s = storiesOf('Button', module) -s.add('Default', () => ) -s.add('Disabled', () => ) -s.add('Primary', () => ) -s.add('Secondary', () => ) -s.add('Loading', () => ) \ No newline at end of file +const pressed = action('button pressed!') + +s.add('Default', () => ) +s.add('Disabled', () => ) +s.add('Primary', () => Example) +s.add('Secondary', () => Example) +s.add('Loading', () => ) diff --git a/packages/roleypoly-design/src/button/Button.test.tsx b/packages/roleypoly-design/src/button/Button.test.tsx new file mode 100644 index 0000000..3932aca --- /dev/null +++ b/packages/roleypoly-design/src/button/Button.test.tsx @@ -0,0 +1,23 @@ +import * as React from 'react' +import { shallow } from 'enzyme' +import * as sinon from 'sinon' + +import Button from './Button' + +describe(') + b.simulate('click') + + expect(spy.calledOnce).toBeTruthy() + }) + + it('does not call onButtonPress when disabled', () => { + const spy = sinon.spy() + const b = shallow() + b.simulate('click') + + expect(spy.notCalled).toBeTruthy() + }) +}) diff --git a/packages/roleypoly-design/src/button/Button.tsx b/packages/roleypoly-design/src/button/Button.tsx index 4362022..70cb87c 100644 --- a/packages/roleypoly-design/src/button/Button.tsx +++ b/packages/roleypoly-design/src/button/Button.tsx @@ -1,10 +1,66 @@ import * as React from 'react' import { - StyledButton -} from './styled-components' + getOverrides +} from '../helpers/overrides' +import { + StyledButton, + StyledPrimaryButton, + StyledSecondaryButton +} from './styled' +import { + ButtonProps +} from './types' -const Button = ({ children, ...rest }: { children: React.ReactChild | React.ReactChild[] }) => - {children} - +export default class Button extends React.Component { + static defaultProps: ButtonProps = { + disabled: false, + primary: false, + secondary: false, + loading: false, + loadingPct: 0, + children: 'Button', + overrides: {} + } -export default Button + handleClick = () => { + if (this.props.disabled === true || typeof this.props.onButtonPress !== 'function') { + return + } + + this.props.onButtonPress() + } + + render () { + const { + overrides = {}, + children, + // removing from rest + loading, + onButtonPress, + ...rest + } = this.props + + const [BaseButton, baseButtonProps] = getOverrides( + overrides.BaseButton, + StyledButton + ) + + console.log({ overrides, BaseButton, baseButtonProps }) + + return + {children} + + } +} + +export const PrimaryButton = (props: ButtonProps) =>