Skip to content

Commit

Permalink
List View: allow expanding and collapsing of nested blocks (#32117)
Browse files Browse the repository at this point in the history
Co-authored-by: jasmussen <[email protected]>
Co-authored-by: James Koster <[email protected]>
  • Loading branch information
3 people committed Jun 15, 2021
1 parent cb0cec7 commit dbba260
Show file tree
Hide file tree
Showing 12 changed files with 227 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const BlockNavigationBlockContents = forwardRef(
(
{
onClick,
onToggleExpanded,
block,
isSelected,
position,
Expand Down Expand Up @@ -98,7 +99,7 @@ const BlockNavigationBlockContents = forwardRef(
ref={ ref }
className={ className }
block={ block }
onClick={ onClick }
onToggleExpanded={ onToggleExpanded }
isSelected={ isSelected }
position={ position }
siblingBlockCount={ siblingBlockCount }
Expand All @@ -114,6 +115,7 @@ const BlockNavigationBlockContents = forwardRef(
className={ className }
block={ block }
onClick={ onClick }
onToggleExpanded={ onToggleExpanded }
isSelected={ isSelected }
position={ position }
siblingBlockCount={ siblingBlockCount }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ import BlockIcon from '../block-icon';
import useBlockDisplayInformation from '../use-block-display-information';
import { getBlockPositionDescription } from './utils';
import BlockTitle from '../block-title';
import BlockNavigationExpander from './expander';

function BlockNavigationBlockSelectButton(
{
className,
block: { clientId },
isSelected,
onClick,
onToggleExpanded,
position,
siblingBlockCount,
level,
Expand Down Expand Up @@ -61,6 +63,7 @@ function BlockNavigationBlockSelectButton(
onDragEnd={ onDragEnd }
draggable={ draggable }
>
<BlockNavigationExpander onClick={ onToggleExpanded } />
<BlockIcon icon={ blockInformation?.icon } showColors />
<BlockTitle clientId={ clientId } />
{ blockInformation?.anchor && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { BlockListBlockContext } from '../block-list/block';
import BlockNavigationBlockSelectButton from './block-select-button';
import { getBlockPositionDescription } from './utils';
import { store as blockEditorStore } from '../../store';
import BlockNavigationExpander from './expander';

const getSlotName = ( clientId ) => `BlockNavigationBlock-${ clientId }`;

Expand Down Expand Up @@ -57,6 +58,7 @@ function BlockNavigationBlockSlot( props, ref ) {
level,
tabIndex,
onFocus,
onToggleExpanded,
} = props;

const blockType = getBlockType( name );
Expand Down Expand Up @@ -86,6 +88,9 @@ function BlockNavigationBlockSlot( props, ref ) {
className
) }
>
<BlockNavigationExpander
onClick={ onToggleExpanded }
/>
<BlockIcon icon={ blockType.icon } showColors />
{ Children.map( fills, ( fill ) =>
cloneElement( fill, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@ export default function BlockNavigationBlock( {
isBranchSelected,
isLastOfSelectedBranch,
onClick,
onToggleExpanded,
position,
level,
rowCount,
siblingBlockCount,
showBlockMovers,
path,
isExpanded,
} ) {
const cellRef = useRef( null );
const [ isHovered, setIsHovered ] = useState( false );
Expand Down Expand Up @@ -142,6 +144,7 @@ export default function BlockNavigationBlock( {
path={ path }
id={ `block-navigation-block-${ clientId }` }
data-block={ clientId }
isExpanded={ isExpanded }
>
<TreeGridCell
className="block-editor-block-navigation-block__contents-cell"
Expand All @@ -152,7 +155,8 @@ export default function BlockNavigationBlock( {
<div className="block-editor-block-navigation-block__contents-container">
<BlockNavigationBlockContents
block={ block }
onClick={ () => onClick( block.clientId ) }
onClick={ onClick }
onToggleExpanded={ onToggleExpanded }
isSelected={ isSelected }
position={ position }
siblingBlockCount={ siblingBlockCount }
Expand Down
28 changes: 26 additions & 2 deletions packages/block-editor/src/components/block-navigation/branch.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { Fragment } from '@wordpress/element';
import BlockNavigationBlock from './block';
import BlockNavigationAppender from './appender';
import { isClientIdSelected } from './utils';
import { useBlockNavigationContext } from './context';

export default function BlockNavigationBranch( props ) {
const {
blocks,
Expand Down Expand Up @@ -42,6 +44,8 @@ export default function BlockNavigationBranch( props ) {
const rowCount = hasAppender ? blockCount + 1 : blockCount;
const appenderPosition = rowCount;

const { expandedState, expand, collapse } = useBlockNavigationContext();

return (
<>
{ map( filteredBlocks, ( block, index ) => {
Expand Down Expand Up @@ -71,11 +75,30 @@ export default function BlockNavigationBranch( props ) {
const isLastOfSelectedBranch =
isLastOfBranch && ! hasNestedBranch && isLastBlock;

const isExpanded = hasNestedBranch
? expandedState[ clientId ] ?? true
: undefined;

const selectBlockWithClientId = ( event ) => {
event.stopPropagation();
selectBlock( clientId );
};

const toggleExpanded = ( event ) => {
event.stopPropagation();
if ( isExpanded === true ) {
collapse( clientId );
} else if ( isExpanded === false ) {
expand( clientId );
}
};

return (
<Fragment key={ clientId }>
<BlockNavigationBlock
block={ block }
onClick={ selectBlock }
onClick={ selectBlockWithClientId }
onToggleExpanded={ toggleExpanded }
isSelected={ isSelected }
isBranchSelected={ isSelectedBranch }
isLastOfSelectedBranch={ isLastOfSelectedBranch }
Expand All @@ -86,8 +109,9 @@ export default function BlockNavigationBranch( props ) {
showBlockMovers={ showBlockMovers }
terminatedLevels={ terminatedLevels }
path={ updatedPath }
isExpanded={ isExpanded }
/>
{ hasNestedBranch && (
{ hasNestedBranch && isExpanded && (
<BlockNavigationBranch
blocks={ innerBlocks }
selectedBlockClientIds={
Expand Down
24 changes: 24 additions & 0 deletions packages/block-editor/src/components/block-navigation/expander.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* WordPress dependencies
*/
import { chevronRightSmall, Icon } from '@wordpress/icons';
export default function BlockNavigationExpander( { onClick } ) {
return (
// Keyboard events are handled by TreeGrid see: components/src/tree-grid/index.js
//
// The expander component is implemented as a pseudo element in the w3 example
// https://www.w3.org/TR/wai-aria-practices/examples/treegrid/treegrid-1.html
//
// We've mimicked this by adding an icon with aria-hidden set to true to hide this from the accessibility tree.
// For the current tree grid implementation, please do not try to make this a button.
//
// eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions
<span
className="block-editor-block-navigation__expander"
onClick={ ( event ) => onClick( event, { forceToggle: true } ) }
aria-hidden="true"
>
<Icon icon={ chevronRightSmall } />
</span>
);
}
60 changes: 52 additions & 8 deletions packages/block-editor/src/components/block-navigation/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@
&.is-branch-selected.is-selected .block-editor-block-navigation-block-contents {
border-radius: 2px 2px 0 0;
}

&[aria-expanded="false"] {
&.is-branch-selected.is-selected .block-editor-block-navigation-block-contents {
border-radius: 2px;
}
}
&.is-branch-selected:not(.is-selected) .block-editor-block-navigation-block-contents {
// Lighten a CSS variable without introducing a new SASS variable
background:
Expand All @@ -60,7 +66,7 @@
align-items: center;
width: 100%;
height: auto;
padding: ($grid-unit-15 / 2) $grid-unit-15;
padding: ($grid-unit-15 / 2) $grid-unit-15 ($grid-unit-15 / 2) 0;
text-align: left;
color: $gray-900;
border-radius: 2px;
Expand Down Expand Up @@ -121,8 +127,8 @@

.block-editor-block-icon {
align-self: flex-start;
margin-right: ( $grid-unit-05 * 2.5 ); // 10px is off the 4px grid, but required for visual alignment between block name and subsequent nested icon
width: 20px;
margin-right: $grid-unit-10;
width: $icon-size;
}

.block-editor-block-navigation-block__menu-cell,
Expand Down Expand Up @@ -274,15 +280,53 @@
}
}

// Chevron container metrics.
.block-editor-block-navigation__expander {
height: $icon-size;
margin-left: $grid-unit-05;
width: $icon-size;
}

// First level of indentation is aria-level 2, max indent is 8.
// Indent is a full icon size, plus 4px which optically aligns child icons to the text label above.
$block-navigation-max-indent: 8;
.block-editor-block-navigation-leaf[aria-level] .block-editor-block-icon {
margin-left: ( $grid-unit-30 + $grid-unit-05 ) * $block-navigation-max-indent;
.block-editor-block-navigation-leaf[aria-level] .block-editor-block-navigation__expander {
margin-left: ( $icon-size ) * $block-navigation-max-indent + 4 * ( $block-navigation-max-indent - 1 );
}

.block-editor-block-navigation-leaf:not([aria-level="1"]) {
.block-editor-block-navigation__expander {
margin-right: 4px;
}
}
@for $i from 0 through $block-navigation-max-indent {
.block-editor-block-navigation-leaf[aria-level="#{ $i + 1 }"] .block-editor-block-icon {
margin-left: ( $grid-unit-30 + $grid-unit-05 ) * $i;

@for $i from 0 to $block-navigation-max-indent {
.block-editor-block-navigation-leaf[aria-level="#{ $i + 1 }"] .block-editor-block-navigation__expander {
@if $i - 1 >= 0 {
margin-left: ( $icon-size * $i ) + 4 * ($i - 1);
}
@else {
margin-left: ( $icon-size * $i );
}
}
}

.block-editor-block-navigation-leaf .block-editor-block-navigation__expander {
visibility: hidden;
}

// Point downwards when open.
.block-editor-block-navigation-leaf[aria-expanded="true"] .block-editor-block-navigation__expander svg {
visibility: visible;
transition: transform 0.2s ease;
transform: rotate(90deg);
@include reduce-motion("transition");
}

// Point rightwards when closed
.block-editor-block-navigation-leaf[aria-expanded="false"] .block-editor-block-navigation__expander svg {
visibility: visible;
transform: rotate(0deg);
transition: transform 0.2s ease;
@include reduce-motion("transition");
}
46 changes: 45 additions & 1 deletion packages/block-editor/src/components/block-navigation/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@

import { __experimentalTreeGrid as TreeGrid } from '@wordpress/components';
import { useDispatch } from '@wordpress/data';
import { useCallback, useEffect, useMemo, useRef } from '@wordpress/element';
import {
useCallback,
useEffect,
useMemo,
useRef,
useReducer,
} from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
Expand All @@ -17,6 +23,16 @@ import useBlockNavigationDropZone from './use-block-navigation-drop-zone';
import { store as blockEditorStore } from '../../store';

const noop = () => {};
const expanded = ( state, action ) => {
switch ( action.type ) {
case 'expand':
return { ...state, ...{ [ action.clientId ]: true } };
case 'collapse':
return { ...state, ...{ [ action.clientId ]: false } };
default:
return state;
}
};

/**
* Wrap `BlockNavigationRows` with `TreeGrid`. BlockNavigationRows is a
Expand Down Expand Up @@ -52,6 +68,7 @@ export default function BlockNavigationTree( {
},
[ selectBlock, onSelect ]
);
const [ expandedState, setExpandedState ] = useReducer( expanded, {} );

let {
ref: treeGridRef,
Expand All @@ -67,18 +84,43 @@ export default function BlockNavigationTree( {
blockDropTarget = undefined;
}

const expand = ( clientId ) => {
if ( ! clientId ) {
return;
}
setExpandedState( { type: 'expand', clientId } );
};
const collapse = ( clientId ) => {
if ( ! clientId ) {
return;
}
setExpandedState( { type: 'collapse', clientId } );
};
const expandRow = ( row ) => {
expand( row?.dataset?.block );
};
const collapseRow = ( row ) => {
collapse( row?.dataset?.block );
};

const contextValue = useMemo(
() => ( {
__experimentalFeatures,
__experimentalPersistentListViewFeatures,
blockDropTarget,
isTreeGridMounted: isMounted.current,
expandedState,
expand,
collapse,
} ),
[
__experimentalFeatures,
__experimentalPersistentListViewFeatures,
blockDropTarget,
isMounted.current,
expandedState,
expand,
collapse,
]
);

Expand All @@ -87,6 +129,8 @@ export default function BlockNavigationTree( {
className="block-editor-block-navigation-tree"
aria-label={ __( 'Block navigation structure' ) }
ref={ treeGridRef }
onCollapseRow={ collapseRow }
onExpandRow={ expandRow }
>
<BlockNavigationContext.Provider value={ contextValue }>
<BlockNavigationBranch
Expand Down
Loading

0 comments on commit dbba260

Please sign in to comment.