Skip to content

Commit 03b85e8

Browse files
authored
Merge pull request #34 from linuxonrails/port_10_sidebar_from_ts_to_js_#19
Port 10 sidebar from ts to js #19
2 parents a6bce94 + 8959bfe commit 03b85e8

File tree

11 files changed

+620
-0
lines changed

11 files changed

+620
-0
lines changed

10 Sidebar/package.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "samplereact",
3+
"version": "1.0.0",
4+
"description": "In this sample we are going to setup the basic plumbing to \"build\" our project and launch it in a dev server.",
5+
"main": "index.js",
6+
"scripts": {
7+
"start": "webpack-dev-server --inline",
8+
"test": "echo \"Error: no test specified\" && exit 1"
9+
},
10+
"author": "",
11+
"license": "ISC",
12+
"devDependencies": {
13+
"babel-core": "^6.18.2",
14+
"babel-loader": "^6.2.7",
15+
"babel-plugin-transform-runtime": "^6.15.0",
16+
"babel-preset-es2015": "^6.18.0",
17+
"babel-preset-react": "^6.16.0",
18+
"css-loader": "^0.25.0",
19+
"file-loader": "^0.9.0",
20+
"html-webpack-plugin": "^2.24.1",
21+
"style-loader": "^0.13.1",
22+
"url-loader": "^0.5.7",
23+
"webpack": "^1.13.3",
24+
"webpack-devserver": "0.0.6"
25+
},
26+
"dependencies": {
27+
"bootstrap": "^3.3.7",
28+
"react": "^15.3.2",
29+
"react-dom": "^15.3.2"
30+
}
31+
}

10 Sidebar/readme.md

Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
# 10 Sidebar
2+
3+
In this sample we are going to implement a single sidebar.
4+
5+
We will take a startup point sample _03 State_:
6+
7+
Summary steps:
8+
9+
- Add some styles.
10+
- Include the new css into the webpack loop.
11+
- Create a sidebar component.
12+
- Let's add some content to the sidebar.
13+
- Add a button to open / close the sidebar.
14+
15+
16+
## Prerequisites
17+
18+
Install [Node.js and npm](https://nodejs.org/en/) (v6.6.0 or newer) if they are not already installed on your computer.
19+
20+
> Verify that you are running at least node v6.x.x and npm 3.x.x by running `node -v` and `npm -v` in a terminal/console window. Older versions may produce errors.
21+
22+
## Steps to build it
23+
24+
- Copy the content from _03 State_ and execute:
25+
26+
```
27+
npm install
28+
```
29+
30+
- Create a file called _src/styles.css_ and add the following styles (http://www.w3schools.com/howto/howto_js_sidenav.asp):
31+
32+
```css
33+
/* The side navigation menu */
34+
.sidenav {
35+
height: 100%; /* 100% Full-height */
36+
width: 0; /* 0 width - change this with JavaScript */
37+
position: fixed; /* Stay in place */
38+
z-index: 1; /* Stay on top */
39+
top: 0;
40+
left: 0;
41+
background-color: #808080; /* Gray*/
42+
overflow-x: hidden; /* Disable horizontal scroll */
43+
padding-top: 60px; /* Place content 60px from the top */
44+
transition: 0.5s; /* 0.5 second transition effect to slide in the sidenav */
45+
}
46+
47+
48+
/* Position and style the close button (top right corner) */
49+
.sidenav .closebtn {
50+
position: absolute;
51+
top: 0;
52+
right: 25px;
53+
font-size: 36px;
54+
margin-left: 50px;
55+
}
56+
57+
/* Style page content - use this if you want to push the page content to the right when you open the side navigation */
58+
#main {
59+
transition: margin-left .5s;
60+
padding: 20px;
61+
}
62+
63+
/* On smaller screens, where height is less than 450px, change the style of the sidenav (less padding and a smaller font size) */
64+
@media screen and (max-height: 450px) {
65+
.sidenav {padding-top: 15px;}
66+
.sidenav a {font-size: 18px;}
67+
}
68+
```
69+
70+
- Add this css file to the webpack entry point:
71+
72+
```javascript
73+
entry: [
74+
'./main.jsx',
75+
'../node_modules/bootstrap/dist/css/bootstrap.css',
76+
'./styles.css'
77+
],
78+
```
79+
80+
- We are going to create now a sidebar component, _src/sidebar.jsx_. Right now we will create just
81+
a rectangle and we will interact with the animation.
82+
83+
```jsx
84+
import * as React from 'react';
85+
86+
export const SidebarComponent = () => (
87+
<div id="mySidenav" className="sidenav">
88+
<span>Basic side bar, first steps</span>
89+
</div>
90+
);
91+
92+
```
93+
94+
- We are going to add a known id to to body section of _src/index.html_ page
95+
96+
```html
97+
<body id="main">
98+
```
99+
100+
- Let's place the component adding into the app.jsx:
101+
102+
```jsx
103+
import { SidebarComponent } from './sidebar';
104+
```
105+
106+
```jsx
107+
return (
108+
<div>
109+
<SidebarComponent />
110+
<HelloComponent userName={this.state.userName} />
111+
<NameEditComponent
112+
userName={this.state.userName}
113+
onChange={this.setUsernameState}
114+
/>
115+
</div>
116+
);
117+
118+
```
119+
120+
- Now is time to run the app, just to check we haven't broken anything (but you will see no results).
121+
122+
```
123+
npm start
124+
```
125+
126+
- Let's start with the interesting part of this implementation, let's add a flag to show/hide the
127+
sidebar _sidebar.jsx_.
128+
129+
```jsx
130+
export const SidebarComponent = props => (
131+
<div id="mySidenav" className="sidenav">
132+
<span>Basic side bar, first steps</span>
133+
</div>
134+
);
135+
```
136+
137+
- Now let's add some logic to show / display the sidebar in case the flag gets
138+
updated
139+
140+
```jsx
141+
export const SidebarComponent = (props) => {
142+
const divStyle = {
143+
width: (props.isVisible) ? '250px' : '0px',
144+
};
145+
146+
return (
147+
<div id="mySidenav" className="sidenav" style={divStyle}>
148+
<span>Basic side bar, first steps</span>
149+
</div>
150+
);
151+
};
152+
153+
SidebarComponent.propTypes = {
154+
isVisible: React.PropTypes.bool.isRequired,
155+
};
156+
157+
```
158+
159+
- Now at app level (in file _app.jsx_) we can add a new member to the state (a boolean flag) and a button to turn it
160+
off and on.
161+
162+
```jsx
163+
import React from 'react';
164+
import { HelloComponent } from './hello';
165+
import { NameEditComponent } from './nameEdit';
166+
import { SidebarComponent } from './sidebar';
167+
168+
export class App extends React.Component {
169+
constructor(props) {
170+
super(props);
171+
172+
this.state = {
173+
userName: 'defaultUserName',
174+
isSidebarVisible: false,
175+
};
176+
177+
this.setUsernameState = this.setUsernameState.bind(this);
178+
this.toggleSidebarVisibility = this.toggleSidebarVisibility.bind(this);
179+
}
180+
181+
setUsernameState(event) {
182+
// If the state gets more complex we should use object.assign
183+
this.setState({
184+
userName: event.target.value,
185+
});
186+
}
187+
188+
toggleSidebarVisibility() {
189+
const newVisibleState = !this.state.isSidebarVisible;
190+
191+
this.setState({
192+
isSidebarVisible: newVisibleState,
193+
});
194+
}
195+
196+
render() {
197+
const buttonStyle = {
198+
marginLeft: '450px',
199+
};
200+
201+
return (
202+
<div>
203+
<SidebarComponent isVisible={this.state.isSidebarVisible} />
204+
<HelloComponent userName={this.state.userName} />
205+
<NameEditComponent
206+
userName={this.state.userName}
207+
onChange={this.setUsernameState}
208+
/>
209+
<input
210+
type="submit"
211+
value="Toggle Sidear"
212+
className="btn btn-default"
213+
style={buttonStyle}
214+
onClick={this.toggleSidebarVisibility}
215+
/>
216+
</div>
217+
);
218+
}
219+
}
220+
221+
```
222+
223+
- If we run our sample, we can see how the sidebar is shown / hidden:
224+
225+
- So far so good, but what happens if we want to make this sidebar a reusable component, we could
226+
just show the frame but the content should be dynamic.
227+
228+
- Let's start by adding some content when instantiating the sidebar (_app.jsx_).
229+
230+
```jsx
231+
<SidebarComponent isVisible={this.state.isSidebarVisible}>
232+
<h1>Test content</h1>
233+
</SidebarComponent>
234+
```
235+
236+
- Now in the _sidebar.jsx_ let's dump this content by using {this.props.children}
237+
238+
```jsx
239+
import * as React from 'react';
240+
241+
export class SidebarComponent extends React.Component {
242+
constructor(props) {
243+
super(props);
244+
this.state = {
245+
divStyle: {
246+
width: '0px',
247+
},
248+
};
249+
}
250+
251+
componentWillReceiveProps(nextProps) {
252+
if (this.props.isVisible !== nextProps.isVisible) {
253+
const widthValue = (nextProps.isVisible) ? '250px' : '0px';
254+
// TODO we could remove this and try to use single source of truth
255+
// a function that just calculates the value based on the visible flag
256+
this.setState({ divStyle: { width: widthValue } });
257+
}
258+
}
259+
260+
render() {
261+
return (
262+
<div id="mySidenav" className="sidenav" style={this.state.divStyle}>
263+
{this.props.children}
264+
</div>
265+
);
266+
}
267+
}
268+
269+
SidebarComponent.propTypes = {
270+
isVisible: React.PropTypes.bool.isRequired,
271+
children: React.PropTypes.element,
272+
};
273+
274+
```
275+
276+
- We can refact to transform the `SidebarComponent` again to be an *stateless* Component. We should keep the interim step, but end up with something like:
277+
278+
```jsx
279+
import * as React from 'react';
280+
281+
export const SidebarComponent = (props) => {
282+
283+
function calculateDivWidth() {
284+
const widthpx = props.isVisible ? '250px' : '0';
285+
const style = { width: widthpx };
286+
287+
return style;
288+
}
289+
290+
return (
291+
<div id="mySidenav" className="sidenav" style={calculateDivWidth()}>
292+
{props.children}
293+
</div>
294+
);
295+
};
296+
297+
SidebarComponent.propTypes = {
298+
isVisible: React.PropTypes.bool.isRequired,
299+
children: React.PropTypes.element,
300+
};
301+
302+
```
303+
304+
We have removed `this` in SidebarComponent.
305+
306+
- We can move the calculateDivWidth to an external resource. We create a file with name _mystyles.js_:
307+
308+
```javascript
309+
export function calculateDivWidth(isVisible) {
310+
const widthpx = isVisible ? '250px' : '0';
311+
const style = { width: widthpx };
312+
313+
return style;
314+
}
315+
316+
```
317+
318+
And then import and use the function in _sidebar.jsx_:
319+
320+
```jsx
321+
import * as React from 'react';
322+
import { calculateDivWidth } from './mystyles';
323+
324+
export const SidebarComponent = props => (
325+
<div id="mySidenav" className="sidenav" style={calculateDivWidth(props.isVisible)}>
326+
{props.children}
327+
</div>
328+
);
329+
330+
SidebarComponent.propTypes = {
331+
isVisible: React.PropTypes.bool.isRequired,
332+
children: React.PropTypes.element,
333+
};
334+
335+
```
336+
337+
- But the best solution is more simple when calculateDivWidth is an small code.
338+
As final step we add as well:
339+
340+
```jsx
341+
import * as React from 'react';
342+
343+
export const SidebarComponent = props => (
344+
<div id="mySidenav" className="sidenav" style={{ width: (props.isVisible) ? '250px' : 0 }}>
345+
{props.children}
346+
</div>
347+
);
348+
349+
SidebarComponent.propTypes = {
350+
isVisible: React.PropTypes.bool.isRequired,
351+
children: React.PropTypes.element,
352+
};
353+
354+
```

0 commit comments

Comments
 (0)