Skip to content

Commit

Permalink
Component: vf-mega-menu (#1720)
Browse files Browse the repository at this point in the history
* Component: vf-mega-menu

The initial implementation to address #1718

Starts as a direct port of https://codesandbox.io/s/vf-megamenu-qexed?file=/index.html

WIP.
  • Loading branch information
khawkins98 authored Nov 19, 2021
1 parent 03a2a68 commit 2fdff72
Show file tree
Hide file tree
Showing 16 changed files with 1,025 additions and 8 deletions.
5 changes: 5 additions & 0 deletions components/vf-componenet-rollup/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 1.4.4

* Add vf-mega-menu Sass and JS.
* https://github.com/visual-framework/vf-core/issues/1718

## 1.4.3

* Removes the unused vf-header component.
Expand Down
1 change: 1 addition & 0 deletions components/vf-componenet-rollup/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ button {

@import 'vf-show-more/vf-show-more.scss';

@import 'vf-mega-menu/vf-mega-menu.scss';
@import 'vf-tree/vf-tree.scss';

/* All Visual Framework Boilerplates */
Expand Down
3 changes: 3 additions & 0 deletions components/vf-componenet-rollup/scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,7 @@ import { emblContentMetaProperties_Read } from "embl-content-meta-properties/emb
import { emblNotifications } from "embl-notifications/embl-notifications";
// emblNotifications();

import { vfMegaMenu } from 'vf-mega-menu/vf-mega-menu';
vfMegaMenu();

// No default invokation
4 changes: 4 additions & 0 deletions components/vf-mega-menu/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
bin
.github
.travis.yml
node_modules
4 changes: 4 additions & 0 deletions components/vf-mega-menu/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
### 1.0.0-alpha.1

* Creates a mega menu.
* https://github.com/visual-framework/vf-core/issues/1718
65 changes: 65 additions & 0 deletions components/vf-mega-menu/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Mega menu component

[![npm version](https://badge.fury.io/js/%40visual-framework%2Fvf-mega-menu.svg)](https://badge.fury.io/js/%40visual-framework%2Fvf-mega-menu)

## About

Paired with a good understanding of a site's information architecture and user journey, the mega menu can empower quick shortcut-style access to popular areas.

## Usage

The mega menu should be seen as a empowering but optional feature. While a mega menu may allow a user to quickly move to a sub-section of a website, or laterally move from one silo to another, that empowering ability should be viewed as an optional user journey.

Some users may fail to notice the mega menu by scrolling past it, be on a mobile device where the menu behaves differently, or the JavaScript-based feature may fail to load making the mega menu inaccessible.

A user journey should always be possible without the mega menu's content.

It is recommended to put your mega menu links at the `vf-global-header` level.

### Caveats

1. The mega menu currently is not designed to work on mobile
2. In principle any content can be inserted into a mega menu
3. Using more than one mega menu on a page is likely to confuse and overwhelm users
4. A mega menu is not a substitute for a good information architecture

### Accessibility

This component targets WCAG 2.1 AA accessibility.

Hiding critical or essintal information in a mega menu is harmful to users.

## Install

This repository is distributed with [npm][https://www.npmjs.com/]. After [installing npm][https://www.npmjs.com/get-npm] and [yarn](https://classic.yarnpkg.com/en/docs/install), you can install `vf-mega-menu` with this command.

```
$ yarn add --dev @visual-framework/vf-mega-menu
```

### JS

You should import this component in `./components/vf-component-rollup/scripts.js` or your other JS process:

```js
import { vfComponentName } from 'vf-mega-menu/vf-mega-menu';
// Or import directly
// import { vfComponentName } from '../components/raw/vf-mega-menu/vf-mega-menu.js';
vfComponentName(); // if needed, invoke it
```

### Sass/CSS

The style files included are written in [Sass](https://sass-lang.com/). If you're using a VF-core project, you can import it like this:

```
@import "@visual-framework/vf-mega-menu/vf-mega-menu.scss";
```

Make sure you import Sass requirements along with the modules. You can use a [project boilerplate](https://stable.visual-framework.dev/building/) or the [`vf-sass-starter`](https://stable.visual-framework.dev/components/vf-sass-starter/)

## Help

- [Read the Visual Framework troubleshooting](https://stable.visual-framework.dev/troubleshooting/)
- [Open a ticket](https://github.com/visual-framework/vf-core/issues)
- [Chat on Slack](https://join.slack.com/t/visual-framework/shared_invite/enQtNDAxNzY0NDg4NTY0LWFhMjEwNGY3ZTk3NWYxNWVjOWQ1ZWE4YjViZmY1YjBkMDQxMTNlNjQ0N2ZiMTQ1ZTZiMGM4NjU5Y2E0MjM3ZGQ)
15 changes: 15 additions & 0 deletions components/vf-mega-menu/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// setup files required

// sass-lint:disable clean-import-paths
@import 'vf-global-variables';
@import 'vf-variables';
@import 'vf-functions';
@import 'vf-mixins';

// component specific styles

@import 'vf-mega-menu.variables.scss';
@import 'vf-mega-menu.scss';

// component variant styles
// @import 'vf-mega-menu--variant.scss';
23 changes: 23 additions & 0 deletions components/vf-mega-menu/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"version": "1.0.0-alpha.0",
"name": "@visual-framework/vf-mega-menu",
"description": "vf-mega-menu component",
"homepage": "",
"author": "VF",
"license": "Apache 2.0",
"style": "vf-mega-menu.css",
"sass": "index.scss",
"main": "build/index.js",
"test": "echo \"Error: no test specified\" && exit 1",
"publishConfig": {
"access": "public"
},
"repo": "https://github.com/visual-framework/vf-core/tree/develop/components/vf-mega-menu",
"bugs": {
"url": "https://github.com/visual-framework/vf-core/issues"
},
"keywords": [
"fractal",
"component"
]
}
29 changes: 29 additions & 0 deletions components/vf-mega-menu/vf-mega-menu.config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# The title shown on the component page
title: Mega menu
# Label shown on index pages
label: Mega menu
status: alpha
# The template used from /components/_previews
#
# Per-variant options
# variants:
# - name: default
# label: Default
# hidden: true
# context:
# children_are_possible:
# variant_title: A Easy Card Title 1
# variant_href: "JavaScript:Void(0);"
# modifier: vf-card--very-easy
# variant_image: ../../assets/vf-card/assets/vf-card-example.png
# Global component context
context:
component-type: container
# custom-values: passed as {{custom-values}}
# - note: you in your custom-values you should use dashes `-`
# and not underscores `_` as underscores prevent inherited template use
# title: Title text
# text: String of text
# image: ../../assets/vf-component-name/assets/vf-component-name.png
# - note on paths: be sure to prefix with `../../`
# - Why? https://github.com/visual-framework/vf-core/issues/364
186 changes: 186 additions & 0 deletions components/vf-mega-menu/vf-mega-menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// vf-mega-menu

// Don't need JS? Then feel free to delete this file.

/*
* A note on the Visual Framework and JavaScript:
* The VF is primarily a CSS framework so we've included only a minimal amount
* of JS in components and it's fully optional (just remove the JavaScript selectors
* i.e. `data-vf-js-tabs`). So if you'd rather use Angular or Bootstrap for your
* tabs, the Visual Framework won't get in the way.
*
* When querying the DOM for elements that should be acted on:
* 🚫 Don't: const tabs = document.querySelectorAll('.vf-tabs');
* ✅ Do: const tabs = document.querySelectorAll('[data-vf-js-tabs]');
*
* This allows users who would prefer not to have this JS engage on an element
* to drop `data-vf-js-component` and still maintain CSS styling.
*/

// Uncomment this boilerplate
// // if you need to import any other components' JS to use here
// import { vfOthercomponent } from vfImportPrefix + '../vf-other-component/vf-other-component';
//

function initMegaMenu(megaMenuComponent) {
//add activated class to this mega menu. This will help us differentiate when menu is processed with JS and when its not.
megaMenuComponent.classList.add("vf-mega-menu__activated");

let previousMenuLinkComponent, previousExpandedSectionComponent;

function getWidth() {
return Math.max(
document.body.scrollWidth,
document.documentElement.scrollWidth,
document.body.offsetWidth,
document.documentElement.offsetWidth,
document.documentElement.clientWidth
);
}

megaMenuComponent.querySelectorAll("[data-vf-js-mega-menu-section-id]").forEach(item => {
item.addEventListener('click', event => {
// For now we do not show the mega menu on mobile.
// We still need to decide on approach for mobile support.
if (getWidth() > 768) {
event.preventDefault();
event.vfMegaMenuLink = true;
const { target: linkComponent } = event;
const newReferences = handleMenuClick(
linkComponent,
previousMenuLinkComponent,
previousExpandedSectionComponent
);
previousMenuLinkComponent = newReferences?.previousMenuLinkComponent;
previousExpandedSectionComponent =
newReferences?.previousExpandedSectionComponent;
} else {
console.warn(`vf-mega-menu: No mega menu shown. Mega menu is currently alpha and not supported on small screen sizes. Your screens size is ${getWidth()}`);
}
// console.log({ linkComponent });
// console.log({ previousMenuLinkComponent });
// const associatedSection = linkComponent.getAttribute(
// "data-vf-js-mega-menu-section-id"
// );
});
});

}




function handleMenuClick(
menuItemComponent,
previousMenuLinkComponent,
previousExpandedSectionComponent
) {
// console.log("expand / collapse");
// debugger;
const sectionAttribute = menuItemComponent.getAttribute(
"data-vf-js-mega-menu-section-id"
);
const section = document.querySelector(
`[data-vf-js-mega-menu-section="${sectionAttribute}"]`
);
// console.log("section", section, sectionAttribute);

if (!section) {
return;
}

// Capture clicks on things other than mega menu elements
// https://www.blustemy.io/detecting-a-click-outside-an-element-in-javascript/
document.addEventListener("click", (evt) => {
let targetElement = evt.target; // clicked element
do {
if (targetElement == section || evt.vfMegaMenuLink == true) {
// This is a click inside. Do nothing, just return.
// console.log("Clicked inside!")
return;
}
// Go up the DOM
targetElement = targetElement.parentNode;
} while (targetElement);

// This is a click outside.
// console.log("Clicked outside!")
if (section.getAttribute("aria-hidden") === "false") {
// console.log("hiding!")
section.setAttribute("aria-hidden", "true");
}
});

//0. if section is visible, just hide it
if (section.getAttribute("aria-hidden") === "false") {
section.setAttribute("aria-hidden", "true");
menuItemComponent.classList.remove("is-expanded");
return;
}

//1. section is hidden. Hide all sections
if (previousExpandedSectionComponent) {
previousExpandedSectionComponent.setAttribute("aria-hidden", "true");
}

//2. remove highlight from previous link
if (previousMenuLinkComponent) {
previousMenuLinkComponent.classList.remove("is-expanded");
}

//3. show new section and add class to new link
section.setAttribute("aria-hidden", "false");
menuItemComponent.classList.add("is-expanded");

//4. return new link and section components to be stored as previous
return {
previousMenuLinkComponent: menuItemComponent,
previousExpandedSectionComponent: section
};
}

// function createMenuSectionMap(megaMenuComponent) {
// const allMenuComponents = megaMenuComponent.querySelectorAll(
// "[data-vf-js-mega-menu-section-id]"
// );

// const menuSectionsMap = new Map();
// allMenuComponents.forEach((component) => {
// const sectionAttribute = component.getAttribute(
// "data-vf-js-mega-menu-section-id"
// );
// const section = megaMenuComponent.querySelector(
// `[data-vf-js-mega-menu-section="${sectionAttribute}"]`
// );
// menuSectionsMap.set(component, section);
// });
// return menuSectionsMap;
// }

/**
* The global function for this component
* @example vfMegaMenu(firstPassedVar)
* @param {string} [firstPassedVar] - An option to be passed
*/
function vfMegaMenu(firstPassedVar) {
firstPassedVar = firstPassedVar || 'defaultVal';

const allMegaMenuComponents = document.querySelectorAll("[data-vf-js-mega-menu]") || [];

//for each mega-menu
allMegaMenuComponents.forEach(initMegaMenu);
}

// // If you need to invoke the component by default
// vfMegaMenu();

// By default your component should be usable with js imports
export { vfMegaMenu };

// You should also import it at ./components/vf-component-rollup/scripts.js
// import { vfMegaMenu } from 'vf-mega-menu/vf-mega-menu';
// Or import directly
// import { vfMegaMenu } from '../components/raw/vf-mega-menu/vf-mega-menu.js';
// And, if needed, invoke it
// vfMegaMenu();

Loading

0 comments on commit 2fdff72

Please sign in to comment.