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

Rewrite frontend in React #1273

Merged
merged 26 commits into from
Nov 28, 2017
Merged

Rewrite frontend in React #1273

merged 26 commits into from
Nov 28, 2017

Conversation

paulmelnikow
Copy link
Member

@paulmelnikow paulmelnikow commented Nov 13, 2017

I rewrote the frontend in React. It's matched feature-for-feature with the current frontend, with only slight changes in the styling. I did not fuss about making the styling identical; the badge popup looks particularly different.

This makes the front end much easier to develop. I'm really looking forward to implementing #701, to which this paves the way.

This makes light use of Next.js, which I'm using for the first time. I like it. It provides webpack config and dev and build tooling, which worked very well and saved a lot of fussing. I also like the way it handles routing, though we're only using one route right now.


Todo:

  • Test markup modal on rows without previewUri
  • Review setting of BASE_URI in dev / production
  • Set up static build and update deploy scripts
  • Update npm start
  • Add npm postinstall?
  • Refactor / make consistent use of CSS

Deferred:

@paulmelnikow paulmelnikow added the frontend The Docusaurus app serving the docs site label Nov 13, 2017
const Category = ({ category, examples, baseUri, isProductionBuild, onClick }) => (
<div>
<h3 id={category.id}>{ category.name }</h3>
<table className='badge'><tbody>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do the ESLint rules not enforce a consistent attribute quotation style? Line 23 uses " but this line uses '. We should use "" everywhere for JSX attributes.

Also, one tag per line please :)

<table className="badge">
  <tbody>

Copy link
Member

@Daniel15 Daniel15 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good. I've left some comments inline.

This makes light use of Next.js,

Any particular advantages of Next.js over create-react-app? Does Next.js have any runtime cost/overhead, or is it purely a dev tool?

baseUri: PropTypes.string.isRequired,
isProductionBuild: PropTypes.bool.isRequired,
onClick: PropTypes.func.isRequired,
};
Copy link
Member

@Daniel15 Daniel15 Nov 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we should avoid propTypes (which only does runtime type checking and can't easily be statically analyzed) and instead use Flow. Flow lets us check the typing statically, and supports React very well.

url += '&suffix=' + escapeField(this.suffixInput.value);
url += '&query=' + this.queryInput.value;
url += '&uri=' + escapeField(this.uriInput.value);
return url;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about using an object of key-value pairs for this? Something like:

buildURI({
  colorB: this.state.colorInput,
  prefix: this.state.prefixInput,
  // ...
})

</datalist>
<input
className="short"
ref={input => { this.labelInput = input; }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not really the "React way" of doing things. React components should rarely directly touch the raw DOM elements. Instead of doing this, you should use controlled components (https://reactjs.org/docs/forms.html)

For example:

<input
  className="short"
  value={this.state.label}
  onChange={evt => this.setState({label: evt.target.value})
/>

export default class MarkupModal extends React.Component {
constructor (props) {
super(props);
this.state = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use class properties to set this, rather than doing it in the constructor:

export default class MarkupModal extends React.Component {
  state = {
    // ...
  };
}

Note: This requires the appropriate Babel preset to be enabled (it should be)


return (
<Modal
isOpen={this.state.isOpen}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isOpen={this.props.example !== null} would avoid the need to have isOpen in state.

Setting state based on props is often an anti-pattern, when the value could just be derived from the props directly.

<h2 id="styles">Styles</h2>

<p>
The following styles are available (flat is the default as of Feb 1st 2015):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove "as of Feb 1st 2015", not really needed any more.

</tr>
<tr>
<td><img src={baseUri + "/badge/style-social-green.svg?style=social"} alt="" /></td>
<td><code>https://img.shields.io/badge/style-social-green.svg?style=social</code></td>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a lot of repetition here. Consider iterating over an array, roughly like:

const styles = [
  'plastic',
  'flat',
  'flat-square',
  ......
];

<table className="badge-img"><tbody>
{styles.map(style => (
      <tr>
        <td><img src={baseUri + '/badge/style-' + style + '-green.svg?style=' + style} alt="" /></td>
        <td><code>https://img.shields.io/badge/style-{style}-green.svg?style={style}</code></td>
      </tr>
))}
</tbody></table>

<td><code>https://img.shields.io/badge/style-flat--square-green.svg?style=flat-square</code></td>
</tr>
<tr>
<td><img src={baseUri + "/badge/style-for--the--badge-green.svg?style=for-the-badge"} alt="" /></td>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use single quotes for strings (`baseUri + '/badge/......)

<tr><td><code>?logoWidth=40</code></td>
<td>Set the horizontal space to give to the logo</td></tr>
<tr><td><code>?link=http://left&amp;link=http://right</code></td>
<td>Specify what clicking on the left/right of a badge should do (esp. for
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation here is a bit weird.

pages/index.js Outdated
isProductionBuild={isProductionBuild} />
<a
href="https://gratipay.com/Shields/"
style={{ textDecoration: 'none', color: 'rgba(0,0,0,0.1)' }}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use CSS file for this? CSS files are better for perf. If you do want to switch to using "CSS in JS", consider using a library like Glamor.

@paulmelnikow
Copy link
Member Author

Thanks for the review! Will address + respond to these soon.

Copy link
Member Author

@paulmelnikow paulmelnikow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have addressed most of these. What's left is the CSS, which I'm going to refactor now, and a few items I want to defer which I listed in the top post.

paulmelnikow added a commit that referenced this pull request Nov 25, 2017
- Support single-server testing and a local dev server (like Next) that is on a different port from the shields server
- Refactor config schema

With this change, the suggestions work locally in #1273.
@paulmelnikow paulmelnikow changed the title [WIP] Rewrite frontend in React Rewrite frontend in React Nov 26, 2017
@paulmelnikow
Copy link
Member Author

Any particular advantages of Next.js over create-react-app? Does Next.js have any runtime cost/overhead, or is it purely a dev tool?

Here's what I've been able to discern:

  • Routing and link support
  • Hot loading
  • Server side rendering
  • Bundle splitting and efficient loading
  • No boilerplate
  • Good support for customizing configuration without additional boilerplate
  • Easy upgrades. The interface surface between app and Next is small and well defined.
  • Comes with styled-jsx

It does have a runtime library.

@paulmelnikow
Copy link
Member Author

I am not thrilled about the size of the built website. I'd be fine with something like 100k. This is clocking in around 400k. I did some profiling and it seems like this is substantially attributable to Next.js overhead, which is difficult to justify in a site this small. Otherwise I think we'd be between 50 and 100k.

@Daniel15 mentioned CRA and Gatsby which are good suggestions. Let's experiment with those and address in a follow-up.

Copy link
Member

@Daniel15 Daniel15 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me!

This is clocking in around 400k.

Wow, that's pretty big for a site that's essentially one page. The shields site likely doesn't need many of the features Next provides. create-react-app is probably a good fit, or just manually configure Webpack (which shouldn't be too difficult given the relative simplicity of the site).

.travis.yml Outdated
@@ -17,7 +17,7 @@ before_script:
script:
- npm run lint
- npm run test:js
- npm run build:production
- if [ "$TRAVIS_NODE_VERSION" == "6" ]; then echo "Skipping build on Node 6."; else make website; fi
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of blacklisting Node 6, what about whitelisting just the one Node version you want to perform the site build on?

Even better, this could be switched to CircleCI and you could use a separate build job for the site, with whichever Node.js version you like.

I guess this is just a temporary transition state though, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems safer to blacklist Node 6, because if we upgrade beyond Node 8 this would just stop running. Since I added check-node-version I could replace this with a semver expression, which expresses the intent even better.

Yea, it's just temporary until we get Node 8 on the server.

And tell us, we might be able to bring it to you anyway!
</p>
<p>
<object
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why <object> rather than <img>?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes embedded links work! Probably that belongs in a Shields FAQ somewhere…

@paulmelnikow paulmelnikow merged commit 4b5bf03 into badges:master Nov 28, 2017
@paulmelnikow paulmelnikow deleted the react-frontend branch November 28, 2017 16:34
@paulmelnikow
Copy link
Member Author

Let’s open new issues for bugs, and track follow-ups here: https://github.com/badges/shields/projects/1

@gempain
Copy link

gempain commented Dec 5, 2017

Great job @paulmelnikow it's nice to see the effort ! Keep up the great work !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
frontend The Docusaurus app serving the docs site
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants