Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanwiemer committed Jan 18, 2018
2 parents e75ed0e + 53c05d0 commit c244642
Show file tree
Hide file tree
Showing 15 changed files with 314 additions and 15 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ Websites built with Gatsby:
* [Verious](https://www.verious.io/) ([source](https://github.com/cpinnix/verious))
* [Developer Ecosystem](https://www.developerecosystem.com/)
* [Steve Meredith's Portfolio](http://www.steveeeie.io/)
* [Landing page of Put.io](https://put.io/)
* [Ryan Wiemer's Portfolio](https://www.ryanwiemer.com)
([source](https://github.com/ryanwiemer/rw))

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title: How Boston.gov used Gatsby to be selected as an Amazon HQ2 candidate city
date: "2018-01-18"
author: "Sam Bhagwat"
---

[Boston.gov](http://boston.gov) is a small team with a huge responsibility: ten people tasked with the digital presence of New England’s most iconic city.

Today, the team and the city scored a huge victory when Amazon announced that Boston was [selected as one among 20 candidate cities](https://www.amazon.com/b?node=17044620011) for its North America HQ2.

![Amazon Boston homepage](./amazon-boston.jpg "Boston city")

When Amazon announced in September that it was looking to build a new headquarters, bringing 50,000 jobs and billions of investment to the chosen city, Boston’s city government jumped to throw their hat in the ring.

As a technology hub, the city wanted to put their best digital foot forward, so they turned to the Boston.gov team to build a website as the city’s application — [amazon.boston.gov](http://amazon.boston.gov) .

With a compressed application process, the Boston.gov team had a week and a half to build two websites -- a public-facing site, as well as a password-protected site with additional information.

They needed tools that let them get off the ground immediately, iterate quickly, and build something beautiful -- so they turned to Gatsby.

![Amazon Boston homepage](./amazon-boston-3.jpg "Mayor Martin J. Walsh")

"Within 10 minutes, we had our environment set up, with the website building and deploying,” said Matthew Crist, lead engineer for the project.

With Gatsby, the team was able to complete the website from basic comps in around a week, compared to about a month for similar projects in the past. “We generally haven’t been able to do anything as fast,” Crist said.

As an engineer, Crist really appreciated Gatsby’s out-of-the-box hot reloading support.
“The fact that you can save your code, tab back to your browser, and see the change sped up development so much,” Crist said.

And the whole team appreciated Gatsby’s support for component libraries.

“We were able to use our pattern library that we’ve built for Boston.gov,” said Crist. “Our city’s website is nothing but components, stacked together to create a page. [amazon.boston.gov](http://amazon.boston.gov) is the same.”

“Our pattern library has already been A/B tested, and stakeholders are already bought in,” said Crystal Torman, an economic development advisor for Boston, and the project’s internal client. “We could make just one decision about website styling and then spend more time drilling into the content.”

"Crystal and I were probably on the phone straight for two days, giving feedback,” said Reilly Zlab, the product manager.

“Normally we don’t do so many iterations in such a short time,” laughs Torman.

![Amazon Boston homepage](./amazon-boston-2.jpg "Boston city")

The iterations paid off.

"Both internally and externally we’ve heard overwhelmingly positive feedback,” Torman says. "People here love the website."

Apparently, so did Amazon.
5 changes: 4 additions & 1 deletion docs/blog/author.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,7 @@
bio: Lives and works in Provo, UT doing UX research for GatsbyJS, music teaching / performance, and keeping up with her dog’s Instagram fans @dgtrwatson. Her background is in persuasive writing & speaking and she enjoys teaching college-level writing courses on the side.
avatar: avatars/shannon-soper.jpg
twitter: "@shannonb_ux"

- id: Sam Bhagwat
bio: Bay Area adoptee. Programmer, econ nerd, startup enthusiast
avatar: avatars/sam-bhagwat.jpg
twitter: "@calcsam"
Binary file added docs/blog/avatars/sam-bhagwat.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/gatsby/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "gatsby",
"description": "React.js Static Site Generator",
"version": "1.9.158",
"version": "1.9.159",
"author": "Kyle Mathews <[email protected]>",
"bin": {
"gatsby": "./dist/bin/gatsby.js"
Expand Down
6 changes: 4 additions & 2 deletions packages/gatsby/src/schema/infer-graphql-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -589,10 +589,12 @@ export function inferObjectStructureFromNodes({
// pointing to a file (from another file).
} else if (
nodes[0].internal.type !== `File` &&
((_.isString(value) && shouldInferFile(nodes, nextSelector, value)) ||
((_.isString(value) &&
!_.isEmpty(value) &&
shouldInferFile(nodes, nextSelector, value)) ||
(_.isArray(value) &&
value.length === 1 &&
_.isString(value[0]) &&
!_.isEmpty(value[0]) &&
shouldInferFile(nodes, `${nextSelector}[0]`, value[0])))
) {
inferredField = inferFromUri(key, types, _.isArray(value))
Expand Down
28 changes: 18 additions & 10 deletions www/src/components/navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Link from "gatsby-link"
import GithubIcon from "react-icons/lib/go/mark-github"
import TwitterIcon from "react-icons/lib/fa/twitter"

import SearchForm from "../components/search-form"
import DiscordIcon from "../components/discord"
import logo from "../gatsby-negative.svg"
import typography, { rhythm, scale } from "../utils/typography"
Expand Down Expand Up @@ -117,30 +118,31 @@ export default ({ pathname }) => {
<Link
to="/"
css={{
alignItems: `center`,
color: `inherit`,
display: `inline-block`,
display: `flex`,
textDecoration: `none`,
marginRight: rhythm(0.5),
}}
>
<img
src={logo}
css={{
display: `inline-block`,
height: rhythm(1.2),
width: rhythm(1.2),
margin: 0,
marginRight: rhythm(2 / 4),
verticalAlign: `middle`,
}}
alt=""
/>
<h1
css={{
...scale(2 / 5),
display: `inline-block`,
margin: 0,
verticalAlign: `middle`,
display: `none`,
[presets.Mobile]: {
display: `block`,
},
}}
>
Gatsby
Expand All @@ -150,10 +152,15 @@ export default ({ pathname }) => {
css={{
display: `none`,
[presets.Tablet]: {
display: `block`,
display: `flex`,
margin: 0,
padding: 0,
listStyle: `none`,
flexGrow: 1,
overflowX: `auto`,
maskImage: `linear-gradient(to right, transparent, white ${rhythm(
1 / 8
)}, white 98%, transparent)`,
},
}}
>
Expand All @@ -165,12 +172,13 @@ export default ({ pathname }) => {
</ul>
<div
css={{
marginLeft: isHomepage ? rhythm(1 / 2) : `auto`,
[presets.Phablet]: {
marginLeft: isHomepage ? `auto` : `auto`,
},
display: `flex`,
marginLeft: `auto`,
}}
>
{!isHomepage && (
<SearchForm key="SearchForm" styles={{ ...navItemStyles }} />
)}
<a
href="https://github.com/gatsbyjs/gatsby"
title="GitHub"
Expand Down
231 changes: 231 additions & 0 deletions www/src/components/search-form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
import React, { Component } from "react"
import PropTypes from "prop-types"
import { navigateTo } from "gatsby-link"
import { rhythm } from "../utils/typography"
import presets from "../utils/presets"

import { css } from "glamor"

// Override default search result styles
css.global(`.searchWrap .algolia-docsearch-suggestion--highlight`, {
backgroundColor: `${presets.lightPurple} !important`,
boxShadow: `inset 0 -2px 0 0 ${presets.lightPurple} !important`,
color: `black`,
fontWeight: `bold`,
})
css.global(`.searchWrap .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column`, {
width: `100% !important`,
})
css.global(`.searchWrap .algolia-docsearch-suggestion--subcategory-column-text:after`, {
display: `none`,
})
css.global(
`.searchWrap .algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion:not(.suggestion-layout-simple) .algolia-docsearch-suggestion--content`,
{ backgroundColor: `${presets.brandLighter} !important` }
)
css.global(
`.searchWrap .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content.algolia-docsearch-suggestion--no-results`,
{
maxWidth: `100%`,
paddingLeft: `0 !important`,
width: `100% !important`,
}
)
css.global(
`.searchWrap .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content.algolia-docsearch-suggestion--no-results:before`,
{ display: `none !important` }
)
css.global(`.searchWrap .algolia-autocomplete .ds-dropdown-menu`, {
position: `fixed !important`,
top: `${rhythm(2)} !important`,
left: `${rhythm(0.5)} !important`,
right: `${rhythm(0.5)} !important`,
minWidth: `calc(100vw - ${rhythm(1)})`,
maxWidth: `calc(100vw - 2rem)`,
maxHeight: `calc(100vh - 5rem)`,
display: `block`,
})
css.global(
`.searchWrap .algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu, .algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu`,
{
left: `${rhythm(0.5)} !important`,
right: `${rhythm(0.5)} !important`,
}
)
css.global(
`.searchWrap .algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu::before`,
{
right: rhythm(5),
}
)
css.global(
`.searchWrap .algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu::before`,
{
left: rhythm(7),
}
)

// use css.insert() for media query with global CSS
css.insert(`@media ${presets.phablet}{
.searchWrap .algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column {
font-weight: 400;
width: 30% !important;
text-align: right;
opacity: 1;
padding: ${rhythm(0.5)} ${rhythm(1)} ${rhythm(0.5)} 0;
}
.searchWrap .algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column:before {
content: "";
position: absolute;
display: block !important;
top: 0;
height: 100%;
width: 1px;
background: #ddd;
right: 0;
}
.searchWrap .algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column:after {
display: none;
}
.searchWrap .algolia-autocomplete .algolia-docsearch-suggestion--content {
width: 70% !important;
max-width: 70%;
display: block;
padding: ${rhythm(0.5)} 0 ${rhythm(0.5)} ${rhythm(1)} !important;
}
.searchWrap .algolia-autocomplete .algolia-docsearch-suggestion--content:before {
content: "";
position: absolute;
display: block !important;
top: 0;
height: 100%;
width: 1px;
background: #ddd;
left: -1px;
}
}`)

css.insert(`@media ${presets.tablet}{
.searchWrap .algolia-autocomplete .ds-dropdown-menu {
top: 100% !important;
position: absolute !important;
max-width: 600px !important;
min-width: 500px !important;
}
.searchWrap .algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu {
right: 0 !important;
left: inherit !important;
}
.searchWrap .algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu::before {
right: ${rhythm(2)};
}
}`)

class SearchForm extends Component {
constructor() {
super()
this.state = { enabled: true }
}

/**
* Replace the default selection event, allowing us to do client-side
* navigation thus avoiding a full page refresh.
*
* Ref: https://github.com/algolia/autocomplete.js#events
*/
autocompleteSelected(e) {
e.stopPropagation()
// Use an anchor tag to parse the absolute url (from autocomplete.js) into a relative url
// eslint-disable-next-line no-undef
const a = document.createElement(`a`)
a.href = e._args[0].url
navigateTo(`${a.pathname}${a.hash}`)
}

componentDidMount() {
if (
typeof window === `undefined` || // eslint-disable-line no-undef
typeof window.docsearch === `undefined` // eslint-disable-line no-undef
) {
console.warn(`Search has failed to load and now is being disabled`)
this.setState({ enabled: false })
return
}

// eslint-disable-next-line no-undef
window.addEventListener(
`autocomplete:selected`,
this.autocompleteSelected,
true
)

// eslint-disable-next-line no-undef
window.docsearch({
apiKey: `71af1f9c4bd947f0252e17051df13f9c`,
indexName: `gatsbyjs`,
inputSelector: `#doc-search`,
debug: false,
})
}

render() {
const { enabled } = this.state
const { styles } = this.props.styles
return enabled ? (
<form
css={{
...styles,
display: `flex`,
flex: `0 0 auto`,
flexDirection: `row`,
alignItems: `center`,
marginLeft: rhythm(1 / 2),
marginBottom: 0,
}}
className="searchWrap"
>
<input
id="doc-search"
css={{
appearance: `none`,
background: `transparent`,
border: 0,
color: presets.brand,
paddingTop: rhythm(1 / 8),
paddingRight: rhythm(1 / 4),
paddingBottom: rhythm(1 / 8),
paddingLeft: rhythm(1),
backgroundImage: `url(/search.svg)`,
backgroundSize: `16px 16px`,
backgroundRepeat: `no-repeat`,
backgroundPositionY: `center`,
backgroundPositionX: `5px`,
overflow: `hidden`,
width: rhythm(1),
transition: `width 0.2s ease`,

":focus": {
outline: 0,
backgroundColor: presets.brandLighter,
borderRadius: presets.radiusLg,
width: rhythm(5),
},

[presets.Desktop]: {
width: rhythm(5),
},
}}
type="search"
placeholder="Search docs"
aria-label="Search docs"
/>
</form>
) : null
}
}

SearchForm.propTypes = {
styles: PropTypes.object,
}

export default SearchForm
Loading

0 comments on commit c244642

Please sign in to comment.