Continuing the tutorial.
This commit is contained in:
@@ -21,8 +21,10 @@ const Routes = () => {
|
|||||||
<Route path="/posts" page={PostPostsPage} name="posts" />
|
<Route path="/posts" page={PostPostsPage} name="posts" />
|
||||||
</Set>
|
</Set>
|
||||||
<Set wrap={BlogLayout}>
|
<Set wrap={BlogLayout}>
|
||||||
|
<Route path="/article/{id:Int}" page={ArticlePage} name="article" />
|
||||||
<Route path="/about" page={AboutPage} name="about" />
|
<Route path="/about" page={AboutPage} name="about" />
|
||||||
<Route path="/" page={HomePage} name="home" />
|
<Route path="/" page={HomePage} name="home" />
|
||||||
|
<Route path="/contact" page={ContactPage} name="contact" />
|
||||||
</Set>
|
</Set>
|
||||||
<Route notfound page={NotFoundPage} />
|
<Route notfound page={NotFoundPage} />
|
||||||
</Router>
|
</Router>
|
||||||
|
|||||||
17
web/src/components/Article/Article.js
Normal file
17
web/src/components/Article/Article.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { Link, routes } from '@redwoodjs/router';
|
||||||
|
|
||||||
|
const Article = ({article}) => {
|
||||||
|
return (
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<h2>
|
||||||
|
<Link to={routes.article({ id: article.id })}>{article.title}</Link>
|
||||||
|
</h2>
|
||||||
|
</header>
|
||||||
|
<div>{article.body}</div>
|
||||||
|
<div>Posted at: {article.createdAt}</div>
|
||||||
|
</article>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Article
|
||||||
7
web/src/components/Article/Article.stories.js
Normal file
7
web/src/components/Article/Article.stories.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import Article from './Article'
|
||||||
|
|
||||||
|
export const generated = () => {
|
||||||
|
return <Article />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { title: 'Components/Article' }
|
||||||
14
web/src/components/Article/Article.test.js
Normal file
14
web/src/components/Article/Article.test.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { render } from '@redwoodjs/testing/web'
|
||||||
|
|
||||||
|
import Article from './Article'
|
||||||
|
|
||||||
|
// Improve this test with help from the Redwood Testing Doc:
|
||||||
|
// https://redwoodjs.com/docs/testing#testing-components
|
||||||
|
|
||||||
|
describe('Article', () => {
|
||||||
|
it('renders successfully', () => {
|
||||||
|
expect(() => {
|
||||||
|
render(<Article />)
|
||||||
|
}).not.toThrow()
|
||||||
|
})
|
||||||
|
})
|
||||||
24
web/src/components/ArticleCell/ArticleCell.js
Normal file
24
web/src/components/ArticleCell/ArticleCell.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import Article from 'src/components/Article';
|
||||||
|
|
||||||
|
export const QUERY = gql`
|
||||||
|
query FindArticleQuery($id: Int!) {
|
||||||
|
article: post(id: $id) {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
body
|
||||||
|
createdAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const Loading = () => <div>Loading...</div>
|
||||||
|
|
||||||
|
export const Empty = () => <div>Empty</div>
|
||||||
|
|
||||||
|
export const Failure = ({ error }) => (
|
||||||
|
<div style={{ color: 'red' }}>Error: {error.message}</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
export const Success = ({ article }) => {
|
||||||
|
return <Article article={article} />
|
||||||
|
}
|
||||||
6
web/src/components/ArticleCell/ArticleCell.mock.js
Normal file
6
web/src/components/ArticleCell/ArticleCell.mock.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// Define your own mock data here:
|
||||||
|
export const standard = () => ({
|
||||||
|
article: {
|
||||||
|
id: 42,
|
||||||
|
},
|
||||||
|
})
|
||||||
20
web/src/components/ArticleCell/ArticleCell.stories.js
Normal file
20
web/src/components/ArticleCell/ArticleCell.stories.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { Loading, Empty, Failure, Success } from './ArticleCell'
|
||||||
|
import { standard } from './ArticleCell.mock'
|
||||||
|
|
||||||
|
export const loading = () => {
|
||||||
|
return Loading ? <Loading /> : null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const empty = () => {
|
||||||
|
return Empty ? <Empty /> : null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const failure = () => {
|
||||||
|
return Failure ? <Failure error={new Error('Oh no')} /> : null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const success = () => {
|
||||||
|
return Success ? <Success {...standard()} /> : null
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { title: 'Cells/ArticleCell' }
|
||||||
41
web/src/components/ArticleCell/ArticleCell.test.js
Normal file
41
web/src/components/ArticleCell/ArticleCell.test.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { render } from '@redwoodjs/testing/web'
|
||||||
|
import { Loading, Empty, Failure, Success } from './ArticleCell'
|
||||||
|
import { standard } from './ArticleCell.mock'
|
||||||
|
|
||||||
|
// Generated boilerplate tests do not account for all circumstances
|
||||||
|
// and can fail without adjustments, e.g. Float and DateTime types.
|
||||||
|
// Please refer to the RedwoodJS Testing Docs:
|
||||||
|
// https://redwoodjs.com/docs/testing#testing-cells
|
||||||
|
// https://redwoodjs.com/docs/testing#jest-expect-type-considerations
|
||||||
|
|
||||||
|
describe('ArticleCell', () => {
|
||||||
|
it('renders Loading successfully', () => {
|
||||||
|
expect(() => {
|
||||||
|
render(<Loading />)
|
||||||
|
}).not.toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders Empty successfully', async () => {
|
||||||
|
expect(() => {
|
||||||
|
render(<Empty />)
|
||||||
|
}).not.toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders Failure successfully', async () => {
|
||||||
|
expect(() => {
|
||||||
|
render(<Failure error={new Error('Oh no')} />)
|
||||||
|
}).not.toThrow()
|
||||||
|
})
|
||||||
|
|
||||||
|
// When you're ready to test the actual output of your component render
|
||||||
|
// you could test that, for example, certain text is present:
|
||||||
|
//
|
||||||
|
// 1. import { screen } from '@redwoodjs/testing/web'
|
||||||
|
// 2. Add test: expect(screen.getByText('Hello, world')).toBeInTheDocument()
|
||||||
|
|
||||||
|
it('renders Success successfully', async () => {
|
||||||
|
expect(() => {
|
||||||
|
render(<Success article={standard().article} />)
|
||||||
|
}).not.toThrow()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import Article from 'src/components/Article';
|
||||||
|
|
||||||
export const QUERY = gql`
|
export const QUERY = gql`
|
||||||
query ArticlesQuery {
|
query ArticlesQuery {
|
||||||
articles: posts {
|
articles: posts {
|
||||||
@@ -19,16 +21,10 @@ export const Failure = ({ error }) => (
|
|||||||
|
|
||||||
export const Success = ({ articles }) => {
|
export const Success = ({ articles }) => {
|
||||||
return (
|
return (
|
||||||
<ul>
|
<>
|
||||||
{articles.map((article) => (
|
{articles.map((article) => (
|
||||||
<article key={article.id}>
|
<Article key={article.id} article={article} />
|
||||||
<header>
|
|
||||||
<h2>{article.title}</h2>
|
|
||||||
</header>
|
|
||||||
<p>{article.body}</p>
|
|
||||||
<div>Created at: {article.createdAt}</div>
|
|
||||||
</article>
|
|
||||||
))}
|
))}
|
||||||
</ul>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
Label,
|
Label,
|
||||||
TextField,
|
TextField,
|
||||||
Submit,
|
Submit,
|
||||||
|
TextAreaField
|
||||||
} from '@redwoodjs/forms'
|
} from '@redwoodjs/forms'
|
||||||
|
|
||||||
const PostForm = (props) => {
|
const PostForm = (props) => {
|
||||||
@@ -48,7 +49,7 @@ const PostForm = (props) => {
|
|||||||
Body
|
Body
|
||||||
</Label>
|
</Label>
|
||||||
|
|
||||||
<TextField
|
<TextAreaField
|
||||||
name="body"
|
name="body"
|
||||||
defaultValue={props.post?.body}
|
defaultValue={props.post?.body}
|
||||||
className="rw-input"
|
className="rw-input"
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ const BlogLayout = ({ children }) => {
|
|||||||
<li>
|
<li>
|
||||||
<Link to={routes.about()}>About</Link>
|
<Link to={routes.about()}>About</Link>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link to={routes.contact()}>Contact Us</Link>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const PostsLayout = ({ children }) => {
|
|||||||
<div className="rw-button-icon">+</div> New Post
|
<div className="rw-button-icon">+</div> New Post
|
||||||
</Link>
|
</Link>
|
||||||
</header>
|
</header>
|
||||||
<main className="rw-main">{children}</main>
|
<main className="rw-main" style={{marginLeft: 'auto', marginRight: 'auto', width: '50%'}}>{children}</main>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
13
web/src/pages/ArticlePage/ArticlePage.js
Normal file
13
web/src/pages/ArticlePage/ArticlePage.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { MetaTags } from '@redwoodjs/web'
|
||||||
|
import ArticleCell from 'src/components/ArticleCell';
|
||||||
|
|
||||||
|
const ArticlePage = ({id}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<MetaTags title="Article" description="Article page" />
|
||||||
|
<ArticleCell id={id} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ArticlePage
|
||||||
7
web/src/pages/ArticlePage/ArticlePage.stories.js
Normal file
7
web/src/pages/ArticlePage/ArticlePage.stories.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import ArticlePage from './ArticlePage'
|
||||||
|
|
||||||
|
export const generated = () => {
|
||||||
|
return <ArticlePage />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { title: 'Pages/ArticlePage' }
|
||||||
14
web/src/pages/ArticlePage/ArticlePage.test.js
Normal file
14
web/src/pages/ArticlePage/ArticlePage.test.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { render } from '@redwoodjs/testing/web'
|
||||||
|
|
||||||
|
import ArticlePage from './ArticlePage'
|
||||||
|
|
||||||
|
// Improve this test with help from the Redwood Testing Doc:
|
||||||
|
// https://redwoodjs.com/docs/testing#testing-pages-layouts
|
||||||
|
|
||||||
|
describe('ArticlePage', () => {
|
||||||
|
it('renders successfully', () => {
|
||||||
|
expect(() => {
|
||||||
|
render(<ArticlePage />)
|
||||||
|
}).not.toThrow()
|
||||||
|
})
|
||||||
|
})
|
||||||
24
web/src/pages/ContactPage/ContactPage.js
Normal file
24
web/src/pages/ContactPage/ContactPage.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { MetaTags } from '@redwoodjs/web'
|
||||||
|
import { Form, TextField, Submit, TextAreaField } from '@redwoodjs/forms'
|
||||||
|
|
||||||
|
const ContactPage = () => {
|
||||||
|
const onSubmit = (data) => {
|
||||||
|
console.log(data)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<MetaTags title="Contact" description="Contact page" />
|
||||||
|
<Form onSubmit={onSubmit}>
|
||||||
|
<label htmlFor="name">Name</label>
|
||||||
|
<TextField name="name" validation={{required: true}} />
|
||||||
|
<label htmlFor="email">Email</label>
|
||||||
|
<TextField name="email" validation={{required: true}} />
|
||||||
|
<label htmlFor="message">Message</label>
|
||||||
|
<TextAreaField name="message" validation={{required: true}} />
|
||||||
|
<Submit>Save</Submit>
|
||||||
|
</Form>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ContactPage
|
||||||
7
web/src/pages/ContactPage/ContactPage.stories.js
Normal file
7
web/src/pages/ContactPage/ContactPage.stories.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import ContactPage from './ContactPage'
|
||||||
|
|
||||||
|
export const generated = () => {
|
||||||
|
return <ContactPage />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { title: 'Pages/ContactPage' }
|
||||||
14
web/src/pages/ContactPage/ContactPage.test.js
Normal file
14
web/src/pages/ContactPage/ContactPage.test.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { render } from '@redwoodjs/testing/web'
|
||||||
|
|
||||||
|
import ContactPage from './ContactPage'
|
||||||
|
|
||||||
|
// Improve this test with help from the Redwood Testing Doc:
|
||||||
|
// https://redwoodjs.com/docs/testing#testing-pages-layouts
|
||||||
|
|
||||||
|
describe('ContactPage', () => {
|
||||||
|
it('renders successfully', () => {
|
||||||
|
expect(() => {
|
||||||
|
render(<ContactPage />)
|
||||||
|
}).not.toThrow()
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user