Skip to content

Commit

Permalink
Trigger sidebar animations only on cross-route navigations (#61402)
Browse files Browse the repository at this point in the history
* Trigger sidebar animations only on cross-route navigations

* Use layout effect when triggering animation

Co-authored-by: jsnajdr <[email protected]>
Co-authored-by: oandregal <[email protected]>
Co-authored-by: t-hamano <[email protected]>
  • Loading branch information
4 people authored May 9, 2024
1 parent bea583e commit 4866c3b
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default function SidebarNavigationItem( {
...props
} ) {
const history = useHistory();
const navigate = useContext( SidebarNavigationContext );
const { navigate } = useContext( SidebarNavigationContext );

// If there is no custom click handler, create one that navigates to `path`.
function handleClick( e ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default function SidebarNavigationScreen( {
);
const location = useLocation();
const history = useHistory();
const navigate = useContext( SidebarNavigationContext );
const { navigate } = useContext( SidebarNavigationContext );
const backPath = backPathProp ?? location.state?.backPath;
const icon = isRTL() ? chevronRight : chevronLeft;

Expand Down
94 changes: 59 additions & 35 deletions packages/edit-site/src/components/sidebar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,62 +7,86 @@ import clsx from 'clsx';
* WordPress dependencies
*/
import {
useCallback,
createContext,
useContext,
useState,
useRef,
useEffect,
useLayoutEffect,
} from '@wordpress/element';
import { focus } from '@wordpress/dom';

export const SidebarNavigationContext = createContext( () => {} );
// Focus a sidebar element after a navigation. The element to focus is either
// specified by `focusSelector` (when navigating back) or it is the first
// tabbable element (usually the "Back" button).
function focusSidebarElement( el, direction, focusSelector ) {
let elementToFocus;
if ( direction === 'back' && focusSelector ) {
elementToFocus = el.querySelector( focusSelector );
}
if ( direction !== null && ! elementToFocus ) {
const [ firstTabbable ] = focus.tabbable.find( el );
elementToFocus = firstTabbable ?? el;
}
elementToFocus?.focus();
}

export default function SidebarContent( { routeKey, children } ) {
const [ navState, setNavState ] = useState( {
// Navigation state that is updated when navigating back or forward. Helps us
// manage the animations and also focus.
function createNavState() {
let state = {
direction: null,
focusSelector: null,
} );
};

const navigate = useCallback( ( direction, focusSelector = null ) => {
setNavState( ( prevState ) => ( {
direction,
focusSelector:
direction === 'forward' && focusSelector
? focusSelector
: prevState.focusSelector,
} ) );
}, [] );
return {
get() {
return state;
},
navigate( direction, focusSelector = null ) {
state = {
direction,
focusSelector:
direction === 'forward' && focusSelector
? focusSelector
: state.focusSelector,
};
},
};
}

function SidebarContentWrapper( { children } ) {
const navState = useContext( SidebarNavigationContext );
const wrapperRef = useRef();
useEffect( () => {
let elementToFocus;
if ( navState.direction === 'back' && navState.focusSelector ) {
elementToFocus = wrapperRef.current.querySelector(
navState.focusSelector
);
}
if ( navState.direction !== null && ! elementToFocus ) {
const [ firstTabbable ] = focus.tabbable.find( wrapperRef.current );
elementToFocus = firstTabbable ?? wrapperRef.current;
}
elementToFocus?.focus();
const [ navAnimation, setNavAnimation ] = useState( null );

useLayoutEffect( () => {
const { direction, focusSelector } = navState.get();
focusSidebarElement( wrapperRef.current, direction, focusSelector );
setNavAnimation( direction );
}, [ navState ] );

const wrapperCls = clsx( 'edit-site-sidebar__screen-wrapper', {
'slide-from-left': navState.direction === 'back',
'slide-from-right': navState.direction === 'forward',
'slide-from-left': navAnimation === 'back',
'slide-from-right': navAnimation === 'forward',
} );

return (
<SidebarNavigationContext.Provider value={ navigate }>
<div ref={ wrapperRef } className={ wrapperCls }>
{ children }
</div>
);
}

export default function SidebarContent( { routeKey, children } ) {
const [ navState ] = useState( createNavState );

return (
<SidebarNavigationContext.Provider value={ navState }>
<div className="edit-site-sidebar__content">
<div
ref={ wrapperRef }
key={ routeKey }
className={ wrapperCls }
>
<SidebarContentWrapper key={ routeKey }>
{ children }
</div>
</SidebarContentWrapper>
</div>
</SidebarNavigationContext.Provider>
);
Expand Down

1 comment on commit 4866c3b

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flaky tests detected in 4866c3b.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/9014036625
📝 Reported issues:

Please sign in to comment.