diff --git a/11 TableMock/package.json b/11 TableMock/package.json index 55a51a6..1e8a276 100644 --- a/11 TableMock/package.json +++ b/11 TableMock/package.json @@ -10,22 +10,22 @@ "author": "", "license": "ISC", "devDependencies": { - "babel-core": "^6.18.2", - "babel-loader": "^6.2.7", - "babel-plugin-transform-runtime": "^6.15.0", - "babel-preset-es2015": "^6.18.0", - "babel-preset-react": "^6.16.0", - "css-loader": "^0.25.0", - "file-loader": "^0.9.0", - "html-webpack-plugin": "^2.24.1", - "style-loader": "^0.13.1", - "url-loader": "^0.5.7", - "webpack": "^1.13.3", - "webpack-devserver": "0.0.6" + "babel-core": "^6.26.0", + "babel-loader": "^7.1.4", + "babel-preset-env": "^1.6.1", + "babel-preset-react": "^6.24.1", + "css-loader": "^0.28.11", + "file-loader": "^1.1.11", + "html-webpack-plugin": "^3.2.0", + "mini-css-extract-plugin": "^0.4.0", + "url-loader": "^1.0.1", + "webpack": "^4.6.0", + "webpack-cli": "^2.0.14", + "webpack-dev-server": "3.1.0" }, "dependencies": { - "bootstrap": "^3.3.7", - "react": "^15.3.2", - "react-dom": "^15.3.2" + "bootstrap": "^4.1.3", + "react": "^16.3.2", + "react-dom": "^16.3.2" } } diff --git a/11 TableMock/readme.md b/11 TableMock/readme.md index 5ea9686..f6af0c9 100644 --- a/11 TableMock/readme.md +++ b/11 TableMock/readme.md @@ -9,10 +9,9 @@ We will take a startup point sample _03 State_: Summary steps: - Define a model entity (we will call it _member_). -- Define a fake api (to take thing simple we will just make it synchronous) +- Define a fake api (to take thing simple we will just make it synchronous). - We will row component, we will call it _memberRow_. -- Create a table component, we will call it _memberTable_ and make use of _memberRow - +- Create a table component, we will call it _memberTable_ and make use of _memberRow. ## Prerequisites @@ -24,189 +23,205 @@ Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are - Copy the content from _03 State_ and execute: - ``` - npm install - ``` +``` +npm install +``` + +- Let's create a model entity in _src/model/member.js_: -- Let's create some mock data in _src/api/memberMockData.js_: +```javascript +class MemberEntity { + constructor() { + this.id = -1; + this.login = ''; + this.avatar_url = ''; + } +} - ```javascript - const MembersMockData = [ - { - id: 1457912, - login: 'brauliodiez', - avatar_url: 'https://avatars.githubusercontent.com/u/1457912?v=3', - }, - { - id: 4374977, - login: 'Nasdan', - avatar_url: 'https://avatars.githubusercontent.com/u/4374977?v=3', - }, - ]; +export default MemberEntity; - export default MembersMockData; +``` - ``` +- Let's create some mock data in _src/api/memberMockData.js_: + +```javascript +const MembersMockData = [ + { + id: 1457912, + login: 'brauliodiez', + avatar_url: 'https://avatars.githubusercontent.com/u/1457912?v=3', + }, + { + id: 4374977, + login: 'Nasdan', + avatar_url: 'https://avatars.githubusercontent.com/u/4374977?v=3', + }, +]; + +export default MembersMockData; + +``` - Define a fake api (to take thing simple we will just make it synchronous) in _src/api/memberAPI.js_: - ```javascript - import MembersMockData from './memberMockData'; - - // Sync mock data API, inspired from: - // https://gist.github.com/coryhouse/fd6232f95f9d601158e4 - class MemberAPI { - // This would be performed on the server in a real app. Just stubbing in. - static _clone(item) { - return ( - // return cloned copy so that the item is passed by value instead of by reference - JSON.parse(JSON.stringify(item)) - ); - } - - // Just return a copy of the mock data - getAllMembers() { - return ( - MemberAPI._clone(MembersMockData) - ); - } +```javascript +import MembersMockData from './memberMockData'; + +// Sync mock data API, inspired from: +// https://gist.github.com/coryhouse/fd6232f95f9d601158e4 +class MemberAPI { + // This would be performed on the server in a real app. Just stubbing in. + static _clone(item) { + return ( + // return cloned copy so that the item is passed by value instead of by reference + JSON.parse(JSON.stringify(item)) + ); } - const memberAPI = new MemberAPI(); + // Just return a copy of the mock data + getAllMembers() { + return ( + MemberAPI._clone(MembersMockData) + ); + } +} - export default memberAPI; +const memberAPI = new MemberAPI(); - ``` +export default memberAPI; -- Now it's time jump into the interesting part, let's delete _hello.jsx_ and _nameEdit.jsx_. - -- We are going to create an stateless component that will display a single row _memberRow.jsx_. - - ```jsx - import * as React from 'react'; - - const MemberRow = props => ( - - - Avatar - - - {props.member.id} - - - {props.member.login} - - - ); - - MemberRow.propTypes = { - // Is impossible to use: - // member: React.PropTypes.instanceOf(MemberEntity), - // with _clone(). - member: React.PropTypes.shape({ - id: React.PropTypes.number, - avatar_url: React.PropTypes.string, - login: React.PropTypes.string, - }), - }; - - export default MemberRow; +``` - ``` +- Now it's time jump into the interesting part, let's delete _src/hello.jsx_ and _src/nameEdit.jsx_. + +- We are going to create an stateless component that will display a single row _src/memberRow.jsx_. + +```jsx +import * as React from 'react'; +import PropTypes from 'prop-types'; + +const MemberRow = props => ( + + + Avatar + + + {props.member.id} + + + {props.member.login} + + +); + +MemberRow.propTypes = { + // Is impossible to use: + // member: PropTypes.instanceOf(MemberEntity), + // with _clone(). + member: PropTypes.shape({ + id: PropTypes.number, + avatar_url: PropTypes.string, + login: PropTypes.string, + }), +}; + +export default MemberRow; + +``` We can't use max-widh in the param style in. We must write 'maxWidth' in the react components. - Then we are going to implement a component that will display a list of members (and will - make use of rows), _membersTable.jsx_: - - ```jsx - import * as React from 'react'; - import memberAPI from './api/memberAPI'; - import MemberRow from './memberRow'; - - class MembersTable extends React.Component { - - constructor(props) { - super(props); - // set initial state - this.state = { members: [] }; - } - - // Standard react lifecycle function: - // https://facebook.github.io/react/docs/component-specs.html - componentWillMount() { - this.setState({ members: memberAPI.getAllMembers() }); - } - - render() { - return ( -
-

Members Page

- - - - - - - - - - { - this.state.members.map(member => - - ) - } - -
- Avatar - - Id - - Name -
-
- ); - } + make use of rows), _src/membersTable.jsx_: + +```jsx +import * as React from 'react'; +import memberAPI from './api/memberAPI'; +import MemberRow from './memberRow'; + +class MembersTable extends React.Component { + + constructor(props) { + super(props); + // set initial state + this.state = { members: [] }; } - export default MembersTable; + // Standard react lifecycle function: + // https://facebook.github.io/react/docs/component-specs.html + componentWillMount() { + this.setState({ members: memberAPI.getAllMembers() }); + } - ``` + render() { + return ( +
+

Members Page

+ + + + + + + + + + { + this.state.members.map(member => + + ) + } + +
+ Avatar + + Id + + Name +
+
+ ); + } +} -- Let's update app.jsx +export default MembersTable; - ```jsx - import React from 'react'; - import MembersTableComponent from './membersTable'; +``` - export class App extends React.Component { - constructor(props) { - super(props); +- Let's update _src/app.jsx_: - this.state = { - userName: 'defaultUserName', - }; +```jsx +import React from 'react'; +import MembersTableComponent from './membersTable'; - this.setUsernameState = this.setUsernameState.bind(this); - } +export class App extends React.Component { + constructor(props) { + super(props); - setUsernameState(event) { - // If the state gets more complex we should use object.assign - this.setState({ - userName: event.target.value, - }); - } + this.state = { + userName: 'defaultUserName', + }; - render() { - return ( -
- -
- ); - } - } + this.setUsernameState = this.setUsernameState.bind(this); + } - ``` + setUsernameState(event) { + // If the state gets more complex we should use object.assign + this.setState({ + userName: event.target.value, + }); + } + + render() { + return ( +
+ +
+ ); + } +} + +``` - Let's run the sample diff --git a/11 TableMock/src/app.jsx b/11 TableMock/src/app.jsx index 4d1ee5b..c5df6ef 100644 --- a/11 TableMock/src/app.jsx +++ b/11 TableMock/src/app.jsx @@ -22,7 +22,7 @@ export class App extends React.Component { render() { return (
- +
); } diff --git a/11 TableMock/src/memberRow.jsx b/11 TableMock/src/memberRow.jsx index 31036ac..40fc584 100644 --- a/11 TableMock/src/memberRow.jsx +++ b/11 TableMock/src/memberRow.jsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import PropTypes from 'prop-types'; const MemberRow = props => ( @@ -16,12 +17,12 @@ const MemberRow = props => ( MemberRow.propTypes = { // Is impossible to use: - // member: React.PropTypes.instanceOf(MemberEntity), + // member: PropTypes.instanceOf(MemberEntity), // with _clone(). - member: React.PropTypes.shape({ - id: React.PropTypes.number, - avatar_url: React.PropTypes.string, - login: React.PropTypes.string, + member: PropTypes.shape({ + id: PropTypes.number, + avatar_url: PropTypes.string, + login: PropTypes.string, }), }; diff --git a/11 TableMock/src/membersTable.jsx b/11 TableMock/src/membersTable.jsx index 0725bef..755ad3c 100644 --- a/11 TableMock/src/membersTable.jsx +++ b/11 TableMock/src/membersTable.jsx @@ -18,8 +18,8 @@ class MembersTable extends React.Component { render() { return ( -
-

Members Page

+
+

Members Page

diff --git a/11 TableMock/src/model/member.js b/11 TableMock/src/model/member.js new file mode 100644 index 0000000..3597969 --- /dev/null +++ b/11 TableMock/src/model/member.js @@ -0,0 +1,9 @@ +class MemberEntity { + constructor() { + this.id = -1; + this.login = ''; + this.avatar_url = ''; + } +} + +export default MemberEntity; diff --git a/11 TableMock/webpack.config.js b/11 TableMock/webpack.config.js index f317390..c0c9b9e 100644 --- a/11 TableMock/webpack.config.js +++ b/11 TableMock/webpack.config.js @@ -74,6 +74,10 @@ module.exports = { filename: 'index.html', // Name of file in ./dist/ template: 'index.html', // Name of template in ./src hash: true - }) + }), + new MiniCssExtractPlugin({ + filename: '[name].css', + chunkFilename: '[id].css' + }), ] }