diff --git a/00 Base/config/webpack/base.js b/00 Base/config/webpack/base.js index dff4c7b..d89604e 100644 --- a/00 Base/config/webpack/base.js +++ b/00 Base/config/webpack/base.js @@ -8,10 +8,7 @@ module.exports = merge( { context: helpers.resolveFromRootPath('src'), resolve: { - extensions: ['.js', '.ts', '.tsx'], - modules: [ - 'node_modules' - ] + extensions: ['.js', '.ts', '.tsx', '.css', '.scss', '.less'], }, entry: { app: ['./index.tsx'], @@ -28,14 +25,18 @@ module.exports = merge( babelCore: '@babel/core', } }, + { + test: /\.css$/, + loaders: ['style-loader', 'css-loader'], + }, + { + test: /\.s[ac]ss$/i, + loaders: ['style-loader', 'css-loader', 'sass-loader'], + }, { test: /\.less$/, - use: [ - { loader: 'style-loader' }, - { loader: 'css-loader' }, - { loader: 'less-loader' } - ] - } + use: ['style-loader', 'css-loader', 'less-loader'], + }, ], }, optimization: { diff --git a/00 Base/package.json b/00 Base/package.json index 2e1ce01..acf78fd 100644 --- a/00 Base/package.json +++ b/00 Base/package.json @@ -13,10 +13,12 @@ "author": "arp82", "license": "MIT", "dependencies": { + "@material-ui/core": "^4.9.9", + "@material-ui/icons": "^4.9.1", + "axios": "^0.19.2", + "babel-polyfill": "^6.26.0", "@babel/preset-react": "^7.9.4", "@babel/preset-stage-0": "^7.8.3", - "@material-ui/core": "^4.9.9", - "axios": "^0.19.0", "babel-core": "^7.0.0-bridge.0", "identity-obj-proxy": "^3.0.0", "jest-transform-css": "^2.0.0", @@ -24,9 +26,10 @@ "react-dom": "^16.8.6", "react-redux": "^7.2.0", "react-router-dom": "^5.0.1", - "react-test-renderer": "^16.13.1", "redux": "^4.0.5", "redux-thunk": "^2.3.0", + "sass-loader": "^8.0.2", + "react-test-renderer": "^16.13.1", "sinon": "^9.0.1", "style-loader": "^1.1.3", "ts-jest": "^25.3.1" @@ -43,14 +46,20 @@ "@types/react-dom": "^16.8.4", "@types/react-router-dom": "^4.3.4", "awesome-typescript-loader": "^5.2.1", + "html-webpack-plugin": "^3.2.0", + "jest": "^24.8.0", + "less": "^3.11.1", + "less-loader": "^5.0.0", + "node-sass": "^4.13.1", + "redux-devtools-extension": "^2.13.8", + "rimraf": "^2.6.3", + "sass-loader": "^7.2.0", + "style-loader": "^1.1.3", + "ts-jest": "^24.0.2", "babel-jest": "^25.2.6", "css-loader": "^3.4.2", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.2", - "html-webpack-plugin": "^3.2.0", - "jest": "^24.9.0", - "less": "^3.11.1", - "less-loader": "^5.0.0", "react-addons-test-utils": "^15.6.2", "redux-mock-store": "^1.5.4", "typescript": "^3.5.2", diff --git a/00 Base/src/app.tsx b/00 Base/src/app.tsx index 93864a7..f234558 100644 --- a/00 Base/src/app.tsx +++ b/00 Base/src/app.tsx @@ -1,8 +1,9 @@ import * as React from 'react'; -import { MessagesSection } from './components'; +import { MyComponent } from './components/myComponent'; +import './styles.less'; -export const App: React.FunctionComponent = props => ( +export const App: React.FunctionComponent = (props) => (
- +
); diff --git a/00 Base/src/components/myComponent/index.ts b/00 Base/src/components/myComponent/index.ts new file mode 100644 index 0000000..bcc7da0 --- /dev/null +++ b/00 Base/src/components/myComponent/index.ts @@ -0,0 +1 @@ +export { MyComponent } from './myComponent'; \ No newline at end of file diff --git a/00 Base/src/components/myComponent/myComponent.tsx b/00 Base/src/components/myComponent/myComponent.tsx new file mode 100644 index 0000000..529195e --- /dev/null +++ b/00 Base/src/components/myComponent/myComponent.tsx @@ -0,0 +1,54 @@ +import * as React from 'react'; + +import { useSelector, useDispatch } from 'react-redux'; +import { + /* fetchfruits, + deleteFruit, + addFruit, */ + fetchPosts, + deletePost, + addPost, +} from './../../redux/actions/index'; +import './styles.less'; + +import Button from '@material-ui/core/Button'; +import Chip from '@material-ui/core/Chip'; + +import { MyInputComponent } from '../myInputComponent/myInputComponent'; +export interface Props { + nameFromProps: string; +} + +export const MyComponent: React.FunctionComponent = (props) => { + const { nameFromProps } = props; + const dispatch = useDispatch(); + const posts = useSelector((state) => state.posts.posts); + + return ( + <> +
+

Hello {nameFromProps}!

+ +
+ {!!posts + ? posts.map((el /* : Object */) => ( + dispatch(deletePost(el.id))} + /> + )) + : null} +
+ dispatch(addPost(newPost))} /> +
+ + ); +}; diff --git a/00 Base/src/components/myComponent/styles.less b/00 Base/src/components/myComponent/styles.less new file mode 100644 index 0000000..3ec5f71 --- /dev/null +++ b/00 Base/src/components/myComponent/styles.less @@ -0,0 +1,12 @@ +.container { + text-align: center; + .chip-container { + display: block; + position: relative; + margin-top: 25px; + .MuiChip-root { + margin-left: 5px; + margin-top: 5px; + } + } +} diff --git a/00 Base/src/components/myInputComponent/index.ts b/00 Base/src/components/myInputComponent/index.ts new file mode 100644 index 0000000..3b0b11d --- /dev/null +++ b/00 Base/src/components/myInputComponent/index.ts @@ -0,0 +1 @@ +export { MyInputComponent } from './myInputComponent'; \ No newline at end of file diff --git a/00 Base/src/components/myInputComponent/myInputComponent.tsx b/00 Base/src/components/myInputComponent/myInputComponent.tsx new file mode 100644 index 0000000..bd63ca7 --- /dev/null +++ b/00 Base/src/components/myInputComponent/myInputComponent.tsx @@ -0,0 +1,31 @@ +import * as React from 'react'; +import TextField from '@material-ui/core/TextField'; +import { Add } from '@material-ui/icons'; +import './styles.less'; + +export interface Props { + addPost: Function; +} + +export const MyInputComponent: React.FunctionComponent = (props) => { + const { addPost } = props; + const [name, setName] = React.useState(''); + + return ( +
+ setName(e.target.value)} + /> + {!!name ? ( + { + addPost(name); + setName(''); + }} + /> + ) : null} +
+ ); +}; diff --git a/00 Base/src/components/myInputComponent/styles.less b/00 Base/src/components/myInputComponent/styles.less new file mode 100644 index 0000000..0af11b8 --- /dev/null +++ b/00 Base/src/components/myInputComponent/styles.less @@ -0,0 +1,9 @@ +.input-component { + .MuiFormControl-root { + margin-top: 10px; + } + .MuiSvgIcon-root { + margin-top: 35px; + color: #00ff00; + } +} diff --git a/00 Base/src/index.tsx b/00 Base/src/index.tsx index 1725370..2c35ff9 100644 --- a/00 Base/src/index.tsx +++ b/00 Base/src/index.tsx @@ -1,16 +1,14 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import { App } from './app'; -import { Provider } from 'react-redux' -import { createStore, compose, applyMiddleware } from 'redux' -import {MessagesReducer} from './redux/reducers/MessagesReducer' -import thunk from 'redux-thunk' +import { Provider } from 'react-redux'; +import store from './redux/store'; -const MessagesStore = createStore(MessagesReducer,compose(applyMiddleware(thunk))); +import { App } from './app'; +import 'babel-polyfill' ReactDOM.render( - + -, - - document.getElementById('root')); + , + document.getElementById('root') +); diff --git a/00 Base/src/myApi/index.ts b/00 Base/src/myApi/index.ts new file mode 100644 index 0000000..3cbca37 --- /dev/null +++ b/00 Base/src/myApi/index.ts @@ -0,0 +1 @@ +export { getListOfFruit, getPosts } from './myApi'; diff --git a/00 Base/src/myApi/myApi.ts b/00 Base/src/myApi/myApi.ts new file mode 100644 index 0000000..8cd6d3d --- /dev/null +++ b/00 Base/src/myApi/myApi.ts @@ -0,0 +1,24 @@ +import * as BEApi from './myBackEndApiEndpoint'; +import axios from 'axios'; + +export const getListOfFruit = (): Promise => { + return BEApi.getFruits('http://fruityfruit.com') + .then(resolveFruits) + .catch(handleError); +}; + +const resolveFruits = (fruits: string[]) => { + return fruits; +}; + +const handleError = () => { + throw new Error('Where is my fruit???'); +}; +export const getPosts = (): any => { + return axios + .get('https://jsonplaceholder.typicode.com/posts') + .then(resolvePosts); +}; +const resolvePosts = (posts: any) => { + return posts; +}; diff --git a/00 Base/src/myApi/myBackEndApiEndpoint.ts b/00 Base/src/myApi/myBackEndApiEndpoint.ts new file mode 100644 index 0000000..1457042 --- /dev/null +++ b/00 Base/src/myApi/myBackEndApiEndpoint.ts @@ -0,0 +1,12 @@ +export const getFruits = (_url: string) => { + return Promise.resolve([ + 'grape', + 'pineapple', + 'watermelon', + 'orange', + 'lemon', + 'strawberry', + 'cherry', + 'peach', + ]); +} \ No newline at end of file diff --git a/00 Base/src/redux/actions/fruitsActions.tsx b/00 Base/src/redux/actions/fruitsActions.tsx new file mode 100644 index 0000000..81d68bf --- /dev/null +++ b/00 Base/src/redux/actions/fruitsActions.tsx @@ -0,0 +1,28 @@ +import { GET_ALL_FRUITS, DELETE_FRUIT, ADD_FRUIT } from './../constants'; +import { getListOfFruit } from '../../myApi/index'; + +export const getAllFruits = (fruits: String[]) => { + return { + type: GET_ALL_FRUITS, + payload: fruits, + }; +}; +export const fetchfruits = () => { + return (dispatch) => { + return getListOfFruit().then((res) => dispatch(getAllFruits(res))); + }; +}; + +export const deleteFruit = (name: string) => { + return { + type: DELETE_FRUIT, + payload: name, + }; +}; + +export const addFruit = (name: string) => { + return { + type: ADD_FRUIT, + payload: name, + }; +}; diff --git a/00 Base/src/redux/actions/index.ts b/00 Base/src/redux/actions/index.ts new file mode 100644 index 0000000..af7b50e --- /dev/null +++ b/00 Base/src/redux/actions/index.ts @@ -0,0 +1,7 @@ +export { + addFruit, + deleteFruit, + fetchfruits, + getAllFruits, +} from './fruitsActions'; +export { fetchPosts, deletePost,addPost } from './postActions'; diff --git a/00 Base/src/redux/actions/postActions.tsx b/00 Base/src/redux/actions/postActions.tsx new file mode 100644 index 0000000..0345d8e --- /dev/null +++ b/00 Base/src/redux/actions/postActions.tsx @@ -0,0 +1,28 @@ +import { GET_POST_FROM_API,DELETE_POST,ADD_POST } from './../constants'; +import { getPosts } from '../../myApi/index'; + +export const getPostFromApi = ( posts: Object[]) => { + return { + type: GET_POST_FROM_API, + payload: posts, + }; +}; +export const fetchPosts = () => { + return (dispatch) => { + return getPosts().then((res) => dispatch(getPostFromApi(res))); + }; +}; + + export const deletePost = (id: Number) => { + return { + type: DELETE_POST, + payload: id, + }; +}; + +export const addPost = (name: string) => { + return { + type: ADD_POST, + payload: name, + }; +}; diff --git a/00 Base/src/redux/constants.ts b/00 Base/src/redux/constants.ts new file mode 100644 index 0000000..1e7fd44 --- /dev/null +++ b/00 Base/src/redux/constants.ts @@ -0,0 +1,7 @@ +export const GET_ALL_FRUITS = 'GET_ALL_FRUITS'; +export const DELETE_FRUIT = 'DELETE_FRUIT'; +export const ADD_FRUIT = 'ADD_FRUIT'; + +export const GET_POST_FROM_API = 'GET_POST_FROM_API'; +export const DELETE_POST = 'DELETE_POST'; +export const ADD_POST = 'ADD_POST'; diff --git a/00 Base/src/redux/reducers/fruitReducer.tsx b/00 Base/src/redux/reducers/fruitReducer.tsx new file mode 100644 index 0000000..e260a37 --- /dev/null +++ b/00 Base/src/redux/reducers/fruitReducer.tsx @@ -0,0 +1,31 @@ +import { GET_ALL_FRUITS, DELETE_FRUIT, ADD_FRUIT } from './../constants'; +const initialState = { + fruitList: [], +}; +export default function (state = initialState, action) { + switch (action.type) { + case GET_ALL_FRUITS: { + return { + ...state, + fruitList: action.payload, + }; + } + case DELETE_FRUIT: { + return { + ...state, + fruitList: state.fruitList.filter((e: string) => e !== action.payload), + }; + } + case ADD_FRUIT: { + return { + ...state, + fruitList: state.fruitList.concat([action.payload]), + }; + } + default: { + return { + ...state, + }; + } + } +} diff --git a/00 Base/src/redux/reducers/index.ts b/00 Base/src/redux/reducers/index.ts new file mode 100644 index 0000000..2026315 --- /dev/null +++ b/00 Base/src/redux/reducers/index.ts @@ -0,0 +1,8 @@ +import { combineReducers } from 'redux'; +import fruitsReducer from './fruitReducer'; +import postReducer from './postReducer' + +export default combineReducers({ + fruits: fruitsReducer, + posts: postReducer +}); diff --git a/00 Base/src/redux/reducers/postReducer.tsx b/00 Base/src/redux/reducers/postReducer.tsx new file mode 100644 index 0000000..92442cb --- /dev/null +++ b/00 Base/src/redux/reducers/postReducer.tsx @@ -0,0 +1,39 @@ +import { GET_POST_FROM_API, DELETE_POST, ADD_POST } from './../constants'; +const initialState = { + posts: [], +}; +export default function (state = initialState, action) { + switch (action.type) { + case GET_POST_FROM_API: { + return { + ...state, + posts: action.payload.data, + }; + } + case DELETE_POST: { + return { + ...state, + posts: state.posts.filter((e) => e.id !== action.payload), + }; + } + + case ADD_POST: { + let tmpPost = { + id: Math.floor(Math.random() * 1000), + userId: 1, + title: action.payload, + body: action.payload, + }; + + return { + ...state, + posts: state.posts.concat([tmpPost]), + }; + } + default: { + return { + ...state, + }; + } + } +} diff --git a/00 Base/src/redux/store.js b/00 Base/src/redux/store.js new file mode 100644 index 0000000..e7c2c33 --- /dev/null +++ b/00 Base/src/redux/store.js @@ -0,0 +1,12 @@ +import { createStore, applyMiddleware, compose } from 'redux'; +import thunk from 'redux-thunk'; +import rootReducer from './reducers/index'; + +const initialState = {}; +const middleware = [thunk]; +const store=createStore(rootReducer,initialState,compose( + applyMiddleware(thunk), + window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() + +)) +export default store; diff --git a/00 Base/src/styles.less b/00 Base/src/styles.less new file mode 100644 index 0000000..af8d565 --- /dev/null +++ b/00 Base/src/styles.less @@ -0,0 +1,3 @@ +body { + background-color: #afafdc; +} diff --git a/00 Base/tsconfig.json b/00 Base/tsconfig.json index 8a26838..3e07872 100644 --- a/00 Base/tsconfig.json +++ b/00 Base/tsconfig.json @@ -11,7 +11,9 @@ "allowJs": true, "suppressImplicitAnyIndexErrors": true, "skipLibCheck": true, - "esModuleInterop": true + "esModuleInterop": true, + "outDir": "generated", }, - "include": ["./src/**/*"] + "include": ["./src/**/*"], + "exclude": ["node_modules", "**/*.test.ts", "dist"] }