Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Responsive Navigation #30047

Merged
merged 35 commits into from
May 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
6d9f03b
Implement responsive navigation menu
Apr 9, 2021
885fcb5
only hide closing button if menu is closed
Apr 9, 2021
fa50591
make sure frontend.js is only being loaded once
Apr 9, 2021
f433a9a
Fix issues with open modal.
jasmussen Apr 9, 2021
7729c3f
make sure scripts are only loaded once
vcanales Apr 15, 2021
ab29f51
Add a focus z index so focus isn't cropped.
jasmussen Apr 16, 2021
6ea6ed1
Fix ariaHidden
jasmussen Apr 16, 2021
f40546d
Fix color issue.
jasmussen Apr 16, 2021
0c2a4dc
Fix safari issue.
jasmussen Apr 16, 2021
3df9544
only enqueue frontend script if needed
vcanales Apr 16, 2021
a7dac30
use isResponsive instead of responsiveNavigation
vcanales Apr 16, 2021
2eb3b68
Fix modal id
vcanales Apr 29, 2021
82f85e1
Update package-lock, move deps to correct place
vcanales Apr 29, 2021
f80f54a
Fix aria-hidden label in the editor
vcanales Apr 30, 2021
d22e43d
Set responsiveness off by default
vcanales May 3, 2021
3808578
Add missing aria attributes
vcanales May 3, 2021
637bb09
update e2e fixture
vcanales May 4, 2021
2cd59a7
remove unnecessary context declaration
vcanales May 4, 2021
ccd361f
remove file existence check
vcanales May 4, 2021
dadce1c
Tests responsiveness on preview page
vcanales May 7, 2021
a59dff6
Refactor server side render of nav menu
vcanales May 7, 2021
986c192
Make sure toggle button labels are translatable
vcanales May 7, 2021
bc9bb9c
Only render open button if menu is closed
vcanales May 7, 2021
0d12f7c
Remove duplicate CSS from rebase.
jasmussen May 5, 2021
a12aeea
Fix focus cropping issue.
jasmussen May 5, 2021
7065648
Simplify overlay style.
jasmussen May 5, 2021
5da3bfd
Fix for page list.
jasmussen May 5, 2021
1b9885f
Remove a few todos.
jasmussen May 5, 2021
dd97947
Fix overlay on desktop breakpoints style, and focus styles.
jasmussen May 6, 2021
dc9d315
Small transparency fix.
jasmussen May 10, 2021
516e7a6
keep parameter order consistency
vcanales May 11, 2021
10adcaa
Address feedback
vcanales May 11, 2021
4b0ecda
Update packages/block-library/src/navigation/frontend.js
vcanales May 11, 2021
b2990cd
remove no-longer needed css rules
vcanales May 11, 2021
2769bd2
edit redundant callback definition
vcanales May 11, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.19",
"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 @@ -58,13 +59,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 @@ -148,11 +153,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 @@ -316,3 +316,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