From ac1c68a4e63e876ce0a4f2e56e18aa5d0aa3a0fa Mon Sep 17 00:00:00 2001 From: Danny Parnella Date: Wed, 7 Apr 2021 17:42:36 -0500 Subject: [PATCH 01/25] create all components, update to use actual button trigger --- .storybook/styles/application.scss | 1 + .../styles/components/_dropdown-nav-bar.scss | 355 ++++++++++++++++++ package.json | 1 + src/controls/dropdown-nav-bar.js | 116 ++++++ src/controls/dropdown-nav-menu-item.js | 96 +++++ src/controls/dropdown-nav-menu-sub-item.js | 52 +++ src/controls/dropdown-nav-menu.js | 81 ++++ src/controls/helpers/get-nav-link.js | 8 + src/controls/helpers/index.js | 4 + src/controls/helpers/is-mobile-menu.js | 22 ++ src/controls/helpers/nav-prop-types.js | 32 ++ src/controls/helpers/toggle-element-array.js | 12 + src/controls/index.js | 1 + stories/controls/dropdown-nav-bar.story.js | 240 ++++++++++++ yarn.lock | 23 ++ 15 files changed, 1044 insertions(+) create mode 100644 .storybook/styles/components/_dropdown-nav-bar.scss create mode 100644 src/controls/dropdown-nav-bar.js create mode 100644 src/controls/dropdown-nav-menu-item.js create mode 100644 src/controls/dropdown-nav-menu-sub-item.js create mode 100644 src/controls/dropdown-nav-menu.js create mode 100644 src/controls/helpers/get-nav-link.js create mode 100644 src/controls/helpers/index.js create mode 100644 src/controls/helpers/is-mobile-menu.js create mode 100644 src/controls/helpers/nav-prop-types.js create mode 100644 src/controls/helpers/toggle-element-array.js create mode 100644 stories/controls/dropdown-nav-bar.story.js diff --git a/.storybook/styles/application.scss b/.storybook/styles/application.scss index 87ec4b9d..8a4d171c 100644 --- a/.storybook/styles/application.scss +++ b/.storybook/styles/application.scss @@ -64,6 +64,7 @@ @import "components/search-results-and-filtering"; @import "components/recruiter-search"; @import "components/tabs"; + @import "components/dropdown-nav-bar"; // Page Styles @import "pages/main"; diff --git a/.storybook/styles/components/_dropdown-nav-bar.scss b/.storybook/styles/components/_dropdown-nav-bar.scss new file mode 100644 index 00000000..17d2c9a6 --- /dev/null +++ b/.storybook/styles/components/_dropdown-nav-bar.scss @@ -0,0 +1,355 @@ +$down-arrow-image-url: url(https://www.australiazoo.com.au/wp-content/themes/some-like-it-neat/assets/images/down-arrow.svg); + +#dropdown-nav-bar { + input[type='checkbox'] { + position: absolute; + top: -9999px; + left: -9999px; + + &:checked { + & ~ .primary-menu { + top: 0; + position: fixed; + overflow: auto; + display: block; + height: 100%; + } + + & ~ .nav { + height: 100%; + } + + & ~ label { + span:nth-child(1) { + transform: translateY(8px) rotate(45deg); + } + span:nth-child(2) { + opacity: 0; + } + span:nth-child(3) { + transform: translateY(-8px) rotate(-45deg); + } + } + } + } + + .primary-menu { + display: none; + background: $white-base; + padding-top: 30px; + + @include media($tablet) { + display: block; + } + + li { + margin: 20px 0px 0px; + padding: 0px 20px; + cursor: pointer; + + @include media($tablet) { + margin: 20px; + padding: 0px; + } + + @include media($tablet) { + margin: 0px 0px 0px 2%; + padding-bottom: 13px; + } + + &:last-child { + margin-bottom: 20px; + } + + &:before { + position: absolute; + bottom: -6px; + content: ''; + height: 3px; + width: 0; + background: #333; + transition: 0.5s; + } + + a { + @include rem(font-size, 20px); + + @include media($tablet) { + font-weight: 600; + letter-spacing: 1px; + text-transform: uppercase; + @include s-base; + } + } + } + } + + .sub-menu { + display: none; + margin-top: 13px; + width: 100%; + z-index: 3; + + @include media($tablet) { + @include rem(padding, 40px); + padding-right: 280px; + min-height: 330px; + box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); + position: absolute; + background-color: $white-base; + border-top: 3px solid $green-dark; + border-bottom-right-radius: 10px; + border-bottom-left-radius: 10px; + columns: 2; + max-width: 900px; + } + + li { + padding: 0px; + + @include media($tablet) { + padding-bottom: 20px; + } + } + + a { + color: black; + padding: 0px; + text-decoration: none; + display: block; + letter-spacing: 0px; + font-size: 20px; + font-weight: 100; + text-transform: none; + @include rem(padding-bottom, 0px); + + span { + font-size: 20px; + } + + &:after { + display: none; + } + } + + .menu-item { + text-align: left; + margin: 0px; + width: inherit; + + a { + margin: 20px 0px 0px; + } + } + + .nav-image { + display: none; + + @include media($tablet) { + display: block; + position: absolute; + right: 0px; + width: 240px; + top: 35px; + } + + img { + border-top-left-radius: 10px; + border-bottom-left-radius: 10px; + height: auto; + } + + span { + display: none; + } + } + } + + @include media($tablet) { + } +} + +.mobile-menu { + float: right; + position: absolute; + right: 15px; + z-index: 3; + cursor: pointer; + + @include media($tablet) { + display: none; + } + + label { + position: absolute; + display: none; + top: 0; + left: 0; + height: 70px; + width: 70px; + cursor: pointer; + } + + span { + display: block; + margin: 4px auto; + height: 4px; + width: 25px; + border-radius: 1px; + background: #333; + transition: 0.5s; + + &:nth-child(1) { + margin-top: 15px; + } + } +} + +.menu-item { + display: inline-block; + text-transform: uppercase; + font-weight: bold; + font-size: 20px; + width: 100%; + + @include media($tablet) { + @include rem(padding-bottom, 20px); + width: auto; + } + + &:hover, + &.active { + &.menu-item-has-children a { + position: relative; + + &:after { + @include media($tablet) { + content: ''; + width: 0; + height: 0; + border-left: 10px solid transparent; + border-right: 10px solid transparent; + border-bottom: 10px solid $green-dark; + position: absolute; + transform: translateX(-50%); + left: 50%; + bottom: -17px; + } + } + } + } +} + +.mobile-nav-buttons { + display: inline-block; + width: 100%; + + @include media($tablet) { + display: none; + } + + a, + button { + border-radius: 0px; + display: inline-block; + text-align: center; + width: 100%; + padding: 16px 25px; + } +} + +#menu-primary-navigation .menu-item-has-children { + /* Show the dropdown menu on hover (except on mobile) or active touch state */ + &:hover { + .sub-menu { + @include media($tablet) { + display: block; + } + } + } + &.active { + .sub-menu { + display: block; + } + } + + .menu-item-button { + display: inline-block; + @include rem(padding, 10px 10px 10px 20px); + background: none; + width: auto; + + &:after { + content: ''; + background-image: $down-arrow-image-url; + background-size: 100%; + display: inline-block; + height: 12px; + width: 18px; + } + + @include media($tablet) { + display: none; + + &.desktop-visible, + &:focus { + display: inline-block; + } + } + .link-secondary.logout { + display: none; + } + } +} + +@mixin nav-button { + color: $white-base; + border-radius: 8px; + cursor: pointer; + font-weight: 800; + letter-spacing: 1px; + text-transform: uppercase; + font-size: 12px; + -webkit-box-shadow: 0px 14px 28px -11px rgba(0, 0, 0, 0.45); + -moz-box-shadow: 0px 14px 28px -11px rgba(0, 0, 0, 0.45); + box-shadow: 0px 14px 28px -11px rgba(0, 0, 0, 0.45); + display: inline; + transition: all 0.3s ease-in-out; + font-family: 'Open Sans', sans-serif; + @include rem(padding, 13px 22px); + line-height: initial; + + a { + color: $white-base; + } +} + +.button { + @include nav-button; + background-color: orange; + + &:hover { + color: $white-base; + background-color: darken(orange, 20%); + } +} + +.button-green { + background-color: $green-base; + + &:hover { + background-color: darken($green-base, 20%); + } + + a { + color: $white-base; + } +} + +.button-green-dark { + background-color: $green-dark; + + &:hover { + background-color: darken($green-dark, 5%); + } +} diff --git a/package.json b/package.json index e717df7a..92d021e8 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "react-color": "^2.14.0", "react-datepicker": "^1.0.3", "react-modal": "^3.11.2", + "react-outside-click-handler": "^1.3.0", "react-router": "^3.2.1", "react-switch": "^5.0.1", "redux-flash": "^2.0.1" diff --git a/src/controls/dropdown-nav-bar.js b/src/controls/dropdown-nav-bar.js new file mode 100644 index 00000000..a7cc938c --- /dev/null +++ b/src/controls/dropdown-nav-bar.js @@ -0,0 +1,116 @@ +import React, { useState, useCallback } from 'react' +import PropTypes from 'prop-types' +import { isMobileMenu, toggleElementArray } from './helpers' +import { menuItemType } from './helpers/nav-prop-types' +import DropdownNavMenu from './dropdown-nav-menu' + +// TODO: Finish documentation +/** + * + * + * @name DropdownNavBar + * @type Function + * @description A control component for navigating among multiple navigation menu items that can include dropdowns with sub-menu items + * @param {Boolean} [vertical] A boolean setting the `className` of the `ul` to 'horizontal' (default), or 'vertical', which determines the alignment of the tabs (optional, default `false`) + * @param {Array} [options] An array of tab values (strings or key-value pairs) + * @param {String|Number} [value] - The value of the current tab + * @param {Function} [onChange] - A function called with the new value when a tab is clicked + * @param {String} [activeClassName] - The class of the active tab, (optional, default `active`) + * @param {String} [baseUrl] - + * @param {Number} [mobileBreakpoint] - + * @param {Array} [menuItems] - + * @example + * + * function Header({ baseUrl, menuItems }) { + * return ( + *
+ *
+ * + * Navigate to the Home Page + * + * + *
+ *
+ * ) + * } + */ + +const propTypes = { + menuItems: PropTypes.arrayOf(menuItemType).isRequired, + mobileBreakpoint: PropTypes.number, + baseUrl: PropTypes.string, +} + +const defaultProps = { + mobileBreakpoint: 1024, + baseUrl: '', +} + +function DropdownNavBar({ menuItems, mobileBreakpoint, baseUrl }) { + const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false) + const [activeMenuIds, setActiveMenuIds] = useState([]) + const clearActiveMenuIds = () => { + // effectively closes submenu + // don't clear out active menu ids if mobile menu + if (isMobileMenu(mobileBreakpoint)) return + setActiveMenuIds([]) + } + const toggleActiveMenuId = (id) => { + /* Specific for mobile view. Keep submenus open unless + dropdown arrow is specifically tapped again to close it */ + setActiveMenuIds(toggleElementArray(activeMenuIds, id)) + } + + const onInteractParentMenu = useCallback( + (e, id) => { + if ( + // Ignore if: + // 1) Mobile menu + isMobileMenu(mobileBreakpoint) || + // 2) Not attempting to open submenu using Enter/Space on keyboard + (e.key && !['Enter', ' '].includes(e.key)) || + // 3) IDs match, as we then want Enter/Space or a tap to activate href link (default behavior) + activeMenuIds.includes(id) + ) + return + e.preventDefault() + setActiveMenuIds([id]) + }, + [activeMenuIds] + ) + + return ( + + ) +} + +DropdownNavBar.propTypes = propTypes +DropdownNavBar.defaultProps = defaultProps + +export default DropdownNavBar diff --git a/src/controls/dropdown-nav-menu-item.js b/src/controls/dropdown-nav-menu-item.js new file mode 100644 index 00000000..b7083457 --- /dev/null +++ b/src/controls/dropdown-nav-menu-item.js @@ -0,0 +1,96 @@ +import React, { useState } from 'react' +import PropTypes from 'prop-types' +import OutsideClickHandler from 'react-outside-click-handler' +import classnames from 'classnames' +import { getNavLink } from './helpers' + +const propTypes = { + name: PropTypes.string.isRequired, + id: PropTypes.number.isRequired, + baseURL: PropTypes.string.isRequired, + path: PropTypes.string.isRequired, + active: PropTypes.bool, + closeSubmenu: PropTypes.func.isRequired, + onInteractParentMenu: PropTypes.func.isRequired, + toggleActiveMenuId: PropTypes.func.isRequired, + isFirstItem: PropTypes.bool.isRequired, + children: PropTypes.node.isRequired, +} + +const defaultProps = { + active: false, +} + +function DropdownNavMenuItem({ + name, + id, + baseURL, + path, + active, + onInteractParentMenu, + closeSubmenu, + toggleActiveMenuId, + isFirstItem, + children, +}) { + // show dropdown button on desktop, will always be shown on mobile + const [showDropdownButton, setShowDropdownButton] = useState(false) + + return ( + +
  • + + { + setShowDropdownButton(true) + closeSubmenu() + }} + onBlur={() => { + setTimeout(() => setShowDropdownButton(false), 10) + }} + onTouchEnd={(e) => onInteractParentMenu(e, id)} + href={getNavLink(baseURL, path)} + className="menu-image-title-after" + > + {name} + +
  • +
    + ) +} + +DropdownNavMenuItem.propTypes = propTypes +DropdownNavMenuItem.defaultProps = defaultProps + +export default DropdownNavMenuItem diff --git a/src/controls/dropdown-nav-menu-sub-item.js b/src/controls/dropdown-nav-menu-sub-item.js new file mode 100644 index 00000000..ac7125a4 --- /dev/null +++ b/src/controls/dropdown-nav-menu-sub-item.js @@ -0,0 +1,52 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { getNavLink } from './helpers' + +const propTypes = { + name: PropTypes.string.isRequired, + id: PropTypes.number.isRequired, + path: PropTypes.string.isRequired, + baseURL: PropTypes.string.isRequired, + isLastItem: PropTypes.bool.isRequired, + closeSubmenu: PropTypes.func.isRequired, +} + +const defaultProps = {} + +function DropdownNavMenuSubItem({ + name, + id, + baseURL, + path, + isLastItem, + closeSubmenu, +}) { + return ( +
  • + { + if ( + e.key === 'Escape' || + /* if interaction is on last item in submenu, close the + submenu only if Tab is entered _without_ Shift being held */ + (isLastItem && e.key === 'Tab' && !e.shiftKey) + ) { + closeSubmenu() + } + }} + href={getNavLink(baseURL, path)} + className="menu-image-title-after" + > + {name} + +
  • + ) +} + +DropdownNavMenuSubItem.propTypes = propTypes +DropdownNavMenuSubItem.defaultProps = defaultProps + +export default DropdownNavMenuSubItem diff --git a/src/controls/dropdown-nav-menu.js b/src/controls/dropdown-nav-menu.js new file mode 100644 index 00000000..8fd62225 --- /dev/null +++ b/src/controls/dropdown-nav-menu.js @@ -0,0 +1,81 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { menuItemType } from './helpers/nav-prop-types' +import DropdownNavMenuItem from './dropdown-nav-menu-item' +import DropdownNavMenuSubItem from './dropdown-nav-menu-sub-item' +import { isEmpty, first, last } from 'lodash' + +const propTypes = { + baseURL: PropTypes.string, + menuItems: PropTypes.arrayOf(menuItemType).isRequired, + activeMenuIds: PropTypes.arrayOf(PropTypes.number).isRequired, + onInteractParentMenu: PropTypes.func.isRequired, + toggleActiveMenuId: PropTypes.func.isRequired, + closeSubmenu: PropTypes.func.isRequired, + children: PropTypes.node, +} + +const defaultProps = { + baseURL: '', + children: null, +} + +function DropdownNavMenu({ + baseURL, + menuItems, + activeMenuIds, + onInteractParentMenu, + toggleActiveMenuId, + closeSubmenu, + children, +}) { + return ( +
    + + {children} +
    + ) +} + +DropdownNavMenu.propTypes = propTypes +DropdownNavMenu.defaultProps = defaultProps + +export default DropdownNavMenu diff --git a/src/controls/helpers/get-nav-link.js b/src/controls/helpers/get-nav-link.js new file mode 100644 index 00000000..710751de --- /dev/null +++ b/src/controls/helpers/get-nav-link.js @@ -0,0 +1,8 @@ +// Return nav link using paths or full url if full link is provided + +function getNavLink(baseURL, path) { + if (path.startsWith('http')) return path + return `${baseURL}/${path}` +} + +export default getNavLink diff --git a/src/controls/helpers/index.js b/src/controls/helpers/index.js new file mode 100644 index 00000000..42d0ffcd --- /dev/null +++ b/src/controls/helpers/index.js @@ -0,0 +1,4 @@ +export isMobileMenu from './is-mobile-menu' +export toggleElementArray from './toggle-element-array' +export getNavLink from './get-nav-link' +export * from './nav-prop-types' diff --git a/src/controls/helpers/is-mobile-menu.js b/src/controls/helpers/is-mobile-menu.js new file mode 100644 index 00000000..7eeb7d4d --- /dev/null +++ b/src/controls/helpers/is-mobile-menu.js @@ -0,0 +1,22 @@ +/** + * + * A helper function to check that the window width is below the + * threshold to show the mobile (hamburger) menu. Returns false + * if mobileBreakpoint is falsey (e.g., no mobile menu is used). + * + * @name isMobileMenu + * @type Function + * @param {Number} mobileBreakpoint - The screen width (in pixels) when mobile menu styling is no longer applied + * @returns {Boolean} - Whether mobile menu styling is currently applied + * @example + * + * isMobileMenu(1024) // -> true (when mobile menu styling is applied at screen widths below 1024 pixels) + * + */ + +function isMobileMenu(mobileBreakpoint) { + // eslint-disable-next-line no-undef + return window.innerWidth < mobileBreakpoint +} + +export default isMobileMenu diff --git a/src/controls/helpers/nav-prop-types.js b/src/controls/helpers/nav-prop-types.js new file mode 100644 index 00000000..46721004 --- /dev/null +++ b/src/controls/helpers/nav-prop-types.js @@ -0,0 +1,32 @@ +import PropTypes from 'prop-types' + +/** + * + * A constant representing the `PropTypes` of the `childItem` prop for the `menuItem` prop, {@link menuItemType} + * + * @constant {PropTypes} childItemType + * + */ + +const childItemType = PropTypes.shape({ + name: PropTypes.string.isRequired, + id: PropTypes.number.isRequired, + path: PropTypes.string.isRequired, +}) + +/** + * + * A constant representing the `PropTypes` of the `menuItem` prop for DropdownNavBar component, {@link DropdownNavBar} + * + * @constant {PropTypes} menuItemType + * + */ + +export const menuItemType = PropTypes.shape({ + name: PropTypes.string.isRequired, + id: PropTypes.number.isRequired, + path: PropTypes.string.isRequired, + imageId: PropTypes.number.isRequired, + imagePath: PropTypes.string.isRequired, + childItems: PropTypes.arrayOf(childItemType), +}) diff --git a/src/controls/helpers/toggle-element-array.js b/src/controls/helpers/toggle-element-array.js new file mode 100644 index 00000000..cff869f1 --- /dev/null +++ b/src/controls/helpers/toggle-element-array.js @@ -0,0 +1,12 @@ +// Return new array with element added or removed if already exists + +function toggleElementArray(arr, el) { + // check if element exists already and remove + if (arr.includes(el)) { + return arr.filter((obj) => obj !== el) + } + // if not, add element to current array + return [...arr, el] +} + +export default toggleElementArray diff --git a/src/controls/index.js b/src/controls/index.js index bb7ed4c9..09dc7b98 100644 --- a/src/controls/index.js +++ b/src/controls/index.js @@ -2,3 +2,4 @@ export ColorPicker from './color-picker' export NavLink from './nav-link' export Paginator from './paginator' export TabBar from './tab-bar' +export DropdownNavBar from './dropdown-nav-bar' diff --git a/stories/controls/dropdown-nav-bar.story.js b/stories/controls/dropdown-nav-bar.story.js new file mode 100644 index 00000000..b7fe118b --- /dev/null +++ b/stories/controls/dropdown-nav-bar.story.js @@ -0,0 +1,240 @@ +import React from 'react' +import { storiesOf } from '@storybook/react' +import { DropdownNavBar as StaticDropdownNavBar } from 'src' +import dynamicInput from '../dynamic-input' + +const DropdownNavBar = dynamicInput({})(StaticDropdownNavBar) + +const mainPaths = { + Exp: { name: 'Experiences', path: 'experiences-australia-zoo' }, + Wild: { name: 'Wildlife', path: 'wildlife' }, + Visit: { name: 'Visit Us', path: 'visit-us' }, + About: { name: 'About Us', path: 'about-us' }, +} + +const menuItems = [ + { + name: mainPaths.Exp.name, + id: 1613, + path: mainPaths.Exp.path, + imageId: 1675, + imagePath: 'wp-content/uploads/2019/01/EXPERIENCES3-1.jpg', + childItems: [ + { + name: 'Animal Encounters', + id: 1614, + path: `${mainPaths.Exp.path}/animal-encounters/`, + }, + { + name: 'Zoo Keeper for a Day', + id: 1615, + path: `${mainPaths.Exp.path}/zoo-keeper-for-a-day/`, + }, + { + name: 'Wildlife Photos', + id: 1616, + path: `${mainPaths.Exp.path}/wildlife-photos/`, + }, + { + name: 'Exhibits', + id: 1617, + path: `${mainPaths.Exp.path}/exhibits/`, + }, + { + name: 'Conservation Shows', + id: 1618, + path: `${mainPaths.Exp.path}/show-times/`, + }, + { + name: 'Tours', + id: 1619, + path: `${mainPaths.Exp.path}/tours/`, + }, + { + name: 'Expeditions', + id: 2823, + path: `${mainPaths.Exp.path}/australia-zoo-expeditions/`, + }, + { + name: 'Gift Ideas', + id: 2393, + path: `${mainPaths.Exp.path}/gift-ideas/`, + }, + ], + }, + { + name: mainPaths.Wild.name, + id: 1622, + path: mainPaths.Wild.path, + imageId: 1674, + imagePath: 'wp-content/uploads/2019/01/WILDLIFE3.jpg', + childItems: [ + { + name: 'Our Animals', + id: 1623, + path: `${mainPaths.Wild.path}/our-animals/`, + }, + { + name: 'Zoo Flora', + id: 1624, + path: `${mainPaths.Wild.path}/zoo-flora/`, + }, + { + name: 'Top 10 Highlights', + id: 1625, + path: `${mainPaths.Wild.path}/top-10-highlights/`, + }, + ], + }, + { + name: mainPaths.Visit.name, + id: 1626, + path: mainPaths.Visit.path, + imageId: 1673, + imagePath: 'wp-content/uploads/2019/01/VISIT_US3.jpg', + childItems: [ + { + name: 'Tickets', + id: 1680, + path: `https://tickets.australiazoo.com.au`, + }, + { + name: 'Annual Pass', + id: 1681, + path: `${mainPaths.Visit.path}/annual-pass/`, + }, + { + name: 'Zoo Map', + id: 1627, + path: `${mainPaths.Visit.path}/zoo-map/`, + }, + { + name: 'Event Calendar', + id: 1628, + path: `${mainPaths.Visit.path}/event-calendar/`, + }, + { + name: 'How to Get Here', + id: 1629, + path: `${mainPaths.Visit.path}/how-to-get-here/`, + }, + { + name: 'Plan Your Day', + id: 1678, + path: `${mainPaths.Visit.path}/plan-your-day/`, + }, + { + name: 'Where to Eat', + id: 1630, + path: `${mainPaths.Visit.path}/where-to-eat/`, + }, + { + name: 'Where to Shop', + id: 1631, + path: `${mainPaths.Visit.path}/where-to-shop/`, + }, + { + name: 'Where to Stay', + id: 1632, + path: `${mainPaths.Visit.path}/where-to-stay/`, + }, + { + name: 'How to Prepare', + id: 1633, + path: `${mainPaths.Visit.path}/how-to-prepare/`, + }, + { + name: 'Services and Facilities', + id: 1676, + path: `${mainPaths.Visit.path}/services-facilities/`, + }, + { + name: 'Birthday Club', + id: 2169, + path: + 'https://onlinesales.centaman.net/AustraliaZoo/JoinTheClub/BirthdayClub/tabid/72/Default.aspx', + }, + { + name: 'Chinese Information', + id: 1677, + path: `${mainPaths.Visit.path}/chinese-information/`, + }, + ], + }, + { + name: mainPaths.About.name, + id: 1634, + path: mainPaths.About.path, + imageId: 1672, + imagePath: 'wp-content/uploads/2019/01/ABOUT_US3.jpg', + childItems: [ + { + name: 'The Irwin Family', + id: 1635, + path: `${mainPaths.About.path}/the-irwins/`, + }, + { + name: 'History', + id: 1637, + path: `${mainPaths.About.path}/history/`, + }, + { + name: 'Awards', + id: 3222, + path: `${mainPaths.About.path}/awards/`, + }, + { + name: 'Our Mission', + id: 1638, + path: `${mainPaths.About.path}/our-mission/`, + }, + { + name: 'Community Partners', + id: 1679, + path: `${mainPaths.About.path}/community-partners/`, + }, + { + name: 'Our Sponsors', + id: 1640, + path: `${mainPaths.About.path}/our-sponsors/`, + }, + { + name: 'Employment', + id: 1932, + path: `${mainPaths.About.path}/employment/`, + }, + { + name: 'Media Resources / Enquiry', + id: 1688, + path: `${mainPaths.About.path}/media-enquiry/`, + }, + { + name: 'Crikey Magazine', + id: 1686, + path: `${mainPaths.About.path}/crikey-mag/`, + }, + { + name: 'Crikey! Club', + id: 1755, + path: `https://crikeyclub.com.au/login.php`, + }, + { + name: 'FAQs', + id: 1636, + path: `${mainPaths.About.path}/faq/`, + }, + { + name: 'Contact Us', + id: 1657, + path: 'contact/', + }, + ], + }, +] + +storiesOf('DropdownNavBar', module).add('default', () => ( + +)) diff --git a/yarn.lock b/yarn.lock index 31a17ecb..2601620c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4390,6 +4390,11 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= +"consolidated-events@^1.1.1 || ^2.0.0": + version "2.0.2" + resolved "https://registry.yarnpkg.com/consolidated-events/-/consolidated-events-2.0.2.tgz#da8d8f8c2b232831413d9e190dc11669c79f4a91" + integrity sha512-2/uRVMdRypf5z/TW/ncD/66l75P5hH2vM/GR8Jf8HLc2xnfJtmina6F6du8+v4Z2vTrMo7jC+W1tmEEuuELgkQ== + constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" @@ -5091,6 +5096,13 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +document.contains@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/document.contains/-/document.contains-1.0.2.tgz#4260abad67a6ae9e135c1be83d68da0db169d5f0" + integrity sha512-YcvYFs15mX8m3AO1QNQy3BlIpSMfNRj3Ujk2BEJxsZG+HZf7/hZ6jr7mDpXrF8q+ff95Vef5yjhiZxm8CGJr6Q== + dependencies: + define-properties "^1.1.3" + documentation@^12.1.1: version "12.1.3" resolved "https://registry.yarnpkg.com/documentation/-/documentation-12.1.3.tgz#b4434e813c9724d9216685d187742b029c9efa7f" @@ -10955,6 +10967,17 @@ react-onclickoutside@^6.7.1: resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.9.0.tgz#a54bc317ae8cf6131a5d78acea55a11067f37a1f" integrity sha512-8ltIY3bC7oGhj2nPAvWOGi+xGFybPNhJM0V1H8hY/whNcXgmDeaeoCMPPd8VatrpTsUWjb/vGzrmu6SrXVty3A== +react-outside-click-handler@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/react-outside-click-handler/-/react-outside-click-handler-1.3.0.tgz#3831d541ac059deecd38ec5423f81e80ad60e115" + integrity sha512-Te/7zFU0oHpAnctl//pP3hEAeobfeHMyygHB8MnjP6sX5OR8KHT1G3jmLsV3U9RnIYo+Yn+peJYWu+D5tUS8qQ== + dependencies: + airbnb-prop-types "^2.15.0" + consolidated-events "^1.1.1 || ^2.0.0" + document.contains "^1.0.1" + object.values "^1.1.0" + prop-types "^15.7.2" + react-popper-tooltip@^2.8.3: version "2.10.0" resolved "https://registry.yarnpkg.com/react-popper-tooltip/-/react-popper-tooltip-2.10.0.tgz#4d8383644d1002a50bd2bf74b2d1214d84ffc77c" From 60ffbf62b262063eb3931bc6260b75e2107eaff9 Mon Sep 17 00:00:00 2001 From: Danny Parnella Date: Mon, 12 Apr 2021 17:38:17 -0500 Subject: [PATCH 02/25] various updates to clean up code --- .storybook/styles/base/_typography.scss | 5 - .../styles/components/_dropdown-nav-bar.scss | 353 ++++-------------- src/controls/dropdown-nav-bar.js | 5 - src/controls/dropdown-nav-menu-item.js | 63 ++-- src/controls/dropdown-nav-menu-sub-item.js | 14 +- src/controls/dropdown-nav-menu.js | 19 +- src/controls/helpers/get-nav-link.js | 4 +- src/controls/helpers/nav-prop-types.js | 3 - stories/controls/dropdown-nav-bar.story.js | 207 ++-------- 9 files changed, 135 insertions(+), 538 deletions(-) diff --git a/.storybook/styles/base/_typography.scss b/.storybook/styles/base/_typography.scss index 78ef7a16..3093b52c 100755 --- a/.storybook/styles/base/_typography.scss +++ b/.storybook/styles/base/_typography.scss @@ -102,11 +102,6 @@ a { &:hover, &:focus { border-bottom: 2px solid $blue-base; } - - &:active, - &:focus { - outline: none; - } } .edit-link { diff --git a/.storybook/styles/components/_dropdown-nav-bar.scss b/.storybook/styles/components/_dropdown-nav-bar.scss index 17d2c9a6..134f26f6 100644 --- a/.storybook/styles/components/_dropdown-nav-bar.scss +++ b/.storybook/styles/components/_dropdown-nav-bar.scss @@ -1,5 +1,3 @@ -$down-arrow-image-url: url(https://www.australiazoo.com.au/wp-content/themes/some-like-it-neat/assets/images/down-arrow.svg); - #dropdown-nav-bar { input[type='checkbox'] { position: absolute; @@ -13,13 +11,10 @@ $down-arrow-image-url: url(https://www.australiazoo.com.au/wp-content/themes/som overflow: auto; display: block; height: 100%; + width: 100%; } - & ~ .nav { - height: 100%; - } - - & ~ label { + & ~ .mobile-menu { span:nth-child(1) { transform: translateY(8px) rotate(45deg); } @@ -33,323 +28,109 @@ $down-arrow-image-url: url(https://www.australiazoo.com.au/wp-content/themes/som } } + .mobile-menu { + position: absolute; + right: 15px; + z-index: 3; + cursor: pointer; + + span { + display: block; + margin: 4px auto; + height: 4px; + width: 25px; + background: #333; + transition: 0.5s; + } + } + .primary-menu { display: none; - background: $white-base; - padding-top: 30px; @include media($tablet) { display: block; } - li { - margin: 20px 0px 0px; - padding: 0px 20px; + .menu-item { + margin-top: 30px; cursor: pointer; + font-size: 20px; + text-align: left; + width: inherit; @include media($tablet) { - margin: 20px; - padding: 0px; - } - - @include media($tablet) { - margin: 0px 0px 0px 2%; - padding-bottom: 13px; - } - - &:last-child { - margin-bottom: 20px; - } - - &:before { - position: absolute; - bottom: -6px; - content: ''; - height: 3px; - width: 0; - background: #333; - transition: 0.5s; + display: inline-block; + margin: 0; + margin-right: 30px; } a { @include rem(font-size, 20px); @include media($tablet) { - font-weight: 600; - letter-spacing: 1px; - text-transform: uppercase; @include s-base; } } - } - } - - .sub-menu { - display: none; - margin-top: 13px; - width: 100%; - z-index: 3; - - @include media($tablet) { - @include rem(padding, 40px); - padding-right: 280px; - min-height: 330px; - box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); - position: absolute; - background-color: $white-base; - border-top: 3px solid $green-dark; - border-bottom-right-radius: 10px; - border-bottom-left-radius: 10px; - columns: 2; - max-width: 900px; - } - li { - padding: 0px; - - @include media($tablet) { - padding-bottom: 20px; + /* Show the dropdown menu on hover (except on mobile) or active touch state */ + &:hover { + .sub-menu { + @include media($tablet) { + display: block; + } + } } - } - - a { - color: black; - padding: 0px; - text-decoration: none; - display: block; - letter-spacing: 0px; - font-size: 20px; - font-weight: 100; - text-transform: none; - @include rem(padding-bottom, 0px); - - span { - font-size: 20px; + &.active { + .sub-menu { + display: block; + } } - &:after { - display: none; - } - } + .menu-item-button { + display: inline-block; + padding: 0 18px; + vertical-align: top; + background: none; + width: auto; + border: none; + + &:after { + content: 'v'; + display: inline-block; + font-size: 20px; + } - .menu-item { - text-align: left; - margin: 0px; - width: inherit; + @include media($tablet) { + display: none; + vertical-align: initial; - a { - margin: 20px 0px 0px; + &.desktop-visible, + &:focus { + display: inline-block; + } + } } } - .nav-image { + .sub-menu { display: none; + padding: 10px 0 20px; @include media($tablet) { - display: block; position: absolute; - right: 0px; - width: 240px; - top: 35px; - } - - img { - border-top-left-radius: 10px; - border-bottom-left-radius: 10px; - height: auto; - } - - span { - display: none; + background-color: $white-base; + padding: 5px 15px; } - } - } - - @include media($tablet) { - } -} - -.mobile-menu { - float: right; - position: absolute; - right: 15px; - z-index: 3; - cursor: pointer; - @include media($tablet) { - display: none; - } - - label { - position: absolute; - display: none; - top: 0; - left: 0; - height: 70px; - width: 70px; - cursor: pointer; - } - - span { - display: block; - margin: 4px auto; - height: 4px; - width: 25px; - border-radius: 1px; - background: #333; - transition: 0.5s; - - &:nth-child(1) { - margin-top: 15px; - } - } -} - -.menu-item { - display: inline-block; - text-transform: uppercase; - font-weight: bold; - font-size: 20px; - width: 100%; - - @include media($tablet) { - @include rem(padding-bottom, 20px); - width: auto; - } - - &:hover, - &.active { - &.menu-item-has-children a { - position: relative; + .menu-item { + padding: 0; + margin-top: 15px; + display: block; - &:after { @include media($tablet) { - content: ''; - width: 0; - height: 0; - border-left: 10px solid transparent; - border-right: 10px solid transparent; - border-bottom: 10px solid $green-dark; - position: absolute; - transform: translateX(-50%); - left: 50%; - bottom: -17px; + padding-bottom: 20px; } } } } } - -.mobile-nav-buttons { - display: inline-block; - width: 100%; - - @include media($tablet) { - display: none; - } - - a, - button { - border-radius: 0px; - display: inline-block; - text-align: center; - width: 100%; - padding: 16px 25px; - } -} - -#menu-primary-navigation .menu-item-has-children { - /* Show the dropdown menu on hover (except on mobile) or active touch state */ - &:hover { - .sub-menu { - @include media($tablet) { - display: block; - } - } - } - &.active { - .sub-menu { - display: block; - } - } - - .menu-item-button { - display: inline-block; - @include rem(padding, 10px 10px 10px 20px); - background: none; - width: auto; - - &:after { - content: ''; - background-image: $down-arrow-image-url; - background-size: 100%; - display: inline-block; - height: 12px; - width: 18px; - } - - @include media($tablet) { - display: none; - - &.desktop-visible, - &:focus { - display: inline-block; - } - } - .link-secondary.logout { - display: none; - } - } -} - -@mixin nav-button { - color: $white-base; - border-radius: 8px; - cursor: pointer; - font-weight: 800; - letter-spacing: 1px; - text-transform: uppercase; - font-size: 12px; - -webkit-box-shadow: 0px 14px 28px -11px rgba(0, 0, 0, 0.45); - -moz-box-shadow: 0px 14px 28px -11px rgba(0, 0, 0, 0.45); - box-shadow: 0px 14px 28px -11px rgba(0, 0, 0, 0.45); - display: inline; - transition: all 0.3s ease-in-out; - font-family: 'Open Sans', sans-serif; - @include rem(padding, 13px 22px); - line-height: initial; - - a { - color: $white-base; - } -} - -.button { - @include nav-button; - background-color: orange; - - &:hover { - color: $white-base; - background-color: darken(orange, 20%); - } -} - -.button-green { - background-color: $green-base; - - &:hover { - background-color: darken($green-base, 20%); - } - - a { - color: $white-base; - } -} - -.button-green-dark { - background-color: $green-dark; - - &:hover { - background-color: darken($green-dark, 5%); - } -} diff --git a/src/controls/dropdown-nav-bar.js b/src/controls/dropdown-nav-bar.js index a7cc938c..2ca01b65 100644 --- a/src/controls/dropdown-nav-bar.js +++ b/src/controls/dropdown-nav-bar.js @@ -11,11 +11,6 @@ import DropdownNavMenu from './dropdown-nav-menu' * @name DropdownNavBar * @type Function * @description A control component for navigating among multiple navigation menu items that can include dropdowns with sub-menu items - * @param {Boolean} [vertical] A boolean setting the `className` of the `ul` to 'horizontal' (default), or 'vertical', which determines the alignment of the tabs (optional, default `false`) - * @param {Array} [options] An array of tab values (strings or key-value pairs) - * @param {String|Number} [value] - The value of the current tab - * @param {Function} [onChange] - A function called with the new value when a tab is clicked - * @param {String} [activeClassName] - The class of the active tab, (optional, default `active`) * @param {String} [baseUrl] - * @param {Number} [mobileBreakpoint] - * @param {Array} [menuItems] - diff --git a/src/controls/dropdown-nav-menu-item.js b/src/controls/dropdown-nav-menu-item.js index b7083457..2fb22b6f 100644 --- a/src/controls/dropdown-nav-menu-item.js +++ b/src/controls/dropdown-nav-menu-item.js @@ -7,24 +7,25 @@ import { getNavLink } from './helpers' const propTypes = { name: PropTypes.string.isRequired, id: PropTypes.number.isRequired, - baseURL: PropTypes.string.isRequired, + baseUrl: PropTypes.string.isRequired, path: PropTypes.string.isRequired, active: PropTypes.bool, closeSubmenu: PropTypes.func.isRequired, onInteractParentMenu: PropTypes.func.isRequired, toggleActiveMenuId: PropTypes.func.isRequired, isFirstItem: PropTypes.bool.isRequired, - children: PropTypes.node.isRequired, + children: PropTypes.node, } const defaultProps = { active: false, + children: null, } function DropdownNavMenuItem({ name, id, - baseURL, + baseUrl, path, active, onInteractParentMenu, @@ -40,12 +41,9 @@ function DropdownNavMenuItem({
  • setShowDropdownButton(false), 10) }} onTouchEnd={(e) => onInteractParentMenu(e, id)} - href={getNavLink(baseURL, path)} - className="menu-image-title-after" + href={getNavLink(baseUrl, path)} > {name} -
  • diff --git a/src/controls/dropdown-nav-menu-sub-item.js b/src/controls/dropdown-nav-menu-sub-item.js index ac7125a4..8147bc48 100644 --- a/src/controls/dropdown-nav-menu-sub-item.js +++ b/src/controls/dropdown-nav-menu-sub-item.js @@ -4,9 +4,8 @@ import { getNavLink } from './helpers' const propTypes = { name: PropTypes.string.isRequired, - id: PropTypes.number.isRequired, path: PropTypes.string.isRequired, - baseURL: PropTypes.string.isRequired, + baseUrl: PropTypes.string.isRequired, isLastItem: PropTypes.bool.isRequired, closeSubmenu: PropTypes.func.isRequired, } @@ -15,17 +14,13 @@ const defaultProps = {} function DropdownNavMenuSubItem({ name, - id, - baseURL, + baseUrl, path, isLastItem, closeSubmenu, }) { return ( -
  • +
  • { if ( @@ -37,8 +32,7 @@ function DropdownNavMenuSubItem({ closeSubmenu() } }} - href={getNavLink(baseURL, path)} - className="menu-image-title-after" + href={getNavLink(baseUrl, path)} > {name} diff --git a/src/controls/dropdown-nav-menu.js b/src/controls/dropdown-nav-menu.js index 8fd62225..512a7b43 100644 --- a/src/controls/dropdown-nav-menu.js +++ b/src/controls/dropdown-nav-menu.js @@ -6,28 +6,25 @@ import DropdownNavMenuSubItem from './dropdown-nav-menu-sub-item' import { isEmpty, first, last } from 'lodash' const propTypes = { - baseURL: PropTypes.string, + baseUrl: PropTypes.string, menuItems: PropTypes.arrayOf(menuItemType).isRequired, activeMenuIds: PropTypes.arrayOf(PropTypes.number).isRequired, onInteractParentMenu: PropTypes.func.isRequired, toggleActiveMenuId: PropTypes.func.isRequired, closeSubmenu: PropTypes.func.isRequired, - children: PropTypes.node, } const defaultProps = { - baseURL: '', - children: null, + baseUrl: '', } function DropdownNavMenu({ - baseURL, + baseUrl, menuItems, activeMenuIds, onInteractParentMenu, toggleActiveMenuId, closeSubmenu, - children, }) { return (
    @@ -39,7 +36,7 @@ function DropdownNavMenu({ key={index} name={parentItem.name} id={parentItem.id} - baseURL={baseURL} + baseUrl={baseUrl} path={parentItem.path} active={activeMenuIds.includes(parentItem.id)} onInteractParentMenu={onInteractParentMenu} @@ -48,7 +45,7 @@ function DropdownNavMenu({ isFirstItem={isFirstParentItem} > {parentItem.childItems && !isEmpty(parentItem.childItems) && ( - +
      {parentItem.childItems.map((childItem, index) => { const isLastChildItem = childItem === last(parentItem.childItems) @@ -56,21 +53,19 @@ function DropdownNavMenu({ ) })} - +
    )} ) })} - {children}
    ) } diff --git a/src/controls/helpers/get-nav-link.js b/src/controls/helpers/get-nav-link.js index 710751de..c5dc6eef 100644 --- a/src/controls/helpers/get-nav-link.js +++ b/src/controls/helpers/get-nav-link.js @@ -1,8 +1,8 @@ // Return nav link using paths or full url if full link is provided -function getNavLink(baseURL, path) { +function getNavLink(baseUrl, path) { if (path.startsWith('http')) return path - return `${baseURL}/${path}` + return `${baseUrl}/${path}` } export default getNavLink diff --git a/src/controls/helpers/nav-prop-types.js b/src/controls/helpers/nav-prop-types.js index 46721004..5b04fb93 100644 --- a/src/controls/helpers/nav-prop-types.js +++ b/src/controls/helpers/nav-prop-types.js @@ -10,7 +10,6 @@ import PropTypes from 'prop-types' const childItemType = PropTypes.shape({ name: PropTypes.string.isRequired, - id: PropTypes.number.isRequired, path: PropTypes.string.isRequired, }) @@ -26,7 +25,5 @@ export const menuItemType = PropTypes.shape({ name: PropTypes.string.isRequired, id: PropTypes.number.isRequired, path: PropTypes.string.isRequired, - imageId: PropTypes.number.isRequired, - imagePath: PropTypes.string.isRequired, childItems: PropTypes.arrayOf(childItemType), }) diff --git a/stories/controls/dropdown-nav-bar.story.js b/stories/controls/dropdown-nav-bar.story.js index b7fe118b..b41a571b 100644 --- a/stories/controls/dropdown-nav-bar.story.js +++ b/stories/controls/dropdown-nav-bar.story.js @@ -5,236 +5,77 @@ import dynamicInput from '../dynamic-input' const DropdownNavBar = dynamicInput({})(StaticDropdownNavBar) -const mainPaths = { - Exp: { name: 'Experiences', path: 'experiences-australia-zoo' }, - Wild: { name: 'Wildlife', path: 'wildlife' }, - Visit: { name: 'Visit Us', path: 'visit-us' }, - About: { name: 'About Us', path: 'about-us' }, -} +const path = '' const menuItems = [ { - name: mainPaths.Exp.name, - id: 1613, - path: mainPaths.Exp.path, - imageId: 1675, - imagePath: 'wp-content/uploads/2019/01/EXPERIENCES3-1.jpg', + name: 'Experiences', + id: 1, + path, childItems: [ { name: 'Animal Encounters', - id: 1614, - path: `${mainPaths.Exp.path}/animal-encounters/`, + path, }, { name: 'Zoo Keeper for a Day', - id: 1615, - path: `${mainPaths.Exp.path}/zoo-keeper-for-a-day/`, + path, }, { name: 'Wildlife Photos', - id: 1616, - path: `${mainPaths.Exp.path}/wildlife-photos/`, + path, }, { name: 'Exhibits', - id: 1617, - path: `${mainPaths.Exp.path}/exhibits/`, - }, - { - name: 'Conservation Shows', - id: 1618, - path: `${mainPaths.Exp.path}/show-times/`, - }, - { - name: 'Tours', - id: 1619, - path: `${mainPaths.Exp.path}/tours/`, - }, - { - name: 'Expeditions', - id: 2823, - path: `${mainPaths.Exp.path}/australia-zoo-expeditions/`, - }, - { - name: 'Gift Ideas', - id: 2393, - path: `${mainPaths.Exp.path}/gift-ideas/`, + path, }, ], }, { - name: mainPaths.Wild.name, - id: 1622, - path: mainPaths.Wild.path, - imageId: 1674, - imagePath: 'wp-content/uploads/2019/01/WILDLIFE3.jpg', + name: 'Wildlife', + id: 2, + path, childItems: [ { name: 'Our Animals', - id: 1623, - path: `${mainPaths.Wild.path}/our-animals/`, + path, }, { name: 'Zoo Flora', - id: 1624, - path: `${mainPaths.Wild.path}/zoo-flora/`, + path, }, { name: 'Top 10 Highlights', - id: 1625, - path: `${mainPaths.Wild.path}/top-10-highlights/`, + path, }, ], }, { - name: mainPaths.Visit.name, - id: 1626, - path: mainPaths.Visit.path, - imageId: 1673, - imagePath: 'wp-content/uploads/2019/01/VISIT_US3.jpg', - childItems: [ - { - name: 'Tickets', - id: 1680, - path: `https://tickets.australiazoo.com.au`, - }, - { - name: 'Annual Pass', - id: 1681, - path: `${mainPaths.Visit.path}/annual-pass/`, - }, - { - name: 'Zoo Map', - id: 1627, - path: `${mainPaths.Visit.path}/zoo-map/`, - }, - { - name: 'Event Calendar', - id: 1628, - path: `${mainPaths.Visit.path}/event-calendar/`, - }, - { - name: 'How to Get Here', - id: 1629, - path: `${mainPaths.Visit.path}/how-to-get-here/`, - }, - { - name: 'Plan Your Day', - id: 1678, - path: `${mainPaths.Visit.path}/plan-your-day/`, - }, - { - name: 'Where to Eat', - id: 1630, - path: `${mainPaths.Visit.path}/where-to-eat/`, - }, - { - name: 'Where to Shop', - id: 1631, - path: `${mainPaths.Visit.path}/where-to-shop/`, - }, - { - name: 'Where to Stay', - id: 1632, - path: `${mainPaths.Visit.path}/where-to-stay/`, - }, - { - name: 'How to Prepare', - id: 1633, - path: `${mainPaths.Visit.path}/how-to-prepare/`, - }, - { - name: 'Services and Facilities', - id: 1676, - path: `${mainPaths.Visit.path}/services-facilities/`, - }, - { - name: 'Birthday Club', - id: 2169, - path: - 'https://onlinesales.centaman.net/AustraliaZoo/JoinTheClub/BirthdayClub/tabid/72/Default.aspx', - }, - { - name: 'Chinese Information', - id: 1677, - path: `${mainPaths.Visit.path}/chinese-information/`, - }, - ], + name: 'Visit Us', + id: 3, + path: 'https://goo.gl/maps/oGeajN5N1Ycy1D4J8', }, { - name: mainPaths.About.name, - id: 1634, - path: mainPaths.About.path, - imageId: 1672, - imagePath: 'wp-content/uploads/2019/01/ABOUT_US3.jpg', + name: 'About Us', + id: 4, + path, childItems: [ { name: 'The Irwin Family', - id: 1635, - path: `${mainPaths.About.path}/the-irwins/`, + path, }, { name: 'History', - id: 1637, - path: `${mainPaths.About.path}/history/`, - }, - { - name: 'Awards', - id: 3222, - path: `${mainPaths.About.path}/awards/`, + path, }, { name: 'Our Mission', - id: 1638, - path: `${mainPaths.About.path}/our-mission/`, - }, - { - name: 'Community Partners', - id: 1679, - path: `${mainPaths.About.path}/community-partners/`, - }, - { - name: 'Our Sponsors', - id: 1640, - path: `${mainPaths.About.path}/our-sponsors/`, - }, - { - name: 'Employment', - id: 1932, - path: `${mainPaths.About.path}/employment/`, - }, - { - name: 'Media Resources / Enquiry', - id: 1688, - path: `${mainPaths.About.path}/media-enquiry/`, - }, - { - name: 'Crikey Magazine', - id: 1686, - path: `${mainPaths.About.path}/crikey-mag/`, - }, - { - name: 'Crikey! Club', - id: 1755, - path: `https://crikeyclub.com.au/login.php`, - }, - { - name: 'FAQs', - id: 1636, - path: `${mainPaths.About.path}/faq/`, - }, - { - name: 'Contact Us', - id: 1657, - path: 'contact/', + path, }, ], }, ] storiesOf('DropdownNavBar', module).add('default', () => ( - + )) From 7b4970701854b2c148463f42944fd52e08948c78 Mon Sep 17 00:00:00 2001 From: Danny Parnella Date: Tue, 13 Apr 2021 11:35:33 -0500 Subject: [PATCH 03/25] update id/class names, add aria-label for menu --- .../styles/components/_dropdown-nav-bar.scss | 6 +- src/controls/dropdown-nav-bar.js | 7 +- src/controls/dropdown-nav-menu.js | 86 +++++++++---------- 3 files changed, 50 insertions(+), 49 deletions(-) diff --git a/.storybook/styles/components/_dropdown-nav-bar.scss b/.storybook/styles/components/_dropdown-nav-bar.scss index 134f26f6..33c88281 100644 --- a/.storybook/styles/components/_dropdown-nav-bar.scss +++ b/.storybook/styles/components/_dropdown-nav-bar.scss @@ -1,11 +1,11 @@ -#dropdown-nav-bar { +.dropdown-nav-bar { input[type='checkbox'] { position: absolute; top: -9999px; left: -9999px; &:checked { - & ~ .primary-menu { + & ~ .dropdown-nav-menu { top: 0; position: fixed; overflow: auto; @@ -44,7 +44,7 @@ } } - .primary-menu { + .dropdown-nav-menu { display: none; @include media($tablet) { diff --git a/src/controls/dropdown-nav-bar.js b/src/controls/dropdown-nav-bar.js index 2ca01b65..11b3e2ed 100644 --- a/src/controls/dropdown-nav-bar.js +++ b/src/controls/dropdown-nav-bar.js @@ -37,14 +37,16 @@ const propTypes = { menuItems: PropTypes.arrayOf(menuItemType).isRequired, mobileBreakpoint: PropTypes.number, baseUrl: PropTypes.string, + menuLabel: PropTypes.string, } const defaultProps = { mobileBreakpoint: 1024, baseUrl: '', + menuLabel: 'Primary navigation', } -function DropdownNavBar({ menuItems, mobileBreakpoint, baseUrl }) { +function DropdownNavBar({ menuItems, mobileBreakpoint, baseUrl, menuLabel }) { const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false) const [activeMenuIds, setActiveMenuIds] = useState([]) const clearActiveMenuIds = () => { @@ -78,7 +80,7 @@ function DropdownNavBar({ menuItems, mobileBreakpoint, baseUrl }) { ) return ( - ) diff --git a/src/controls/dropdown-nav-menu-item.js b/src/controls/dropdown-nav-menu-item.js index 02268614..36e8352c 100644 --- a/src/controls/dropdown-nav-menu-item.js +++ b/src/controls/dropdown-nav-menu-item.js @@ -10,11 +10,11 @@ const propTypes = { path: PropTypes.string.isRequired, isExternalPath: PropTypes.bool.isRequired, isSubmenuOpen: PropTypes.bool, - closeDesktopSubmenu: PropTypes.func.isRequired, + closeDesktopSubmenus: PropTypes.func.isRequired, toggleSubmenu: PropTypes.func.isRequired, isFirstItem: PropTypes.bool.isRequired, children: PropTypes.node, - hideMenuButtonBeforeFocus: PropTypes.bool.isRequired, + hideSubmenuButtonBeforeFocus: PropTypes.bool.isRequired, } const defaultProps = { @@ -28,24 +28,24 @@ function DropdownNavMenuItem({ path, isExternalPath, isSubmenuOpen, - closeDesktopSubmenu, + closeDesktopSubmenus, toggleSubmenu, isFirstItem, children, - hideMenuButtonBeforeFocus, + hideSubmenuButtonBeforeFocus, }) { - // show menu button on desktop, will always be shown on mobile - const [showMenuButton, setShowMenuButton] = useState( - !hideMenuButtonBeforeFocus + // show submenu button on desktop, will always be shown on mobile + const [showSubmenuButton, setShowSubmenuButton] = useState( + !hideSubmenuButtonBeforeFocus ) const menuItemOnFocus = () => { - if (hideMenuButtonBeforeFocus) setShowMenuButton(true) - closeDesktopSubmenu() + if (hideSubmenuButtonBeforeFocus) setShowSubmenuButton(true) + closeDesktopSubmenus() } const menuItemOnBlur = () => { - if (hideMenuButtonBeforeFocus) { + if (hideSubmenuButtonBeforeFocus) { // timeout needed to move from link to button without it disappearing - setTimeout(() => setShowMenuButton(false), 0) + setTimeout(() => setShowSubmenuButton(false), 0) } } const menuItemProps = { @@ -61,7 +61,7 @@ function DropdownNavMenuItem({ })} role="none" > - + {isExternalPath ? ( {name} @@ -75,7 +75,7 @@ function DropdownNavMenuItem({