Skip to content

Commit

Permalink
Responsive Navigation (#30047)
Browse files Browse the repository at this point in the history
* Implement responsive navigation menu

Gives the user the option to have the Navigation Block show up behind a
button on smaller screens.
This menu can be controlled with the keyboard.

* only hide closing button if menu is closed

Make sure that the button does not disappear when resizing the viewport
while the menu is open.

* make sure frontend.js is only being loaded once

* Fix issues with open modal.

* make sure scripts are only loaded once

* Add a focus z index so focus isn't cropped.

* Fix ariaHidden

* Fix color issue.

* Fix safari issue.

* only enqueue frontend script if needed

Needed when:
- Responsive navigation is on.
- Script hasn't been loaded already.

Also, only attempt to load the file if it exists. This only really guards against
potential build problems, as in theory it should never be absent.

* use isResponsive instead of responsiveNavigation

* Fix modal id

* Update package-lock, move deps to correct place

* Fix aria-hidden label in the editor

* Set responsiveness off by default

* Add missing aria attributes

* update e2e fixture

* remove unnecessary context declaration

* remove file existence check

* Tests responsiveness on preview page

Also add function to turn responsiveness on.

* Refactor server side render of nav menu

Early return if responsiveness is off.

* Make sure toggle button labels are translatable

* Only render open button if menu is closed

* Remove duplicate CSS from rebase.

* Fix focus cropping issue.

* Simplify overlay style.

* Fix for page list.

* Remove a few todos.

* Fix overlay on desktop breakpoints style, and focus styles.

* Small transparency fix.

* keep parameter order consistency

* Address feedback

* Update packages/block-library/src/navigation/frontend.js

Co-authored-by: Miguel Fonseca <miguelcsf@gmail.com>

* remove no-longer needed css rules

* edit redundant callback definition

Co-authored-by: jasmussen <joen@automattic.com>
Co-authored-by: Miguel Fonseca <miguelcsf@gmail.com>
  • Loading branch information
3 people authored May 12, 2021
1 parent 854a31f commit b9ab1a8
Show file tree
Hide file tree
Showing 14 changed files with 598 additions and 21 deletions.
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/block-library/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"fast-average-color": "4.3.0",
"lodash": "^4.17.21",
"memize": "^1.1.0",
"micromodal": "^0.4.6",
"moment": "^2.22.1",
"react-easy-crop": "^3.0.0",
"tinycolor2": "^1.4.2"
Expand Down
4 changes: 3 additions & 1 deletion packages/block-library/src/navigation-link/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"name": "core/navigation-link",
"title": "Custom Link",
"category": "design",
"parent": [ "core/navigation" ],
"parent": [
"core/navigation"
],
"description": "Add a page, link, or another item to your navigation.",
"textdomain": "default",
"attributes": {
Expand Down
15 changes: 13 additions & 2 deletions packages/block-library/src/navigation/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
"title": "Navigation",
"category": "design",
"description": "A collection of blocks that allow visitors to get around your site.",
"keywords": [ "menu", "navigation", "links" ],
"keywords": [
"menu",
"navigation",
"links"
],
"textdomain": "default",
"attributes": {
"orientation": {
Expand Down Expand Up @@ -34,6 +38,10 @@
"showSubmenuIcon": {
"type": "boolean",
"default": true
},
"isResponsive": {
"type": "boolean",
"default": false
}
},
"providesContext": {
Expand All @@ -48,7 +56,10 @@
"orientation": "orientation"
},
"supports": {
"align": [ "wide", "full" ],
"align": [
"wide",
"full"
],
"anchor": true,
"html": false,
"inserter": true,
Expand Down
23 changes: 22 additions & 1 deletion packages/block-library/src/navigation/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import useBlockNavigator from './use-block-navigator';

import NavigationPlaceholder from './placeholder';
import PlaceholderPreview from './placeholder-preview';
import ResponsiveWrapper from './responsive-wrapper';

const ALLOWED_BLOCKS = [
'core/navigation-link',
Expand Down Expand Up @@ -59,13 +60,17 @@ function Navigation( {
const [ isPlaceholderShown, setIsPlaceholderShown ] = useState(
! hasExistingNavItems
);
const [ isResponsiveMenuOpen, setResponsiveMenuVisibility ] = useState(
false
);

const { selectBlock } = useDispatch( blockEditorStore );

const blockProps = useBlockProps( {
className: classnames( className, {
[ `items-justified-${ attributes.itemsJustification }` ]: attributes.itemsJustification,
'is-vertical': attributes.orientation === 'vertical',
'is-responsive': attributes.isResponsive,
} ),
} );

Expand Down Expand Up @@ -149,11 +154,27 @@ function Navigation( {
} }
label={ __( 'Show submenu indicator icons' ) }
/>
<ToggleControl
checked={ attributes.isResponsive }
onChange={ ( value ) => {
setAttributes( {
isResponsive: value,
} );
} }
label={ __( 'Enable responsive menu' ) }
/>
</PanelBody>
) }
</InspectorControls>
<nav { ...blockProps }>
<ul { ...innerBlocksProps } />
<ResponsiveWrapper
id={ clientId }
onToggle={ setResponsiveMenuVisibility }
isOpen={ isResponsiveMenuOpen }
isResponsive={ attributes.isResponsive }
>
<ul { ...innerBlocksProps }></ul>
</ResponsiveWrapper>
</nav>
</>
);
Expand Down
73 changes: 73 additions & 0 deletions packages/block-library/src/navigation/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -346,3 +346,76 @@ $color-control-label-height: 20px;
margin-right: $grid-unit-15;
}
}


/**
* Mobile menu.
*/

// These needs extra specificity in the editor.
.wp-block-navigation__responsive-container:not(.is-menu-open) {
.components-button.wp-block-navigation__responsive-container-close {
@include break-small {
display: none;
}
}
}
.components-button.wp-block-navigation__responsive-container-open {
@include break-small {
display: none;
}
}

// Emulate the fullscreen editing inside the editor.
.wp-block-navigation__responsive-container.is-menu-open {
position: fixed;

// Handle top position.
// For now, the editing of menu items in the mobile view only happens <600px.
// That means we only have to consider the big adminbar height (<783px).
// And in that view we also know that the toolbar is stacked.
body.admin-bar & {
top: $admin-bar-height-big + $header-height + $block-toolbar-height;

@include break-medium() {
top: $header-height + $border-width;
}
}
}

// Without this, the block cannot be selected, nor does the right container get focus.
// @todo: this is disruptive. Ideally we can retire a few of the containers,
// so focus is applied naturally on the block container.
// It's important the right container has focus, otherwise you can't press
// "Delete" to remove the block.
.wp-block-navigation__responsive-close {
@include break-small() {
pointer-events: none;

.wp-block-navigation__responsive-container-close,
.block-editor-block-list__layout * {
pointer-events: all;
}
}

// Page List items should remain inert.
.wp-block-pages-list__item__link {
pointer-events: none;
}
}

// The menu and close buttons need higher specificity in the editor.
.components-button.wp-block-navigation__responsive-container-open.wp-block-navigation__responsive-container-open,
.components-button.wp-block-navigation__responsive-container-close.wp-block-navigation__responsive-container-close {
padding: 0;
height: auto;
color: inherit;
}

// Customize the mobile editing.
// This can be revisited in the future, but for now, inherit design from the parent.
.is-menu-open .wp-block-navigation__responsive-container-content * {
.block-list-appender {
margin-top: $grid-unit-20;
}
}
23 changes: 23 additions & 0 deletions packages/block-library/src/navigation/frontend.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* External dependencies
*/
import MicroModal from 'micromodal';

function navigationToggleModal( modal ) {
const triggerButton = document.querySelector(
`button[data-micromodal-trigger="${ modal.id }"]`
);
const closeButton = modal.querySelector( 'button[data-micromodal-close]' );
// Use aria-hidden to determine the status of the modal, as this attribute is
// managed by micromodal.
const isHidden = 'true' === modal.getAttribute( 'aria-hidden' );
triggerButton.setAttribute( 'aria-expanded', ! isHidden );
closeButton.setAttribute( 'aria-expanded', ! isHidden );
modal.classList.toggle( 'has-modal-open', ! isHidden );
}

MicroModal.init( {
onShow: navigationToggleModal,
onClose: navigationToggleModal,
openClass: 'is-menu-open',
} );
49 changes: 46 additions & 3 deletions packages/block-library/src/navigation/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,17 @@ function render_block_core_navigation( $attributes, $content, $block ) {
}

unset( $attributes['rgbTextColor'], $attributes['rgbBackgroundColor'] );
$should_load_frontend_script = $attributes['isResponsive'] && ! wp_script_is( 'core_block_navigation_load_frontend_scripts' );

if ( $should_load_frontend_script ) {
wp_enqueue_script(
'core_block_navigation_load_frontend_scripts',
plugins_url( 'frontend.js', __DIR__ . '/navigation/frontend.js' ),
array(),
false,
true
);
}

if ( empty( $block->inner_blocks ) ) {
return '';
Expand All @@ -131,7 +142,8 @@ function render_block_core_navigation( $attributes, $content, $block ) {
$colors['css_classes'],
$font_sizes['css_classes'],
( isset( $attributes['orientation'] ) && 'vertical' === $attributes['orientation'] ) ? array( 'is-vertical' ) : array(),
isset( $attributes['itemsJustification'] ) ? array( 'items-justified-' . $attributes['itemsJustification'] ) : array()
isset( $attributes['itemsJustification'] ) ? array( 'items-justified-' . $attributes['itemsJustification'] ) : array(),
isset( $attributes['isResponsive'] ) && true === $attributes['isResponsive'] ? array( 'is-responsive' ) : array()
);

$inner_blocks_html = '';
Expand All @@ -148,10 +160,40 @@ function render_block_core_navigation( $attributes, $content, $block ) {
)
);

$modal_unique_id = uniqid();

// Determine whether or not navigation elements should be wrapped in the markup required to make it responsive,
// return early if they don't.
if ( ! isset( $attributes['isResponsive'] ) || false === $attributes['isResponsive'] ) {
return sprintf(
'<nav %1$s><ul class="wp-block-navigation__container">%2$s</ul></nav>',
$wrapper_attributes,
$inner_blocks_html
);
}

$responsive_container_markup = sprintf(
'<button aria-expanded="false" aria-haspopup="true" aria-label="%3$s" class="wp-block-navigation__responsive-container-open" data-micromodal-trigger="modal-%1$s"><svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img" aria-hidden="true" focusable="false"><rect x="4" y="7.5" width="16" height="1.5" /><rect x="4" y="15" width="16" height="1.5" /></svg></button>
<div class="wp-block-navigation__responsive-container" id="modal-%1$s" aria-hidden="true">
<div class="wp-block-navigation__responsive-close" tabindex="-1" data-micromodal-close>
<div class="wp-block-navigation__responsive-dialog" role="dialog" aria-modal="true" aria-labelledby="modal-%1$s-title" >
<button aria-label="%4$s" data-micromodal-close class="wp-block-navigation__responsive-container-close"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" role="img" aria-hidden="true" focusable="false"><path d="M13 11.8l6.1-6.3-1-1-6.1 6.2-6.1-6.2-1 1 6.1 6.3-6.5 6.7 1 1 6.5-6.6 6.5 6.6 1-1z"></path></svg></button>
<div class="wp-block-navigation__responsive-container-content" id="modal-%1$s-content">
<ul class="wp-block-navigation__container">%2$s</ul>
</div>
</div>
</div>
</div>',
$modal_unique_id,
$inner_blocks_html,
__( 'Open menu' ), // Open button label.
__( 'Close menu' ) // Close button label.
);

return sprintf(
'<nav %1$s><ul class="wp-block-navigation__container">%2$s</ul></nav>',
'<nav %1$s>%2$s</nav>',
$wrapper_attributes,
$inner_blocks_html
$responsive_container_markup
);
}

Expand Down Expand Up @@ -201,4 +243,5 @@ function block_core_navigation_typographic_presets_backcompatibility( $parsed_bl
}
return $parsed_block;
}

add_filter( 'render_block_data', 'block_core_navigation_typographic_presets_backcompatibility' );
91 changes: 91 additions & 0 deletions packages/block-library/src/navigation/responsive-wrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/**
* External dependencies
*/
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import { close, Icon } from '@wordpress/icons';
import { Button } from '@wordpress/components';
import { SVG, Rect } from '@wordpress/primitives';
import { __ } from '@wordpress/i18n';

export default function ResponsiveWrapper( {
children,
id,
isOpen,
isResponsive,
onToggle,
} ) {
if ( ! isResponsive ) {
return children;
}
const responsiveContainerClasses = classnames(
'wp-block-navigation__responsive-container',
{
'is-menu-open': isOpen,
}
);

const modalId = `${ id }-modal`;

return (
<>
{ ! isOpen && (
<Button
aria-haspopup="true"
aria-expanded={ isOpen }
aria-label={ __( 'Open menu' ) }
className="wp-block-navigation__responsive-container-open"
onClick={ () => onToggle( true ) }
>
<SVG
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
role="img"
aria-hidden="true"
focusable="false"
>
<Rect x="4" y="7.5" width="16" height="1.5" />
<Rect x="4" y="15" width="16" height="1.5" />
</SVG>
</Button>
) }

<div
className={ responsiveContainerClasses }
id={ modalId }
aria-hidden={ ! isOpen }
>
<div
className="wp-block-navigation__responsive-close"
tabIndex="-1"
>
<div
className="wp-block-navigation__responsive-dialog"
role="dialog"
aria-modal="true"
aria-labelledby={ `${ modalId }-title` }
>
<Button
className="wp-block-navigation__responsive-container-close"
aria-label={ __( 'Close menu' ) }
onClick={ () => onToggle( false ) }
>
<Icon icon={ close } />
</Button>
<div
className="wp-block-navigation__responsive-container-content"
id={ `${ modalId }-content` }
>
{ children }
</div>
</div>
</div>
</div>
</>
);
}
Loading

0 comments on commit b9ab1a8

Please sign in to comment.