[design]: overrides pattern, tests, yay!

This commit is contained in:
41666 2019-05-20 04:33:31 -04:00
parent 0ccb5fa32f
commit dd6f02f4e2
No known key found for this signature in database
GPG key ID: BC51D07640DC10AF
22 changed files with 6165 additions and 1328 deletions

View file

@ -1,9 +1,14 @@
module.exports = { module.exports = {
testMatch: ['**/*.test.js'], preset: "ts-jest",
verbose: true, verbose: true,
bail: true, bail: true,
jsx: true,
testEnvironment: 'node',
setupFilesAfterEnv: ['<rootDir>/setupTests.js'], setupFilesAfterEnv: ['<rootDir>/setupTests.js'],
snapshotSerializers: [ snapshotSerializers: [
'enzyme-to-json/serializer' 'enzyme-to-json/serializer'
] ],
transform: {
".(ts|tsx)": "<rootDir>/node_modules/ts-jest/preprocessor.js"
},
} }

View file

@ -13,11 +13,11 @@
"flow": "flow", "flow": "flow",
"remotedebug": "remotedebug_ios_webkit_adapter --port=9000 > /dev/null", "remotedebug": "remotedebug_ios_webkit_adapter --port=9000 > /dev/null",
"test": "jest --color --coverage", "test": "jest --color --coverage",
"lint": "run-p lint:*", "lint": "lerna run lint-staged",
"lint:css": "stylelint 'packages/roleypoly-ui/**/*.js'",
"lint:js": "standard",
"rpcrepl": "babel-node packages/roleypoly-server/util/rpcrepl.js", "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, "private": true,
"workspaces": [ "workspaces": [
@ -25,63 +25,67 @@
], ],
"husky": { "husky": {
"hooks": { "hooks": {
"pre-commit": "yarn lint" "pre-commit": "lerna run --concurrency 1 --stream precommit"
} }
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.4.3", "@babel/cli": "^7.4.4",
"@babel/core": "^7.4.3", "@babel/core": "^7.4.4",
"@babel/node": "^7.2.2", "@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-proposal-optional-chaining": "^7.2.0",
"@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/plugin-transform-runtime": "^7.4.3", "@babel/plugin-transform-runtime": "^7.4.4",
"@babel/preset-env": "^7.4.3", "@babel/preset-env": "^7.4.4",
"@babel/preset-flow": "^7.0.0", "@babel/preset-flow": "^7.0.0",
"@babel/preset-react": "^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", "await-outside": "^2.1.2",
"babel-eslint": "^10.0.1", "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-styled-components": "^1.10.0",
"babel-plugin-transform-flow-strip-types": "^6.22.0",
"chokidar": "^2.1.5", "chokidar": "^2.1.5",
"enzyme": "^3.9.0", "enzyme": "^3.9.0",
"enzyme-adapter-react-16": "^1.12.1", "enzyme-adapter-react-16": "^1.13.1",
"enzyme-to-json": "^3.3.5", "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", "husky": "^1.3.1",
"jest": "^24.7.1", "jest": "^24.8.0",
"jest-styled-components": "^6.3.1", "jest-styled-components": "^6.3.1",
"lerna": "^3.13.2", "lerna": "^3.14.1",
"next": "^8.0.4", "lint-staged": "^8.1.7",
"next": "^8.1.0",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"react": "^16.8.6", "react": "^16.8.6",
"react-dom": "^16.8.6", "react-dom": "^16.8.6",
"react-test-renderer": "^16.8.6", "react-test-renderer": "^16.8.6",
"require-context.macro": "^1.0.4",
"standard": "12.0.1", "standard": "12.0.1",
"styled-components": "^4.2.0", "styled-components": "^4.2.0",
"stylelint": "^10.0.1", "stylelint": "^10.0.1",
"stylelint-config-standard": "^18.3.0", "stylelint-config-standard": "^18.3.0",
"stylelint-config-styled-components": "^0.1.1", "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": { "lint-staged": {
"parser": "babel-eslint", "*.{ts,tsx}": [
"plugins": [ "stylelint --fix",
"flowtype" "tslint --fix",
"jest",
"git add"
], ],
"globals": [ "*.{js,jsx}": [
"$Shape", "standard --fix",
"$Call" "jest --bail --findRelatedTests",
], "git add"
"ignore": [
"flow-typed/*",
"packages/*/lib/*",
"packages/*/dist/*"
] ]
} },
"dependencies": {}
} }

View file

@ -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"
]
}
}
}

View file

@ -1,3 +1,2 @@
lib
node_modules node_modules
*.log *.log

View file

@ -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

View file

@ -1,2 +0,0 @@
import '@storybook/addon-actions/register';
import '@storybook/addon-links/register';

View file

@ -0,0 +1,3 @@
import '@storybook/addon-actions/register'
import '@storybook/addon-links/register'
import '@storybook/addon-knobs/register'

View file

@ -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)

View file

@ -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)

View file

@ -12,24 +12,34 @@
], ],
"devDependencies": { "devDependencies": {
"@babel/core": "^7.4.4", "@babel/core": "^7.4.4",
"@babel/preset-typescript": "^7.3.3",
"@storybook/addon-actions": "^5.0.11", "@storybook/addon-actions": "^5.0.11",
"@storybook/addon-info": "^5.0.11", "@storybook/addon-info": "^5.0.11",
"@storybook/addon-knobs": "^5.0.11", "@storybook/addon-knobs": "^5.0.11",
"@storybook/addon-links": "^5.0.11", "@storybook/addon-links": "^5.0.11",
"@storybook/addon-storyshots": "^5.0.11",
"@storybook/addons": "^5.0.11", "@storybook/addons": "^5.0.11",
"@storybook/react": "^5.0.11", "@storybook/react": "^5.0.11",
"@types/enzyme-adapter-react-16": "^1.0.5",
"@types/jest": "^24.0.13", "@types/jest": "^24.0.13",
"@types/node": "^12.0.2", "@types/node": "^12.0.2",
"@types/react": "^16.8.17", "@types/react": "^16.8.17",
"@types/react-dom": "^16.8.4", "@types/react-dom": "^16.8.4",
"@types/react-test-renderer": "^16.8.1",
"@types/storybook__addon-actions": "^3.4.2", "@types/storybook__addon-actions": "^3.4.2",
"@types/storybook__addon-knobs": "^5.0.0", "@types/storybook__addon-knobs": "^5.0.0",
"@types/storybook__addon-links": "^3.3.4", "@types/storybook__addon-links": "^3.3.4",
"@types/storybook__addon-storyshots": "^4.0.0",
"@types/storybook__react": "^4.0.1", "@types/storybook__react": "^4.0.1",
"@types/styled-components": "4.1.8", "@types/styled-components": "4.1.8",
"awesome-typescript-loader": "^5.2.1", "awesome-typescript-loader": "^5.2.1",
"babel-loader": "^8.0.6", "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-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", "stylelint": "^10.0.1",
"tslint": "^5.16.0", "tslint": "^5.16.0",
"tslint-config-standard": "^8.0.1", "tslint-config-standard": "^8.0.1",
@ -37,6 +47,9 @@
}, },
"scripts": { "scripts": {
"storybook": "start-storybook -p 6006", "storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook" "build-storybook": "build-storybook",
"precommit": "lint-staged",
"build": "tsc",
"tsc": "tsc"
} }
} }

View file

@ -0,0 +1,51 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Storyshots Button Default 1`] = `
<button
className="sc-bdVaJa hPnBtV"
disabled={false}
onClick={[Function]}
>
Example
</button>
`;
exports[`Storyshots Button Disabled 1`] = `
<button
className="sc-bdVaJa hPnBtV"
disabled={true}
onClick={[Function]}
>
Example
</button>
`;
exports[`Storyshots Button Loading 1`] = `
<button
className="sc-bdVaJa hPnBtV"
disabled={false}
onClick={[Function]}
>
Example
</button>
`;
exports[`Storyshots Button Primary 1`] = `
<button
className="sc-bdVaJa hPnBtV"
disabled={false}
onClick={[Function]}
>
Example
</button>
`;
exports[`Storyshots Button Secondary 1`] = `
<button
className="sc-bdVaJa hPnBtV"
disabled={false}
onClick={[Function]}
>
Example
</button>
`;

View file

@ -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')
})

View file

@ -1,12 +1,15 @@
import * as React from 'react' import * as React from 'react'
import { storiesOf } from '@storybook/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) const s = storiesOf('Button', module)
s.add('Default', () => <Button>Example</Button>) const pressed = action('button pressed!')
s.add('Disabled', () => <Button>Example</Button>)
s.add('Primary', () => <Button>Example</Button>) s.add('Default', () => <Button onButtonPress={pressed}>Example</Button>)
s.add('Secondary', () => <Button>Example</Button>) s.add('Disabled', () => <Button disabled onButtonPress={pressed}>Example</Button>)
s.add('Loading', () => <Button>Example</Button>) s.add('Primary', () => <PrimaryButton onButtonPress={pressed}>Example</PrimaryButton>)
s.add('Secondary', () => <SecondaryButton onButtonPress={pressed}>Example</SecondaryButton>)
s.add('Loading', () => <Button loading onButtonPress={pressed}>Example</Button>)

View file

@ -0,0 +1,23 @@
import * as React from 'react'
import { shallow } from 'enzyme'
import * as sinon from 'sinon'
import Button from './Button'
describe('<Button />', () => {
it('calls onButtonPress when not disabled', () => {
const spy = sinon.spy()
const b = shallow(<Button onButtonPress={spy}>Testing</Button>)
b.simulate('click')
expect(spy.calledOnce).toBeTruthy()
})
it('does not call onButtonPress when disabled', () => {
const spy = sinon.spy()
const b = shallow(<Button disabled onButtonPress={spy}>Testing</Button>)
b.simulate('click')
expect(spy.notCalled).toBeTruthy()
})
})

View file

@ -1,10 +1,66 @@
import * as React from 'react' import * as React from 'react'
import { import {
StyledButton getOverrides
} from './styled-components' } from '../helpers/overrides'
import {
StyledButton,
StyledPrimaryButton,
StyledSecondaryButton
} from './styled'
import {
ButtonProps
} from './types'
const Button = ({ children, ...rest }: { children: React.ReactChild | React.ReactChild[] }) => <StyledButton {...rest}> export default class Button extends React.Component<ButtonProps> {
{children} static defaultProps: ButtonProps = {
</StyledButton> 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 <BaseButton {...rest} {...baseButtonProps} onClick={this.handleClick}>
{children}
</BaseButton>
}
}
export const PrimaryButton = (props: ButtonProps) => <Button {...props} overrides={{
BaseButton: {
component: StyledPrimaryButton
}
}} />
export const SecondaryButton = (props: ButtonProps) => <Button {...props} overrides={{
BaseButton: {
component: StyledSecondaryButton
}
}} />

View file

@ -2,4 +2,4 @@ export { default as default } from './Button'
export { export {
StyledButton StyledButton
} from './styled-components' } from './styled'

View file

@ -56,3 +56,25 @@ export const StyledButton = styled.button`
} }
} }
` `
StyledButton.defaultProps = {
theme: {
actions: {
primary: '#46b646',
primaryText: '#efefef',
secondary: '#e95353',
secondaryText: '#efefef'
}
}
}
export const StyledPrimaryButton = styled(StyledButton)`
background-color: ${props => props.theme.actions.primary};
color: ${props => props.theme.actions.primaryText};
`
StyledPrimaryButton.defaultProps = StyledButton.defaultProps
export const StyledSecondaryButton = styled(StyledButton)`
background-color: ${props => props.theme.actions.secondary};
color: ${props => props.theme.actions.secondaryText};
`

View file

@ -0,0 +1,15 @@
import {
OverridesT
} from '../helpers/overrides'
// import Button from './Button'
export type ButtonProps = {
overrides?: OverridesT<any>,
children: React.ReactChild | React.ReactChild[], // children is required
onButtonPress?: () => void,
disabled?: boolean,
primary?: boolean,
secondary?: boolean,
loading?: boolean,
loadingPct?: number
}

View file

@ -1,38 +1,9 @@
{ {
"extends": "../../tsconfig.json",
"compilerOptions": { "compilerOptions": {
"outDir": "./lib", "outDir": "./lib"
"module": "commonjs",
"target": "es2018",
"lib": [
"es5",
"es6",
"es7",
"es2017",
"dom"
],
"sourceMap": true,
"allowJs": false,
"jsx": "react",
"moduleResolution": "node",
"rootDirs": [
"src"
],
"baseUrl": "src",
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true,
"declaration": true,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}, },
"exclude": [ "include": [
"node_modules", "./src"
"build",
"scripts"
] ]
} }

View file

@ -1,3 +1,4 @@
const enzyme = require('enzyme') const enzyme = require('enzyme')
const Adapter = require('enzyme-adapter-react-16') const Adapter = require('enzyme-adapter-react-16')
require('babel-plugin-require-context-hook/register')();
enzyme.configure({ adapter: new Adapter() }) enzyme.configure({ adapter: new Adapter() })

46
tsconfig.json Normal file
View file

@ -0,0 +1,46 @@
{
"compilerOptions": {
"outDir": "./lib",
"module": "commonjs",
"target": "es6",
"lib": [
"es5",
"es6",
"es7",
"es2017",
"dom"
],
"sourceMap": true,
"allowJs": false,
"jsx": "react",
"moduleResolution": "node",
"rootDirs": [
"src"
],
"baseUrl": "src",
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": false,
"strictNullChecks": true,
"suppressImplicitAnyIndexErrors": true,
"noUnusedLocals": true,
"declaration": true,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
},
"exclude": [
"**/node_modules",
// don't include stories
"**/*.story.tsx",
"**/*.story.ts",
"**/*.stories.tsx",
"**/*.stories.ts",
// don't include tests
"**/__tests?__",
"**/*.test.tsx",
"**/*.test.ts"
]
}

6986
yarn.lock

File diff suppressed because it is too large Load diff