From b124f87048b906e9105b1ff29ded0af4de57e146 Mon Sep 17 00:00:00 2001
From: Robert Anderson <robert@noisysocks.com>
Date: Thu, 8 Dec 2022 15:28:25 +1100
Subject: [PATCH 1/2] Style Book: Clear Navigator history when selecting a
 block

---
 .../navigator-provider/component.tsx          | 23 +++++++++-------
 .../src/components/global-styles/palette.js   |  2 +-
 .../global-styles/screen-block-list.js        |  2 +-
 .../components/global-styles/screen-block.js  |  5 +++-
 .../components/global-styles/screen-colors.js |  3 ++-
 .../src/components/global-styles/ui.js        | 26 ++++++++++++++-----
 test/e2e/specs/site-editor/style-book.spec.js | 18 +++++++++++++
 7 files changed, 59 insertions(+), 20 deletions(-)

diff --git a/packages/components/src/navigator/navigator-provider/component.tsx b/packages/components/src/navigator/navigator-provider/component.tsx
index 57b18f6d92dba0..e80beaec9d14c4 100644
--- a/packages/components/src/navigator/navigator-provider/component.tsx
+++ b/packages/components/src/navigator/navigator-provider/component.tsx
@@ -43,8 +43,8 @@ function UnconnectedNavigatorProvider(
 
 	const goTo: NavigatorContextType[ 'goTo' ] = useCallback(
 		( path, options = {} ) => {
-			setLocationHistory( [
-				...locationHistory,
+			setLocationHistory( ( prevLocationHistory ) => [
+				...prevLocationHistory,
 				{
 					...options,
 					path,
@@ -53,21 +53,24 @@ function UnconnectedNavigatorProvider(
 				},
 			] );
 		},
-		[ locationHistory ]
+		[]
 	);
 
 	const goBack: NavigatorContextType[ 'goBack' ] = useCallback( () => {
-		if ( locationHistory.length > 1 ) {
-			setLocationHistory( [
-				...locationHistory.slice( 0, -2 ),
+		setLocationHistory( ( prevLocationHistory ) => {
+			if ( prevLocationHistory.length <= 1 ) {
+				return prevLocationHistory;
+			}
+			return [
+				...prevLocationHistory.slice( 0, -2 ),
 				{
-					...locationHistory[ locationHistory.length - 2 ],
+					...prevLocationHistory[ prevLocationHistory.length - 2 ],
 					isBack: true,
 					hasRestoredFocus: false,
 				},
-			] );
-		}
-	}, [ locationHistory ] );
+			];
+		} );
+	}, [] );
 
 	const navigatorContextValue: NavigatorContextType = useMemo(
 		() => ( {
diff --git a/packages/edit-site/src/components/global-styles/palette.js b/packages/edit-site/src/components/global-styles/palette.js
index 4d37b234eb9afe..0b4bbbbbb7f63e 100644
--- a/packages/edit-site/src/components/global-styles/palette.js
+++ b/packages/edit-site/src/components/global-styles/palette.js
@@ -49,7 +49,7 @@ function Palette( { name } ) {
 
 	const screenPath = ! name
 		? '/colors/palette'
-		: '/blocks/' + name + '/colors/palette';
+		: '/blocks/' + encodeURIComponent( name ) + '/colors/palette';
 	const paletteButtonText =
 		colors.length > 0
 			? sprintf(
diff --git a/packages/edit-site/src/components/global-styles/screen-block-list.js b/packages/edit-site/src/components/global-styles/screen-block-list.js
index a203a99ba276ab..30c03c17bec014 100644
--- a/packages/edit-site/src/components/global-styles/screen-block-list.js
+++ b/packages/edit-site/src/components/global-styles/screen-block-list.js
@@ -68,7 +68,7 @@ function BlockMenuItem( { block } ) {
 
 	return (
 		<NavigationButtonAsItem
-			path={ '/blocks/' + block.name }
+			path={ '/blocks/' + encodeURIComponent( block.name ) }
 			aria-label={ navigationButtonLabel }
 		>
 			<HStack justify="flex-start">
diff --git a/packages/edit-site/src/components/global-styles/screen-block.js b/packages/edit-site/src/components/global-styles/screen-block.js
index b9d2502ef8bc10..57860a8623c6ae 100644
--- a/packages/edit-site/src/components/global-styles/screen-block.js
+++ b/packages/edit-site/src/components/global-styles/screen-block.js
@@ -20,7 +20,10 @@ function ScreenBlock( { name } ) {
 			<Spacer paddingX={ 4 }>
 				<BlockPreviewPanel name={ name } />
 			</Spacer>
-			<ContextMenu parentMenu={ '/blocks/' + name } name={ name } />
+			<ContextMenu
+				parentMenu={ '/blocks/' + encodeURIComponent( name ) }
+				name={ name }
+			/>
 		</>
 	);
 }
diff --git a/packages/edit-site/src/components/global-styles/screen-colors.js b/packages/edit-site/src/components/global-styles/screen-colors.js
index 87d74604e63e14..c713cb5ee5818f 100644
--- a/packages/edit-site/src/components/global-styles/screen-colors.js
+++ b/packages/edit-site/src/components/global-styles/screen-colors.js
@@ -174,7 +174,8 @@ function ButtonColorItem( { name, parentMenu } ) {
 }
 
 function ScreenColors( { name } ) {
-	const parentMenu = name === undefined ? '' : '/blocks/' + name;
+	const parentMenu =
+		name === undefined ? '' : '/blocks/' + encodeURIComponent( name );
 
 	return (
 		<>
diff --git a/packages/edit-site/src/components/global-styles/ui.js b/packages/edit-site/src/components/global-styles/ui.js
index 8f7fa28e93eee6..532a211597a81f 100644
--- a/packages/edit-site/src/components/global-styles/ui.js
+++ b/packages/edit-site/src/components/global-styles/ui.js
@@ -43,7 +43,8 @@ function GlobalStylesNavigationScreen( { className, ...props } ) {
 }
 
 function ContextScreens( { name } ) {
-	const parentMenu = name === undefined ? '' : '/blocks/' + name;
+	const parentMenu =
+		name === undefined ? '' : '/blocks/' + encodeURIComponent( name );
 
 	return (
 		<>
@@ -124,14 +125,27 @@ function ContextScreens( { name } ) {
 
 function GlobalStylesStyleBook( { onClose } ) {
 	const navigator = useNavigator();
+	const { path } = navigator.location;
 	return (
 		<StyleBook
 			isSelected={ ( blockName ) =>
-				navigator.location.path.startsWith( '/blocks/' + blockName )
-			}
-			onSelect={ ( blockName ) =>
-				navigator.goTo( '/blocks/' + blockName )
+				// Match '/blocks/core%2Fbutton' and
+				// '/blocks/core%2Fbutton/typography', but not
+				// '/blocks/core%2Fbuttons'.
+				path === `/blocks/${ encodeURIComponent( blockName ) }` ||
+				path.startsWith(
+					`/blocks/${ encodeURIComponent( blockName ) }/`
+				)
 			}
+			onSelect={ ( blockName ) => {
+				// Clear navigator history by going back to the root.
+				const depth = path.match( /\//g ).length;
+				for ( let i = 0; i < depth; i++ ) {
+					navigator.goBack();
+				}
+				// Now go to the selected block.
+				navigator.goTo( '/blocks/' + encodeURIComponent( blockName ) );
+			} }
 			onClose={ onClose }
 		/>
 	);
@@ -159,7 +173,7 @@ function GlobalStylesUI( { isStyleBookOpened, onCloseStyleBook } ) {
 			{ blocks.map( ( block ) => (
 				<GlobalStylesNavigationScreen
 					key={ 'menu-block-' + block.name }
-					path={ '/blocks/' + block.name }
+					path={ '/blocks/' + encodeURIComponent( block.name ) }
 				>
 					<ScreenBlock name={ block.name } />
 				</GlobalStylesNavigationScreen>
diff --git a/test/e2e/specs/site-editor/style-book.spec.js b/test/e2e/specs/site-editor/style-book.spec.js
index 779e54322a9102..4f3d092a5c8d93 100644
--- a/test/e2e/specs/site-editor/style-book.spec.js
+++ b/test/e2e/specs/site-editor/style-book.spec.js
@@ -98,6 +98,24 @@ test.describe( 'Style Book', () => {
 		).toBeVisible();
 	} );
 
+	test( 'should clear Global Styles navigator history when example is clicked', async ( {
+		page,
+	} ) => {
+		await page.click( 'role=button[name="Blocks styles"]' );
+		await page.click( 'role=button[name="Heading block styles"]' );
+		await page.click( 'role=button[name="Typography styles"]' );
+
+		await page.click(
+			'role=button[name="Open Quote styles in Styles panel"i]'
+		);
+
+		await page.click( 'role=button[name="Navigate to the previous view"]' );
+
+		await expect(
+			page.locator( 'role=button[name="Navigate to the previous view"]' )
+		).not.toBeVisible();
+	} );
+
 	test( 'should disappear when closed', async ( { page } ) => {
 		await page.click(
 			'role=region[name="Style Book"i] >> role=button[name="Close Style Book"i]'

From 5e4129b20ffd6689494588891c1b4246fe5004a0 Mon Sep 17 00:00:00 2001
From: Robert Anderson <robert@noisysocks.com>
Date: Thu, 8 Dec 2022 16:53:47 +1100
Subject: [PATCH 2/2] Update CHANGELOG.md

---
 packages/components/CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index 6d8383e749ab1c..c3d9966a68a085 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -15,6 +15,7 @@
 
 -   `ColorPalette`: show "Clear" button even when colors array is empty ([#46001](https://github.com/WordPress/gutenberg/pull/46001)).
 -   `InputControl`: Fix internal `Flex` wrapper usage that could add an unintended `height: 100%` ([#46213](https://github.com/WordPress/gutenberg/pull/46213)).
+-   `Navigator`: Allow calling `goTo` and `goBack` twice in one render cycle ([#46391](https://github.com/WordPress/gutenberg/pull/46391)).
 
 ### Enhancements