Skip to content

Commit

Permalink
[gatsby-link] Add location object as valid to prop (gatsbyjs#3407)
Browse files Browse the repository at this point in the history
* Add tests for location path

* Add location object as valid to prop

* Update tests

* Update typescript type
  • Loading branch information
nsimonson authored and KyleAMathews committed Jan 8, 2018
1 parent f19fc2a commit d458fe7
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 15 deletions.
1 change: 0 additions & 1 deletion packages/gatsby-link/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import * as React from "react";
import { NavLinkProps } from "react-router-dom";

export interface GatsbyLinkProps extends NavLinkProps {
to: string;
onClick?: (event: any) => void
className?: string
}
Expand Down
81 changes: 78 additions & 3 deletions packages/gatsby-link/src/__tests__/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import React from "react"
import ReactDOM from "react-dom"
import { MemoryRouter } from "react-router-dom"

const getInstance = (props, pathPrefix = ``) => {
Object.assign(global.window, {
__PREFIX_PATHS__: pathPrefix ? true : false,
__PATH_PREFIX__: pathPrefix,
})

const context = { router: { history: {} } }

const Link = require(`../`).default
return new Link(props)
return new Link(props, context)
}

const getNavigateTo = () => {
Expand All @@ -27,8 +33,9 @@ const getWithPrefix = (pathPrefix = ``) => {
describe(`<Link />`, () => {
it(`does not fail to initialize when __PREFIX_PATHS__ is not defined`, () => {
expect(() => {
const context = { router: { history: {} } }
const Link = require(`../`).default
const link = new Link({}) //eslint-disable-line no-unused-vars
const link = new Link({}, context) //eslint-disable-line no-unused-vars
}).not.toThrow()
})

Expand All @@ -39,7 +46,7 @@ describe(`<Link />`, () => {
to,
})

expect(instance.state.to).toEqual(to)
expect(instance.state.to.pathname).toEqual(to)
})

/*
Expand All @@ -60,6 +67,74 @@ describe(`<Link />`, () => {
})
})

describe(`the location to link to`, () => {
global.___loader = {
enqueue: jest.fn(),
}

it(`accepts to as a string`, () => {
const location = `/courses?sort=name`

const node = document.createElement(`div`)
const Link = require(`../`).default

ReactDOM.render(
<MemoryRouter>
<Link to={location}>link</Link>
</MemoryRouter>,
node
)

const href = node.querySelector(`a`).getAttribute(`href`)

expect(href).toEqual(location)
})

it(`accepts a location "to" prop`, () => {
const location = {
pathname: `/courses`,
search: `?sort=name`,
hash: `#the-hash`,
state: { fromDashboard: true },
}

const node = document.createElement(`div`)
const Link = require(`../`).default

ReactDOM.render(
<MemoryRouter>
<Link to={location}>link</Link>
</MemoryRouter>,
node
)

const href = node.querySelector(`a`).getAttribute(`href`)

expect(href).toEqual(`/courses?sort=name#the-hash`)
})

it(`resolves to with no pathname using current location`, () => {
const location = {
search: `?sort=name`,
hash: `#the-hash`,
}

const node = document.createElement(`div`)
const Link = require(`../`).default

ReactDOM.render(
<MemoryRouter initialEntries={[`/somewhere`]}>
<Link to={location}>link</Link>
</MemoryRouter>,
node
)

const href = node.querySelector(`a`).getAttribute(`href`)

expect(href).toEqual(`/somewhere?sort=name#the-hash`)
})
})

it(`navigateTo is called with correct args`, () => {
getNavigateTo()(`/some-path`)

Expand Down
35 changes: 24 additions & 11 deletions packages/gatsby-link/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import React from "react"
import { Link, NavLink } from "react-router-dom"
import PropTypes from "prop-types"
import { createLocation as cL, createPath } from "history"

let pathPrefix = `/`
if (typeof __PREFIX_PATHS__ !== `undefined` && __PREFIX_PATHS__) {
Expand All @@ -16,6 +17,12 @@ function normalizePath(path) {
return path.replace(/^\/\//g, `/`)
}

function createLocation(path, history) {
const location = cL(path, null, null, history.location)
location.pathname = withPrefix(location.pathname)
return location
}

const NavLinkPropTypes = {
activeClassName: PropTypes.string,
activeStyle: PropTypes.object,
Expand Down Expand Up @@ -45,37 +52,43 @@ const handleIntersection = (el, cb) => {
}

class GatsbyLink extends React.Component {
constructor(props) {
constructor(props, context) {
super()
// Default to no support for IntersectionObserver
let IOSupported = false
if (typeof window !== `undefined` && window.IntersectionObserver) {
IOSupported = true
}

const { history } = context.router
const to = createLocation(props.to, history)

this.state = {
to: withPrefix(props.to),
path: createPath(to),
to,
IOSupported,
}
this.handleRef = this.handleRef.bind(this)
}

componentWillReceiveProps(nextProps) {
if (this.props.to !== nextProps.to) {
const to = createLocation(nextProps.to, history)
this.setState({
to: withPrefix(nextProps.to),
path: createPath(to),
to,
})
// Preserve non IO functionality if no support
if (!this.state.IOSupported) {
___loader.enqueue(this.state.to)
___loader.enqueue(this.state.path)
}
}
}

componentDidMount() {
// Preserve non IO functionality if no support
if (!this.state.IOSupported) {
___loader.enqueue(this.state.to)
___loader.enqueue(this.state.path)
}
}

Expand All @@ -85,7 +98,7 @@ class GatsbyLink extends React.Component {
if (this.state.IOSupported && ref) {
// If IO supported and element reference found, setup Observer functionality
handleIntersection(ref, () => {
___loader.enqueue(this.state.to)
___loader.enqueue(this.state.path)
})
}
}
Expand Down Expand Up @@ -116,15 +129,15 @@ class GatsbyLink extends React.Component {
) {
// Is this link pointing to a hash on the same page? If so,
// just scroll there.
let pathname = this.state.to
let pathname = this.state.path
if (pathname.split(`#`).length > 1) {
pathname = pathname
.split(`#`)
.slice(0, -1)
.join(``)
}
if (pathname === window.location.pathname) {
const hashFragment = this.state.to
const hashFragment = this.state.path
.split(`#`)
.slice(1)
.join(`#`)
Expand All @@ -139,7 +152,7 @@ class GatsbyLink extends React.Component {
// loaded before continuing.
if (process.env.NODE_ENV === `production`) {
e.preventDefault()
window.___navigateTo(this.state.to)
window.___navigateTo(this.state.path)
}
}

Expand All @@ -157,7 +170,7 @@ GatsbyLink.propTypes = {
...NavLinkPropTypes,
innerRef: PropTypes.func,
onClick: PropTypes.func,
to: PropTypes.string.isRequired,
to: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
}

GatsbyLink.contextTypes = {
Expand All @@ -167,5 +180,5 @@ GatsbyLink.contextTypes = {
export default GatsbyLink

export const navigateTo = pathname => {
window.___navigateTo(withPrefix(pathname))
window.___navigateTo(pathname)
}

0 comments on commit d458fe7

Please sign in to comment.