Skip to content

Set of react components, hooks and providers to easily kickoff a navigation environment in web projects.

License

Notifications You must be signed in to change notification settings

moxystudio/react-navigation

Repository files navigation

react-navigation

NPM version Downloads Build Status Coverage Status Dependency status Dev Dependency status

Set of react components, hooks and providers to easily kickoff a navigation environment in web projects.

Installation

$ npm install @moxy/react-navigation

This library is written in modern JavaScript and is published in both CommonJS and ES module transpiled variants. If you target older browsers please make sure to transpile accordingly.

Motivation

Most websites have a navbar and a drawer to provide an easy access to other pages. The creation process is usually remade project after project with little to no reutilization.

By analysing a few projects, most of the logic was identical in every single one. This raised a few questions and one solution to the problem would be to have a set of react tools to simplify the process.

A concern that came from the proposed solution was that every single website has different designs for a navbar or a drawer. Although this is true, their behaviour is usually the same. So, the solution should only deal with logic and behaviours while giving total freedom of the content that is rendered.

⚠️ Note: If you are using this package with [@moxy/next-layout](https://github.com/moxystudio/next-layout), where Layout components don't unmount on each page change, beware you will have to listen to router events to make sure the drawer closes even if the page change is triggered by the browser's history buttons. See Handling router events section to check how it can be done in Next.js projects.

Usage

import React from 'react';
import { NavigationProvider, Navbar, Drawer, useNavigation } from '@moxy/react-navigation';

const MyNavigationHelper = () => {
    const { drawer } = useNavigation();

    return (
        <>
            <span>{ `Is Drawer Open?  ${drawer.isOpen}` }</span>
            <button onClick={ drawer.open }>Open Drawer</button>
            <button onClick={ drawer.close }>Close Drawer</button>
            <button onClick={ drawer.toggle }>Toggle Drawer</button>
        </>
    );
}

const App = ({ Page, pageProps }) => (
    <NavigationProvider>
        <Navbar
            placement="top"
            behaviour="hideOnScrollDown"
            className="navbar">
            <p>This is the content of the navbar.</p>
            <MyNavigationHelper />
        </Navbar>
        <Drawer
            withOverlay
            placement="left"
            className="drawer"
            overlayClassName="overlay">
            <p>This is the content of the navbar.</p>
        </Drawer>
        <Page { ...pageProps } />
    </NavigationProvider>
);

export default App;

Import the styleguide base styles in the app's entry CSS file:

@import "@moxy/react-navigation/dist/index.css";

..or in your entry JavaScript file:

import '@moxy/react-navigation/dist/index.css';

Handling router events

Taking MyNavigationHelper component as example:

import { useRouter } from 'next/router';

const MyNavigationHelper = () => {
    const router = useRouter();

    const {
        drawer: {
            open: openDrawer,
            close: closeDrawer,
            toggle: toggleDrawer,
            isOpen: isDrawerOpen,
        }
    } = useNavigation();

    useEffect(() => {
        const handleRouteChange = () => {
            closeDrawer();
        };

        router.events.on('routeChangeStart', handleRouteChange);

        return () => {
            router.events.off('routeChangeStart', handleRouteChange);
        };
    }, [closeDrawer, router.events]);

    return (
        <>
            <span>{ `Is Drawer Open?  ${isDrawerOpen}` }</span>
            <button onClick={ openDrawer }>Open Drawer</button>
            <button onClick={ closeDrawer }>Close Drawer</button>
            <button onClick={ toggleDrawer }>Toggle Drawer</button>
        </>
    );
}

We are listening to routeChangeStart event for the sake of this example. You can check here a complete list of the supported events for the Next.js Router.

Styling

Each provided component accepts classNames for styling. Although there might be a need to style conditionally, some auxiliary data attributes are provided and can be styled like so:

.drawer,
.drawer[data-open="false"] {
    background-color: red;
}

.drawer[data-open="true"] {
    background-color: blue;
}

.overlay,
.overlay[data-open="false"] {
    opacity: 0;
}

.overlay[data-open="true"] {
    opacity: 0.5;
}

API

NavigationProvider

A context is created to help interact with navigation elements. The NavigationProvider component acts as a provider to allow other consuming components to subscribe to context changes.

All consumers that are descendants of the NavigationProvider will re-render whenever the navigation context changes.

Any consumer component that uses the useNavigation hook or the withNavigation HOC must be descendant from this provider.

useNavigation

Hook that returns the current state of the navigation context.

withNavigation

HOC that provides the current state of the navigation context in the form of a prop named navigation.

Navbar

Navbar is a component that renders a navigation bar with a few pre-available benefits. No content is rendered by default based on the uniqueness of every navbar, delegating that responsibility to the developer.

The following props are available for the Navbar component:

children

Type: any | Required: false

The content of the navbar.

placement

Type: string | Options: top, right, bottom, left | Default: top

The placement of the navbar in relation to the viewport.

behaviour

Type: string | Options: hideOnScrollDown | Required: false

A predefined navbar behaviour.

className

Type: string | Required: false

A className to apply to the component.

Drawer

Drawer is a component that renders a drawer with a few pre-available benefits. No content is rendered by default based on the uniqueness of every drawer, delegating that responsibility to the developer.

The following props are available for the Drawer component:

children

Type: any | Required: false

The content of the drawer.

placement

Type: string | Options: top, right, bottom, left | Default: left

The placement of the drawer in relation to the viewport.

withOverlay

Type: boolean | Default: true

An overlay that renders together with the drawer. When clicked, closes the drawer.

lockBodyScroll

Type: boolean | Default: true

Disables body scroll whenever the drawer is open. It keeps the drawer scroll if needed.

className

Type: string | Required: false

A className to apply to the drawer.

overlayClassName

Type: string | Required: false

A className to apply to the overlay.

Tests

$ npm test
$ npm test -- --watch # during development

Demo

A demo Next.js project is available in the /demo folder so you can try out this component.

First, build the react-navigation project with:

$ npm run build

To run the demo, do the following inside the demo's folder:

$ npm i
$ npm run dev

Note: Everytime a change is made to the package a rebuild is required to reflect those changes on the demo.

FAQ

I can't override the component's CSS, what's happening?

There is an ongoing next.js issue about the loading order of modules and global CSS in development mode. This has been fixed in v9.3.6-canary.0, so you can either update next.js to a version higher than v9.3.5, or simply increase the CSS specificity when overriding component's classes, as we did in the demo, e.g. having the page or section CSS wrap the component's one.

License

Released under the MIT License.

About

Set of react components, hooks and providers to easily kickoff a navigation environment in web projects.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •