lsirivong / gatsby-plugin-modal-routing

A gatsby plugin for routable modals
44 stars 30 forks source link

gatsby-plugin-modal-routing

Adds support for viewing gatsby pages within modals at their gatsby defined routes.

Examples

Install

npm install --save gatsby-plugin-modal-routing

Motivation

The problem: how to handle modals (stateful routes) with gatsby's page based routing?

We want a modal to open within the context of whatever page it was linked to, change the browser's URL, and render server-side allowing permalink navigation.

Current Gatsby V2 examples which use PageRenderer flicker when re-rendering content underneath the modal. This plugin aims to handle modal routing edge cases and provide a consistent rendering experience with a flexible API.

How to use

Add the plugin to your gatsby-config.js:

// gatsby-config.js

module.exports = {
  plugins: [
    `gatsby-plugin-modal-routing`
  ]
];

Plugin Options

module.exports = {
  plugins: [
    {
      resolve: `gatsby-plugin-modal-routing`,
      options: {
        // A selector to set react-modal's app root to, default is `#___gatsby`
        // See http://reactcommunity.org/react-modal/accessibility/#app-element
        appElement: '#___gatsby',

        // Object of props that will be passed to the react-modal container
        // See http://reactcommunity.org/react-modal/#usage
        modalProps: { },
      }
    }
  ]
];

Rendering page content in modals

Any gatsby page may be rendered in a modal if it is routed to appropriately (see next section below for creating a modal link).

The ModalRoutingContext React.Context component can be used to conditionally render content if the page is rendered in a modal.

The Context consumer is passes an object with modal and closeTo properties to it's child render function

Example:

// pages/modal-example.js

import React from 'react'
import { Link } from 'gatsby'
import { ModalRoutingContext } from 'gatsby-plugin-modal-routing'

const ModalExamplePage = () => (
  <ModalRoutingContext.Consumer>
    {({ modal, closeTo }) => (
      <div>
        {modal ? (
          <Link to={closeTo}>
            Close
          </Link>
        ) : (
          <header>
            <h1>
              Website Title
            </h1>
          </header>
        )}

        <h2>Modal Page</h2>

        <Link to="/">Go back to the homepage</Link>
      </div>
    )}
  </ModalRoutingContext.Consumer>
)

export default ModalExamplePage

Opening a page in a modal

Pages can be opened in a modal context by passing the { modal: true } flag to Link state.

Example:

// src/components/some-component.js

import { Link } from 'gatsby'

...

<Link
  to="/login/"
  state={{
    modal: true
  }}
>
  Login
</Link>

gatsby-plugin-modal-routing also provides a Link component as a convenience to encapsulate this flag for you.

This is equivalent to the example above:

// src/components/some-component.js

import { Link } from 'gatsby-plugin-modal-routing'

...

<Link
  to="/login/"
  asModal
>
  Login
</Link>

Scroll State

When the site opens a modal, gatsby's default scroll update is prevented, so that the underlying page remains scrolled at the same position.

When routing to a non-modal page from a modal, gatsby's default scroll update is allowed, causing the page to scroll to the top. This is the case even if the non-modal page is the same as the underlying page.

To prevent this, pass the { noScroll: true } flag to Link state.

// src/components/modal-content.js

import { Link } from 'gatsby'

...

<Link
  to="/"
  state={{
    noScroll: true
  }}
>
  Close Modal
</Link>

As a convenience, this plugin's Link component will detect if the to pathname matches the content rendered under the modal and set the noScroll flag for you.

// src/components/modal-content.js

import { Link } from 'gatsby-plugin-modal-routing'

...

<Link
  to="/"
>
  Close Modal
</Link>

To prevent scrolling on the underlying page after navigation to a modal is complete, see this thread for different strategies.