for designers
Homebrew
https://brew.sh/
https://nodejs.org/en/
https://www.npmjs.com/get-npm
https://yarnpkg.com/en/docs/install
NPM/YARN
main purpose
- Install files into the node_modules-folder
- Run scripts
yarn add react-dom
-
Adds react-dom to the node_modules-folder
-
Adds react-dom to package.json
Install files into the node_modules-folder
yarn
-
Checks in the projects package.json
which packages to install -
Install those packages to the node_modules-folder
Install files into the node_modules-folder
$ yarn add react-dom
## same as
$ npm install react-dom
$ npm install -g react-dom
## Save the package on computer, not just in project
Install files into the node_modules-folder
import ReactDOM from 'react-dom';
Install files into the node_modules-folder
in terminal
in your js-file
$ yarn add react-dom
{
"name": "test",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^16.1.1",
"react-dom": "^16.1.1",
"react-scripts": "1.0.17"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
package.json
Run scripts
{
"name": "test",
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^16.1.1",
"react-dom": "^16.1.1",
"react-scripts": "1.0.17"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
yarn start
yarn test
yarn build
yarn eject
EsLint
//before
var uxdesigners = 4;
//With ES6:
// if we know the value won't be changed
const uxdesigners = 4;
// if we know the value will be changed later on
let uxdesigner = 0;
Variables
//before:
var uxdesigners = 4;
var variableName = null;
if (uxdesigners > 8) {
variableName = 'wow we are many';
} else {
variableName = 'We are not that many';
}
//es6
const variableName = uxdesigners > 8 ? 'wow we are many' : 'We are not that many';
If/else
//Ternary-operator
const fee = 'The fee is ' + (isMember ? '$2.00' : '$10.00');
const isAvailable = details.status === 'available';
// if details.status matches available we are creating the const isAvailable where the value is true. If not: false.
const variableName = expand && 'element is expanded';
// if expand !== null the const variableName has a value. If not its null.
If/else
Ternary & Logical Operators
== equal to === equal value and equal type != not equal !== not equal value or not equal type > greater than < less than >= greater than or equal to <= less than or equal to && and || or
! not
https://dorey.github.io/JavaScript-Equality-Table/
.map
var array1 = [1, 4, 9, 16];
// pass a function to map
const map1 = array1.map(x => x * 2);
console.log(map1);
// expected output: Array [2, 8, 18, 32]
Template Strings
//before:
var name = "Bob", time = "today";
'Hello ' + name + ' how are you ' + time + '?';
//es6
const name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
Arrow functions
// ES5
var multiply = function(x, y) {
return x * y;
};
// ES6
var multiply = (x, y) => { return x * y };
Arrow functions
// If we have no parameters, we express an arrow function like this:
const funcName = () => { statements }
// When you only have one parameter, the opening parenthesis are optional:
const funcName = parameters => { statements }
Combining it
const smartPhones = [
{ name:'iphone', price:649 },
{ name:'Galaxy S6', price:576 },
{ name:'Galaxy Note 5', price:489 }
];
// ES5
smartPhones.map(
function(smartPhone) {
return smartPhone.price;
}
); // [649, 576, 489]
// ES6
smartPhones.map(
smartPhone => smartPhone.price
); // [649, 576, 489]
smartPhones.map(smartPhone => smartPhone.price);
smartPhones.map(smartPhone => (
smartPhone.price
));
smartPhones.map((smartPhone, index) => (
smartPhone.price
));
- react
- react-dom
npm install -g create-react-app
create-react-app my-app
cd my-app/
npm start
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
src/app.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
);
}
}
export default App;
Components
Component
<Button href="#">
Text for button
</Button>
<a href="#" class="c-btn">
Text for button
</a>
👇
react
HTML-output
Stateless functional component
import React from 'react';
const Button = ({ children, href }) => {
return (
<a href={href} className="c-btn">
{children}
</a>
);
};
export default Button;
// Since there is no variables++ we don't need return
const Button = ({ children, href }) => (
<a href={href} className="c-btn">
{children}
</a>
);
Stateful component
import React, { Component } from 'react';
class App extends Component {
render() {
const { children, href } = this.props;
return (
<a href={href} className="c-btn">
{children}
</a>
);
}
}
export default App;
Stateful component example
<AccordionItem buttonText="Click to See">
Text that appears and disappear
when you click the button
</AccordionItem>
Stateful component
import React, { Component } from 'react';
class AccordionItem extends Component {
constructor(props) {
super(props);
this.state = {
expanded: this.props.expanded
};
this.handleToggle = this.handleToggle.bind(this);
}
handleToggle() {
this.setState({ expanded: !this.state.expanded });
}
render() {
const { buttonText, children } = this.props;
const { expanded } = this.state;
return (
<div>
<button onClick={this.handleToggle}>
{buttonText}
</button>
{expanded &&
<div>
{children}
</div>
}
</div>
);
}
}
export default AccordionItem;
PropTypes
PropTypes
import React from 'react';
import PropTypes from 'prop-types';
const Button = ({ children, callback, disabled}) => {
return (
<button className="c-btn" onClick={callback} disabled={disabled} >
{children}
</button>
);
};
Button.defaultProps = {
disabled: false
};
Button.propTypes = {
callback: PropTypes.func.isRequired,
children: PropTypes.oneOfType([
PropTypes.string,
PropTypes.array,
PropTypes.element
]).isRequired,
disabled: PropTypes.bool
};
export default Button;
PropTypes
MenuMain.propTypes = {
menuItems: PropTypes.arrayOf(PropTypes.shape({
icon: PropTypes.string,
id: PropTypes.string,
name: PropTypes.string,
subpage: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string,
name: PropTypes.string,
to: PropTypes.string
}))
})).isRequired,
shortcuts: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string,
icon: PropTypes.string,
name: PropTypes.string,
to: PropTypes.string
})).isRequired
};
{
"mainMenu": [{
"name": "Konto og kort",
"icon": "wallet",
"id": "12asd",
"subpage": [{
"name": "Totaloversikten",
"to": "/",
"id": "jghkadfs"
},
{
"name": "Kontoutskrift",
"to": "index.php?file=/betaling/kontoutskrift/",
"id": "656"
}]
}]
}
👉
Task
Create 3 new, empty
files in CRA
- src/list.js
- src/listItem.js
- src/data/shortcuts.js
Add this to shortcuts.js
Rewrite your App.js
import React from 'react';
import shortcuts from './data/shortcuts';
import List from './List';
const App = () => (
<List listItems={shortcuts} />
);
export default App;
Continue
- Create List.js with a <ul> that maps over the listItems we sent down in app.js
- Create ListeItem.js with a <li> and an <a> that recieves a name + to-prop.
List.js
import React from 'react';
import PropTypes from 'prop-types';
import ListItem from './ListItem';
const List = ({listItems}) => (
<ul>
{listItems.map(listItem => (
<ListItem name={listItem.name}
to={listItem.to}
key={listItem.id} />
))}
</ul>
);
List.propTypes = {
listItems: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string,
name: PropTypes.string,
to: PropTypes.string
})).isRequired
};
export default List;
ListItem.js
import React from 'react';
import PropTypes from 'prop-types';
const ListItem = ({name, to}) => (
<li>
<a href={to}>{name}</a>
</li>
);
ListItem.propTypes = {
name: PropTypes.string.isRequired,
to: PropTypes.string.isRequired
};
export default ListItem;
React devtools
KodeksUI
$ cd KodeksUI
$ source setup.sh
Setup
$ cd KodeksUI/core
$ yarn storybook
Start storybook
$ cd KodeksUI/apps/my-savings
$ yarn start
Start my-savings
Component structure
Required for component-folders
in core
-
component.js
-
component.scss
-
component.stories.js
Goto.js
import React from 'react';
import PropTypes from 'prop-types';
import './Goto.scss';
const Goto = ({ children, className, href, callback }) => {
const Classes = className || '';
if (callback) {
return (
<button className={Classes} onClick={callback}>
{children}
</button>
)
};
return (
<a href={href} className={Classes}>
{children}
</a>
);
};
Goto.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.element),
PropTypes.element,
PropTypes.string,
PropTypes.array]).isRequired,
className: PropTypes.string,
href: PropTypes.string,
callback: PropTypes.func
};
Goto.defaultProps = {
className: 'c-goto',
href: '#',
callback: null
};
export default Goto;
Goto.scss
.c-goto {
display: inline-block;
position: relative;
white-space: nowrap;
text-decoration: none;
padding-right: 30px;
background-color: transparent;
border-width: 0;
font-weight: bold;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
font-family: $global-font-heading;
cursor: pointer;
&::after {
font-weight: normal;
content: "";
font-family: 'icons';
color: $color-blue;
line-height: 1;
padding-left: 6px;
position: absolute;
top: 45%;
transform: translate(0, -50%);
transition: all 0.1s ease-in-out;
background-repeat: no-repeat;
background-image: url("data:image/svg+xml,%3Csvg width='12' height='12' viewBox='0 0 12 12' xmlns='http://www.w3.org/2000/svg'%3E %3Cpath d='M6.2 0L5 1.3 8.6 5H0v2h8.7L5 10.6 6 12l6-6' fill='%23008EFF'/%3E %3C/svg%3E ");
width: 18px;
height: 12px;
background-position: right center;
}
&:hover,
&:focus {
outline: none;
text-decoration: none;
&::after {
transform: translate(3px, -50%);
text-decoration: none;
}
}
}
Goto.stories.js
import React from 'react';
import { storiesOf } from '@storybook/react';
import Center from '../../../../.storybook/center'
import {withInfo} from '@storybook/addon-info';
import Goto from './Goto';
storiesOf('Buttons', module)
.addDecorator(Center)
.add('Goto href', withInfo('Tekst med lenke')(() =>
<Goto href="#"> Goto lenke </Goto>
))
.add('Goto button', withInfo('Tekst som oppførerer seg som en knapp')(() =>
<Goto callback={() => alert('Klikk')}> Goto button</Goto>
));
CSS
.block {}
.block__element {}
.block--modifier {}
.site-search {} /* Block */
.site-search__field {} /* Element */
.site-search--full {} /* Modifier */
<!-- Before -->
<form class="site-search full">
<input type="text" class="field">
<input type="Submit" value ="Search" class="button">
</form>
<!-- After -->
<form class="site-search site-search--full">
<input type="text" class="site-search__field">
<input type="Submit" value ="Search" class="site-search__button">
</form>
Kodeks SCSS
// i core: Importeres inn i
// js til hver komponent
import './Checkbox.scss';
// i hver app import
// inn i SCSS-hovedfil
@import "components/Header/_Header.scss";
PREFIX!
.c-button {
} //component
.c-name-of-component {
} //component
.u-margin-bottom {
} //utility
Task
Create a list.css-file in CRA
and style the list
GIT
React for designers
By Marius Hauken
React for designers
- 116