Commit 3d40ab56 authored by Kristian Freeman's avatar Kristian Freeman

Add SFCs exercise

parent c0d59ff8
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env
npm-debug.log*
yarn-debug.log*
yarn-error.log*
default:
CI=true yarn run test
run:
yarn run start
This diff is collapsed.
{
"name": "sfcs",
"version": "0.1.0",
"private": true,
"dependencies": {
"enzyme": "^2.9.1",
"faker": "^4.1.0",
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-test-renderer": "^15.6.1"
},
"devDependencies": {
"react-scripts": "0.9.5"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<!--
Notice the use of %PUBLIC_URL% in the tag above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body style="margin: 0; font-family: sans-serif;">
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start`.
To create a production bundle, use `npm run build`.
-->
</body>
</html>
import React from "react";
import Header from "./Header";
import Post from "./Post";
import Sidebar from "./Sidebar";
// Make sure to visit src/Provider.js at the beginning
// of this exercise!
//
// Once you've done that, we can come back and rewrite the
// Blog component as a stateless functional component, or
// SFC. SFCs are written as arrow functions, with a single
// argument: a props object. The function should return
// HTML, similar to the render function in a class-based
// component. For instance:
//
// const MyComponent = props => <h1>Hello!</h1>
//
// Remember that a props object is a great opportunity
// to use destructuring. For instance, given a props object
// with a key count:
//
// const MyComponent = ({ count }) =>
// <h1>Count is {count}!</h1>
//
// Given the data passed into the Blog component by its new
// parent Provider, use destructuring to replace any previous
// references to blogTitle, blogAuthor, and posts from state,
// inside of your SFC.
//
// Once you've done this, proceed to each component and update
// it to an SFC as well.
export default class Blog extends React.Component {
constructor() {
super();
this.setState({
blogTitle: null,
blogAuthor: null,
posts: []
});
}
componentDidMount() {
const url = "https://bytesized-training-assets.herokuapp.com/blog.json";
fetch(url).then(resp =>
resp.json().then(json => {
this.setState({
blogTitle: json.blogTitle,
blogAuthor: json.blogAuthor,
posts: json.posts
});
})
);
}
render() {
const { blogTitle, blogAuthor, posts } = this.state;
return (
<div>
<Header blogTitle={blogTitle} blogAuthor={blogAuthor} />
<div style={{ display: "flex" }}>
<div style={{ width: "75%" }}>
{posts.map((item, index) => (
<Post key={index} post={item} />
))}
</div>
<div style={{ height: "100%", width: "25%" }}>
<Sidebar posts={posts} />
</div>
</div>
</div>
);
}
}
import React from "react";
// This component should be rewritten as an SFC, or
// stateless functional component. Note the
// props being passed in currently, and rewrite
// the component to have these provided as part of
// the "props" function argument for your new SFC.
// If you're stuck, re-read the comments in src/Blog.js!
export default class Comment extends React.Component {
render() {
const {
comment: { commentAuthor, commentBody, postDate }
} = this.props;
return (
<div>
<h5>{commentAuthor}</h5>
<p>{commentBody}</p>
<p>
<small>{postDate}</small>
</p>
</div>
);
}
}
import React from 'react';
import ReactDOM from 'react-dom';
import { shallow } from 'enzyme';
import Comment from './Comment';
const comment = { commentAuthor: "Kristian", commentBody: "Hi", postDate: new Date().toString() }
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<Comment comment={comment} />, div);
});
it('<Comment> renders props', () => {
const wrapper = shallow(
<Comment comment={comment} />
);
expect(wrapper.text().includes("Kristian")).toEqual(true);
expect(wrapper.text().includes("Hi")).toEqual(true);
});
import React from "react";
// This component should be rewritten as an SFC, or
// stateless functional component. Note the
// props being passed in currently, and rewrite
// the component to have these provided as part of
// the "props" function argument for your new SFC.
// If you're stuck, re-read the comments in src/Blog.js!
export default class Header extends React.Component {
render() {
const { blogTitle, blogAuthor } = this.props;
return (
<div style={{ background: "#eee", padding: "1em 4em" }}>
<h1>{blogTitle}</h1>
<h2>{blogAuthor}</h2>
</div>
);
}
}
import React from 'react';
import ReactDOM from 'react-dom';
import { shallow } from 'enzyme';
import Header from './Header';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<Header />, div);
});
it('<Header> renders props', () => {
const wrapper = shallow(
<Header blogAuthor="Kristian" blogTitle="Hi" />
);
expect(wrapper.text().includes("Kristian")).toEqual(true);
expect(wrapper.text().includes("Hi")).toEqual(true);
});
import React from "react";
import Comment from "./Comment";
// This component should be rewritten as an SFC, or
// stateless functional component. Note the
// props being passed in currently, and rewrite
// the component to have these provided as part of
// the "props" function argument for your new SFC.
// If you're stuck, re-read the comments in src/Blog.js!
export default class Post extends React.Component {
render() {
const {
post: { postTitle, postContent, comments }
} = this.props;
return (
<div style={{ padding: "2em" }}>
<h1>{postTitle}</h1>
<p>{postContent}</p>
{comments.map((comment, index) => (
<Comment key={index} comment={comment} />
))}
</div>
);
}
}
import React from "react";
import Blog from "./Blog";
// Consider the idea of "presentational" components
// versus what is commonly referred to as a "container":
// a component that requests or manages data.
//
// For instance, a Provider component, as has begun to
// be defined below, could replace the Blog componentDidMount
// functionality, by storing all the relevant blog data in
// state, and simply passing it down to the Blog component.
//
// In doing so, the Blog component becomes another presentational
// component, like the subcomponents that it renders in its
// component tree.
//
// Let's try it! Move any data retrieving or storage - that is,
// anything related to the Blog component's state - into the
// Provider component. That includes the constructor setup,
// componentDidMount, and the destructuring inside of the
// render function.
//
// Once you've done this, make sure to go into src/index.js and
// define Provider as the "root" component for your application.
// If you don't do this, your whole app will stop working!
//
// Inside of Provider, render the Blog component and pass
// in the relevant data as props - that means everything inside
// of state. Once you've done this, you'll be able to rewrite
// every component inside of the Blog component tree as a
// stateless functional component.
export default class Provider extends React.Component {}
import React from "react";
// This component should be rewritten as an SFC, or
// stateless functional component. Note the
// props being passed in currently, and rewrite
// the component to have these provided as part of
// the "props" function argument for your new SFC.
// If you're stuck, re-read the comments in src/Blog.js!
export default class Sidebar extends React.Component {
render() {
const { posts } = this.props;
return (
<div style={{ background: "#eee", padding: "1em" }}>
<h3>Recent posts</h3>
<ul>
{posts.map(({ postDate, postTitle }, index) => (
<li key={index}>
{postTitle} | {postDate}
</li>
))}
</ul>
</div>
);
}
}
import React from 'react';
import ReactDOM from 'react-dom';
import { shallow } from 'enzyme';
import Sidebar from './Sidebar';
const posts = [{ postTitle: "Hi" }];
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<Sidebar posts={posts} />, div);
});
it('<Sidebar> renders props', () => {
const wrapper = shallow(
<Sidebar posts={posts} />
);
expect(wrapper.text().includes("Hi")).toEqual(true);
});
import React from "react";
import ReactDOM from "react-dom";
import Blog from "./Blog";
const root = document.getElementById("root");
if (root) {
ReactDOM.render(<Blog />, root);
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment