Skip to content

Commit 2d5d8a7

Browse files
committed
sample 09 sidebar completed
1 parent 6100926 commit 2d5d8a7

16 files changed

Lines changed: 748 additions & 0 deletions

hooks/09_Sidebar/.babelrc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"presets": [
3+
[
4+
"@babel/preset-env",
5+
{
6+
"useBuiltIns": "entry"
7+
}
8+
]
9+
]
10+
}

hooks/09_Sidebar/Readme.md

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
# 09 Sidebar
2+
3+
In this example we are going to add a sidebar to our application, we will start with a specific
4+
implementation, then we will make it generic.
5+
6+
# Steps
7+
8+
- We will take as starting point sample _08 ColorPickerRefactor_, let's copy the content
9+
from that file and execute _npm install_.
10+
11+
```bash
12+
npm install
13+
```
14+
15+
- Create a file called _src/sidebar.css_ and add the following styles (http://www.w3schools.com/howto/howto_js_sidenav.asp):
16+
17+
_./src/components/sidebar.css_
18+
19+
```css
20+
/* The side navigation menu */
21+
.sidenav {
22+
height: 100%; /* 100% Full-height */
23+
width: 0; /* 0 width - change this with JavaScript */
24+
position: fixed; /* Stay in place */
25+
z-index: 1; /* Stay on top */
26+
top: 0;
27+
left: 0;
28+
background-color: #808080; /* Gray*/
29+
overflow-x: hidden; /* Disable horizontal scroll */
30+
padding-top: 60px; /* Place content 60px from the top */
31+
transition: 0.5s; /* 0.5 second transition effect to slide in the sidenav */
32+
}
33+
34+
/* Position and style the close button (top right corner) */
35+
.sidenav .closebtn {
36+
position: absolute;
37+
top: 0;
38+
right: 25px;
39+
font-size: 36px;
40+
margin-left: 50px;
41+
}
42+
43+
/* Style page content - use this if you want to push the page content to the right when you open the side navigation */
44+
#main {
45+
transition: margin-left 0.5s;
46+
padding: 20px;
47+
}
48+
49+
/* On smaller screens, where height is less than 450px, change the style of the sidenav (less padding and a smaller font size) */
50+
@media screen and (max-height: 450px) {
51+
.sidenav {
52+
padding-top: 15px;
53+
}
54+
.sidenav a {
55+
font-size: 18px;
56+
}
57+
}
58+
```
59+
60+
- We are going to use CSS Modules, so let's configure it.
61+
62+
_./webpack.config.js_
63+
64+
```diff
65+
module.exports = {
66+
context: path.join(basePath, "src"),
67+
resolve: {
68+
- extensions: ['.js', '.ts', '.tsx']
69+
+ extensions: ['.js', '.ts', '.tsx', '.css']
70+
},
71+
```
72+
73+
- We will only use CSS Modules for custom app stylesheets. We will not use CSS Modules for other CSS files, like Bootstrap (folder node_modules).
74+
75+
_./webpack.config.js_
76+
77+
```diff
78+
{
79+
test: /\.css$/,
80+
+ include: /node_modules/,
81+
use: [MiniCssExtractPlugin.loader, "css-loader"]
82+
},
83+
+ // Use CSS modules for custom stylesheets
84+
+ {
85+
+ test: /\.css$/,
86+
+ exclude: /node_modules/,
87+
+ use: [
88+
+ MiniCssExtractPlugin.loader,
89+
+ {
90+
+ loader: 'css-loader',
91+
+ options: {
92+
+ modules: true,
93+
+ localIdentName: '[name]__[local]___[hash:base64:5]',
94+
+ camelCase: true,
95+
+ },
96+
+ },
97+
+ ]
98+
+ },
99+
+ // Do not use CSS modules in node_modules folder
100+
101+
```
102+
103+
- We are going to create now a sidebar component, _src/sidebar.tsx_. Right now we will create just
104+
a rectangle and we will interact with the animation.
105+
106+
We need to install node typings, since we are goin to make use of _require_ to import from
107+
the _css_.
108+
109+
```bash
110+
npm install @types/node --save-dev
111+
```
112+
113+
_./src/components/sidebar.tsx_
114+
115+
```jsx
116+
import * as React from "react";
117+
118+
const classNames = require("./sidebar.css");
119+
120+
export const SidebarComponent = () => (
121+
<div id="mySidenav" className={classNames.sidenav}>
122+
<span>Basic side bar, first steps</span>
123+
</div>
124+
);
125+
```
126+
127+
- Let's add this component to the _index_ barrel.
128+
129+
_./src/components/index.ts_
130+
131+
```diff
132+
export * from "./hello";
133+
export * from "./nameEdit";
134+
export * from "./colorBrowser";
135+
export * from "./colorPicker";
136+
+ export * from "./sidebar";
137+
```
138+
139+
- We are going to add a known id to the body section of _src/index.html_ page
140+
141+
_./src/index.html_
142+
143+
```diff
144+
- <body>
145+
+ <body id="main">
146+
```
147+
148+
- Let's place the component adding it into the `app.tsx`:
149+
150+
_./src/app.tsx_
151+
152+
```diff
153+
import * as React from "react";
154+
- import { HelloComponent, NameEditComponent, ColorBrowser, ColorPicker } from "./components";
155+
+ import { HelloComponent, NameEditComponent, ColorBrowser, ColorPicker, SidebarComponent } from "./components";
156+
import { Color } from "./model/color";
157+
```
158+
159+
_./src/app.tsx_
160+
161+
```diff
162+
return (
163+
<>
164+
+ <SidebarComponent>
165+
<ColorBrowser color={color} />
166+
```
167+
168+
- Let's start with the interesting part of this implementation, let's add a flag to show/hide the
169+
sidebar _sidebar.tsx_.
170+
171+
_./src/components/sidebar.tsx_
172+
173+
```diff
174+
import * as React from 'react';
175+
176+
const classNames = require('./sidebar.css');
177+
178+
+ interface Props {
179+
+ isVisible: boolean;
180+
+ }
181+
182+
- export const SidebarComponent = () =>
183+
+ export const SidebarComponent = (props: Props) =>
184+
<div id="mySidenav" className={classNames.sidenav}>
185+
<span>Basic sidebar, first steps</span>
186+
</div>
187+
```
188+
189+
- Now let's add some logic to show / hide the sidebar in case the flag gets updated
190+
191+
_./src/sidebar.tsx_
192+
193+
```diff
194+
import * as React from 'react';
195+
196+
const classNames = require('./sidebar.css');
197+
198+
interface Props {
199+
isVisible: boolean;
200+
};
201+
202+
+ const divStyle = (props: Props): React.CSSProperties => ({
203+
+ width: (props.isVisible) ? '23rem' : '0rem'
204+
+ });
205+
206+
export const SidebarComponent = (props: Props) =>
207+
- <div id="mySidenav" className={classNames.sidenav}>
208+
+ <div id="mySidenav" className={classNames.sidenav}
209+
+ style={divStyle(props)}
210+
+ >
211+
<span>Basic sidebar, first steps</span>
212+
</div>
213+
```
214+
215+
- Let's make a quick test we will show always the side bar:
216+
217+
_./src/app.tsx_
218+
219+
```diff
220+
return (
221+
<>
222+
- <SidebarComponent />
223+
+ <SidebarComponent isVisible={true}/>
224+
<ColorBrowser color={color} />
225+
```
226+
227+
- If we start the project we should now see the sidebar that we have created (a gray rectangle).
228+
229+
```bash
230+
npm start
231+
```
232+
233+
_What if I cannot see the sidebar?_ Check that your styles and webpackconfig has been applied,
234+
you may need to start and top webpack-dev-sever (relaunch _npm \_start_), check with dev tools
235+
that you are loading the CSS styles.
236+
237+
- Now at app level we can remember the visible status and add a button to toggle the
238+
visibility of the sidebar.
239+
240+
_./src/app.tsx_
241+
242+
```diff
243+
export const App = () => {
244+
const [name, setName] = React.useState("defaultUserName");
245+
const [editingName, setEditingName] = React.useState("defaultUserName");
246+
const [color, setColor] = React.useState<Color>({
247+
red: 20,
248+
green: 40,
249+
blue: 180
250+
});
251+
+ const[isVisible, setVisible] = React.useState(false);
252+
```
253+
254+
_./src/app.tsx_
255+
256+
```diff
257+
return (
258+
<>
259+
- <SidebarComponent isVisible={true} />
260+
+ <SidebarComponent isVisible={isVisible} />
261+
<ColorBrowser color={color} />
262+
<ColorPicker color={color} onColorUpdated={setColor} />
263+
<HelloComponent userName={name} />
264+
<NameEditComponent
265+
initialUserName={name}
266+
editingName={editingName}
267+
onNameUpdated={setUsernameState}
268+
onEditingNameUpdated={setEditingName}
269+
disabled={editingName === "" || editingName === name}
270+
/>
271+
+ <div style={{float: 'right'}}>
272+
+ <button
273+
+ onClick={() => setVisible(!isVisible)}>
274+
+ Toggle Sidebar
275+
+ </button>
276+
+ </div>
277+
</>
278+
```
279+
280+
- Let's start the application to check how it behaves:
281+
282+
```bash
283+
npm start
284+
```
285+
286+
> Excercise: the inline call to the function in _onClick_ is not considered a
287+
> good pratice (on each render the function will be recreated), let's refactor this in two
288+
> steps:
289+
290+
- First we will extract this logic to a function, we will call it _toggleSidebarVisbility_.
291+
- Then let's wrap visibility + toggleSidebarVisibility in a custom hook.
292+
293+
* So far so good, but what happens if we want to make this sidebar a reusable component? We could just show the frame but the content should be dynamic.
294+
295+
* Let's start by adding some content when instantiating the sidebar (_app.tsx_).
296+
297+
_./src/app.tsx_
298+
299+
```diff
300+
<>
301+
- <SidebarComponent isVisible={isVisible} />
302+
+ <SidebarComponent isVisible={isVisible}>
303+
+ <h1>Cool Scfi movies</h1>
304+
+ <ul>
305+
+ <li><a href="https://www.imdb.com/title/tt0816692/">Interstellar</a></li>
306+
+ <li><a href="https://www.imdb.com/title/tt0083658/">Blade Runner</a></li>
307+
+ <li><a href="https://www.imdb.com/title/tt0062622/">2001: a space odyssey</a></li>
308+
+ </ul>
309+
+ </SidebarComponent>
310+
<ColorBrowser color={color} />
311+
```
312+
313+
> We got an error, _children_ is not defined, that's something we are going to fix in the
314+
> next step...
315+
316+
- Now in the _sidebar.tsx_ let's dump this content by using {this.props.children}
317+
318+
_./src/components/sidebar.tsx_
319+
320+
```diff
321+
- export const SidebarComponent = (props: Props) => (
322+
+ export const SidebarComponent: React.StatelessComponent<Props> = (props) => (
323+
324+
<div id="mySidenav" className={classNames.sidenav} style={divStyle(props)}>
325+
- <span>Basic side bar, first steps</span>
326+
+ {props.children}
327+
</div>
328+
);
329+
```
330+
331+
- Let's try the sample
332+
333+
```
334+
npm start
335+
```

hooks/09_Sidebar/package.json

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"name": "react-typescript-by-sample",
3+
"version": "1.0.0",
4+
"description": "React Typescript examples",
5+
"main": "index.js",
6+
"scripts": {
7+
"start": "webpack-dev-server --mode development --inline --hot --open",
8+
"build": "webpack --mode development"
9+
},
10+
"keywords": [
11+
"react",
12+
"typescript",
13+
"hooks"
14+
],
15+
"author": "Braulio Diez Botella",
16+
"license": "MIT",
17+
"devDependencies": {
18+
"@babel/cli": "^7.2.3",
19+
"@babel/core": "^7.2.2",
20+
"@babel/polyfill": "^7.2.5",
21+
"@babel/preset-env": "^7.3.1",
22+
"@types/node": "^11.9.4",
23+
"@types/react": "^16.8.3",
24+
"@types/react-dom": "^16.8.1",
25+
"awesome-typescript-loader": "^5.2.1",
26+
"babel-loader": "^8.0.5",
27+
"css-loader": "^2.1.0",
28+
"file-loader": "^3.0.1",
29+
"html-webpack-plugin": "^3.2.0",
30+
"mini-css-extract-plugin": "^0.5.0",
31+
"style-loader": "^0.23.1",
32+
"typescript": "^3.3.3",
33+
"url-loader": "^1.1.2",
34+
"webpack": "^4.29.3",
35+
"webpack-cli": "^3.2.3",
36+
"webpack-dev-server": "^3.1.14"
37+
},
38+
"dependencies": {
39+
"react": "^16.8.2",
40+
"react-dom": "^16.8.2"
41+
}
42+
}

0 commit comments

Comments
 (0)