Coder Social home page Coder Social logo

entkenntnis / frontend_reasoning_exercise Goto Github PK

View Code? Open in Web Editor NEW

This project forked from serlo/frontend

0.0 2.0 0.0 2.84 MB

BACKUP of several branches (reasoning-exercise, etc. ...)

Home Page: frontend-dal123.vercel.app

License: MIT License

JavaScript 1.41% TypeScript 95.33% CSS 3.10% Dockerfile 0.17%

frontend_reasoning_exercise's Introduction

How To Frontend

Welcome to the new serlo.org frontend.

Getting started

Installation

Install Node.js (>=10) and yarn on your system.

Clone this repo, install dependencies and start the dev server:

git clone https://github.com/serlo/frontend.git
cd frontend
yarn
yarn dev

The server is now running on localhost:3000.

Creating pages

Routes are mapped to individual files in the pages-folder. Create a page by adding following file:

// pages/helloworld.tsx

function HelloWorld() {
  return <p>Welcome to the frontend!</p>
}

export default HelloWorld

Visit localhost:3000/helloworld to view this page.

Adding styles

You can attach styles to html elements and use them in your component:

// pages/helloworld.tsx

import styled from 'styled-components'

function HelloWorld() {
  return <BigParagraph>Welcome to the frontend!</BigParagraph>
}

const BigParagraph = styled.p`
  text-align: center;
  font-size: 3rem;
  color: lightgreen;
`

export default HelloWorld

Building components

Use functional components and hooks to split your code into reusable pieces. Some basic features are shown in this example:

// pages/helloworld.tsx

import React from 'react'
import styled from 'styled-components'

function HelloWorld() {
  return <ClickMeTitle title="Welcome to the frontend!" />
}

function ClickMeTitle(props) {
  const { title } = props
  const [clicked, setClicked] = React.useState(false)
  const smiley = clicked ? ' :)' : ''
  return (
    <BigParagraph onClick={() => setClicked(!clicked)}>
      {title + smiley}
    </BigParagraph>
  )
}

const BigParagraph = styled.p`
  text-align: center;
  font-size: 3rem;
  color: lightgreen;
`

export default HelloWorld

Visit localhost:3000/helloworld. Click on the text. Every click should toggle a smiley face:

grafik

Basic Features

TypeScript

We love types. They help us to maintain code and keep the codebase consistent. We also love rapid development and prototyping. You decide: Add your type declarations immediately as you code or later when the codebase stabilizes. The choice is up to you:

function HelloWorld() {
  return <Greeter title="Hello" subline="Welcome to the frontend!" />
}

interface GreeterProps {
  title: string
  subline?: string
}

function Greeter({ title, subline }: GreeterProps) {
  return (
    <>
      <h1>{title}</h1>
      {subline && <small>{subline}</small>}
    </>
  )
}

export default HelloWorld

Components

The frontend is a growing collection of components. Package every part of the UI as a component, save them in src/components and let the file name match the components name. Export the component as a default and type the props. A complete component file would look like this:

// src/components/Greeter.tsx

interface GreeterProps {
  title: string
  subline?: string
}

export default function Greeter({ title, subline }: GreeterProps) {
  return (
    <>
      <h1>{title}</h1>
      {subline && <small>{subline}</small>}
    </>
  )
}

Visit localhost:3000/__gallery for a quick overview of all components. Consider adding your new component into the gallery.

Responsive Design

Users will come to the frontend using very different devices, from narrow smartphones to very wide screens. Adapt your components and change there appearing with media queries:

import styled from 'styled-components'

function HelloWorld() {
  return (
    <ResponsiveBox>
      <GrowingParagraph>Hallo</GrowingParagraph>
      <GrowingParagraph>Welt</GrowingParagraph>
    </ResponsiveBox>
  )
}

const ResponsiveBox = styled.div`
  display: flex;
  @media (max-width: 500px) {
    flex-direction: column;
  }
`

const GrowingParagraph = styled.p`
  flex-grow: 1;
  text-align: center;
  font-size: 2rem;
  padding: 16px;
  background-color: lightgreen;
`

export default HelloWorld

This example makes use of flexbox. On wide screens, both paragraphs are shown next to each other:

On smaller screens, they are below each other:

Theming

We can improve the previous example by extracting commenly used constants like breakpoints or colors into a theme. The file src/theme.tsx defines our global theme which you can access in every component:

import styled from 'styled-components'

function HelloWorld() {
  return (
    <ResponsiveBox>
      <GrowingParagraph>Hallo</GrowingParagraph>
      <GrowingParagraph>Welt</GrowingParagraph>
    </ResponsiveBox>
  )
}

const ResponsiveBox = styled.div`
  display: flex;
  @media (max-width: ${props => props.theme.breakpoints.sm}) {
    flex-direction: column;
  }
`

const GrowingParagraph = styled.p`
  flex-grow: 1;
  text-align: center;
  font-size: 2rem;
  padding: 16px;
  background-color: ${props => props.theme.colors.brand};
`

export default HelloWorld

Colors

Visit localhost:3000/_colors to see all the colors predefined in the theme.

Units

There exists a bunch of different length units. Most of the time, px is fine. Sometimes there are better alternativs, especially in regard of a11y:

  • Use rem for font-size, so users can zoom the text (e.g. farsighted people or users on 4k monitors)
  • Use dimensionless values for line-height to scale well.
  • Test your component how it behaves when text zooms and eventually make adjustments.

Icons

Add some eye candy by using icons. We integrated Font Awesome and adding icons is straight forward:

import styled from 'styled-components'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faCoffee } from '@fortawesome/free-solid-svg-icons'

function HelloWorld() {
  return (
    <BigIcon>
      <FontAwesomeIcon icon={faCoffee} size="1x" />
    </BigIcon>
  )
}

const BigIcon = styled.div`
  text-align: center;
  font-size: 3rem;
  color: brown;
  margin: 30px;
`

export default HelloWorld

Style Adaption

Often you need two components with only slightly different styles. Adapt your styles based on props:

import styled from 'styled-components'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faCandyCane } from '@fortawesome/free-solid-svg-icons'

function HelloWorld() {
  return (
    <BigIcon iconColor="pink">
      <FontAwesomeIcon icon={faCandyCane} size="1x" />
    </BigIcon>
  )
}

const BigIcon = styled.div<{ iconColor: string }>`
  text-align: center;
  font-size: 3rem;
  color: ${props => props.iconColor};
  margin: 30px;
`

export default HelloWorld

This is one of the rare places where types are mandatory.

Polished

To boost your creativity, we included a bunch of useful css helper from polished:

import React from 'react'
import styled from 'styled-components'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faCandyCane } from '@fortawesome/free-solid-svg-icons'
import { lighten } from 'polished'

function HelloWorld() {
  const [lighter, setLighter] = React.useState(0)
  return (
    <>
      <p>Click it:</p>
      <BigIcon lighter={lighter} onClick={() => setLighter(lighter + 0.01)}>
        <FontAwesomeIcon icon={faCandyCane} size="1x" />
      </BigIcon>
    </>
  )
}

const BigIcon = styled.div<{ lighter: number }>`
  text-align: center;
  font-size: 3rem;
  color: ${props => lighten(props.lighter, 'pink')};
  margin: 30px;
`

export default HelloWorld

Import your helper from polished and use it in interpolations.

Assets

Put static content like images or documents into the public/_assets folder.

Example: The file public/_assets/img/placeholder.png is accessible via localhost:3000/_assets/img/placeholder.png

You can use assets in your components:

function HelloWorld() {
  return <img src="/_assets/img/placeholder.png" alt="placeholder" />
}

export default HelloWorld

SVG

You can import a svg directly. They are inlined and usable as component:

import SerloLogo from '../public/_assets/img/serlo-logo.svg'

function HelloWorld() {
  return <SerloLogo />
}

export default HelloWorld

Code Formatting

Format your code in a consistent way by running

yarn prettify

Make sure your code is properly formatted before every commit.

Tooltips, Dropdowns & Menus

You can add elements that pop out of the page with Tippy. A basic drop button looks like this:

import styled from 'styled-components'
import Tippy from '@tippyjs/react'

function HelloWorld() {
  return (
    <Wall>
      <Tippy
        content={<Drop>Surprise )(</Drop>}
        trigger="click"
        placement="bottom-start"
      >
        <button>Click Me!</button>
      </Tippy>
    </Wall>
  )
}

const Wall = styled.div`
  margin-top: 100px;
  display: flex;
  justify-content: center;
`

const Drop = styled.div`
  background-color: lightgreen;
  padding: 5px;
  box-shadow: 8px 8px 2px 1px rgba(0, 255, 0, 0.2);
`

export default HelloWorld

Surround the target element with the Tippy component and pass the content to it. There are many more props to explore.

Modals

Show information to the user with modals. react-modal provides the necessary functionality. This example shows how you can get started:

import React from 'react'
import Modal from '../components/Modal' // our wrapper

const centeredModal = {
  overlay: {
    display: 'flex',
    justifyContent: 'center',
    alignItems: 'center'
  },
  content: {
    position: 'static'
  }
}

function HelloWorld() {
  const [open, setOpen] = React.useState(false)
  return (
    <>
      <button onClick={() => setOpen(true)}>Open modal</button>
      <Modal
        isOpen={open}
        onRequestClose={() => setOpen(false)}
        style={centeredModal}
      >
        This is the content of the modal
      </Modal>
    </>
  )
}

export default HelloWorld

You handle the state by yourself. The Modal component has many options available. Import the modal from src/reactmodal.tsx. This takes care of the app element.

Formulas

You can use KaTeX to render formulas:

import styled from 'styled-components'
import Math from '../src/math'

function HelloWorld() {
  return (
    <>
      <Paragraph>
        This changed the world:{' '}
        <Math formula={'c = \\pm\\sqrt{a^2 + b^2}'} inline />.
      </Paragraph>
      <Paragraph>This too:</Paragraph>
      <CenteredParagraph>
        <Math formula={'E = mc^2'} />
      </CenteredParagraph>
    </>
  )
}

const Paragraph = styled.p`
  margin: 20px;
  font-size: 18px;
`

const CenteredParagraph = styled.p`
  text-align: center;
  font-size: 18px;
`

export default HelloWorld

Our math component takes two props: formula is the LaTeX string, inline is optional and will make the formula a bit smaller. The rendered formula is a span that can be placed anywhere.

Advanced Topics

Data Fetching

Data fetching is handled by our GraphQL data fetcher. Look at src/fetcher/README.md for a detailed explanation.

Deployment

Build and run the frontend with these commands:

yarn start

This will trigger a production build (docker and docker-compose need to be installed). To stop the created docker image, run:

yarn stop

To get detailed information about bundle size and a summarize of all output artifacts, run this:

yarn analyze

Results are saved to .next/analyze/client.html and .next/analyze/server.html.

Importing Component dynamically

If some part of a page is heavy and only relevant for a smaller fraction of users, import it dynamically. Write your component as usual:

// src/fancycomponent.tsx

function FancyComponent() {
  return <p>This is some heavy component</p>
}

export default FancyComponent

Use a dynamic import to load the component:

// pages/helloworld.tsx

import React from 'react'
import dynamic from 'next/dynamic'

const FancyComponent = dynamic(() => import('../src/fancycomponent'))

function HelloWorld() {
  const [visible, setVisible] = React.useState(false)
  return (
    <>
      <p>
        <button onClick={() => setVisible(true)}>Load ...</button>
      </p>
      {visible && <FancyComponent />}
    </>
  )
}

export default HelloWorld

The source code of FancyComponent is splitting into a separate chunk and is only loaded when users click the button.

Reusing CSS Snippets

You can extend components by adding style snippets. These snippets are functions that add new props to a styled component:

import styled from 'styled-components'

function HelloWorld() {
  return (
    <>
      <ChatParagraph side="left">Hey, how are you?</ChatParagraph>
      <ChatParagraph side="right">I'm fine!</ChatParagraph>
    </>
  )
}

interface SideProps {
  side: 'left' | 'right'
}

function withSide(props: SideProps) {
  if (props.side === 'left') {
    return `
      color: blue;
      text-align: left;
    `
  } else if (props.side === 'right') {
    return `
      color: green;
      text-align: right;
    `
  } else {
    return ''
  }
}

const ChatParagraph = styled.p<SideProps>`
  ${withSide}
  margin: 20px;
`

export default HelloWorld

This example adds the side prop to the ChatParagraph and allows users to change the appearance of the component.

You can reuse this function in another component:

const AnotherChatParagraph = styled.p<SideProps>`
  ${withSide}
  margin: 15px;
  border: 3px solid gray;
`

_document.js and _app.js

Your pages get wrapped in two components, _document.js and _app.js. You can override both files. The document contains everything that is outside of your react app, especially the html and body tag. This is a good place to set styles on these or to define the language. The document is rendered on the server only.

The app is the entrypoint of your page and is rendered client-side as well. You can add global providers or import css files here.

Listening to Scroll & Resize

It is possible to listen to scroll and resize events as a very very (!!) last resort for responsive design, e.g. if media queries are insufficient. Use useEffect to accomplish this task:

import React from 'react'
import styled from 'styled-components'

function HelloWorld() {
  const [gray, setGray] = React.useState(false)

  React.useEffect(() => {
    function handleScroll() {
      const scrollY = window.pageYOffset
      setGray(scrollY > 250)
    }

    window.addEventListener('scroll', handleScroll)
    return () => window.removeEventListener('scroll', handleScroll)
  }, [])

  return (
    <BigDiv>
      <Par gray={gray}>Please scroll down a little bit ...</Par>
    </BigDiv>
  )
}

const BigDiv = styled.div`
  height: 4000px;
`

const Par = styled.p<{ gray: boolean }>`
  font-size: 3rem;
  text-align: center;
  margin-top: 500px;
  ${props => (props.gray ? 'color:lightgray;' : '')}
`

export default HelloWorld

This text will gray out if you scroll down. useEffect with an empty dependency array is called once on mount. The return value is called when the component unmounts and will remove the event listener. Set the state directly within the event handler.

Peer dependencies

Here is a list of included peer dependencies:

  • styled-components depends on react-is

FAQ

Is there any css reset?

No, we are not using any css resets. Each component should reset their own styles.

Do I have to vendor prefix my css?

No, styled components takes care of this already.

Can I add external css?

Only if it is absolutely necessary. You are able to import external .css files in pages/_app.js. These stylesheets are always global and included in every page. If possible, use a package that supports styled components.

Some client specific objects (window, document) are causing trouble with server side rendering. What can I do?

Delay these parts of the code after your component mounted, using the useEffect hook:

import React from 'react'
import styled from 'styled-components'

function HelloWorld() {
  const [href, setHref] = React.useState(undefined)

  React.useEffect(() => {
    setHref(window.location.href)
  }, [])

  return href ? <BigDiv>Your site's url is {href}</BigDiv> : null
}

const BigDiv = styled.div`
  text-align: center;
  margin-top: 100px;
`

export default HelloWorld

Using the state is important: This ensures that server side rendering and client side hydration matches up.

How can I detect whether I am serverside or clientside?

The most idomatic way to do this is checking the type of window:

if (typeof window === 'undefined') {
  // serverside
}

A bigger example:

function HelloWorld(props) {
  return <>{JSON.stringify(props.data)}</>
}

HelloWorld.getInitialProps = async () => {
  if (typeof window === 'undefined') {
    const fs = await import('fs')
    const util = await import('util')
    const data = await util.promisify(fs.readFile)('package.json', 'utf-8')
    console.log(data)
    return { data: JSON.parse(data) }
  }
  return {}
}

export default HelloWorld

The fs module is only available in nodejs, but it's ok to use it when you check that you are serverside and load it with a dynamic import. There is also some async/await syntax shown here.

How can I focus an element?

To focus a html element, you need access to the underlying DOM node. Use the ref hook for this.

What does this syntax mean?

JavaScript compilers allow a greater range of syntax. Here is a small cheatsheet.

Destructuring Object

const { title, url } = props
// -->
const title = props.title
const url = props.url

Destructuing Array

const [open, setOpen] = React.useState(false)
// -->
const __temp = React.useState(false)
const open = __temp[0]
const setOpen = __temp[1]

Object Property Shorthand

return { title, content }
// -->
return { title: title, content: content }

String Interpolation

return `The key ${key} can not be found in ${db}.`
// -->
return 'The key ' + key + ' can not be found in ' + db + '.'

JSX

return <Par gray={true}>This is a paragraph</Par>
// -->
return React.createElement(Par, { gray: true }, `This is a paragraph`)

How can I change the state of a sibling?

Generally, you can't and shouldn't. Extract the state to the parent instead and pass change handlers:

import React from 'react'

function HelloWorld() {
  return <Parent />
}

function Parent() {
  const [msg, setMsg] = React.useState('hello')
  return (
    <>
      <Brother setMsg={setMsg} />
      <hr />
      <Sister msg={msg} />
    </>
  )
}

function Brother(props) {
  const { setMsg } = props
  return <button onClick={() => setMsg('Yeah!')}>Click here</button>
}

function Sister(props) {
  const { msg } = props
  return <p>{msg}</p>
}

export default HelloWorld

The brother can pass a message to its sister by declaring the state in the parent. React takes care of updating and rendering.

How can I change the port of the dev server?

You can change the port by running yarn dev --port 8080.

frontend_reasoning_exercise's People

Contributors

entkenntnis avatar elbotho avatar inyono avatar kulla avatar themetalkate avatar

Watchers

James Cloos avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.