Skip to content

Commit

Permalink
Site Editor: Make sidebar back button go *back* instead of *up* if po…
Browse files Browse the repository at this point in the history
…ssible (#52456)

* Navigator: Add replace option to goTo() and goToParent()

* Site Editor: Make sidebar back button go back instead of up if possible
  • Loading branch information
noisysocks authored Jul 13, 2023
1 parent 9d92d61 commit f1caa12
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 96 deletions.
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Enhancements

- `TextControl`: Add `id` prop to allow for custom IDs in `TextControl`s ([#52028](https://github.com/WordPress/gutenberg/pull/52028)).
- `Navigator`: Add `replace` option to `navigator.goTo()` and `navigator.goToParent()` ([#52456](https://github.com/WordPress/gutenberg/pull/52456)).

### Bug Fix

Expand Down
53 changes: 30 additions & 23 deletions packages/components/src/navigator/navigator-provider/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ function UnconnectedNavigatorProvider(
focusTargetSelector,
isBack = false,
skipFocus = false,
replace = false,
...restOptions
} = options;

Expand All @@ -172,34 +173,38 @@ function UnconnectedNavigatorProvider(
skipFocus,
};

if ( prevLocationHistory.length < 1 ) {
return [ newLocation ];
if ( prevLocationHistory.length === 0 ) {
return replace ? [] : [ newLocation ];
}

return [
...prevLocationHistory.slice(
prevLocationHistory.length > MAX_HISTORY_LENGTH - 1
? 1
: 0,
-1
),
// Assign `focusTargetSelector` to the previous location in history
// (the one we just navigated from).
{
...prevLocationHistory[
prevLocationHistory.length - 1
],
focusTargetSelector,
},
newLocation,
];
const newLocationHistory = prevLocationHistory.slice(
prevLocationHistory.length > MAX_HISTORY_LENGTH - 1 ? 1 : 0,
-1
);

if ( ! replace ) {
newLocationHistory.push(
// Assign `focusTargetSelector` to the previous location in history
// (the one we just navigated from).
{
...prevLocationHistory[
prevLocationHistory.length - 1
],
focusTargetSelector,
}
);
}

newLocationHistory.push( newLocation );

return newLocationHistory;
} );
},
[ goBack ]
);

const goToParent: NavigatorContextType[ 'goToParent' ] =
useCallback( () => {
const goToParent: NavigatorContextType[ 'goToParent' ] = useCallback(
( options = {} ) => {
const currentPath =
currentLocationHistory.current[
currentLocationHistory.current.length - 1
Expand All @@ -214,8 +219,10 @@ function UnconnectedNavigatorProvider(
if ( parentPath === undefined ) {
return;
}
goTo( parentPath, { isBack: true } );
}, [ goTo ] );
goTo( parentPath, { ...options, isBack: true } );
},
[ goTo ]
);

const navigatorContextValue: NavigatorContextType = useMemo(
() => ( {
Expand Down
5 changes: 4 additions & 1 deletion packages/components/src/navigator/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ export type NavigateOptions = {
focusTargetSelector?: string;
isBack?: boolean;
skipFocus?: boolean;
replace?: boolean;
};

export type NavigateToParentOptions = Omit< NavigateOptions, 'isBack' >;

export type NavigatorLocation = NavigateOptions & {
isInitial?: boolean;
path?: string;
Expand All @@ -28,7 +31,7 @@ export type Navigator = {
params: MatchParams;
goTo: ( path: string, options?: NavigateOptions ) => void;
goBack: () => void;
goToParent: () => void;
goToParent: ( options?: NavigateToParentOptions ) => void;
};

export type NavigatorContext = Navigator & {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import {
__experimentalHStack as HStack,
__experimentalHeading as Heading,
__experimentalNavigatorToParentButton as NavigatorToParentButton,
__experimentalUseNavigator as useNavigator,
__experimentalVStack as VStack,
} from '@wordpress/components';
Expand Down Expand Up @@ -41,7 +40,7 @@ export default function SidebarNavigationScreen( {
};
}, [] );
const { getTheme } = useSelect( coreStore );
const { goTo } = useNavigator();
const navigator = useNavigator();
const theme = getTheme( currentlyPreviewingTheme() );
const icon = isRTL() ? chevronRight : chevronLeft;

Expand All @@ -58,16 +57,24 @@ export default function SidebarNavigationScreen( {
className="edit-site-sidebar-navigation-screen__title-icon"
>
{ ! isRoot && ! backPath && (
<NavigatorToParentButton
as={ SidebarButton }
icon={ isRTL() ? chevronRight : chevronLeft }
<SidebarButton
onClick={ () => {
if ( navigator.location.isInitial ) {
navigator.goToParent( { replace: true } );
} else {
navigator.goBack();
}
} }
icon={ icon }
label={ __( 'Back' ) }
showTooltip={ false }
/>
) }
{ ! isRoot && backPath && (
<SidebarButton
onClick={ () => goTo( backPath, { isBack: true } ) }
onClick={ () =>
navigator.goTo( backPath, { isBack: true } )
}
icon={ icon }
label={ __( 'Back' ) }
showTooltip={ false }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ export function getPathFromURL( urlParams ) {
return path;
}

function isSubset( subset, superset ) {
return Object.entries( subset ).every( ( [ key, value ] ) => {
return superset[ key ] === value;
} );
}

export default function useSyncPathWithURL() {
const history = useHistory();
const { params: urlParams } = useLocation();
Expand All @@ -44,76 +50,77 @@ export default function useSyncPathWithURL() {
params: navigatorParams,
goTo,
} = useNavigator();
const currentUrlParams = useRef( urlParams );
const currentPath = useRef( navigatorLocation.path );
const isMounting = useRef( true );

useEffect( () => {
// The navigatorParams are only initially filled properly when the
// navigator screens mount. so we ignore the first synchronisation.
if ( isMounting.current ) {
isMounting.current = false;
return;
}

function updateUrlParams( newUrlParams ) {
if (
Object.entries( newUrlParams ).every( ( [ key, value ] ) => {
return currentUrlParams.current[ key ] === value;
} )
) {
useEffect(
() => {
// The navigatorParams are only initially filled properly when the
// navigator screens mount. so we ignore the first synchronisation.
if ( isMounting.current ) {
isMounting.current = false;
return;
}
const updatedParams = {
...currentUrlParams.current,
...newUrlParams,
};
currentUrlParams.current = updatedParams;
history.push( updatedParams );
}

if ( navigatorParams?.postType && navigatorParams?.postId ) {
updateUrlParams( {
postType: navigatorParams?.postType,
postId: navigatorParams?.postId,
path: undefined,
} );
} else if (
navigatorLocation.path.startsWith( '/page/' ) &&
navigatorParams?.postId
) {
updateUrlParams( {
postType: 'page',
postId: navigatorParams?.postId,
path: undefined,
} );
} else if ( navigatorLocation.path === '/patterns' ) {
updateUrlParams( {
postType: undefined,
postId: undefined,
canvas: undefined,
path: navigatorLocation.path,
} );
} else {
updateUrlParams( {
postType: undefined,
postId: undefined,
categoryType: undefined,
categoryId: undefined,
path:
navigatorLocation.path === '/'
? undefined
: navigatorLocation.path,
} );
}
}, [ navigatorLocation?.path, navigatorParams, history ] );
function updateUrlParams( newUrlParams ) {
if ( isSubset( newUrlParams, urlParams ) ) {
return;
}
const updatedParams = {
...urlParams,
...newUrlParams,
};
history.push( updatedParams );
}

useEffect( () => {
currentUrlParams.current = urlParams;
const path = getPathFromURL( urlParams );
if ( currentPath.current !== path ) {
currentPath.current = path;
goTo( path );
}
}, [ urlParams, goTo ] );
if ( navigatorParams?.postType && navigatorParams?.postId ) {
updateUrlParams( {
postType: navigatorParams?.postType,
postId: navigatorParams?.postId,
path: undefined,
} );
} else if (
navigatorLocation.path.startsWith( '/page/' ) &&
navigatorParams?.postId
) {
updateUrlParams( {
postType: 'page',
postId: navigatorParams?.postId,
path: undefined,
} );
} else if ( navigatorLocation.path === '/patterns' ) {
updateUrlParams( {
postType: undefined,
postId: undefined,
canvas: undefined,
path: navigatorLocation.path,
} );
} else {
updateUrlParams( {
postType: undefined,
postId: undefined,
categoryType: undefined,
categoryId: undefined,
path:
navigatorLocation.path === '/'
? undefined
: navigatorLocation.path,
} );
}
},
// Trigger only when navigator changes to prevent infinite loops.
// eslint-disable-next-line react-hooks/exhaustive-deps
[ navigatorLocation?.path, navigatorParams ]
);

useEffect(
() => {
const path = getPathFromURL( urlParams );
if ( navigatorLocation.path !== path ) {
goTo( path );
}
},
// Trigger only when URL changes to prevent infinite loops.
// eslint-disable-next-line react-hooks/exhaustive-deps
[ urlParams ]
);
}

1 comment on commit f1caa12

@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 f1caa12.
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/5539912616
📝 Reported issues:

Please sign in to comment.