[Settings] Implement URL routing with query parameters and active sidebar item (#52721)

* set up SidebarNavigationScreen

* install deps

* GlobalStylesProvider

* Add labels to pages/sections

* get types in order

* get SettingItem working

* tidy up

* pass icon from PHP

* add icons to each Page

* layout styles

* edit site styles

* organize

* edit-site to 6.10

* unit tests

* lint

* lint

* Add changefile(s) from automation for the following project(s): @woocommerce/settings-editor, woocommerce, woocommerce/client/admin

* lint

* better linting

* [Settings] Create a route file to handle populating Settings (#52692)

* Add settings route

* Add changelog

* Add TODO comment

* Add changelog

* Rebase on sidebar changes

* Fix types and tests

* Fix path

* Convert to tab

* Update test

* Update sidebar to reflect the route with the active item and linking

* Add changelog

* Add changelog

* fix merge conflict errors

* Revert pnpm-lock.yaml

* [Settings] Update Modern Routes (#52757)

* update Sidebar

* cleanup

* Add changefile(s) from automation for the following project(s): @woocommerce/settings-editor

* export sidebar

* export route methods

* Revert "export route methods"

This reverts commit 46a6cd1e0a.

* handle modern pages

* Add changefile(s) from automation for the following project(s): @woocommerce/settings-editor, woocommerce

* cleanup

* remove export

* update tests

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Chi-Hsuan Huang <chihsuan.tw@gmail.com>

---------

Co-authored-by: paul sealock <psealock@gmail.com>
Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Chi-Hsuan Huang 2024-11-13 16:23:30 +08:00 committed by GitHub
parent a518c49517
commit 295a2d83e2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 130 additions and 61 deletions

View File

@ -0,0 +1,4 @@
Significance: patch
Type: tweak
Comment: Organizes unreleased component

View File

@ -0,0 +1,4 @@
Significance: patch
Type: update
Update sidebar to reflect the route with the active item and linking

View File

@ -16,8 +16,6 @@ import {
} from '@wordpress/hooks';
/* eslint-disable @woocommerce/dependency-group */
// @ts-ignore No types for this exist yet.
import SidebarNavigationScreen from '@wordpress/edit-site/build-module/components/sidebar-navigation-screen';
// @ts-ignore No types for this exist yet.
import { privateApis as routerPrivateApis } from '@wordpress/router';
// @ts-ignore No types for this exist yet.
import { unlock } from '@wordpress/edit-site/build-module/lock-unlock';
@ -32,21 +30,29 @@ import { Route, Location } from './types';
const { useLocation } = unlock( routerPrivateApis );
const NotFound = () => {
return (
<h1 style={ { color: 'white' } }>
{ __( 'Page not found', 'woocommerce' ) }
</h1>
);
return <h1>{ __( 'Page not found', 'woocommerce' ) }</h1>;
};
/**
* Default route when a page is not found.
* Default route when active page is not found.
*
* @param {string} activePage - The active page.
* @param {typeof window.wcSettings.admin.settingsPages} pages - The pages.
*
*/
const getNotFoundRoute = ( page: string ): Route => ( {
key: page,
const getNotFoundRoute = (
activePage: string,
pages: typeof window.wcSettings.admin.settingsPages
): Route => ( {
key: activePage,
areas: {
sidebar: null,
sidebar: (
<Sidebar
activePage={ activePage }
pages={ pages }
pageTitle={ 'Settings' }
/>
),
content: <NotFound />,
edit: null,
},
@ -59,29 +65,35 @@ const getNotFoundRoute = ( page: string ): Route => ( {
/**
* Creates a route configuration for legacy settings pages.
*
* @param {string} page - The page identifier.
* @param {string} activePage - The active page.
* @param {typeof window.wcSettings.admin.settingsPages} pages - The pages.
*/
const getLegacyRoute = (
page: string,
activePage: string,
pages: typeof window.wcSettings.admin.settingsPages
): Route => ( {
key: page,
areas: {
sidebar: (
<SidebarNavigationScreen
title={ 'Settings Title TBD' }
isRoot
content={ <Sidebar pages={ pages } /> }
/>
),
content: <div>Content Placeholder: current tab: { page }</div>,
edit: null,
},
widths: {
content: undefined,
edit: undefined,
},
} );
): Route => {
const pageTitle =
pages[ activePage ]?.label || __( 'Settings', 'woocommerce' );
return {
key: activePage,
areas: {
sidebar: (
<Sidebar
activePage={ activePage }
pages={ pages }
pageTitle={ pageTitle }
/>
),
content: <div>Content Placeholder</div>,
edit: null,
},
widths: {
content: undefined,
edit: undefined,
},
};
};
const PAGES_FILTER = 'woocommerce_admin_settings_pages';
@ -139,19 +151,36 @@ export const useActiveRoute = () => {
const modernRoutes = useModernRoutes();
return useMemo( () => {
const { tab: page = 'general' } = location.params;
const pageSettings = settingsPages?.[ page ];
const { tab: activePage = 'general' } = location.params;
const pageSettings = settingsPages?.[ activePage ];
if ( ! pageSettings ) {
return getNotFoundRoute( page );
return getNotFoundRoute( activePage, settingsPages );
}
// Handle legacy pages.
if ( ! pageSettings.is_modern ) {
return getLegacyRoute( page, settingsPages );
return getLegacyRoute( activePage, settingsPages );
}
const modernRoute = modernRoutes[ activePage ];
// Handle modern pages.
return modernRoutes[ page ] || getNotFoundRoute( page );
}, [ settingsPages, location, modernRoutes ] );
if ( ! modernRoute ) {
return getNotFoundRoute( activePage, settingsPages );
}
// Sidebar is responsibility of WooCommerce, not extensions so add it here.
modernRoute.areas.sidebar = (
<Sidebar
activePage={ activePage }
pages={ settingsPages }
pageTitle={ pageSettings.label }
/>
);
// Make sure we have a key.
modernRoute.key = activePage;
return modernRoute;
}, [ settingsPages, location.params, modernRoutes ] );
};

View File

@ -6,6 +6,8 @@ import { createElement } from '@wordpress/element';
// @ts-expect-error missing type.
// eslint-disable-next-line @wordpress/no-unsafe-wp-apis
import { __experimentalItemGroup as ItemGroup } from '@wordpress/components';
// @ts-ignore No types for this exist yet.
import SidebarNavigationScreen from '@wordpress/edit-site/build-module/components/sidebar-navigation-screen';
import * as IconPackage from '@wordpress/icons';
/* eslint-enable @woocommerce/dependency-group */
@ -16,9 +18,11 @@ import { SettingItem } from './setting-item';
const { Icon, ...icons } = IconPackage;
export const Sidebar = ( {
const SidebarNavigationScreenContent = ( {
activePage,
pages,
}: {
activePage: string;
pages: typeof window.wcSettings.admin.settingsPages;
} ) => {
return (
@ -30,7 +34,7 @@ export const Sidebar = ( {
key={ slug }
slug={ slug }
label={ label }
isActive={ false }
isActive={ activePage === slug }
icon={
<Icon
icon={
@ -45,3 +49,26 @@ export const Sidebar = ( {
</ItemGroup>
);
};
export const Sidebar = ( {
activePage,
pages,
pageTitle,
}: {
activePage: string;
pages: typeof window.wcSettings.admin.settingsPages;
pageTitle: string;
} ) => {
return (
<SidebarNavigationScreen
title={ pageTitle }
isRoot
content={
<SidebarNavigationScreenContent
activePage={ activePage }
pages={ pages }
/>
}
/>
);
};

View File

@ -63,11 +63,12 @@ export function SettingItem( {
params: { postType, page },
} = useLocation();
const linkInfo = useLink( {
const { href, onClick } = useLink( {
page,
postType,
slug,
tab: slug,
} );
return (
<HStack
justify="flex-start"
@ -77,7 +78,8 @@ export function SettingItem( {
>
<SidebarNavigationItem
icon={ icon }
{ ...linkInfo }
href={ href }
onClick={ onClick }
aria-current={ isActive ? 'true' : undefined }
>
{ label }

View File

@ -33,19 +33,11 @@ jest.mock( '@wordpress/edit-site/build-module/lock-unlock', () => ( {
unlock: jest.fn( ( apis ) => apis ),
} ) );
jest.mock(
'@wordpress/edit-site/build-module/components/sidebar-navigation-screen',
() => ( {
__esModule: true,
default: ( { children }: { children: React.ReactNode } ) => (
<div data-testid="sidebar-navigation-screen">{ children }</div>
),
} )
);
jest.mock( '../sidebar', () => ( {
__esModule: true,
Sidebar: jest.fn(),
Sidebar: ( { children }: { children: React.ReactNode } ) => (
<div data-testid="sidebar-navigation-screen">{ children }</div>
),
} ) );
const mockSettingsPages = {
@ -141,11 +133,11 @@ describe( 'route.tsx', () => {
admin: {
settingsPages: {
modern: {
is_modern: true,
label: 'Modern',
icon: 'published',
slug: 'modern',
icon: 'settings',
sections: [],
is_modern: true,
},
},
},
@ -153,9 +145,7 @@ describe( 'route.tsx', () => {
( applyFilters as jest.Mock ).mockReturnValue( {
modern: {
key: 'modern',
areas: {
sidebar: null,
content: <div>Modern Page</div>,
},
},
@ -163,6 +153,7 @@ describe( 'route.tsx', () => {
const { result } = renderHook( () => useActiveRoute() );
expect( result.current.key ).toBe( 'modern' );
expect( result.current.areas.sidebar ).toBeDefined();
} );
} );

View File

@ -0,0 +1,4 @@
Significance: patch
Type: tweak
Comment: Organizes unreleased component

View File

@ -38,6 +38,13 @@ if ( ! class_exists( 'WC_Settings_Page', false ) ) :
*/
protected $label = '';
/**
* Setting page is modern.
*
* @var bool
*/
protected $is_modern = false;
/**
* Constructor.
*/
@ -130,10 +137,11 @@ if ( ! class_exists( 'WC_Settings_Page', false ) ) :
}
$pages[ $this->id ] = array(
'label' => html_entity_decode( $this->label ),
'slug' => $this->id,
'icon' => $this->icon,
'sections' => $sections_data,
'label' => html_entity_decode( $this->label ),
'slug' => $this->id,
'icon' => $this->icon,
'sections' => $sections_data,
'is_modern' => $this->is_modern,
);
return $pages;