Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor frontend main page and badge-example code #2441

Merged
merged 29 commits into from
Dec 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c9ba4dd
Minor readability improvements; null -> undefined
paulmelnikow Dec 2, 2018
8126a1f
Get badge listing working
paulmelnikow Dec 2, 2018
e4379d9
Markup modal working
paulmelnikow Dec 3, 2018
c7cad70
Fix some problems with the service definition export
paulmelnikow Dec 3, 2018
30f2732
`longCache` is not really needed with static badges
paulmelnikow Dec 3, 2018
4c7f351
Suggestions working
paulmelnikow Dec 3, 2018
6a0d3e3
examples-page -> main
paulmelnikow Dec 3, 2018
79057ba
Clean diff
paulmelnikow Dec 3, 2018
13fcbda
Fix tests, remove dead code
paulmelnikow Dec 3, 2018
564353a
Fix suggest tests
paulmelnikow Dec 3, 2018
1b5a10f
Remove obsolete code
paulmelnikow Dec 3, 2018
37bfedd
Add test coverage for server code changes
paulmelnikow Dec 3, 2018
bf4db6c
Clean lint
paulmelnikow Dec 3, 2018
866bba7
Avoid a runtime warning about uncontrolled components
paulmelnikow Dec 3, 2018
11b4bcb
Remove unused dependency
paulmelnikow Dec 3, 2018
9970aa9
Remove dead code
paulmelnikow Dec 3, 2018
debfa46
Silence the linter
paulmelnikow Dec 3, 2018
ea2f1a4
Try to fix the base URL on Heroku
paulmelnikow Dec 3, 2018
b475529
Restore section headings in search results
paulmelnikow Dec 3, 2018
972ec69
Merge branch 'master' into frontend-example-refactor-2
paulmelnikow Dec 4, 2018
c45fc6e
Rm unused helper method, fix tests
paulmelnikow Dec 4, 2018
1d31b62
Update frontend/components/main.js
chris48s Dec 8, 2018
79fbbe9
Merge branch 'master' into frontend-example-refactor-2
paulmelnikow Dec 8, 2018
1a8feaf
Fix issue with documentation not rendering
paulmelnikow Dec 8, 2018
4409563
Rm console.log
paulmelnikow Dec 8, 2018
63747a4
Add link to service def schema
paulmelnikow Dec 8, 2018
5240559
asNative -> toArray
paulmelnikow Dec 8, 2018
9fba1aa
Fix LGTM warning
paulmelnikow Dec 8, 2018
74741f7
Try to fix another LGTM. was this a prettier induced error?
paulmelnikow Dec 8, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc-frontend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ settings:
rules:
no-console: "error"

import/extensions: ["error", "never", { "json": "always" }]
import/extensions: ["error", "never", { "json": "always", "yml": "always" }]
3 changes: 3 additions & 0 deletions doc/TUTORIAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,9 @@ module.exports = class GemVersion extends BaseJsonService {

Save, run `npm start`, and you can see it [locally](http://127.0.0.1:3000/).

If you update `examples`, you don't have to restart the server. Run `npm run
defs` in another terminal window and the frontend will update.

### (4.5) Write Tests <!-- Change the link below when you change the heading -->
[write tests]: #45-write-tests

Expand Down
205 changes: 74 additions & 131 deletions frontend/components/badge-examples.js
Original file line number Diff line number Diff line change
@@ -1,149 +1,92 @@
import React from 'react'
import { Link } from 'react-router-dom'
import PropTypes from 'prop-types'
import classNames from 'classnames'
import resolveBadgeUrl from '../lib/badge-url'
import { badgeUrlFromPath, staticBadgeUrl } from '../../lib/make-badge-url'

const Badge = ({
title,
exampleUrl,
previewUrl,
urlPattern,
documentation,
baseUrl,
longCache,
shouldDisplay = () => true,
onClick,
}) => {
const handleClick = onClick
? () =>
onClick({
title,
exampleUrl,
previewUrl,
urlPattern,
documentation,
})
: undefined
export default class BadgeExamples extends React.Component {
static propTypes = {
definitions: PropTypes.array.isRequired,
baseUrl: PropTypes.string,
longCache: PropTypes.bool.isRequired,
onClick: PropTypes.func.isRequired,
}

renderExample(exampleData) {
const { baseUrl, longCache, onClick } = this.props
const { title, example, preview } = exampleData

let previewUrl
// There are two alternatives for `preview`. Refer to the schema in
// `services/service-definitions.js`.
if (preview.label !== undefined) {
const { label, message, color } = preview
previewUrl = staticBadgeUrl({ baseUrl, label, message, color })
} else {
const { path, queryParams } = preview
previewUrl = badgeUrlFromPath({ baseUrl, path, queryParams, longCache })
}

// There are two alternatives for `example`. Refer to the schema in
// `services/service-definitions.js`.
let exampleUrl
if (example.pattern !== undefined) {
const { pattern, namedParams, queryParams } = example
exampleUrl = badgeUrlFromPath({
baseUrl,
path: pattern,
namedParams,
queryParams,
})
} else {
const { path, queryParams } = example
exampleUrl = badgeUrlFromPath({ baseUrl, path, queryParams })
}

const key = `${title} ${previewUrl} ${exampleUrl}`

const previewImage = previewUrl ? (
<img
className={classNames('badge-img', { clickable: onClick })}
onClick={handleClick}
src={resolveBadgeUrl(previewUrl, baseUrl, { longCache })}
alt=""
/>
) : (
'\u00a0'
) // non-breaking space
const resolvedExampleUrl = resolveBadgeUrl(
urlPattern || previewUrl,
baseUrl,
{ longCache: false }
)
const handleClick = () => onClick(exampleData)

if (shouldDisplay()) {
return (
<tr>
<th
className={classNames({ clickable: onClick })}
onClick={handleClick}
>
<tr key={key}>
<th className="clickable" onClick={handleClick}>
{title}:
</th>
<td>{previewImage}</td>
<td>
<code
className={classNames({ clickable: onClick })}
<img
className="badge-img clickable"
onClick={handleClick}
>
{resolvedExampleUrl}
src={previewUrl}
alt=""
/>
</td>
<td>
<code className="clickable" onClick={handleClick}>
{exampleUrl}
</code>
</td>
</tr>
)
}
return null
}
Badge.propTypes = {
title: PropTypes.string.isRequired,
exampleUrl: PropTypes.string,
previewUrl: PropTypes.string,
urlPattern: PropTypes.string,
documentation: PropTypes.string,
baseUrl: PropTypes.string,
longCache: PropTypes.bool.isRequired,
shouldDisplay: PropTypes.func,
onClick: PropTypes.func.isRequired,
}

const Category = ({ category, examples, baseUrl, longCache, onClick }) => {
if (examples.filter(example => example.shouldDisplay()).length === 0) {
return null
}
return (
<div>
<Link to={`/examples/${category.id}`}>
<h3 id={category.id}>{category.name}</h3>
</Link>
<table className="badge">
<tbody>
{examples.map(badgeData => (
<Badge
key={badgeData.key}
{...badgeData}
baseUrl={baseUrl}
longCache={longCache}
onClick={onClick}
/>
))}
</tbody>
</table>
</div>
)
}
Category.propTypes = {
category: PropTypes.shape({
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
}).isRequired,
examples: PropTypes.arrayOf(
PropTypes.shape({
title: PropTypes.string.isRequired,
exampleUrl: PropTypes.string,
previewUrl: PropTypes.string,
urlPattern: PropTypes.string,
documentation: PropTypes.string,
})
).isRequired,
baseUrl: PropTypes.string,
longCache: PropTypes.bool.isRequired,
onClick: PropTypes.func.isRequired,
}
render() {
const { definitions } = this.props

const BadgeExamples = ({ categories, baseUrl, longCache, onClick }) => (
<div>
{categories.map((categoryData, i) => (
<Category
key={i}
{...categoryData}
baseUrl={baseUrl}
longCache={longCache}
onClick={onClick}
/>
))}
</div>
)
BadgeExamples.propTypes = {
categories: PropTypes.arrayOf(
PropTypes.shape({
category: Category.propTypes.category,
examples: Category.propTypes.examples,
})
),
baseUrl: PropTypes.string,
longCache: PropTypes.bool.isRequired,
onClick: PropTypes.func.isRequired,
}
if (!definitions) {
return null
}

const flattened = definitions.reduce((accum, current) => {
const { examples } = current
return accum.concat(examples)
}, [])

export { Badge, BadgeExamples }
return (
<div>
<table className="badge">
<tbody>
{flattened.map(exampleData => this.renderExample(exampleData))}
</tbody>
</table>
</div>
)
}
}
32 changes: 32 additions & 0 deletions frontend/components/category-headings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Link } from 'react-router-dom'

const CategoryHeading = ({ category }) => {
const { id, name } = category

return (
<Link to={`/examples/${id}`}>
<h3 id={id}>{name}</h3>
</Link>
)
}
CategoryHeading.propTypes = {
category: PropTypes.shape({
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
}).isRequired,
}

const CategoryHeadings = ({ categories }) => (
<div>
{categories.map(category => (
<CategoryHeading category={category} key={category.id} />
))}
</div>
)
CategoryHeadings.propTypes = {
categories: PropTypes.arrayOf(CategoryHeading.propTypes.category).isRequired,
}

export { CategoryHeading, CategoryHeadings }
109 changes: 0 additions & 109 deletions frontend/components/examples-page.js

This file was deleted.

Loading