docbrown/app/javascript/crayons/navigation/MobileDrawerNavigation/MobileDrawerNavigation.jsx
Suzanne Aitchison a6b4d8e91d
Create crayons mobile drawer components (#14495)
* add MobileDrawer component

* WIP begin navigation component

* rough working version complete

* update story to allow for actual navigation

* add component tests

* add docs

* add HTML variants

* add a max width to the drawer
2021-08-16 08:41:26 +01:00

115 lines
3.3 KiB
JavaScript

import { h, Fragment } from 'preact';
import { useState } from 'preact/hooks';
import PropTypes from 'prop-types';
import { MobileDrawer } from '@crayons/MobileDrawer';
import { Button } from '@crayons/Button';
const OverflowIcon = () => (
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M7 12a2 2 0 11-4 0 2 2 0 014 0zm7 0a2 2 0 11-4 0 2 2 0 014 0zm5 2a2 2 0 100-4 2 2 0 000 4z"
/>
</svg>
);
const CheckIcon = () => (
<svg
aria-hidden="true"
className="check-icon"
fill="currentColor"
width="24"
height="24"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M10 15.172l9.192-9.193 1.415 1.414L10 18l-6.364-6.364 1.414-1.414 4.95 4.95z" />
</svg>
);
/**
* Renders a page heading with a button which activates a <MobileDrawer /> with a list of the given navigation links.
*
* @param {object} props
* @param {number} headingLevel The level of heading to render as the page title (e.g. 1-6)
* @param {string} navigationTitle The title to be used for the navigation element (e.g. 'Feed timeframes')
* @param {Array} navigationLinks An array of navigationLink objects to display
*
* @example
* <MobileDrawerNavigation
* headingLevel={2}
* navigationTitle="Feed options"
* navigationLinks={links} />
*/
export const MobileDrawerNavigation = ({
headingLevel,
navigationTitle,
navigationLinks,
}) => {
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const currentPage = navigationLinks.find((item) => item.isCurrentPage);
const Heading = `h${headingLevel}`;
return (
<Fragment>
<div className="flex justify-between">
<Heading>{currentPage.displayName}</Heading>
<Button
aria-label={navigationTitle}
icon={OverflowIcon}
size="s"
contentType="icon"
variant="ghost"
onClick={() => setIsDrawerOpen(true)}
/>
</div>
{isDrawerOpen && (
<MobileDrawer
title={navigationTitle}
onClose={() => setIsDrawerOpen(false)}
>
<nav aria-label={navigationTitle} className="drawer-navigation">
<ul className="list-none">
{navigationLinks.map((linkDetails) => {
const { url, isCurrentPage, displayName } = linkDetails;
return (
<li
key={`link-${url}`}
className="drawer-navigation__item py-2"
>
<a href={url} aria-current={isCurrentPage ? 'page' : null}>
{displayName}
</a>
<CheckIcon />
</li>
);
})}
</ul>
</nav>
<Button
variant="secondary"
className="w-100 mt-4"
onClick={() => setIsDrawerOpen(false)}
>
Cancel
</Button>
</MobileDrawer>
)}
</Fragment>
);
};
MobileDrawerNavigation.propTypes = {
headingLevel: PropTypes.oneOf([1, 2, 3, 4, 5, 6]).isRequired,
navigationTitle: PropTypes.string.isRequired,
navigationLinks: PropTypes.arrayOf(
PropTypes.shape({
url: PropTypes.string,
isCurrentPage: PropTypes.bool,
displayName: PropTypes.string,
}),
).isRequired,
};