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

  1. Install files into the node_modules-folder
  2. 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

Made with Slides.com