diff --git a/package-lock.json b/package-lock.json index b8f11cf..7d6f728 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7960,8 +7960,7 @@ "hoist-non-react-statics": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz", - "integrity": "sha512-6Bl6XsDT1ntE0lHbIhr4Kp2PGcleGZ66qu5Jqk8lc0Xc/IeG6gVLmwUGs/K0Us+L8VWoKgj0uWdPMataOsm31w==", - "dev": true + "integrity": "sha512-6Bl6XsDT1ntE0lHbIhr4Kp2PGcleGZ66qu5Jqk8lc0Xc/IeG6gVLmwUGs/K0Us+L8VWoKgj0uWdPMataOsm31w==" }, "home-or-tmp": { "version": "2.0.0", @@ -12224,6 +12223,20 @@ "minimist": "0.0.8" } }, + "mobx": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-5.0.3.tgz", + "integrity": "sha1-U7l/Kg+bDdd3TJYkn4G/LVE9jhw=" + }, + "mobx-react": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-5.2.3.tgz", + "integrity": "sha512-OuSlF2nJEa1PGookZcZnINbvEK4iWNNYiqUh6aebk2AkWxj3sG8OafDOQMcMYApQALTHRsrBIjOx/K8TFxcz7w==", + "requires": { + "hoist-non-react-statics": "^2.5.0", + "react-lifecycles-compat": "^3.0.2" + } + }, "moment": { "version": "2.22.2", "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", @@ -15694,6 +15707,11 @@ } } }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, "read-all-stream": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz", diff --git a/package.json b/package.json index 19ec5f1..14815b9 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,8 @@ "build": "npm run clean-dist && webpack -p --config=configs/webpack/prod.js", "clean-dist": "rm -f -r -d dist", "lint": "npm run lint:ts && npm run lint:sass", - "lint:ts": "tslint './src/**/*.ts*' --format stylish --force", - "lint:sass": "stylelint ./src/**/*.scss", + "lint:ts": "tslint './src/**/**/*.ts*' './src/**/*.ts*' './src/*.ts*' './*.ts*' --format stylish --force", + "lint:sass": "stylelint ./src/**/**/*.scss ./src/**/*.scss ./src/*.scss", "start": "npm run start-dev", "start-dev": "webpack-dev-server --config=configs/webpack/dev.js", "test": "npm-run-all test:unit test:e2e", @@ -72,6 +72,9 @@ "webpack-dev-server": "^3.1.4", "webpack-merge": "^4.1.2" }, - "dependencies": {}, + "dependencies": { + "mobx": "^5.0.3", + "mobx-react": "^5.2.3" + }, "postcss": {} } diff --git a/plop-templates/component.tsx b/plop-templates/component.tsx index 4511667..6351779 100644 --- a/plop-templates/component.tsx +++ b/plop-templates/component.tsx @@ -1,14 +1,24 @@ -import * as React from 'react'; -import './{{ properCase name}}.scss'; +import * as React from "react"; +{{#if observer}} +import { observer } from "mobx-react"; +import {{ camelCase store_name }} from "../../stores/{{ properCase store_name }}"; +{{/if}} +import "./{{ properCase name}}.scss"; export interface {{ properCase name }}Props {} -export default class {{ properCase name }} extends React.Component<{{ properCase name }}Props, undefined> { - render() { - return ( -
+{{#if observer}}@observer{{/if}} class {{ properCase name }} extends React.Component<{{ properCase name }}Props, undefined> { + render() { + return ( +
{{ titleCase name }} -
- ); - } +
+ ); + } } + +{{#if observer}} +export default (props) => <{{ properCase name }} {{ camelCase store_name }}={ {{ camelCase store_name }} } { ...props } />; +{{else}} +export default <{{ properCase name }} />; +{{/if}} \ No newline at end of file diff --git a/plop-templates/index.ts b/plop-templates/index.ts index 2955f8c..e909ea6 100644 --- a/plop-templates/index.ts +++ b/plop-templates/index.ts @@ -1,2 +1,2 @@ -import {{ properCase name }} from './{{ properCase name }}'; -export default {{ properCase name }}; \ No newline at end of file +import {{ properCase name }} from "./{{ properCase name }}"; +export default {{ properCase name }}; diff --git a/plop-templates/store.ts b/plop-templates/store.ts new file mode 100644 index 0000000..1615f02 --- /dev/null +++ b/plop-templates/store.ts @@ -0,0 +1,13 @@ +import { observable, action } from "mobx"; +import { observer } from "mobx-react"; + +class {{ properCase name }} { + @observable counter = 0; + + @action.bound + increment() { + this.counter += 1; + } +} + +export default new {{ properCase name }}(); diff --git a/plopfile.ts b/plopfile.ts index de772b3..6291b3c 100644 --- a/plopfile.ts +++ b/plopfile.ts @@ -1,33 +1,63 @@ module.exports = function(plop) { - // create your generators here - plop.setGenerator('New component', { - description: 'Create a new TSX + SCSS component for React.', - prompts: [ - { - type: 'input', - name: 'name', - message: 'Component name' - } - ], // array of inquirer prompts - actions: [ - { - type: 'add', - path: 'src/components/{{ properCase name }}/{{ properCase name }}.tsx', - templateFile: 'plop-templates/component.tsx', - abortOnFail: true - }, - { - type: 'add', - path: 'src/components/{{ properCase name }}/{{ properCase name }}.scss', - templateFile: 'plop-templates/component.scss', - abortOnFail: true - }, - { - type: 'add', - path: 'src/components/{{ properCase name }}/index.ts', - templateFile: 'plop-templates/index.ts', - abortOnFail: true - } - ] // array of actions - }); + // create your generators here + plop.setGenerator("New component", { + description: "Create a new TSX + SCSS component for React.", + prompts: [ + { + type: "input", + name: "name", + message: "Component name:" + }, + { + type: "list", + choices: [{ name: "No, it does not observe a store", value: false }, { name: "Yes, it observes a store", value: true }], + name: "observer", + message: "Is the component a MobX observer?" + }, + { + type: "input", + name: "store_name", + message: "MobX store name:", + when: answers => answers.observer + }, + ], // array of inquirer prompts + actions: [ + { + type: "add", + path: "src/components/{{ properCase name }}/{{ properCase name }}.tsx", + templateFile: "plop-templates/component.tsx", + abortOnFail: true + }, + { + type: "add", + path: "src/components/{{ properCase name }}/{{ properCase name }}.scss", + templateFile: "plop-templates/component.scss", + abortOnFail: true + }, + { + type: "add", + path: "src/components/{{ properCase name }}/index.ts", + templateFile: "plop-templates/index.ts", + abortOnFail: true + } + ] // array of actions + }); + plop.setGenerator("New MobX state store", { + description: "Create a new store for MobX.", + prompts: [ + { + type: "input", + name: "name", + message: "Store name:" + } + ], // array of inquirer prompts + actions: [ + { + type: "add", + path: "src/stores/{{ properCase name }}.ts", + templateFile: "plop-templates/store.ts", + abortOnFail: true + } + ] // array of actions + }); }; diff --git a/src/components/App/App.tsx b/src/components/App/App.tsx index 367760d..ffda586 100644 --- a/src/components/App/App.tsx +++ b/src/components/App/App.tsx @@ -1,18 +1,28 @@ import * as React from "react"; +import { observer } from "mobx-react"; import "./App.scss"; - -const reactLogo = require("../../assets/img/react_logo.svg"); +import appState from "../../stores/AppStore"; +import Button from "../Button"; export interface AppProps { + appState: { + increment: () => string, + counter: number + }; } -export default class App extends React.Component { - render() { - return ( -
-

Aalto-yliopiston sähköinsinöörikilta!

-

Sähköä, viinaa, naisia.

-
- ); - } +@observer class App extends React.Component { + constructor(props) { + super(props); + } + + render() { + return
+

Aalto-yliopiston sähköinsinöörikilta!

+

Sähköä, viinaa, naisia.

+ +
; + } } + +export default props => ; \ No newline at end of file diff --git a/src/components/Button/Button.scss b/src/components/Button/Button.scss new file mode 100644 index 0000000..df61ff2 --- /dev/null +++ b/src/components/Button/Button.scss @@ -0,0 +1,20 @@ +@import "../../index.scss"; + +.button { + -webkit-appearance: none; + border-radius: none; + padding: 0.5rem 1rem; + margin: 0.5rem; + border: 2px solid $blue; + font-size: 1rem; + font-weight: 500; + + &:hover { + border: 2px solid $dark-blue; + } + + &:active, + &:focus { + outline: none; + } +} diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx new file mode 100644 index 0000000..27d76cd --- /dev/null +++ b/src/components/Button/Button.tsx @@ -0,0 +1,16 @@ +import * as React from "react"; +import "./Button.scss"; + +export interface ButtonProps { + onClick: () => void; +} + +export default class Button extends React.Component { + render() { + return ( + + ); + } +} diff --git a/src/components/Button/index.ts b/src/components/Button/index.ts new file mode 100644 index 0000000..846ff85 --- /dev/null +++ b/src/components/Button/index.ts @@ -0,0 +1,2 @@ +import Button from "./Button"; +export default Button; \ No newline at end of file diff --git a/src/stores/AppStore.ts b/src/stores/AppStore.ts new file mode 100644 index 0000000..cd3d386 --- /dev/null +++ b/src/stores/AppStore.ts @@ -0,0 +1,13 @@ +import { observable, action } from "mobx"; +import { observer } from "mobx-react"; + +class AppStore { + @observable counter = 0; + + @action.bound + increment() { + this.counter += 1; + } +} + +export default new AppStore(); diff --git a/tsconfig.json b/tsconfig.json index 46c2bb6..665fd83 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,8 @@ "module": "commonjs", "target": "es5", "jsx": "react", - "lib": ["es5", "es6", "dom"] + "lib": ["es5", "es6", "dom"], + "experimentalDecorators": true }, "include": [ "./src/**/*"